1
Fork 0

Merge commit '2ca58e7dda' into clippyup

This commit is contained in:
flip1995 2020-07-14 14:59:59 +02:00
parent b57ceb45b0
commit 6f25adbd5a
143 changed files with 4317 additions and 1084 deletions

View file

@ -12,7 +12,7 @@ labels: L-lint
- Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds* - Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
*What benefit of this lint over old code?* *What is the advantage of the recommended code over the original code*
For example: For example:
- Remove bounce checking inserted by ... - Remove bounce checking inserted by ...

View file

@ -240,7 +240,8 @@ jobs:
- 'Geal/nom' - 'Geal/nom'
- 'rust-lang/stdarch' - 'rust-lang/stdarch'
- 'serde-rs/serde' - 'serde-rs/serde'
- 'chronotope/chrono' # FIXME: chrono currently cannot be compiled with `--all-targets`
# - 'chronotope/chrono'
- 'hyperium/hyper' - 'hyperium/hyper'
- 'rust-random/rand' - 'rust-random/rand'
- 'rust-lang/futures-rs' - 'rust-lang/futures-rs'

View file

@ -1352,6 +1352,7 @@ Released 2018-09-13
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
@ -1508,9 +1509,11 @@ Released 2018-09-13
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
@ -1575,6 +1578,7 @@ Released 2018-09-13
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
@ -1586,6 +1590,7 @@ Released 2018-09-13
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
@ -1612,6 +1617,7 @@ Released 2018-09-13
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option

View file

@ -245,7 +245,7 @@ this to work, you will need the fix of `git subtree` available
[here][gitgitgadget-pr]. [here][gitgitgadget-pr].
[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
[subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree [subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
[`rust-lang/rust`]: https://github.com/rust-lang/rust [`rust-lang/rust`]: https://github.com/rust-lang/rust
## Issue and PR triage ## Issue and PR triage

View file

@ -2,8 +2,8 @@
use crate::reexport::Name; use crate::reexport::Name;
use crate::utils::{ use crate::utils::{
first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg, first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help,
span_lint_and_then, without_block_comments, span_lint_and_sugg, span_lint_and_then, without_block_comments,
}; };
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
@ -17,7 +17,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol; use rustc_span::symbol::{Symbol, SymbolStr};
use semver::Version; use semver::Version;
static UNIX_SYSTEMS: &[&str] = &[ static UNIX_SYSTEMS: &[&str] = &[
@ -182,6 +182,29 @@ declare_clippy_lint! {
"unknown_lints for scoped Clippy lints" "unknown_lints for scoped Clippy lints"
} }
declare_clippy_lint! {
/// **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
///
/// **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
/// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
///
/// **Known problems:** None.
///
/// **Example:**
/// Bad:
/// ```rust
/// #![deny(clippy::restriction)]
/// ```
///
/// Good:
/// ```rust
/// #![deny(clippy::as_conversions)]
/// ```
pub BLANKET_CLIPPY_RESTRICTION_LINTS,
style,
"enabling the complete restriction group"
}
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
/// with `#[rustfmt::skip]`. /// with `#[rustfmt::skip]`.
@ -249,15 +272,17 @@ declare_lint_pass!(Attributes => [
DEPRECATED_SEMVER, DEPRECATED_SEMVER,
USELESS_ATTRIBUTE, USELESS_ATTRIBUTE,
UNKNOWN_CLIPPY_LINTS, UNKNOWN_CLIPPY_LINTS,
BLANKET_CLIPPY_RESTRICTION_LINTS,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Attributes { impl<'tcx> LateLintPass<'tcx> for Attributes {
fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
if let Some(items) = &attr.meta_item_list() { if let Some(items) = &attr.meta_item_list() {
if let Some(ident) = attr.ident() { if let Some(ident) = attr.ident() {
match &*ident.as_str() { let ident = &*ident.as_str();
match ident {
"allow" | "warn" | "deny" | "forbid" => { "allow" | "warn" | "deny" | "forbid" => {
check_clippy_lint_names(cx, items); check_clippy_lint_names(cx, ident, items);
}, },
_ => {}, _ => {},
} }
@ -363,38 +388,43 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
} }
} }
#[allow(clippy::single_match_else)] fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) {
fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) { fn extract_name(lint: &NestedMetaItem) -> Option<SymbolStr> {
let lint_store = cx.lints();
for lint in items {
if_chain! { if_chain! {
if let Some(meta_item) = lint.meta_item(); if let Some(meta_item) = lint.meta_item();
if meta_item.path.segments.len() > 1; if meta_item.path.segments.len() > 1;
if let tool_name = meta_item.path.segments[0].ident; if let tool_name = meta_item.path.segments[0].ident;
if tool_name.as_str() == "clippy"; if tool_name.as_str() == "clippy";
let name = meta_item.path.segments.last().unwrap().ident.name; let lint_name = meta_item.path.segments.last().unwrap().ident.name;
if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(
&name.as_str(),
Some(tool_name.name),
);
then { then {
return Some(lint_name.as_str());
}
}
None
}
let lint_store = cx.lints();
for lint in items {
if let Some(lint_name) = extract_name(lint) {
if let CheckLintNameResult::Tool(Err((None, _))) =
lint_store.check_lint_name(&lint_name, Some(sym!(clippy)))
{
span_lint_and_then( span_lint_and_then(
cx, cx,
UNKNOWN_CLIPPY_LINTS, UNKNOWN_CLIPPY_LINTS,
lint.span(), lint.span(),
&format!("unknown clippy lint: clippy::{}", name), &format!("unknown clippy lint: clippy::{}", lint_name),
|diag| { |diag| {
let name_lower = name.as_str().to_lowercase(); let name_lower = lint_name.to_lowercase();
let symbols = lint_store.get_lints().iter().map( let symbols = lint_store
|l| Symbol::intern(&l.name_lower()) .get_lints()
).collect::<Vec<_>>(); .iter()
let sugg = find_best_match_for_name( .map(|l| Symbol::intern(&l.name_lower()))
symbols.iter(), .collect::<Vec<_>>();
&format!("clippy::{}", name_lower), let sugg = find_best_match_for_name(symbols.iter(), &format!("clippy::{}", name_lower), None);
None, if lint_name.chars().any(char::is_uppercase)
); && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok()
if name.as_str().chars().any(char::is_uppercase) {
&& lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() {
diag.span_suggestion( diag.span_suggestion(
lint.span(), lint.span(),
"lowercase the lint name", "lowercase the lint name",
@ -409,10 +439,19 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
} },
);
} else if lint_name == "restriction" && ident != "allow" {
span_lint_and_help(
cx,
BLANKET_CLIPPY_RESTRICTION_LINTS,
lint.span(),
"restriction lints are not meant to be all enabled",
None,
"try enabling only the lints you really need",
); );
} }
}; }
} }
} }
@ -442,15 +481,14 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
} }
fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool { fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool {
if let Some(stmt) = block.stmts.first() { block.stmts.first().map_or(
match &stmt.kind { block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)),
|stmt| match &stmt.kind {
StmtKind::Local(_) => true, StmtKind::Local(_) => true,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr), StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr),
_ => false, _ => false,
} },
} else { )
block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e))
}
} }
fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool { fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool {
@ -460,11 +498,10 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &
ExprKind::Ret(None) | ExprKind::Break(_, None) => false, ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
ExprKind::Call(path_expr, _) => { ExprKind::Call(path_expr, _) => {
if let ExprKind::Path(qpath) = &path_expr.kind { if let ExprKind::Path(qpath) = &path_expr.kind {
if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { tables
!match_def_path(cx, fun_id, &paths::BEGIN_PANIC) .qpath_res(qpath, path_expr.hir_id)
} else { .opt_def_id()
true .map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC))
}
} else { } else {
true true
} }

View file

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// non-async-aware MutexGuard. /// non-async-aware MutexGuard.
/// ///
/// **Why is this bad?** The Mutex types found in syd::sync and parking_lot /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
/// are not designed to operator in an async context across await points. /// are not designed to operate in an async context across await points.
/// ///
/// There are two potential solutions. One is to use an asynx-aware Mutex /// There are two potential solutions. One is to use an asynx-aware Mutex
/// type. Many asynchronous foundation crates provide such a Mutex type. The /// type. Many asynchronous foundation crates provide such a Mutex type. The

View file

@ -115,7 +115,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
COLLAPSIBLE_IF, COLLAPSIBLE_IF,
block.span, block.span,
"this `else { if .. }` block can be collapsed", "this `else { if .. }` block can be collapsed",
"try", "collapse nested if block",
snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
applicability, applicability,
); );
@ -142,7 +142,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
let rhs = Sugg::ast(cx, check_inner, ".."); let rhs = Sugg::ast(cx, check_inner, "..");
diag.span_suggestion( diag.span_suggestion(
expr.span, expr.span,
"try", "collapse nested if block",
format!( format!(
"if {} {}", "if {} {}",
lhs.and(&rhs), lhs.and(&rhs),

View file

@ -122,8 +122,5 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
} }
fn kind_is_cmp(kind: BinOpKind) -> bool { fn kind_is_cmp(kind: BinOpKind) -> bool {
match kind { matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
_ => false,
}
} }

View file

@ -153,5 +153,13 @@ declare_deprecated_lint! {
/// ///
/// **Deprecation reason:** Associated-constants are now preferred. /// **Deprecation reason:** Associated-constants are now preferred.
pub REPLACE_CONSTS, pub REPLACE_CONSTS,
"associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants" "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants"
}
declare_deprecated_lint! {
/// **What it does:** Nothing. This lint has been deprecated.
///
/// **Deprecation reason:** The regex! macro does not exist anymore.
pub REGEX_MACRO,
"the regex! macro has been removed from the regex crate in 2018"
} }

View file

@ -73,9 +73,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) { fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
match method_name { match method_name {
"deref" => { "deref" => {
if cx.tcx.lang_items().deref_trait().map_or(false, |id| { let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| {
implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[]) implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[])
}) { });
if impls_deref_trait {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
EXPLICIT_DEREF_METHODS, EXPLICIT_DEREF_METHODS,
@ -88,9 +89,10 @@ fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var
} }
}, },
"deref_mut" => { "deref_mut" => {
if cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| { let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| {
implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[]) implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[])
}) { });
if impls_deref_mut_trait {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
EXPLICIT_DEREF_METHODS, EXPLICIT_DEREF_METHODS,

View file

@ -214,20 +214,20 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
} }
fn is_valid_operator(op: BinOp) -> bool { fn is_valid_operator(op: BinOp) -> bool {
match op.node { matches!(
op.node,
BinOpKind::Sub BinOpKind::Sub
| BinOpKind::Div | BinOpKind::Div
| BinOpKind::Eq | BinOpKind::Eq
| BinOpKind::Lt | BinOpKind::Lt
| BinOpKind::Le | BinOpKind::Le
| BinOpKind::Gt | BinOpKind::Gt
| BinOpKind::Ge | BinOpKind::Ge
| BinOpKind::Ne | BinOpKind::Ne
| BinOpKind::And | BinOpKind::And
| BinOpKind::Or | BinOpKind::Or
| BinOpKind::BitXor | BinOpKind::BitXor
| BinOpKind::BitAnd | BinOpKind::BitAnd
| BinOpKind::BitOr => true, | BinOpKind::BitOr
_ => false, )
}
} }

View file

@ -105,10 +105,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
_ => return false, _ => return false,
} }
match map.find(map.get_parent_node(id)) { matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_)))
Some(Node::Param(_)) => true,
_ => false,
}
} }
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {

View file

@ -175,10 +175,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a
fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
match (&lhs.kind, &rhs.kind) { match (&lhs.kind, &rhs.kind) {
(ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
(l, r) => match (l, r) { (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
(ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false,
(_, _) => true,
},
} }
} }

View file

@ -1,11 +1,11 @@
use crate::consts::{ use crate::consts::{
constant, constant_simple, Constant, constant, constant_simple, Constant,
Constant::{F32, F64}, Constant::{Int, F32, F64},
}; };
use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -293,6 +293,121 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
} }
} }
fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) {
if value == Int(2) {
if let Some(parent) = get_parent_expr(cx, expr) {
if let Some(grandparent) = get_parent_expr(cx, parent) {
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind {
if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
return;
}
}
}
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Add, ..
},
ref lhs,
ref rhs,
) = parent.kind
{
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
parent.span,
"square can be computed more efficiently",
"consider using",
format!(
"{}.mul_add({}, {})",
Sugg::hir(cx, &args[0], ".."),
Sugg::hir(cx, &args[0], ".."),
Sugg::hir(cx, &other_addend, ".."),
),
Applicability::MachineApplicable,
);
return;
}
}
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"square can be computed more efficiently",
"consider using",
format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")),
Applicability::MachineApplicable,
);
}
}
}
fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Add, ..
},
ref add_lhs,
ref add_rhs,
) = args[0].kind
{
// check if expression of the form x * x + y * y
if_chain! {
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind;
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind;
if are_exprs_equal(cx, lmul_lhs, lmul_rhs);
if are_exprs_equal(cx, rmul_lhs, rmul_rhs);
then {
return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, "..")));
}
}
// check if expression of the form x.powi(2) + y.powi(2)
if_chain! {
if let ExprKind::MethodCall(
PathSegment { ident: lmethod_name, .. },
ref _lspan,
ref largs,
_
) = add_lhs.kind;
if let ExprKind::MethodCall(
PathSegment { ident: rmethod_name, .. },
ref _rspan,
ref rargs,
_
) = add_rhs.kind;
if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]);
if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]);
if Int(2) == lvalue && Int(2) == rvalue;
then {
return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
}
}
}
None
}
fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
if let Some(message) = detect_hypot(cx, args) {
span_lint_and_sugg(
cx,
IMPRECISE_FLOPS,
expr.span,
"hypotenuse can be computed more accurately",
"consider using",
message,
Applicability::MachineApplicable,
);
}
}
// TODO: Lint expressions of the form `x.exp() - y` where y > 1 // TODO: Lint expressions of the form `x.exp() - y` where y > 1
// and suggest usage of `x.exp_m1() - (y - 1)` instead // and suggest usage of `x.exp_m1() - (y - 1)` instead
fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
@ -344,6 +459,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
rhs, rhs,
) = &expr.kind ) = &expr.kind
{ {
if let Some(parent) = get_parent_expr(cx, expr) {
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind {
if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
return;
}
}
}
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
(inner_lhs, inner_rhs, rhs) (inner_lhs, inner_rhs, rhs)
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
@ -479,6 +602,100 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
} }
} }
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
if_chain! {
if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind;
if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind;
then {
return method_name_a.as_str() == method_name_b.as_str() &&
args_a.len() == args_b.len() &&
(
["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1])
);
}
}
false
}
fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
// check if expression of the form x.logN() / y.logN()
if_chain! {
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Div, ..
},
lhs,
rhs,
) = &expr.kind;
if are_same_base_logs(cx, lhs, rhs);
if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind;
if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind;
then {
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"log base can be expressed more clearly",
"consider using",
format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),),
Applicability::MachineApplicable,
);
}
}
}
fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Div, ..
},
div_lhs,
div_rhs,
) = &expr.kind;
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Mul, ..
},
mul_lhs,
mul_rhs,
) = &div_lhs.kind;
if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs);
if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs);
then {
// TODO: also check for constant values near PI/180 or 180/PI
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
(F32(180_f32) == lvalue || F64(180_f64) == lvalue)
{
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"conversion to degrees can be done more accurately",
"consider using",
format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")),
Applicability::MachineApplicable,
);
} else if
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
{
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"conversion to radians can be done more accurately",
"consider using",
format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")),
Applicability::MachineApplicable,
);
}
}
}
}
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind {
@ -489,6 +706,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
"ln" => check_ln1p(cx, expr, args), "ln" => check_ln1p(cx, expr, args),
"log" => check_log_base(cx, expr, args), "log" => check_log_base(cx, expr, args),
"powf" => check_powf(cx, expr, args), "powf" => check_powf(cx, expr, args),
"powi" => check_powi(cx, expr, args),
"sqrt" => check_hypot(cx, expr, args),
_ => {}, _ => {},
} }
} }
@ -496,6 +715,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
check_expm1(cx, expr); check_expm1(cx, expr);
check_mul_add(cx, expr); check_mul_add(cx, expr);
check_custom_abs(cx, expr); check_custom_abs(cx, expr);
check_log_division(cx, expr);
check_radians(cx, expr);
} }
} }
} }

View file

@ -305,18 +305,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
} }
fn is_block(expr: &Expr) -> bool { fn is_block(expr: &Expr) -> bool {
if let ExprKind::Block(..) = expr.kind { matches!(expr.kind, ExprKind::Block(..))
true
} else {
false
}
} }
/// Check if the expression is an `if` or `if let` /// Check if the expression is an `if` or `if let`
fn is_if(expr: &Expr) -> bool { fn is_if(expr: &Expr) -> bool {
if let ExprKind::If(..) = expr.kind { matches!(expr.kind, ExprKind::If(..))
true
} else {
false
}
} }

View file

@ -645,13 +645,7 @@ fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
use hir::ExprKind::{Field, Index, Path}; use hir::ExprKind::{Field, Index, Path};
match e.kind { match e.kind {
Path(ref qpath) => { Path(ref qpath) => !matches!(qpath_res(cx, qpath, e.hir_id), Res::Local(_)),
if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) {
false
} else {
true
}
},
Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner), Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
_ => false, _ => false,
} }

View file

@ -135,13 +135,10 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
} }
} }
impl<'tcx> ArmVisitor<'_, 'tcx> { impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
if let Some(arm_mutex) = self.found_mutex { self.found_mutex
SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
} else {
false
}
} }
} }

View file

@ -302,16 +302,12 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr)); let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr));
match ty.kind { match ty.kind {
ty::Dynamic(ref tt, ..) => { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
if let Some(principal) = tt.principal() { cx.tcx
cx.tcx .associated_items(principal.def_id())
.associated_items(principal.def_id()) .in_definition_order()
.in_definition_order() .any(|item| is_is_empty(cx, &item))
.any(|item| is_is_empty(cx, &item)) }),
} else {
false
}
},
ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
ty::Adt(id, _) => has_is_empty_impl(cx, id.did), ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
ty::Array(..) | ty::Slice(..) | ty::Str => true, ty::Array(..) | ty::Slice(..) | ty::Str => true,

View file

@ -1,6 +1,5 @@
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for `let`-bindings, which are subsequently /// **What it does:** Checks for `let`-bindings, which are subsequently
@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> {
borrows: bool, borrows: bool,
} }
impl BorrowVisitor<'_, '_> {
fn fn_def_id(&self, expr: &Expr<'_>) -> Option<DefId> {
match &expr.kind {
ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id),
ExprKind::Call(
Expr {
kind: ExprKind::Path(qpath),
..
},
..,
) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(),
_ => None,
}
}
}
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
type Map = Map<'tcx>; type Map = Map<'tcx>;
@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
return; return;
} }
if let Some(def_id) = self.fn_def_id(expr) { if let Some(def_id) = fn_def_id(self.cx, expr) {
self.borrows = self self.borrows = self
.cx .cx
.tcx .tcx

View file

@ -229,6 +229,7 @@ mod main_recursion;
mod manual_async_fn; mod manual_async_fn;
mod manual_non_exhaustive; mod manual_non_exhaustive;
mod map_clone; mod map_clone;
mod map_identity;
mod map_unit_fn; mod map_unit_fn;
mod match_on_vec_items; mod match_on_vec_items;
mod matches; mod matches;
@ -263,10 +264,12 @@ mod non_copy_const;
mod non_expressive_names; mod non_expressive_names;
mod open_options; mod open_options;
mod option_env_unwrap; mod option_env_unwrap;
mod option_if_let_else;
mod overflow_check_conditional; mod overflow_check_conditional;
mod panic_unimplemented; mod panic_unimplemented;
mod partialeq_ne_impl; mod partialeq_ne_impl;
mod path_buf_push_overwrite; mod path_buf_push_overwrite;
mod pattern_type_mismatch;
mod precedence; mod precedence;
mod ptr; mod ptr;
mod ptr_offset_with_cast; mod ptr_offset_with_cast;
@ -274,11 +277,11 @@ mod question_mark;
mod ranges; mod ranges;
mod redundant_clone; mod redundant_clone;
mod redundant_field_names; mod redundant_field_names;
mod redundant_pattern_matching;
mod redundant_pub_crate; mod redundant_pub_crate;
mod redundant_static_lifetimes; mod redundant_static_lifetimes;
mod reference; mod reference;
mod regex; mod regex;
mod repeat_once;
mod returns; mod returns;
mod serde_api; mod serde_api;
mod shadow; mod shadow;
@ -459,7 +462,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
); );
store.register_removed( store.register_removed(
"clippy::replace_consts", "clippy::replace_consts",
"associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants", "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants",
);
store.register_removed(
"clippy::regex_macro",
"the regex! macro has been removed from the regex crate in 2018",
); );
// end deprecated lints, do not remove this comment, its used in `update_lints` // end deprecated lints, do not remove this comment, its used in `update_lints`
@ -473,6 +480,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&assign_ops::ASSIGN_OP_PATTERN, &assign_ops::ASSIGN_OP_PATTERN,
&assign_ops::MISREFACTORED_ASSIGN_OP, &assign_ops::MISREFACTORED_ASSIGN_OP,
&atomic_ordering::INVALID_ATOMIC_ORDERING, &atomic_ordering::INVALID_ATOMIC_ORDERING,
&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
&attrs::DEPRECATED_CFG_ATTR, &attrs::DEPRECATED_CFG_ATTR,
&attrs::DEPRECATED_SEMVER, &attrs::DEPRECATED_SEMVER,
&attrs::EMPTY_LINE_AFTER_OUTER_ATTR, &attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
@ -608,17 +616,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&manual_async_fn::MANUAL_ASYNC_FN, &manual_async_fn::MANUAL_ASYNC_FN,
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
&map_clone::MAP_CLONE, &map_clone::MAP_CLONE,
&map_identity::MAP_IDENTITY,
&map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::OPTION_MAP_UNIT_FN,
&map_unit_fn::RESULT_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN,
&match_on_vec_items::MATCH_ON_VEC_ITEMS, &match_on_vec_items::MATCH_ON_VEC_ITEMS,
&matches::INFALLIBLE_DESTRUCTURING_MATCH, &matches::INFALLIBLE_DESTRUCTURING_MATCH,
&matches::MATCH_AS_REF, &matches::MATCH_AS_REF,
&matches::MATCH_BOOL, &matches::MATCH_BOOL,
&matches::MATCH_LIKE_MATCHES_MACRO,
&matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_OVERLAPPING_ARM,
&matches::MATCH_REF_PATS, &matches::MATCH_REF_PATS,
&matches::MATCH_SINGLE_BINDING, &matches::MATCH_SINGLE_BINDING,
&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
&matches::MATCH_WILD_ERR_ARM, &matches::MATCH_WILD_ERR_ARM,
&matches::REDUNDANT_PATTERN_MATCHING,
&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
&matches::SINGLE_MATCH, &matches::SINGLE_MATCH,
&matches::SINGLE_MATCH_ELSE, &matches::SINGLE_MATCH_ELSE,
@ -726,6 +737,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&non_expressive_names::SIMILAR_NAMES, &non_expressive_names::SIMILAR_NAMES,
&open_options::NONSENSICAL_OPEN_OPTIONS, &open_options::NONSENSICAL_OPEN_OPTIONS,
&option_env_unwrap::OPTION_ENV_UNWRAP, &option_env_unwrap::OPTION_ENV_UNWRAP,
&option_if_let_else::OPTION_IF_LET_ELSE,
&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
&panic_unimplemented::PANIC, &panic_unimplemented::PANIC,
&panic_unimplemented::PANIC_PARAMS, &panic_unimplemented::PANIC_PARAMS,
@ -734,6 +746,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&panic_unimplemented::UNREACHABLE, &panic_unimplemented::UNREACHABLE,
&partialeq_ne_impl::PARTIALEQ_NE_IMPL, &partialeq_ne_impl::PARTIALEQ_NE_IMPL,
&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
&pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
&precedence::PRECEDENCE, &precedence::PRECEDENCE,
&ptr::CMP_NULL, &ptr::CMP_NULL,
&ptr::MUT_FROM_REF, &ptr::MUT_FROM_REF,
@ -746,14 +759,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&ranges::REVERSED_EMPTY_RANGES, &ranges::REVERSED_EMPTY_RANGES,
&redundant_clone::REDUNDANT_CLONE, &redundant_clone::REDUNDANT_CLONE,
&redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_field_names::REDUNDANT_FIELD_NAMES,
&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING,
&redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_pub_crate::REDUNDANT_PUB_CRATE,
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
&reference::DEREF_ADDROF, &reference::DEREF_ADDROF,
&reference::REF_IN_DEREF, &reference::REF_IN_DEREF,
&regex::INVALID_REGEX, &regex::INVALID_REGEX,
&regex::REGEX_MACRO,
&regex::TRIVIAL_REGEX, &regex::TRIVIAL_REGEX,
&repeat_once::REPEAT_ONCE,
&returns::NEEDLESS_RETURN, &returns::NEEDLESS_RETURN,
&returns::UNUSED_UNIT, &returns::UNUSED_UNIT,
&serde_api::SERDE_API_MISUSE, &serde_api::SERDE_API_MISUSE,
@ -946,7 +958,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_doc::MissingDoc::new());
store.register_late_pass(|| box missing_inline::MissingInline); store.register_late_pass(|| box missing_inline::MissingInline);
store.register_late_pass(|| box if_let_some_result::OkIfLet); store.register_late_pass(|| box if_let_some_result::OkIfLet);
store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching);
store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
let enum_variant_size_threshold = conf.enum_variant_size_threshold; let enum_variant_size_threshold = conf.enum_variant_size_threshold;
@ -990,7 +1001,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box checked_conversions::CheckedConversions); store.register_late_pass(|| box checked_conversions::CheckedConversions);
store.register_late_pass(|| box integer_division::IntegerDivision); store.register_late_pass(|| box integer_division::IntegerDivision);
store.register_late_pass(|| box inherent_to_string::InherentToString); store.register_late_pass(|| box inherent_to_string::InherentToString);
store.register_late_pass(|| box trait_bounds::TraitBounds); let max_trait_bounds = conf.max_trait_bounds;
store.register_late_pass(move || box trait_bounds::TraitBounds::new(max_trait_bounds));
store.register_late_pass(|| box comparison_chain::ComparisonChain); store.register_late_pass(|| box comparison_chain::ComparisonChain);
store.register_late_pass(|| box mut_key::MutableKeyType); store.register_late_pass(|| box mut_key::MutableKeyType);
store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic); store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic);
@ -1027,7 +1039,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
let array_size_threshold = conf.array_size_threshold; let array_size_threshold = conf.array_size_threshold;
store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold)); store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic); store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic);
store.register_early_pass(|| box as_conversions::AsConversions); store.register_early_pass(|| box as_conversions::AsConversions);
store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_early_pass(|| box utils::internal_lints::ProduceIce);
store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box let_underscore::LetUnderscore);
@ -1043,6 +1055,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
store.register_late_pass(|| box unnamed_address::UnnamedAddress); store.register_late_pass(|| box unnamed_address::UnnamedAddress);
store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box dereference::Dereferencing);
store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box future_not_send::FutureNotSend);
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box if_let_mutex::IfLetMutex);
@ -1057,6 +1070,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
}); });
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box macro_use::MacroUseImports::default());
store.register_late_pass(|| box map_identity::MapIdentity);
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
store.register_late_pass(|| box repeat_once::RepeatOnce);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@ -1090,6 +1106,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::TODO),
LintId::of(&panic_unimplemented::UNIMPLEMENTED), LintId::of(&panic_unimplemented::UNIMPLEMENTED),
LintId::of(&panic_unimplemented::UNREACHABLE), LintId::of(&panic_unimplemented::UNREACHABLE),
LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_REUSE),
LintId::of(&shadow::SHADOW_SAME), LintId::of(&shadow::SHADOW_SAME),
LintId::of(&strings::STRING_ADD), LintId::of(&strings::STRING_ADD),
@ -1146,6 +1163,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&needless_continue::NEEDLESS_CONTINUE), LintId::of(&needless_continue::NEEDLESS_CONTINUE),
LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
LintId::of(&non_expressive_names::SIMILAR_NAMES), LintId::of(&non_expressive_names::SIMILAR_NAMES),
LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE),
LintId::of(&ranges::RANGE_MINUS_ONE),
LintId::of(&ranges::RANGE_PLUS_ONE), LintId::of(&ranges::RANGE_PLUS_ONE),
LintId::of(&shadow::SHADOW_UNRELATED), LintId::of(&shadow::SHADOW_UNRELATED),
LintId::of(&strings::STRING_ADD_ASSIGN), LintId::of(&strings::STRING_ADD_ASSIGN),
@ -1186,6 +1205,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(&attrs::DEPRECATED_CFG_ATTR), LintId::of(&attrs::DEPRECATED_CFG_ATTR),
LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::DEPRECATED_SEMVER),
LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::MISMATCHED_TARGET_OS),
@ -1273,13 +1293,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_clone::MAP_CLONE),
LintId::of(&map_identity::MAP_IDENTITY),
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(&matches::MATCH_AS_REF), LintId::of(&matches::MATCH_AS_REF),
LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_OVERLAPPING_ARM),
LintId::of(&matches::MATCH_REF_PATS), LintId::of(&matches::MATCH_REF_PATS),
LintId::of(&matches::MATCH_SINGLE_BINDING), LintId::of(&matches::MATCH_SINGLE_BINDING),
LintId::of(&matches::REDUNDANT_PATTERN_MATCHING),
LintId::of(&matches::SINGLE_MATCH), LintId::of(&matches::SINGLE_MATCH),
LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
@ -1364,18 +1387,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ptr::PTR_ARG), LintId::of(&ptr::PTR_ARG),
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(&question_mark::QUESTION_MARK), LintId::of(&question_mark::QUESTION_MARK),
LintId::of(&ranges::RANGE_MINUS_ONE),
LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&ranges::REVERSED_EMPTY_RANGES),
LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_clone::REDUNDANT_CLONE),
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::DEREF_ADDROF),
LintId::of(&reference::REF_IN_DEREF), LintId::of(&reference::REF_IN_DEREF),
LintId::of(&regex::INVALID_REGEX), LintId::of(&regex::INVALID_REGEX),
LintId::of(&regex::REGEX_MACRO),
LintId::of(&regex::TRIVIAL_REGEX), LintId::of(&regex::TRIVIAL_REGEX),
LintId::of(&repeat_once::REPEAT_ONCE),
LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&returns::UNUSED_UNIT), LintId::of(&returns::UNUSED_UNIT),
LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&serde_api::SERDE_API_MISUSE),
@ -1437,6 +1458,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_group(true, "clippy::style", Some("clippy_style"), vec![ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK),
LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blacklisted_name::BLACKLISTED_NAME),
@ -1470,8 +1492,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_clone::MAP_CLONE),
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_OVERLAPPING_ARM),
LintId::of(&matches::MATCH_REF_PATS), LintId::of(&matches::MATCH_REF_PATS),
LintId::of(&matches::REDUNDANT_PATTERN_MATCHING),
LintId::of(&matches::SINGLE_MATCH), LintId::of(&matches::SINGLE_MATCH),
LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
@ -1508,9 +1532,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ptr::PTR_ARG), LintId::of(&ptr::PTR_ARG),
LintId::of(&question_mark::QUESTION_MARK), LintId::of(&question_mark::QUESTION_MARK),
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(&regex::REGEX_MACRO),
LintId::of(&regex::TRIVIAL_REGEX), LintId::of(&regex::TRIVIAL_REGEX),
LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&returns::UNUSED_UNIT), LintId::of(&returns::UNUSED_UNIT),
@ -1550,6 +1572,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::MUT_RANGE_BOUND),
LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&map_identity::MAP_IDENTITY),
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(&matches::MATCH_AS_REF), LintId::of(&matches::MATCH_AS_REF),
@ -1580,10 +1603,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(&precedence::PRECEDENCE), LintId::of(&precedence::PRECEDENCE),
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(&ranges::RANGE_MINUS_ONE),
LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::DEREF_ADDROF),
LintId::of(&reference::REF_IN_DEREF), LintId::of(&reference::REF_IN_DEREF),
LintId::of(&repeat_once::REPEAT_ONCE),
LintId::of(&swap::MANUAL_SWAP), LintId::of(&swap::MANUAL_SWAP),
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),

View file

@ -129,10 +129,10 @@ fn check_fn_inner<'tcx>(
} }
let mut bounds_lts = Vec::new(); let mut bounds_lts = Vec::new();
let types = generics.params.iter().filter(|param| match param.kind { let types = generics
GenericParamKind::Type { .. } => true, .params
_ => false, .iter()
}); .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
for typ in types { for typ in types {
for bound in typ.bounds { for bound in typ.bounds {
let mut visitor = RefVisitor::new(cx); let mut visitor = RefVisitor::new(cx);
@ -337,10 +337,10 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) {
if let Some(ref last_path_segment) = last_path_segment(qpath).args { if let Some(ref last_path_segment) = last_path_segment(qpath).args {
if !last_path_segment.parenthesized if !last_path_segment.parenthesized
&& !last_path_segment.args.iter().any(|arg| match arg { && !last_path_segment
GenericArg::Lifetime(_) => true, .args
_ => false, .iter()
}) .any(|arg| matches!(arg, GenericArg::Lifetime(_)))
{ {
let hir_id = ty.hir_id; let hir_id = ty.hir_id;
match self.cx.qpath_res(qpath, hir_id) { match self.cx.qpath_res(qpath, hir_id) {

View file

@ -264,10 +264,13 @@ impl LiteralDigitGrouping {
let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
(exponent, &["32", "64"][..], 'f') (exponent, &["32", "64"][..], 'f')
} else if let Some(fraction) = &mut num_lit.fraction {
(fraction, &["32", "64"][..], 'f')
} else { } else {
(&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') num_lit
.fraction
.as_mut()
.map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| {
(fraction, &["32", "64"][..], 'f')
})
}; };
let mut split = part.rsplit('_'); let mut split = part.rsplit('_');

View file

@ -686,13 +686,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
NeverLoopResult::AlwaysBreak NeverLoopResult::AlwaysBreak
} }
}, },
ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => { ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
if let Some(ref e) = *e { combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) }),
} else {
NeverLoopResult::AlwaysBreak
}
},
ExprKind::InlineAsm(ref asm) => asm ExprKind::InlineAsm(ref asm) => asm
.operands .operands
.iter() .iter()
@ -1881,13 +1877,9 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
// IntoIterator is currently only implemented for array sizes <= 32 in rustc // IntoIterator is currently only implemented for array sizes <= 32 in rustc
match ty.kind { match ty.kind {
ty::Array(_, n) => { ty::Array(_, n) => n
if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) { .try_eval_usize(cx.tcx, cx.param_env)
(0..=32).contains(&val) .map_or(false, |val| (0..=32).contains(&val)),
} else {
false
}
},
_ => false, _ => false,
} }
} }
@ -1899,11 +1891,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<
return None; return None;
} }
if let StmtKind::Local(ref local) = block.stmts[0].kind { if let StmtKind::Local(ref local) = block.stmts[0].kind {
if let Some(expr) = local.init { local.init //.map(|expr| expr)
Some(expr)
} else {
None
}
} else { } else {
None None
} }
@ -2023,15 +2011,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
if let PatKind::Binding(.., ident, _) = local.pat.kind { if let PatKind::Binding(.., ident, _) = local.pat.kind {
self.name = Some(ident.name); self.name = Some(ident.name);
self.state = if let Some(ref init) = local.init { self.state = local.init.as_ref().map_or(VarState::Declared, |init| {
if is_integer_const(&self.cx, init, 0) { if is_integer_const(&self.cx, init, 0) {
VarState::Warn VarState::Warn
} else { } else {
VarState::Declared VarState::Declared
} }
} else { })
VarState::Declared
}
} }
} }
} }
@ -2105,17 +2091,11 @@ fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<HirId> {
} }
fn is_loop(expr: &Expr<'_>) -> bool { fn is_loop(expr: &Expr<'_>) -> bool {
match expr.kind { matches!(expr.kind, ExprKind::Loop(..))
ExprKind::Loop(..) => true,
_ => false,
}
} }
fn is_conditional(expr: &Expr<'_>) -> bool { fn is_conditional(expr: &Expr<'_>) -> bool {
match expr.kind { matches!(expr.kind, ExprKind::Match(..))
ExprKind::Match(..) => true,
_ => false,
}
} }
fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {

View file

@ -0,0 +1,126 @@
use crate::utils::{
is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks,
span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
///
/// **Why is this bad?** It can be written more concisely without the call to `map`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
/// ```
/// Use instead:
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
}
declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
impl<'tcx> LateLintPass<'tcx> for MapIdentity {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if expr.span.from_expansion() {
return;
}
if_chain! {
if let Some([caller, func]) = get_map_argument(cx, expr);
if is_expr_identity_function(cx, func);
then {
span_lint_and_sugg(
cx,
MAP_IDENTITY,
expr.span.trim_start(caller.span).unwrap(),
"unnecessary map of the identity function",
"remove the call to `map`",
String::new(),
Applicability::MachineApplicable
)
}
}
}
}
/// Returns the arguments passed into map() if the expression is a method call to
/// map(). Otherwise, returns None.
fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
if_chain! {
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
if args.len() == 2 && method.ident.as_str() == "map";
let caller_ty = cx.tables().expr_ty(&args[0]);
if match_trait_method(cx, expr, &paths::ITERATOR)
|| is_type_diagnostic_item(cx, caller_ty, sym!(result_type))
|| is_type_diagnostic_item(cx, caller_ty, sym!(option_type));
then {
Some(args)
} else {
None
}
}
}
/// Checks if an expression represents the identity function
/// Only examines closures and `std::convert::identity`
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
_ => false,
}
}
/// Checks if a function's body represents the identity function
/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
/// return x; }`
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
let params = func.params;
let body = remove_blocks(&func.value);
// if there's less/more than one parameter, then it is not the identity function
if params.len() != 1 {
return false;
}
match body.kind {
ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
ExprKind::Ret(Some(ref ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
ExprKind::Block(ref block, _) => {
if_chain! {
if block.stmts.len() == 1;
if let StmtKind::Semi(ref expr) | StmtKind::Expr(ref expr) = block.stmts[0].kind;
if let ExprKind::Ret(Some(ref ret_val)) = expr.kind;
then {
match_expr_param(cx, ret_val, params[0].pat)
} else {
false
}
}
},
_ => false,
}
}
/// Returns true iff an expression returns the same thing as a parameter's pattern
fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
if let PatKind::Binding(_, _, ident, _) = pat.kind {
match_var(expr, ident.name) && !(cx.tables().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
} else {
false
}
}

View file

@ -13,14 +13,14 @@ use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::CtorKind; use rustc_hir::def::CtorKind;
use rustc_hir::{ use rustc_hir::{
Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat,
QPath, RangeEnd, PatKind, QPath, RangeEnd,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::{Span, Spanned};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::Bound; use std::collections::Bound;
@ -409,6 +409,74 @@ declare_clippy_lint! {
"a match on a struct that binds all fields but still uses the wildcard pattern" "a match on a struct that binds all fields but still uses the wildcard pattern"
} }
declare_clippy_lint! {
/// **What it does:** Lint for redundant pattern matching over `Result` or
/// `Option`
///
/// **Why is this bad?** It's more concise and clear to just use the proper
/// utility function
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// if let Ok(_) = Ok::<i32, i32>(42) {}
/// if let Err(_) = Err::<i32, i32>(42) {}
/// if let None = None::<()> {}
/// if let Some(_) = Some(42) {}
/// match Ok::<i32, i32>(42) {
/// Ok(_) => true,
/// Err(_) => false,
/// };
/// ```
///
/// The more idiomatic use would be:
///
/// ```rust
/// if Ok::<i32, i32>(42).is_ok() {}
/// if Err::<i32, i32>(42).is_err() {}
/// if None::<()>.is_none() {}
/// if Some(42).is_some() {}
/// Ok::<i32, i32>(42).is_ok();
/// ```
pub REDUNDANT_PATTERN_MATCHING,
style,
"use the proper utility function avoiding an `if let`"
}
declare_clippy_lint! {
/// **What it does:** Checks for `match` or `if let` expressions producing a
/// `bool` that could be written using `matches!`
///
/// **Why is this bad?** Readability and needless complexity.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let x = Some(5);
///
/// // Bad
/// let a = match x {
/// Some(0) => true,
/// _ => false,
/// };
///
/// let a = if let Some(0) = x {
/// true
/// } else {
/// false
/// };
///
/// // Good
/// let a = matches!(x, Some(0));
/// ```
pub MATCH_LIKE_MATCHES_MACRO,
style,
"a match that could be written with the matches! macro"
}
#[derive(Default)] #[derive(Default)]
pub struct Matches { pub struct Matches {
infallible_destructuring_match_linted: bool, infallible_destructuring_match_linted: bool,
@ -427,7 +495,9 @@ impl_lint_pass!(Matches => [
WILDCARD_IN_OR_PATTERNS, WILDCARD_IN_OR_PATTERNS,
MATCH_SINGLE_BINDING, MATCH_SINGLE_BINDING,
INFALLIBLE_DESTRUCTURING_MATCH, INFALLIBLE_DESTRUCTURING_MATCH,
REST_PAT_IN_FULLY_BOUND_STRUCTS REST_PAT_IN_FULLY_BOUND_STRUCTS,
REDUNDANT_PATTERN_MATCHING,
MATCH_LIKE_MATCHES_MACRO
]); ]);
impl<'tcx> LateLintPass<'tcx> for Matches { impl<'tcx> LateLintPass<'tcx> for Matches {
@ -435,6 +505,10 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
if in_external_macro(cx.sess(), expr.span) { if in_external_macro(cx.sess(), expr.span) {
return; return;
} }
redundant_pattern_match::check(cx, expr);
check_match_like_matches(cx, expr);
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
check_single_match(cx, ex, arms, expr); check_single_match(cx, ex, arms, expr);
check_match_bool(cx, ex, arms, expr); check_match_bool(cx, ex, arms, expr);
@ -530,16 +604,22 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
// the lint noisy in unnecessary situations // the lint noisy in unnecessary situations
return; return;
} }
let els = remove_blocks(&arms[1].body); let els = arms[1].body;
let els = if is_unit_expr(els) { let els = if is_unit_expr(remove_blocks(els)) {
None None
} else if let ExprKind::Block(_, _) = els.kind { } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
// matches with blocks that contain statements are prettier as `if let + else` if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
Some(els) // single statement/expr "else" block, don't lint
return;
} else {
// block with 2+ statements or 1 expr and 1+ statement
Some(els)
}
} else { } else {
// allow match arms with just expressions // not a block, don't lint
return; return;
}; };
let ty = cx.tables().expr_ty(ex); let ty = cx.tables().expr_ty(ex);
if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
check_single_match_single_pattern(cx, ex, arms, expr, els); check_single_match_single_pattern(cx, ex, arms, expr, els);
@ -802,13 +882,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
// Some simple checks for exhaustive patterns. // Some simple checks for exhaustive patterns.
// There is a room for improvements to detect more cases, // There is a room for improvements to detect more cases,
// but it can be more expensive to do so. // but it can be more expensive to do so.
let is_pattern_exhaustive = |pat: &&Pat<'_>| { let is_pattern_exhaustive =
if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind { |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
true
} else {
false
}
};
if patterns.iter().all(is_pattern_exhaustive) { if patterns.iter().all(is_pattern_exhaustive) {
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
} }
@ -989,6 +1064,79 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
} }
} }
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
match match_source {
MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
_ => return,
}
}
}
/// Lint a `match` or desugared `if let` for replacement by `matches!`
fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) {
if_chain! {
if arms.len() == 2;
if cx.tables().expr_ty(expr).is_bool();
if is_wild(&arms[1].pat);
if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared);
if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared);
if first != second;
then {
let mut applicability = Applicability::MachineApplicable;
let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard {
format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability))
} else {
format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability))
};
span_lint_and_sugg(
cx,
MATCH_LIKE_MATCHES_MACRO,
expr.span,
&format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }),
"try this",
format!(
"{}matches!({}, {})",
if first { "" } else { "!" },
snippet_with_applicability(cx, ex.span, "..", &mut applicability),
pat_and_guard,
),
applicability,
)
}
}
}
/// Extract a `bool` or `{ bool }`
fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
match ex {
ExprKind::Lit(Spanned {
node: LitKind::Bool(b), ..
}) => Some(*b),
ExprKind::Block(
rustc_hir::Block {
stmts: &[],
expr: Some(exp),
..
},
_,
) if desugared => {
if let ExprKind::Lit(Spanned {
node: LitKind::Bool(b), ..
}) = exp.kind
{
Some(b)
} else {
None
}
},
_ => None,
}
}
fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
return; return;
@ -1179,10 +1327,7 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool {
// Checks if arm has the form `None => None` // Checks if arm has the form `None => None`
fn is_none_arm(arm: &Arm<'_>) -> bool { fn is_none_arm(arm: &Arm<'_>) -> bool {
match arm.pat.kind { matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
_ => false,
}
} }
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
@ -1293,6 +1438,229 @@ where
None None
} }
mod redundant_pattern_match {
use super::REDUNDANT_PATTERN_MATCHING;
use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_mir::const_eval::is_const_fn;
use rustc_span::source_map::Symbol;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
match match_source {
MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
_ => {},
}
}
}
fn find_sugg_for_if_let<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
op: &Expr<'_>,
arms: &[Arm<'_>],
keyword: &'static str,
) {
fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
return Some("is_ok()");
}
if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
return Some("is_err()");
}
if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
return Some("is_some()");
}
if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
return Some("is_none()");
}
None
}
let hir_id = expr.hir_id;
let good_method = match arms[0].pat.kind {
PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
if let PatKind::Wild = patterns[0].kind {
find_suggestion(cx, hir_id, path)
} else {
None
}
},
PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
_ => None,
};
let good_method = match good_method {
Some(method) => method,
None => return,
};
// check that `while_let_on_iterator` lint does not trigger
if_chain! {
if keyword == "while";
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
if method_path.ident.name == sym!(next);
if match_trait_method(cx, op, &paths::ITERATOR);
then {
return;
}
}
span_lint_and_then(
cx,
REDUNDANT_PATTERN_MATCHING,
arms[0].pat.span,
&format!("redundant pattern matching, consider using `{}`", good_method),
|diag| {
// while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let expr_span = expr.span;
// while let ... = ... { ... }
// ^^^
let op_span = op.span.source_callsite();
// while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^
let span = expr_span.until(op_span.shrink_to_hi());
diag.span_suggestion(
span,
"try this",
format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
Applicability::MachineApplicable, // snippet
);
},
);
}
fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
if arms.len() == 2 {
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
let hir_id = expr.hir_id;
let found_good_method = match node_pair {
(
PatKind::TupleStruct(ref path_left, ref patterns_left, _),
PatKind::TupleStruct(ref path_right, ref patterns_right, _),
) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
find_good_method_for_match(
arms,
path_left,
path_right,
&paths::RESULT_OK,
&paths::RESULT_ERR,
"is_ok()",
"is_err()",
|| can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
|| can_suggest(cx, hir_id, sym!(result_type), "is_err"),
)
} else {
None
}
},
(PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
| (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
if patterns.len() == 1 =>
{
if let PatKind::Wild = patterns[0].kind {
find_good_method_for_match(
arms,
path_left,
path_right,
&paths::OPTION_SOME,
&paths::OPTION_NONE,
"is_some()",
"is_none()",
|| can_suggest(cx, hir_id, sym!(option_type), "is_some"),
|| can_suggest(cx, hir_id, sym!(option_type), "is_none"),
)
} else {
None
}
},
_ => None,
};
if let Some(good_method) = found_good_method {
span_lint_and_then(
cx,
REDUNDANT_PATTERN_MATCHING,
expr.span,
&format!("redundant pattern matching, consider using `{}`", good_method),
|diag| {
let span = expr.span.to(op.span);
diag.span_suggestion(
span,
"try this",
format!("{}.{}", snippet(cx, op.span, "_"), good_method),
Applicability::MaybeIncorrect, // snippet
);
},
);
}
}
}
#[allow(clippy::too_many_arguments)]
fn find_good_method_for_match<'a>(
arms: &[Arm<'_>],
path_left: &QPath<'_>,
path_right: &QPath<'_>,
expected_left: &[&str],
expected_right: &[&str],
should_be_left: &'a str,
should_be_right: &'a str,
can_suggest_left: impl Fn() -> bool,
can_suggest_right: impl Fn() -> bool,
) -> Option<&'a str> {
let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
(&(*arms[0].body).kind, &(*arms[1].body).kind)
} else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
(&(*arms[1].body).kind, &(*arms[0].body).kind)
} else {
return None;
};
match body_node_pair {
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
(LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
(LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
_ => None,
},
_ => None,
}
}
fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
if !in_constant(cx, hir_id) {
return true;
}
// Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
cx.tcx
.get_diagnostic_item(diag_item)
.and_then(|def_id| {
cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
cx.tcx
.associated_items(*imp)
.in_definition_order()
.find_map(|item| match item.kind {
ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
_ => None,
})
})
})
.map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
}
}
#[test] #[test]
fn test_overlapping() { fn test_overlapping() {
use rustc_span::source_map::DUMMY_SP; use rustc_span::source_map::DUMMY_SP;

View file

@ -1844,10 +1844,10 @@ fn lint_expect_fun_call(
ty::Ref(ty::ReStatic, ..) ty::Ref(ty::ReStatic, ..)
) )
}), }),
hir::ExprKind::Path(ref p) => match cx.qpath_res(p, arg.hir_id) { hir::ExprKind::Path(ref p) => matches!(
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true, cx.qpath_res(p, arg.hir_id),
_ => false, hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _)
}, ),
_ => false, _ => false,
} }
} }
@ -2028,13 +2028,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
.tables() .tables()
.expr_adjustments(arg) .expr_adjustments(arg)
.iter() .iter()
.filter(|adj| { .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
if let ty::adjustment::Adjust::Deref(_) = adj.kind {
true
} else {
false
}
})
.count(); .count();
let derefs: String = iter::repeat('*').take(deref_count).collect(); let derefs: String = iter::repeat('*').take(deref_count).collect();
snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
@ -2044,7 +2038,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
} }
span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| { span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| {
if let Some((text, snip)) = snip { if let Some((text, snip)) = snip {
diag.span_suggestion(expr.span, text, snip, Applicability::Unspecified); diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
} }
}); });
} }
@ -2460,13 +2454,9 @@ fn derefs_to_slice<'tcx>(
ty::Slice(_) => true, ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)),
ty::Array(_, size) => { ty::Array(_, size) => size
if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) { .try_eval_usize(cx.tcx, cx.param_env)
size < 32 .map_or(false, |size| size < 32),
} else {
false
}
},
ty::Ref(_, inner, _) => may_slice(cx, inner), ty::Ref(_, inner, _) => may_slice(cx, inner),
_ => false, _ => false,
} }

View file

@ -77,13 +77,10 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
} }
(true, true) (true, true)
}, },
hir::ExprKind::Block(ref block, _) => { hir::ExprKind::Block(ref block, _) => block
if let Some(expr) = &block.expr { .expr
check_expression(cx, arg_id, &expr) .as_ref()
} else { .map_or((false, false), |expr| check_expression(cx, arg_id, &expr)),
(false, false)
}
},
hir::ExprKind::Match(_, arms, _) => { hir::ExprKind::Match(_, arms, _) => {
let mut found_mapping = false; let mut found_mapping = false;
let mut found_filtering = false; let mut found_filtering = false;

View file

@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MinMaxPass {
} }
} }
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug, Clone, Copy)]
enum MinMax { enum MinMax {
Min, Min,
Max, Max,
@ -86,16 +86,15 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt
if args.len() != 2 { if args.len() != 2 {
return None; return None;
} }
if let Some(c) = constant_simple(cx, cx.tables(), &args[0]) { constant_simple(cx, cx.tables(), &args[0]).map_or_else(
if constant_simple(cx, cx.tables(), &args[1]).is_none() { || constant_simple(cx, cx.tables(), &args[1]).map(|c| (m, c, &args[0])),
// otherwise ignore |c| {
Some((m, c, &args[1])) if constant_simple(cx, cx.tables(), &args[1]).is_none() {
} else { // otherwise ignore
None Some((m, c, &args[1]))
} } else {
} else if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) { None
Some((m, c, &args[0])) }
} else { },
None )
}
} }

View file

@ -3,11 +3,11 @@ use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty, self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
TyKind, UnOp, StmtKind, TyKind, UnOp,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::DesugaringKind; use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::{ExpnKind, Span}; use rustc_span::source_map::{ExpnKind, Span};
@ -371,8 +371,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
if op.is_comparison() { if op.is_comparison() {
check_nan(cx, left, expr); check_nan(cx, left, expr);
check_nan(cx, right, expr); check_nan(cx, right, expr);
check_to_owned(cx, left, right); check_to_owned(cx, left, right, true);
check_to_owned(cx, right, left); check_to_owned(cx, right, left, false);
} }
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
if is_allowed(cx, left) || is_allowed(cx, right) { if is_allowed(cx, left) || is_allowed(cx, right) {
@ -570,11 +570,30 @@ fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
matches!(&walk_ptrs_ty(cx.tables().expr_ty(expr)).kind, ty::Array(_, _)) matches!(&walk_ptrs_ty(cx.tables().expr_ty(expr)).kind, ty::Array(_, _))
} }
fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) { fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
#[derive(Default)]
struct EqImpl {
ty_eq_other: bool,
other_eq_ty: bool,
}
impl EqImpl {
fn is_implemented(&self) -> bool {
self.ty_eq_other || self.other_eq_ty
}
}
fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
})
}
let (arg_ty, snip) = match expr.kind { let (arg_ty, snip) = match expr.kind {
ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => { ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => {
if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) { if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
(cx.tables().expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, "..")) (cx.tables().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
} else { } else {
return; return;
} }
@ -582,7 +601,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
ExprKind::Call(ref path, ref v) if v.len() == 1 => { ExprKind::Call(ref path, ref v) if v.len() == 1 => {
if let ExprKind::Path(ref path) = path.kind { if let ExprKind::Path(ref path) = path.kind {
if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) { if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
(cx.tables().expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, "..")) (cx.tables().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
} else { } else {
return; return;
} }
@ -593,28 +612,19 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
_ => return, _ => return,
}; };
let other_ty = cx.tables().expr_ty_adjusted(other); let other_ty = cx.tables().expr_ty(other);
let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() {
Some(id) => id,
None => return,
};
let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| { let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()]) let with_deref = arg_ty
}); .builtin_deref(true)
let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| { .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()]) .unwrap_or_default();
});
let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]);
if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other { if !with_deref.is_implemented() && !without_deref.is_implemented() {
return; return;
} }
let other_gets_derefed = match other.kind { let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::UnDeref, _));
ExprKind::Unary(UnOp::UnDeref, _) => true,
_ => false,
};
let lint_span = if other_gets_derefed { let lint_span = if other_gets_derefed {
expr.span.to(other.span) expr.span.to(other.span)
@ -634,18 +644,34 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
return; return;
} }
let try_hint = if deref_arg_impl_partial_eq_other { let expr_snip;
// suggest deref on the left let eq_impl;
format!("*{}", snip) if with_deref.is_implemented() {
expr_snip = format!("*{}", snip);
eq_impl = with_deref;
} else { } else {
// suggest dropping the to_owned on the left expr_snip = snip.to_string();
snip.to_string() eq_impl = without_deref;
}; };
let span;
let hint;
if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
span = expr.span;
hint = expr_snip;
} else {
span = expr.span.to(other.span);
if eq_impl.ty_eq_other {
hint = format!("{} == {}", expr_snip, snippet(cx, other.span, ".."));
} else {
hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip);
}
}
diag.span_suggestion( diag.span_suggestion(
lint_span, span,
"try", "try",
try_hint, hint,
Applicability::MachineApplicable, // snippet Applicability::MachineApplicable, // snippet
); );
}, },
@ -656,16 +682,10 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
/// `unused_variables`'s idea /// `unused_variables`'s idea
/// of what it means for an expression to be "used". /// of what it means for an expression to be "used".
fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(parent) = get_parent_expr(cx, expr) { get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind {
match parent.kind { ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => { _ => is_used(cx, parent),
SpanlessEq::new(cx).eq_expr(rhs, expr) })
},
_ => is_used(cx, parent),
}
} else {
true
}
} }
/// Tests whether an expression is in a macro expansion (e.g., something /// Tests whether an expression is in a macro expansion (e.g., something
@ -674,12 +694,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
use rustc_span::hygiene::MacroKind; use rustc_span::hygiene::MacroKind;
if expr.span.from_expansion() { if expr.span.from_expansion() {
let data = expr.span.ctxt().outer_expn_data(); let data = expr.span.ctxt().outer_expn_data();
matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _))
if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind {
true
} else {
false
}
} else { } else {
false false
} }
@ -694,7 +709,7 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
} }
} }
fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) { fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
if_chain! { if_chain! {
if let TyKind::Ptr(ref mut_ty) = ty.kind; if let TyKind::Ptr(ref mut_ty) = ty.kind;
if let ExprKind::Lit(ref lit) = e.kind; if let ExprKind::Lit(ref lit) = e.kind;

View file

@ -641,28 +641,22 @@ fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
); );
} }
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
if let PatKind::Wild = pat.kind {
true
} else {
false
}
}
if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) { if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
if let Some((left_index, left_pat)) = patterns[..rest_index] if let Some((left_index, left_pat)) = patterns[..rest_index]
.iter() .iter()
.rev() .rev()
.take_while(is_wild) .take_while(|pat| matches!(pat.kind, PatKind::Wild))
.enumerate() .enumerate()
.last() .last()
{ {
span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0); span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
} }
if let Some((right_index, right_pat)) = if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last() .iter()
.take_while(|pat| matches!(pat.kind, PatKind::Wild))
.enumerate()
.last()
{ {
span_lint( span_lint(
cx, cx,

View file

@ -71,10 +71,11 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp
fn is_executable(cx: &LateContext<'_>) -> bool { fn is_executable(cx: &LateContext<'_>) -> bool {
use rustc_session::config::CrateType; use rustc_session::config::CrateType;
cx.tcx.sess.crate_types().iter().any(|t: &CrateType| match t { cx.tcx
CrateType::Executable => true, .sess
_ => false, .crate_types()
}) .iter()
.any(|t: &CrateType| matches!(t, CrateType::Executable))
} }
declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);

View file

@ -80,10 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
// can't be implemented for unsafe new // can't be implemented for unsafe new
return; return;
} }
if impl_item.generics.params.iter().any(|gen| match gen.kind { if impl_item
hir::GenericParamKind::Type { .. } => true, .generics
_ => false, .params
}) { .iter()
.any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
{
// when the result of `new()` depends on a type parameter we should not require // when the result of `new()` depends on a type parameter we should not require
// an // an
// impl of `Default` // impl of `Default`

View file

@ -238,10 +238,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
let ty = if needs_check_adjustment { let ty = if needs_check_adjustment {
let adjustments = cx.tables().expr_adjustments(dereferenced_expr); let adjustments = cx.tables().expr_adjustments(dereferenced_expr);
if let Some(i) = adjustments.iter().position(|adj| match adj.kind { if let Some(i) = adjustments
Adjust::Borrow(_) | Adjust::Deref(_) => true, .iter()
_ => false, .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_)))
}) { {
if i == 0 { if i == 0 {
cx.tables().expr_ty(dereferenced_expr) cx.tables().expr_ty(dereferenced_expr)
} else { } else {

View file

@ -0,0 +1,267 @@
use crate::utils;
use crate::utils::sugg::Sugg;
use crate::utils::{match_type, paths, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:**
/// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
/// idiomatically done with `Option::map_or` (if the else bit is a simple
/// expression) or `Option::map_or_else` (if the else bit is a longer
/// block).
///
/// **Why is this bad?**
/// Using the dedicated functions of the Option type is clearer and
/// more concise than an if let expression.
///
/// **Known problems:**
/// This lint uses whether the block is just an expression or if it has
/// more statements to decide whether to use `Option::map_or` or
/// `Option::map_or_else`. If you have a single expression which calls
/// an expensive function, then it would be more efficient to use
/// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
///
/// Also, this lint uses a deliberately conservative metric for checking
/// if the inside of either body contains breaks or continues which will
/// cause it to not suggest a fix if either block contains a loop with
/// continues or breaks contained within the loop.
///
/// **Example:**
///
/// ```rust
/// # let optional: Option<u32> = Some(0);
/// # fn do_complicated_function() -> u32 { 5 };
/// let _ = if let Some(foo) = optional {
/// foo
/// } else {
/// 5
/// };
/// let _ = if let Some(foo) = optional {
/// foo
/// } else {
/// let y = do_complicated_function();
/// y*y
/// };
/// ```
///
/// should be
///
/// ```rust
/// # let optional: Option<u32> = Some(0);
/// # fn do_complicated_function() -> u32 { 5 };
/// let _ = optional.map_or(5, |foo| foo);
/// let _ = optional.map_or_else(||{
/// let y = do_complicated_function();
/// y*y
/// }, |foo| foo);
/// ```
pub OPTION_IF_LET_ELSE,
pedantic,
"reimplementation of Option::map_or"
}
declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
/// Returns true iff the given expression is the result of calling `Result::ok`
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables().expr_ty(&receiver), &paths::RESULT)
} else {
false
}
}
/// A struct containing information about occurences of the
/// `if let Some(..) = .. else` construct that this lint detects.
struct OptionIfLetElseOccurence {
option: String,
method_sugg: String,
some_expr: String,
none_expr: String,
wrap_braces: bool,
}
struct ReturnBreakContinueMacroVisitor {
seen_return_break_continue: bool,
}
impl ReturnBreakContinueMacroVisitor {
fn new() -> ReturnBreakContinueMacroVisitor {
ReturnBreakContinueMacroVisitor {
seen_return_break_continue: false,
}
}
}
impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
if self.seen_return_break_continue {
// No need to look farther if we've already seen one of them
return;
}
match &ex.kind {
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
self.seen_return_break_continue = true;
},
// Something special could be done here to handle while or for loop
// desugaring, as this will detect a break if there's a while loop
// or a for loop inside the expression.
_ => {
if utils::in_macro(ex.span) {
self.seen_return_break_continue = true;
} else {
rustc_hir::intravisit::walk_expr(self, ex);
}
},
}
}
}
fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
recursive_visitor.visit_expr(expression);
recursive_visitor.seen_return_break_continue
}
/// Extracts the body of a given arm. If the arm contains only an expression,
/// then it returns the expression. Otherwise, it returns the entire block
fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
if let ExprKind::Block(
Block {
stmts: statements,
expr: Some(expr),
..
},
_,
) = &arm.body.kind
{
if let [] = statements {
Some(&expr)
} else {
Some(&arm.body)
}
} else {
None
}
}
/// If this is the else body of an if/else expression, then we need to wrap
/// it in curcly braces. Otherwise, we don't.
fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
if let Some(Expr {
kind:
ExprKind::Match(
_,
arms,
MatchSource::IfDesugar {
contains_else_clause: true,
}
| MatchSource::IfLetDesugar {
contains_else_clause: true,
},
),
..
}) = parent.expr
{
expr.hir_id == arms[1].body.hir_id
} else {
false
}
})
}
fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
format!(
"{}{}",
Sugg::hir(cx, cond_expr, "..").maybe_par(),
if as_mut {
".as_mut()"
} else if as_ref {
".as_ref()"
} else {
""
}
)
}
/// If this expression is the option if let/else construct we're detecting, then
/// this function returns an `OptionIfLetElseOccurence` struct with details if
/// this construct is found, or None if this construct is not found.
fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OptionIfLetElseOccurence> {
if_chain! {
if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
if arms.len() == 2;
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
if utils::match_qpath(struct_qpath, &paths::OPTION_SOME);
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
if !contains_return_break_continue_macro(arms[0].body);
if !contains_return_break_continue_macro(arms[1].body);
then {
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
let some_body = extract_body_from_arm(&arms[0])?;
let none_body = extract_body_from_arm(&arms[1])?;
let method_sugg = match &none_body.kind {
ExprKind::Block(..) => "map_or_else",
_ => "map_or",
};
let capture_name = id.name.to_ident_string();
let wrap_braces = should_wrap_in_braces(cx, expr);
let (as_ref, as_mut) = match &cond_expr.kind {
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
_ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
};
let cond_expr = match &cond_expr.kind {
// Pointer dereferencing happens automatically, so we can omit it in the suggestion
ExprKind::Unary(UnOp::UnDeref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
_ => cond_expr,
};
Some(OptionIfLetElseOccurence {
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
method_sugg: method_sugg.to_string(),
some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")),
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
wrap_braces,
})
} else {
None
}
}
}
impl<'a> LateLintPass<'a> for OptionIfLetElse {
fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) {
if let Some(detection) = detect_option_if_let_else(cx, expr) {
span_lint_and_sugg(
cx,
OPTION_IF_LET_ELSE,
expr.span,
format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
"try",
format!(
"{}{}.{}({}, {}){}",
if detection.wrap_braces { "{ " } else { "" },
detection.option,
detection.method_sugg,
detection.none_expr,
detection.some_expr,
if detection.wrap_braces { " }" } else { "" },
),
Applicability::MaybeIncorrect,
);
}
}
}

View file

@ -0,0 +1,311 @@
use crate::utils::{last_path_segment, span_lint_and_help};
use rustc_hir::{
intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
QPath, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
declare_clippy_lint! {
/// **What it does:** Checks for patterns that aren't exact representations of the types
/// they are applied to.
///
/// To satisfy this lint, you will have to adjust either the expression that is matched
/// against or the pattern itself, as well as the bindings that are introduced by the
/// adjusted patterns. For matching you will have to either dereference the expression
/// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
/// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
/// to use the inverse. You can leave them as plain bindings if you wish for the value
/// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
/// a reference into the matched structure.
///
/// If you are looking for a way to learn about ownership semantics in more detail, it
/// is recommended to look at IDE options available to you to highlight types, lifetimes
/// and reference semantics in your code. The available tooling would expose these things
/// in a general way even outside of the various pattern matching mechanics. Of course
/// this lint can still be used to highlight areas of interest and ensure a good understanding
/// of ownership semantics.
///
/// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable
/// because it increases ownership hints in the code, and will guard against some changes
/// in ownership.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// This example shows the basic adjustments necessary to satisfy the lint. Note how
/// the matched expression is explicitly dereferenced with `*` and the `inner` variable
/// is bound to a shared borrow via `ref inner`.
///
/// ```rust,ignore
/// // Bad
/// let value = &Some(Box::new(23));
/// match value {
/// Some(inner) => println!("{}", inner),
/// None => println!("none"),
/// }
///
/// // Good
/// let value = &Some(Box::new(23));
/// match *value {
/// Some(ref inner) => println!("{}", inner),
/// None => println!("none"),
/// }
/// ```
///
/// The following example demonstrates one of the advantages of the more verbose style.
/// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
/// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
/// accidentally modify the wrong part of the structure.
///
/// ```rust,ignore
/// // Bad
/// let mut values = vec![(2, 3), (3, 4)];
/// for (a, b) in &mut values {
/// *a += *b;
/// }
///
/// // Good
/// let mut values = vec![(2, 3), (3, 4)];
/// for &mut (ref mut a, b) in &mut values {
/// *a += b;
/// }
/// ```
pub PATTERN_TYPE_MISMATCH,
restriction,
"type of pattern does not match the expression type"
}
declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if let StmtKind::Local(ref local) = stmt.kind {
if let Some(init) = &local.init {
if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) {
let pat = &local.pat;
if in_external_macro(cx.sess(), pat.span) {
return;
}
let deref_possible = match local.source {
LocalSource::Normal => DerefPossible::Possible,
_ => DerefPossible::Impossible,
};
apply_lint(cx, pat, init_ty, deref_possible);
}
}
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(ref expr, arms, source) = expr.kind {
match source {
MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
if let Some(expr_ty) = cx.tables().node_type_opt(expr.hir_id) {
'pattern_checks: for arm in arms {
let pat = &arm.pat;
if in_external_macro(cx.sess(), pat.span) {
continue 'pattern_checks;
}
if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
break 'pattern_checks;
}
}
}
},
_ => (),
}
}
}
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
_: intravisit::FnKind<'tcx>,
_: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
_: Span,
hir_id: HirId,
) {
if let Some(fn_sig) = cx.tables().liberated_fn_sigs().get(hir_id) {
for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) {
apply_lint(cx, &param.pat, ty, DerefPossible::Impossible);
}
}
}
}
#[derive(Debug, Clone, Copy)]
enum DerefPossible {
Possible,
Impossible,
}
fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
if let Some((span, mutability, level)) = maybe_mismatch {
span_lint_and_help(
cx,
PATTERN_TYPE_MISMATCH,
span,
"type of pattern does not match the expression type",
None,
&format!(
"{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
match (deref_possible, level) {
(DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
_ => "",
},
match mutability {
Mutability::Mut => "&mut _",
Mutability::Not => "&_",
},
),
);
true
} else {
false
}
}
#[derive(Debug, Copy, Clone)]
enum Level {
Top,
Lower,
}
#[allow(rustc::usage_of_ty_tykind)]
fn find_first_mismatch<'tcx>(
cx: &LateContext<'tcx>,
pat: &Pat<'_>,
ty: Ty<'tcx>,
level: Level,
) -> Option<(Span, Mutability, Level)> {
if let PatKind::Ref(ref sub_pat, _) = pat.kind {
if let TyKind::Ref(_, sub_ty, _) = ty.kind {
return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
}
}
if let TyKind::Ref(_, _, mutability) = ty.kind {
if is_non_ref_pattern(&pat.kind) {
return Some((pat.span, mutability, level));
}
}
if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
if let Some(variant) = get_variant(adt_def, qpath) {
let field_defs = &variant.fields;
return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
}
}
}
if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
if let Some(variant) = get_variant(adt_def, qpath) {
let field_defs = &variant.fields;
let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
return find_first_mismatch_in_tuple(cx, pats, ty_iter);
}
}
}
if let PatKind::Tuple(ref pats, _) = pat.kind {
if let TyKind::Tuple(..) = ty.kind {
return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
}
}
if let PatKind::Or(sub_pats) = pat.kind {
for pat in sub_pats {
let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
if let Some(mismatch) = maybe_mismatch {
return Some(mismatch);
}
}
}
None
}
fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
if adt_def.is_struct() {
if let Some(variant) = adt_def.variants.iter().next() {
return Some(variant);
}
}
if adt_def.is_enum() {
let pat_ident = last_path_segment(qpath).ident;
for variant in &adt_def.variants {
if variant.ident == pat_ident {
return Some(variant);
}
}
}
None
}
fn find_first_mismatch_in_tuple<'tcx, I>(
cx: &LateContext<'tcx>,
pats: &[&Pat<'_>],
ty_iter_src: I,
) -> Option<(Span, Mutability, Level)>
where
I: IntoIterator<Item = Ty<'tcx>>,
{
let mut field_tys = ty_iter_src.into_iter();
'fields: for pat in pats {
let field_ty = if let Some(ty) = field_tys.next() {
ty
} else {
break 'fields;
};
let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
if let Some(mismatch) = maybe_mismatch {
return Some(mismatch);
}
}
None
}
fn find_first_mismatch_in_struct<'tcx>(
cx: &LateContext<'tcx>,
field_pats: &[FieldPat<'_>],
field_defs: &[FieldDef],
substs_ref: SubstsRef<'tcx>,
) -> Option<(Span, Mutability, Level)> {
for field_pat in field_pats {
'definitions: for field_def in field_defs {
if field_pat.ident == field_def.ident {
let field_ty = field_def.ty(cx.tcx, substs_ref);
let pat = &field_pat.pat;
let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
if let Some(mismatch) = maybe_mismatch {
return Some(mismatch);
}
break 'definitions;
}
}
}
None
}
fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
match pat_kind {
PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
_ => false,
}
}

View file

@ -148,17 +148,11 @@ fn is_arith_expr(expr: &Expr) -> bool {
#[must_use] #[must_use]
fn is_bit_op(op: BinOpKind) -> bool { fn is_bit_op(op: BinOpKind) -> bool {
use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr}; use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr};
match op { matches!(op, BitXor | BitAnd | BitOr | Shl | Shr)
BitXor | BitAnd | BitOr | Shl | Shr => true,
_ => false,
}
} }
#[must_use] #[must_use]
fn is_arith_op(op: BinOpKind) -> bool { fn is_arith_op(op: BinOpKind) -> bool {
use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub}; use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub};
match op { matches!(op, Add | Sub | Mul | Div | Rem)
Add | Sub | Mul | Div | Rem => true,
_ => false,
}
} }

View file

@ -52,6 +52,11 @@ declare_clippy_lint! {
/// exclusive ranges, because they essentially add an extra branch that /// exclusive ranges, because they essentially add an extra branch that
/// LLVM may fail to hoist out of the loop. /// LLVM may fail to hoist out of the loop.
/// ///
/// This will cause a warning that cannot be fixed if the consumer of the
/// range only accepts a specific range type, instead of the generic
/// `RangeBounds` trait
/// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
///
/// **Example:** /// **Example:**
/// ```rust,ignore /// ```rust,ignore
/// for x..(y+1) { .. } /// for x..(y+1) { .. }
@ -72,7 +77,10 @@ declare_clippy_lint! {
/// **Why is this bad?** The code is more readable with an exclusive range /// **Why is this bad?** The code is more readable with an exclusive range
/// like `x..y`. /// like `x..y`.
/// ///
/// **Known problems:** None. /// **Known problems:** This will cause a warning that cannot be fixed if
/// the consumer of the range only accepts a specific range type, instead of
/// the generic `RangeBounds` trait
/// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
/// ///
/// **Example:** /// **Example:**
/// ```rust,ignore /// ```rust,ignore
@ -83,7 +91,7 @@ declare_clippy_lint! {
/// for x..y { .. } /// for x..y { .. }
/// ``` /// ```
pub RANGE_MINUS_ONE, pub RANGE_MINUS_ONE,
complexity, pedantic,
"`x..=(y-1)` reads better as `x..y`" "`x..=(y-1)` reads better as `x..y`"
} }

View file

@ -1,260 +0,0 @@
use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_mir::const_eval::is_const_fn;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Symbol;
declare_clippy_lint! {
/// **What it does:** Lint for redundant pattern matching over `Result` or
/// `Option`
///
/// **Why is this bad?** It's more concise and clear to just use the proper
/// utility function
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// if let Ok(_) = Ok::<i32, i32>(42) {}
/// if let Err(_) = Err::<i32, i32>(42) {}
/// if let None = None::<()> {}
/// if let Some(_) = Some(42) {}
/// match Ok::<i32, i32>(42) {
/// Ok(_) => true,
/// Err(_) => false,
/// };
/// ```
///
/// The more idiomatic use would be:
///
/// ```rust
/// if Ok::<i32, i32>(42).is_ok() {}
/// if Err::<i32, i32>(42).is_err() {}
/// if None::<()>.is_none() {}
/// if Some(42).is_some() {}
/// Ok::<i32, i32>(42).is_ok();
/// ```
pub REDUNDANT_PATTERN_MATCHING,
style,
"use the proper utility function avoiding an `if let`"
}
declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]);
impl<'tcx> LateLintPass<'tcx> for RedundantPatternMatching {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
match match_source {
MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
_ => return,
}
}
}
}
fn find_sugg_for_if_let<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
op: &Expr<'_>,
arms: &[Arm<'_>],
keyword: &'static str,
) {
fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
return Some("is_ok()");
}
if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
return Some("is_err()");
}
if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
return Some("is_some()");
}
if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
return Some("is_none()");
}
None
}
let hir_id = expr.hir_id;
let good_method = match arms[0].pat.kind {
PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
if let PatKind::Wild = patterns[0].kind {
find_suggestion(cx, hir_id, path)
} else {
None
}
},
PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
_ => None,
};
let good_method = match good_method {
Some(method) => method,
None => return,
};
// check that `while_let_on_iterator` lint does not trigger
if_chain! {
if keyword == "while";
if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
if method_path.ident.name == sym!(next);
if match_trait_method(cx, op, &paths::ITERATOR);
then {
return;
}
}
span_lint_and_then(
cx,
REDUNDANT_PATTERN_MATCHING,
arms[0].pat.span,
&format!("redundant pattern matching, consider using `{}`", good_method),
|diag| {
// while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let expr_span = expr.span;
// while let ... = ... { ... }
// ^^^
let op_span = op.span.source_callsite();
// while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^
let span = expr_span.until(op_span.shrink_to_hi());
diag.span_suggestion(
span,
"try this",
format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
Applicability::MachineApplicable, // snippet
);
},
);
}
fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
if arms.len() == 2 {
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
let hir_id = expr.hir_id;
let found_good_method = match node_pair {
(
PatKind::TupleStruct(ref path_left, ref patterns_left, _),
PatKind::TupleStruct(ref path_right, ref patterns_right, _),
) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
find_good_method_for_match(
arms,
path_left,
path_right,
&paths::RESULT_OK,
&paths::RESULT_ERR,
"is_ok()",
"is_err()",
|| can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
|| can_suggest(cx, hir_id, sym!(result_type), "is_err"),
)
} else {
None
}
},
(PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
| (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
if patterns.len() == 1 =>
{
if let PatKind::Wild = patterns[0].kind {
find_good_method_for_match(
arms,
path_left,
path_right,
&paths::OPTION_SOME,
&paths::OPTION_NONE,
"is_some()",
"is_none()",
|| can_suggest(cx, hir_id, sym!(option_type), "is_some"),
|| can_suggest(cx, hir_id, sym!(option_type), "is_none"),
)
} else {
None
}
},
_ => None,
};
if let Some(good_method) = found_good_method {
span_lint_and_then(
cx,
REDUNDANT_PATTERN_MATCHING,
expr.span,
&format!("redundant pattern matching, consider using `{}`", good_method),
|diag| {
let span = expr.span.to(op.span);
diag.span_suggestion(
span,
"try this",
format!("{}.{}", snippet(cx, op.span, "_"), good_method),
Applicability::MaybeIncorrect, // snippet
);
},
);
}
}
}
#[allow(clippy::too_many_arguments)]
fn find_good_method_for_match<'a>(
arms: &[Arm<'_>],
path_left: &QPath<'_>,
path_right: &QPath<'_>,
expected_left: &[&str],
expected_right: &[&str],
should_be_left: &'a str,
should_be_right: &'a str,
can_suggest_left: impl Fn() -> bool,
can_suggest_right: impl Fn() -> bool,
) -> Option<&'a str> {
let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
(&(*arms[0].body).kind, &(*arms[1].body).kind)
} else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
(&(*arms[1].body).kind, &(*arms[0].body).kind)
} else {
return None;
};
match body_node_pair {
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
(LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
(LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
_ => None,
},
_ => None,
}
}
fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
if !in_constant(cx, hir_id) {
return true;
}
// Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
cx.tcx
.get_diagnostic_item(diag_item)
.and_then(|def_id| {
cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
cx.tcx
.associated_items(*imp)
.in_definition_order()
.find_map(|item| match item.kind {
ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
_ => None,
})
})
})
.map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
}

View file

@ -1,9 +1,9 @@
use crate::consts::{constant, Constant}; use crate::consts::{constant, Constant};
use crate::utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_lint_and_help}; use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{LitKind, StrStyle}; use rustc_ast::ast::{LitKind, StrStyle};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Block, BorrowKind, Crate, Expr, ExprKind, HirId}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{BytePos, Span}; use rustc_span::source_map::{BytePos, Span};
@ -46,66 +46,15 @@ declare_clippy_lint! {
"trivial regular expressions" "trivial regular expressions"
} }
declare_clippy_lint! {
/// **What it does:** Checks for usage of `regex!(_)` which (as of now) is
/// usually slower than `Regex::new(_)` unless called in a loop (which is a bad
/// idea anyway).
///
/// **Why is this bad?** Performance, at least for now. The macro version is
/// likely to catch up long-term, but for now the dynamic version is faster.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```ignore
/// regex!("foo|bar")
/// ```
pub REGEX_MACRO,
style,
"use of `regex!(_)` instead of `Regex::new(_)`"
}
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Regex { pub struct Regex {
spans: FxHashSet<Span>, spans: FxHashSet<Span>,
last: Option<HirId>, last: Option<HirId>,
} }
impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]); impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
impl<'tcx> LateLintPass<'tcx> for Regex { impl<'tcx> LateLintPass<'tcx> for Regex {
fn check_crate(&mut self, _: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
self.spans.clear();
}
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
if_chain! {
if self.last.is_none();
if let Some(ref expr) = block.expr;
if match_type(cx, cx.tables().expr_ty(expr), &paths::REGEX);
if let Some(span) = is_expn_of(expr.span, "regex");
then {
if !self.spans.contains(&span) {
span_lint(
cx,
REGEX_MACRO,
span,
"`regex!(_)` found. \
Please use `Regex::new(_)`, which is faster for now."
);
self.spans.insert(span);
}
self.last = Some(block.hir_id);
}
}
}
fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'_>) {
if self.last.map_or(false, |id| block.hir_id == id) {
self.last = None;
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { if_chain! {
if let ExprKind::Call(ref fun, ref args) = expr.kind; if let ExprKind::Call(ref fun, ref args) = expr.kind;
@ -150,12 +99,7 @@ fn is_trivial_regex(s: &regex_syntax::hir::Hir) -> Option<&'static str> {
use regex_syntax::hir::Anchor::{EndText, StartText}; use regex_syntax::hir::Anchor::{EndText, StartText};
use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal}; use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal};
let is_literal = |e: &[regex_syntax::hir::Hir]| { let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_)));
e.iter().all(|e| match *e.kind() {
Literal(_) => true,
_ => false,
})
};
match *s.kind() { match *s.kind() {
Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"), Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"),

View file

@ -0,0 +1,82 @@
use crate::consts::{constant_context, Constant};
use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
/// - `.to_string()` for `str`
/// - `.clone()` for `String`
/// - `.to_vec()` for `slice`
///
/// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// fn main() {
/// let x = String::from("hello world").repeat(1);
/// }
/// ```
/// Use instead:
/// ```rust
/// fn main() {
/// let x = String::from("hello world").clone();
/// }
/// ```
pub REPEAT_ONCE,
complexity,
"using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
}
declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
if path.ident.name == sym!(repeat);
if let Some(Constant::Int(1)) = constant_context(cx, cx.tables()).expr(&args[1]);
if !in_macro(args[0].span);
then {
let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0]));
if ty.is_str() {
span_lint_and_sugg(
cx,
REPEAT_ONCE,
expr.span,
"calling `repeat(1)` on str",
"consider using `.to_string()` instead",
format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)),
Applicability::MachineApplicable,
);
} else if ty.builtin_index().is_some() {
span_lint_and_sugg(
cx,
REPEAT_ONCE,
expr.span,
"calling `repeat(1)` on slice",
"consider using `.to_vec()` instead",
format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)),
Applicability::MachineApplicable,
);
} else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
span_lint_and_sugg(
cx,
REPEAT_ONCE,
expr.span,
"calling `repeat(1)` on a string literal",
"consider using `.clone()` instead",
format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)),
Applicability::MachineApplicable,
);
}
}
}
}
}

View file

@ -259,15 +259,15 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
if let Some(rpos) = fn_source.rfind("->") { fn_source
#[allow(clippy::cast_possible_truncation)] .rfind("->")
( .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), (
Applicability::MachineApplicable, #[allow(clippy::cast_possible_truncation)]
) ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
} else { Applicability::MachineApplicable,
(ty.span, Applicability::MaybeIncorrect) )
} })
} else { } else {
(ty.span, Applicability::MaybeIncorrect) (ty.span, Applicability::MaybeIncorrect)
}; };

View file

@ -165,14 +165,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &
fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool { fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool {
let var_ty = cx.tables().node_type_opt(pat_id); let var_ty = cx.tables().node_type_opt(pat_id);
if let Some(var_ty) = var_ty { var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..)))
match var_ty.kind {
ty::Adt(..) => false,
_ => true,
}
} else {
false
}
} }
fn check_pat<'tcx>( fn check_pat<'tcx>(

View file

@ -25,13 +25,7 @@ declare_clippy_lint! {
fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match &expr.kind { match &expr.kind {
ExprKind::Struct(..) | ExprKind::Tup(..) => true, ExprKind::Struct(..) | ExprKind::Tup(..) => true,
ExprKind::Path(qpath) => { ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)),
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(qpath, expr.hir_id) {
true
} else {
false
}
},
_ => false, _ => false,
} }
} }

View file

@ -1,19 +1,19 @@
use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash}; use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{GenericBound, Generics, WherePredicate}; use rustc_hir::{GenericBound, Generics, WherePredicate};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
#[derive(Copy, Clone)]
pub struct TraitBounds;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
/// ///
/// **Why is this bad?** Repeating the type for every bound makes the code /// **Why is this bad?** Repeating the type for every bound makes the code
/// less readable than combining the bounds /// less readable than combining the bounds
/// ///
/// **Known problems:** None.
///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// pub fn foo<T>(t: T) where T: Copy, T: Clone {} /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
@ -29,6 +29,18 @@ declare_clippy_lint! {
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
} }
#[derive(Copy, Clone)]
pub struct TraitBounds {
max_trait_bounds: u64,
}
impl TraitBounds {
#[must_use]
pub fn new(max_trait_bounds: u64) -> Self {
Self { max_trait_bounds }
}
}
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
impl<'tcx> LateLintPass<'tcx> for TraitBounds { impl<'tcx> LateLintPass<'tcx> for TraitBounds {
@ -44,9 +56,14 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
let mut map = FxHashMap::default(); let mut map = FxHashMap::default();
let mut applicability = Applicability::MaybeIncorrect; let mut applicability = Applicability::MaybeIncorrect;
for bound in gen.where_clause.predicates { for bound in gen.where_clause.predicates {
if let WherePredicate::BoundPredicate(ref p) = bound { if_chain! {
if let WherePredicate::BoundPredicate(ref p) = bound;
if p.bounds.len() as u64 <= self.max_trait_bounds;
if !in_macro(p.span);
let h = hash(&p.bounded_ty); let h = hash(&p.bounded_ty);
if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) { if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
then {
let mut hint_string = format!( let mut hint_string = format!(
"consider combining the bounds: `{}:", "consider combining the bounds: `{}:",
snippet(cx, p.bounded_ty.span, "_") snippet(cx, p.bounded_ty.span, "_")

View file

@ -775,11 +775,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg {
.iter() .iter()
.filter(|arg| { .filter(|arg| {
if is_unit(cx.tables().expr_ty(arg)) && !is_unit_literal(arg) { if is_unit(cx.tables().expr_ty(arg)) && !is_unit_literal(arg) {
if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind { !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar))
false
} else {
true
}
} else { } else {
false false
} }
@ -899,17 +895,11 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
} }
fn is_unit(ty: Ty<'_>) -> bool { fn is_unit(ty: Ty<'_>) -> bool {
match ty.kind { matches!(ty.kind, ty::Tuple(slice) if slice.is_empty())
ty::Tuple(slice) if slice.is_empty() => true,
_ => false,
}
} }
fn is_unit_literal(expr: &Expr<'_>) -> bool { fn is_unit_literal(expr: &Expr<'_>) -> bool {
match expr.kind { matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty())
ExprKind::Tup(ref slice) if slice.is_empty() => true,
_ => false,
}
} }
declare_clippy_lint! { declare_clippy_lint! {
@ -1154,10 +1144,7 @@ fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
} }
fn is_isize_or_usize(typ: Ty<'_>) -> bool { fn is_isize_or_usize(typ: Ty<'_>) -> bool {
match typ.kind { matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true,
_ => false,
}
} }
fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) { fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) {
@ -1205,16 +1192,19 @@ fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast
// has parens on the outside, they are no longer needed. // has parens on the outside, they are no longer needed.
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let opt = snippet_opt(cx, op.span); let opt = snippet_opt(cx, op.span);
let sugg = if let Some(ref snip) = opt { let sugg = opt.as_ref().map_or_else(
if should_strip_parens(op, snip) { || {
&snip[1..snip.len() - 1] applicability = Applicability::HasPlaceholders;
} else { ".."
snip.as_str() },
} |snip| {
} else { if should_strip_parens(op, snip) {
applicability = Applicability::HasPlaceholders; &snip[1..snip.len() - 1]
".." } else {
}; snip.as_str()
}
},
);
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -1734,10 +1724,10 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
TyKind::TraitObject(ref param_bounds, _) => { TyKind::TraitObject(ref param_bounds, _) => {
let has_lifetime_parameters = param_bounds.iter().any(|bound| { let has_lifetime_parameters = param_bounds.iter().any(|bound| {
bound.bound_generic_params.iter().any(|gen| match gen.kind { bound
GenericParamKind::Lifetime { .. } => true, .bound_generic_params
_ => false, .iter()
}) .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. }))
}); });
if has_lifetime_parameters { if has_lifetime_parameters {
// complex trait bounds like A<'a, 'b> // complex trait bounds like A<'a, 'b>

View file

@ -58,10 +58,10 @@ declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COM
impl LateLintPass<'_> for UnnamedAddress { impl LateLintPass<'_> for UnnamedAddress {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
fn is_comparison(binop: BinOpKind) -> bool { fn is_comparison(binop: BinOpKind) -> bool {
match binop { matches!(
BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true, binop,
_ => false, BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt
} )
} }
fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
@ -72,11 +72,7 @@ impl LateLintPass<'_> for UnnamedAddress {
} }
fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ty::FnDef(..) = cx.tables().expr_ty(expr).kind { matches!(cx.tables().expr_ty(expr).kind, ty::FnDef(..))
true
} else {
false
}
} }
if_chain! { if_chain! {

View file

@ -5,24 +5,23 @@ use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, subst::GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** /// **What it does:**
/// Detects when people use `Vec::sort_by` and pass in a function /// Detects uses of `Vec::sort_by` passing in a closure
/// which compares the two arguments, either directly or indirectly. /// which compares the two arguments, either directly or indirectly.
/// ///
/// **Why is this bad?** /// **Why is this bad?**
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
/// possible) than to use `Vec::sort_by` and and a more complicated /// possible) than to use `Vec::sort_by` and a more complicated
/// closure. /// closure.
/// ///
/// **Known problems:** /// **Known problems:**
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
/// imported by a use statement in the current frame, then a `use` /// imported by a use statement, then it will need to be added manually.
/// statement that imports it will need to be added (which this lint
/// can't do).
/// ///
/// **Example:** /// **Example:**
/// ///
@ -201,28 +200,41 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
}; };
let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
let unstable = name == "sort_unstable_by"; let unstable = name == "sort_unstable_by";
if_chain! { if_chain! {
if let ExprKind::Path(QPath::Resolved(_, Path { if let ExprKind::Path(QPath::Resolved(_, Path {
segments: [PathSegment { ident: left_name, .. }], .. segments: [PathSegment { ident: left_name, .. }], ..
})) = &left_expr.kind; })) = &left_expr.kind;
if left_name == left_ident; if left_name == left_ident;
then { then {
Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
} } else {
else { if !key_returns_borrow(cx, left_expr) {
Some(LintTrigger::SortByKey(SortByKeyDetection { return Some(LintTrigger::SortByKey(SortByKeyDetection {
vec_name, vec_name,
unstable, unstable,
closure_arg, closure_arg,
closure_body, closure_body,
reverse reverse
})) }))
}
} }
} }
} else {
None
} }
} }
None
}
fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(def_id) = utils::fn_def_id(cx, expr) {
let output = cx.tcx.fn_sig(def_id).output();
let ty = output.skip_binder();
return matches!(ty.kind, ty::Ref(..))
|| ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
}
false
} }
impl LateLintPass<'_> for UnnecessarySortBy { impl LateLintPass<'_> for UnnecessarySortBy {

View file

@ -72,8 +72,8 @@ impl EarlyLintPass for UnnestedOrPatterns {
} }
fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
if !cx.sess.opts.unstable_features.is_nightly_build() { if !cx.sess.features_untracked().or_patterns {
// User cannot do `#![feature(or_patterns)]`, so bail. // Do not suggest nesting the patterns if the feature `or_patterns` is not enabled.
return; return;
} }
@ -400,8 +400,8 @@ fn extend_with_matching(
/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool { fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. ps1.len() == ps2.len()
&& ps1.len() == ps2.len() && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
&& over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
&& over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
} }

View file

@ -167,14 +167,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind; if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind;
then { then {
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
let should_check = if let Some(ref params) = *parameters { let should_check = parameters.as_ref().map_or(
!params.parenthesized && !params.args.iter().any(|arg| match arg { true,
GenericArg::Lifetime(_) => true, |params| !params.parenthesized
_ => false, &&!params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
}) );
} else {
true
};
if should_check { if should_check {
let visitor = &mut UseSelfVisitor { let visitor = &mut UseSelfVisitor {

View file

@ -387,10 +387,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
} }
pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
match (l, r) { matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)))
(Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) => true,
_ => false,
}
} }
pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool { pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {

View file

@ -65,42 +65,45 @@ pub fn get_attr<'a>(
}; };
let attr_segments = &attr.path.segments; let attr_segments = &attr.path.segments;
if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
if let Some(deprecation_status) = BUILTIN_ATTRIBUTES
BUILTIN_ATTRIBUTES .iter()
.iter() .find_map(|(builtin_name, deprecation_status)| {
.find_map(|(builtin_name, deprecation_status)| { if *builtin_name == attr_segments[1].ident.to_string() {
if *builtin_name == attr_segments[1].ident.to_string() { Some(deprecation_status)
Some(deprecation_status) } else {
} else { None
None }
})
.map_or_else(
|| {
sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
false
},
|deprecation_status| {
let mut diag =
sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
match *deprecation_status {
DeprecationStatus::Deprecated => {
diag.emit();
false
},
DeprecationStatus::Replaced(new_name) => {
diag.span_suggestion(
attr_segments[1].ident.span,
"consider using",
new_name.to_string(),
Applicability::MachineApplicable,
);
diag.emit();
false
},
DeprecationStatus::None => {
diag.cancel();
attr_segments[1].ident.to_string() == name
},
} }
})
{
let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
match *deprecation_status {
DeprecationStatus::Deprecated => {
diag.emit();
false
}, },
DeprecationStatus::Replaced(new_name) => { )
diag.span_suggestion(
attr_segments[1].ident.span,
"consider using",
new_name.to_string(),
Applicability::MachineApplicable,
);
diag.emit();
false
},
DeprecationStatus::None => {
diag.cancel();
attr_segments[1].ident.to_string() == name
},
}
} else {
sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
false
}
} else { } else {
false false
} }

View file

@ -156,6 +156,8 @@ define_Conf! {
(array_size_threshold, "array_size_threshold": u64, 512_000), (array_size_threshold, "array_size_threshold": u64, 512_000),
/// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
(vec_box_size_threshold, "vec_box_size_threshold": u64, 4096), (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
/// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
(max_trait_bounds, "max_trait_bounds": u64, 3),
/// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
(max_struct_bools, "max_struct_bools": u64, 3), (max_struct_bools, "max_struct_bools": u64, 3),
/// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have

View file

@ -703,6 +703,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
} }
for segment in path.segments { for segment in path.segments {
segment.ident.name.hash(&mut self.s); segment.ident.name.hash(&mut self.s);
self.hash_generic_args(segment.generic_args().args);
} }
}, },
QPath::TypeRelative(ref ty, ref segment) => { QPath::TypeRelative(ref ty, ref segment) => {
@ -711,13 +712,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}, },
}, },
TyKind::OpaqueDef(_, arg_list) => { TyKind::OpaqueDef(_, arg_list) => {
for arg in *arg_list { self.hash_generic_args(arg_list);
match arg {
GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
GenericArg::Type(ref ty) => self.hash_ty(&ty),
GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
}
}
}, },
TyKind::TraitObject(_, lifetime) => { TyKind::TraitObject(_, lifetime) => {
self.hash_lifetime(lifetime); self.hash_lifetime(lifetime);
@ -735,4 +730,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(&self.cx.tcx.hir().body(body_id).value); self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
self.maybe_typeck_tables = old_maybe_typeck_tables; self.maybe_typeck_tables = old_maybe_typeck_tables;
} }
fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
for arg in arg_list {
match arg {
GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
GenericArg::Type(ref ty) => self.hash_ty(&ty),
GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
}
}
}
} }

View file

@ -102,11 +102,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
#[must_use] #[must_use]
pub fn in_macro(span: Span) -> bool { pub fn in_macro(span: Span) -> bool {
if span.from_expansion() { if span.from_expansion() {
if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind { !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
false
} else {
true
}
} else { } else {
false false
} }
@ -127,10 +123,7 @@ pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
/// Checks if given pattern is a wildcard (`_`) /// Checks if given pattern is a wildcard (`_`)
pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool { pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
match pat.kind { matches!(pat.kind, PatKind::Wild)
PatKind::Wild => true,
_ => false,
}
} }
/// Checks if type is struct, enum or union type with the given def path. /// Checks if type is struct, enum or union type with the given def path.
@ -153,11 +146,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
let def_id = cx.tables().type_dependent_def_id(expr.hir_id).unwrap(); let def_id = cx.tables().type_dependent_def_id(expr.hir_id).unwrap();
let trt_id = cx.tcx.trait_of_item(def_id); let trt_id = cx.tcx.trait_of_item(def_id);
if let Some(trt_id) = trt_id { trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
match_def_path(cx, trt_id, path)
} else {
false
}
} }
/// Checks if an expression references a variable of the given name. /// Checks if an expression references a variable of the given name.
@ -600,21 +589,15 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
/// // ^^^^^^^^^^ /// // ^^^^^^^^^^
/// ``` /// ```
pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span { pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
if let Some(first_char_pos) = first_char_in_first_line(cx, span) { first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
span.with_lo(first_char_pos)
} else {
span
}
} }
fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> { fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
let line_span = line_span(cx, span); let line_span = line_span(cx, span);
if let Some(snip) = snippet_opt(cx, line_span) { snippet_opt(cx, line_span).and_then(|snip| {
snip.find(|c: char| !c.is_whitespace()) snip.find(|c: char| !c.is_whitespace())
.map(|pos| line_span.lo() + BytePos::from_usize(pos)) .map(|pos| line_span.lo() + BytePos::from_usize(pos))
} else { })
None
}
} }
/// Returns the indentation of the line of a span /// Returns the indentation of the line of a span
@ -626,11 +609,7 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
/// // ^^ -- will return 4 /// // ^^ -- will return 4
/// ``` /// ```
pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
if let Some(snip) = snippet_opt(cx, line_span(cx, span)) { snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
snip.find(|c: char| !c.is_whitespace())
} else {
None
}
} }
/// Extends the span to the beginning of the spans line, incl. whitespaces. /// Extends the span to the beginning of the spans line, incl. whitespaces.
@ -738,25 +717,21 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
let enclosing_node = map let enclosing_node = map
.get_enclosing_scope(hir_id) .get_enclosing_scope(hir_id)
.and_then(|enclosing_id| map.find(enclosing_id)); .and_then(|enclosing_id| map.find(enclosing_id));
if let Some(node) = enclosing_node { enclosing_node.and_then(|node| match node {
match node { Node::Block(block) => Some(block),
Node::Block(block) => Some(block), Node::Item(&Item {
Node::Item(&Item { kind: ItemKind::Fn(_, _, eid),
kind: ItemKind::Fn(_, _, eid), ..
.. })
}) | Node::ImplItem(&ImplItem {
| Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(_, eid),
kind: ImplItemKind::Fn(_, eid), ..
.. }) => match cx.tcx.hir().body(eid).value.kind {
}) => match cx.tcx.hir().body(eid).value.kind { ExprKind::Block(ref block, _) => Some(block),
ExprKind::Block(ref block, _) => Some(block),
_ => None,
},
_ => None, _ => None,
} },
} else { _ => None,
None })
}
} }
/// Returns the base type for HIR references and pointers. /// Returns the base type for HIR references and pointers.
@ -1328,11 +1303,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
_ => None, _ => None,
}; };
if let Some(did) = did { did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
must_use_attr(&cx.tcx.get_attrs(did)).is_some()
} else {
false
}
} }
pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
@ -1385,6 +1356,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
) )
} }
/// Returns the `DefId` of the callee if the given expression is a function or method call.
pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
match &expr.kind {
ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id),
ExprKind::Call(
Expr {
kind: ExprKind::Path(qpath),
..
},
..,
) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(),
_ => None,
}
}
pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool { pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
lints.iter().any(|lint| { lints.iter().any(|lint| {
matches!( matches!(

View file

@ -51,7 +51,7 @@ impl<'a> NumericLiteral<'a> {
pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> { pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) { if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
let (unsuffixed, suffix) = split_suffix(&src, lit_kind); let (unsuffixed, suffix) = split_suffix(&src, lit_kind);
let float = if let LitKind::Float(..) = lit_kind { true } else { false }; let float = matches!(lit_kind, LitKind::Float(..));
Some(NumericLiteral::new(unsuffixed, suffix, float)) Some(NumericLiteral::new(unsuffixed, suffix, float))
} else { } else {
None None
@ -200,12 +200,10 @@ impl<'a> NumericLiteral<'a> {
fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) { fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
debug_assert!(lit_kind.is_numeric()); debug_assert!(lit_kind.is_numeric());
if let Some(suffix_length) = lit_suffix_length(lit_kind) { lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length); let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
(unsuffixed, Some(suffix)) (unsuffixed, Some(suffix))
} else { })
(src, None)
}
} }
fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> { fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {

View file

@ -98,7 +98,6 @@ pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];

View file

@ -325,22 +325,22 @@ pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
/// parenthesis will always be added for a mix of these. /// parenthesis will always be added for a mix of these.
pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> { pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
/// Returns `true` if the operator is a shift operator `<<` or `>>`. /// Returns `true` if the operator is a shift operator `<<` or `>>`.
fn is_shift(op: &AssocOp) -> bool { fn is_shift(op: AssocOp) -> bool {
matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight) matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
} }
/// Returns `true` if the operator is a arithmetic operator /// Returns `true` if the operator is a arithmetic operator
/// (i.e., `+`, `-`, `*`, `/`, `%`). /// (i.e., `+`, `-`, `*`, `/`, `%`).
fn is_arith(op: &AssocOp) -> bool { fn is_arith(op: AssocOp) -> bool {
matches!( matches!(
*op, op,
AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
) )
} }
/// Returns `true` if the operator `op` needs parenthesis with the operator /// Returns `true` if the operator `op` needs parenthesis with the operator
/// `other` in the direction `dir`. /// `other` in the direction `dir`.
fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool { fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
other.precedence() < op.precedence() other.precedence() < op.precedence()
|| (other.precedence() == op.precedence() || (other.precedence() == op.precedence()
&& ((op != other && associativity(op) != dir) && ((op != other && associativity(op) != dir)
@ -349,14 +349,14 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
|| is_shift(other) && is_arith(op) || is_shift(other) && is_arith(op)
} }
let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs { let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs {
needs_paren(&op, lop, Associativity::Left) needs_paren(op, lop, Associativity::Left)
} else { } else {
false false
}; };
let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs { let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs {
needs_paren(&op, rop, Associativity::Right) needs_paren(op, rop, Associativity::Right)
} else { } else {
false false
}; };
@ -424,13 +424,13 @@ enum Associativity {
/// they are considered /// they are considered
/// associative. /// associative.
#[must_use] #[must_use]
fn associativity(op: &AssocOp) -> Associativity { fn associativity(op: AssocOp) -> Associativity {
use rustc_ast::util::parser::AssocOp::{ use rustc_ast::util::parser::AssocOp::{
Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater, Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract, GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
}; };
match *op { match op {
Assign | AssignOp(_) => Associativity::Right, Assign | AssignOp(_) => Associativity::Right,
Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both, Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
@ -492,20 +492,20 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
/// before it on its line. /// before it on its line.
fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> { fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
let lo = cx.sess().source_map().lookup_char_pos(span.lo()); let lo = cx.sess().source_map().lookup_char_pos(span.lo());
if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) { lo.file
if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') { .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
// We can mix char and byte positions here because we only consider `[ \t]`. .and_then(|line| {
if lo.col == CharPos(pos) { if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
Some(line[..pos].into()) // We can mix char and byte positions here because we only consider `[ \t]`.
if lo.col == CharPos(pos) {
Some(line[..pos].into())
} else {
None
}
} else { } else {
None None
} }
} else { })
None
}
} else {
None
}
} }
/// Convenience extension trait for `DiagnosticBuilder`. /// Convenience extension trait for `DiagnosticBuilder`.

View file

@ -23,7 +23,11 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// // Bad
/// println!(""); /// println!("");
///
/// // Good
/// println!();
/// ``` /// ```
pub PRINTLN_EMPTY_STRING, pub PRINTLN_EMPTY_STRING,
style, style,
@ -32,8 +36,7 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** This lint warns when you use `print!()` with a format /// **What it does:** This lint warns when you use `print!()` with a format
/// string that /// string that ends in a newline.
/// ends in a newline.
/// ///
/// **Why is this bad?** You should use `println!()` instead, which appends the /// **Why is this bad?** You should use `println!()` instead, which appends the
/// newline. /// newline.
@ -125,7 +128,12 @@ declare_clippy_lint! {
/// ```rust /// ```rust
/// # use std::fmt::Write; /// # use std::fmt::Write;
/// # let mut buf = String::new(); /// # let mut buf = String::new();
///
/// // Bad
/// writeln!(buf, ""); /// writeln!(buf, "");
///
/// // Good
/// writeln!(buf);
/// ``` /// ```
pub WRITELN_EMPTY_STRING, pub WRITELN_EMPTY_STRING,
style, style,
@ -147,7 +155,12 @@ declare_clippy_lint! {
/// # use std::fmt::Write; /// # use std::fmt::Write;
/// # let mut buf = String::new(); /// # let mut buf = String::new();
/// # let name = "World"; /// # let name = "World";
///
/// // Bad
/// write!(buf, "Hello {}!\n", name); /// write!(buf, "Hello {}!\n", name);
///
/// // Good
/// writeln!(buf, "Hello {}!", name);
/// ``` /// ```
pub WRITE_WITH_NEWLINE, pub WRITE_WITH_NEWLINE,
style, style,
@ -168,7 +181,12 @@ declare_clippy_lint! {
/// ```rust /// ```rust
/// # use std::fmt::Write; /// # use std::fmt::Write;
/// # let mut buf = String::new(); /// # let mut buf = String::new();
///
/// // Bad
/// writeln!(buf, "{}", "foo"); /// writeln!(buf, "{}", "foo");
///
/// // Good
/// writeln!(buf, "foo");
/// ``` /// ```
pub WRITE_LITERAL, pub WRITE_LITERAL,
style, style,
@ -279,13 +297,13 @@ impl EarlyLintPass for Write {
if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
if fmt_str.symbol == Symbol::intern("") { if fmt_str.symbol == Symbol::intern("") {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let suggestion = match expr { let suggestion = expr.map_or_else(
Some(expr) => snippet_with_applicability(cx, expr.span, "v", &mut applicability), || {
None => {
applicability = Applicability::HasPlaceholders; applicability = Applicability::HasPlaceholders;
Cow::Borrowed("v") Cow::Borrowed("v")
}, },
}; |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable),
);
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,

View file

@ -382,13 +382,8 @@ pub fn main() {
let should_describe_lints = || { let should_describe_lints = || {
let args: Vec<_> = env::args().collect(); let args: Vec<_> = env::args().collect();
args.windows(2).any(|args| { args.windows(2)
args[1] == "help" .any(|args| args[1] == "help" && matches!(args[0].as_str(), "-W" | "-A" | "-D" | "-F"))
&& match args[0].as_str() {
"-W" | "-A" | "-D" | "-F" => true,
_ => false,
}
})
}; };
if !wrapper_mode && should_describe_lints() { if !wrapper_mode && should_describe_lints() {

View file

@ -80,6 +80,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "blacklisted_name", module: "blacklisted_name",
}, },
Lint {
name: "blanket_clippy_restriction_lints",
group: "style",
desc: "enabling the complete restriction group",
deprecation: None,
module: "attrs",
},
Lint { Lint {
name: "blocks_in_if_conditions", name: "blocks_in_if_conditions",
group: "style", group: "style",
@ -1144,6 +1151,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "methods", module: "methods",
}, },
Lint {
name: "map_identity",
group: "complexity",
desc: "using iterator.map(|x| x)",
deprecation: None,
module: "map_identity",
},
Lint { Lint {
name: "map_unwrap_or", name: "map_unwrap_or",
group: "pedantic", group: "pedantic",
@ -1165,6 +1179,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "matches", module: "matches",
}, },
Lint {
name: "match_like_matches_macro",
group: "style",
desc: "a match that could be written with the matches! macro",
deprecation: None,
module: "matches",
},
Lint { Lint {
name: "match_on_vec_items", name: "match_on_vec_items",
group: "pedantic", group: "pedantic",
@ -1606,6 +1627,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "option_env_unwrap", module: "option_env_unwrap",
}, },
Lint {
name: "option_if_let_else",
group: "pedantic",
desc: "reimplementation of Option::map_or",
deprecation: None,
module: "option_if_let_else",
},
Lint { Lint {
name: "option_map_or_none", name: "option_map_or_none",
group: "style", group: "style",
@ -1683,6 +1711,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "path_buf_push_overwrite", module: "path_buf_push_overwrite",
}, },
Lint {
name: "pattern_type_mismatch",
group: "restriction",
desc: "type of pattern does not match the expression type",
deprecation: None,
module: "pattern_type_mismatch",
},
Lint { Lint {
name: "possible_missing_comma", name: "possible_missing_comma",
group: "correctness", group: "correctness",
@ -1755,7 +1790,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
}, },
Lint { Lint {
name: "range_minus_one", name: "range_minus_one",
group: "complexity", group: "pedantic",
desc: "`x..=(y-1)` reads better as `x..y`", desc: "`x..=(y-1)` reads better as `x..y`",
deprecation: None, deprecation: None,
module: "ranges", module: "ranges",
@ -1828,7 +1863,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
group: "style", group: "style",
desc: "use the proper utility function avoiding an `if let`", desc: "use the proper utility function avoiding an `if let`",
deprecation: None, deprecation: None,
module: "redundant_pattern_matching", module: "matches",
}, },
Lint { Lint {
name: "redundant_pub_crate", name: "redundant_pub_crate",
@ -1852,11 +1887,11 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
module: "reference", module: "reference",
}, },
Lint { Lint {
name: "regex_macro", name: "repeat_once",
group: "style", group: "complexity",
desc: "use of `regex!(_)` instead of `Regex::new(_)`", desc: "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` ",
deprecation: None, deprecation: None,
module: "regex", module: "repeat_once",
}, },
Lint { Lint {
name: "rest_pat_in_fully_bound_structs", name: "rest_pat_in_fully_bound_structs",

View file

@ -12,19 +12,11 @@ use std::path::{Path, PathBuf};
mod cargo; mod cargo;
fn host_lib() -> PathBuf { fn host_lib() -> PathBuf {
if let Some(path) = option_env!("HOST_LIBS") { option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
PathBuf::from(path)
} else {
cargo::CARGO_TARGET_DIR.join(env!("PROFILE"))
}
} }
fn clippy_driver_path() -> PathBuf { fn clippy_driver_path() -> PathBuf {
if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") { option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
PathBuf::from(path)
} else {
cargo::TARGET_LIB.join("clippy-driver")
}
} }
// When we'll want to use `extern crate ..` for a dependency that is used // When we'll want to use `extern crate ..` for a dependency that is used

View file

@ -0,0 +1,109 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "bitflags"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "ctrlc"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c"
dependencies = [
"kernel32-sys",
"nix",
"winapi 0.2.8",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "libc"
version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
[[package]]
name = "multiple_crate_versions"
version = "0.1.0"
dependencies = [
"ansi_term",
"ctrlc",
]
[[package]]
name = "nix"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32"
dependencies = [
"bitflags",
"cfg-if",
"libc",
"void",
]
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View file

@ -1,4 +1,4 @@
error: multiple versions for dependency `winapi`: 0.2.8, 0.3.8 error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
| |
= note: `-D clippy::multiple-crate-versions` implied by `-D warnings` = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`

View file

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1 error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
error: aborting due to previous error error: aborting due to previous error

View file

@ -1,5 +1,11 @@
#![warn(clippy::inline_always, clippy::deprecated_semver)] #![warn(clippy::inline_always, clippy::deprecated_semver)]
#![allow(clippy::assertions_on_constants)] #![allow(clippy::assertions_on_constants)]
// Test that the whole restriction group is not enabled
#![warn(clippy::restriction)]
#![deny(clippy::restriction)]
#![forbid(clippy::restriction)]
#![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)]
#[inline(always)] #[inline(always)]
fn test_attr_lint() { fn test_attr_lint() {
assert!(true) assert!(true)

View file

@ -1,5 +1,5 @@
error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
--> $DIR/attrs.rs:3:1 --> $DIR/attrs.rs:9:1
| |
LL | #[inline(always)] LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
@ -7,7 +7,7 @@ LL | #[inline(always)]
= note: `-D clippy::inline-always` implied by `-D warnings` = note: `-D clippy::inline-always` implied by `-D warnings`
error: the since field must contain a semver-compliant version error: the since field must contain a semver-compliant version
--> $DIR/attrs.rs:23:14 --> $DIR/attrs.rs:29:14
| |
LL | #[deprecated(since = "forever")] LL | #[deprecated(since = "forever")]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
@ -15,10 +15,35 @@ LL | #[deprecated(since = "forever")]
= note: `-D clippy::deprecated-semver` implied by `-D warnings` = note: `-D clippy::deprecated-semver` implied by `-D warnings`
error: the since field must contain a semver-compliant version error: the since field must contain a semver-compliant version
--> $DIR/attrs.rs:26:14 --> $DIR/attrs.rs:32:14
| |
LL | #[deprecated(since = "1")] LL | #[deprecated(since = "1")]
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: aborting due to 3 previous errors error: restriction lints are not meant to be all enabled
--> $DIR/attrs.rs:4:9
|
LL | #![warn(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
= help: try enabling only the lints you really need
error: restriction lints are not meant to be all enabled
--> $DIR/attrs.rs:5:9
|
LL | #![deny(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: try enabling only the lints you really need
error: restriction lints are not meant to be all enabled
--> $DIR/attrs.rs:6:11
|
LL | #![forbid(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: try enabling only the lints you really need
error: aborting due to 6 previous errors

View file

@ -0,0 +1,40 @@
// run-rustfix
#![allow(
unused,
clippy::redundant_clone,
clippy::deref_addrof,
clippy::no_effect,
clippy::unnecessary_operation
)]
use std::cell::RefCell;
use std::rc::{self, Rc};
use std::sync::{self, Arc};
fn main() {}
fn is_ascii(ch: char) -> bool {
ch.is_ascii()
}
fn clone_on_copy() {
42;
vec![1].clone(); // ok, not a Copy type
Some(vec![1]).clone(); // ok, not a Copy type
*(&42);
let rc = RefCell::new(0);
*rc.borrow();
// Issue #4348
let mut x = 43;
let _ = &x.clone(); // ok, getting a ref
'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
is_ascii('z');
// Issue #5436
let mut vec = Vec::new();
vec.push(42);
}

40
tests/ui/clone_on_copy.rs Normal file
View file

@ -0,0 +1,40 @@
// run-rustfix
#![allow(
unused,
clippy::redundant_clone,
clippy::deref_addrof,
clippy::no_effect,
clippy::unnecessary_operation
)]
use std::cell::RefCell;
use std::rc::{self, Rc};
use std::sync::{self, Arc};
fn main() {}
fn is_ascii(ch: char) -> bool {
ch.is_ascii()
}
fn clone_on_copy() {
42.clone();
vec![1].clone(); // ok, not a Copy type
Some(vec![1]).clone(); // ok, not a Copy type
(&42).clone();
let rc = RefCell::new(0);
rc.borrow().clone();
// Issue #4348
let mut x = 43;
let _ = &x.clone(); // ok, getting a ref
'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
is_ascii('z'.clone());
// Issue #5436
let mut vec = Vec::new();
vec.push(42.clone());
}

View file

@ -0,0 +1,34 @@
error: using `clone` on a `Copy` type
--> $DIR/clone_on_copy.rs:22:5
|
LL | 42.clone();
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
|
= note: `-D clippy::clone-on-copy` implied by `-D warnings`
error: using `clone` on a `Copy` type
--> $DIR/clone_on_copy.rs:26:5
|
LL | (&42).clone();
| ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
error: using `clone` on a `Copy` type
--> $DIR/clone_on_copy.rs:29:5
|
LL | rc.borrow().clone();
| ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
error: using `clone` on a `Copy` type
--> $DIR/clone_on_copy.rs:35:14
|
LL | is_ascii('z'.clone());
| ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
error: using `clone` on a `Copy` type
--> $DIR/clone_on_copy.rs:39:14
|
LL | vec.push(42.clone());
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
error: aborting due to 5 previous errors

View file

@ -0,0 +1,93 @@
// run-rustfix
#![allow(unused, clippy::redundant_clone)] // See #5700
// Define the types in each module to avoid trait impls leaking between modules.
macro_rules! impl_types {
() => {
#[derive(PartialEq)]
pub struct Owned;
pub struct Borrowed;
impl ToOwned for Borrowed {
type Owned = Owned;
fn to_owned(&self) -> Owned {
Owned {}
}
}
impl std::borrow::Borrow<Borrowed> for Owned {
fn borrow(&self) -> &Borrowed {
static VALUE: Borrowed = Borrowed {};
&VALUE
}
}
};
}
// Only Borrowed == Owned is implemented
mod borrowed_eq_owned {
impl_types!();
impl PartialEq<Owned> for Borrowed {
fn eq(&self, _: &Owned) -> bool {
true
}
}
pub fn compare() {
let owned = Owned {};
let borrowed = Borrowed {};
if borrowed == owned {}
if borrowed == owned {}
}
}
// Only Owned == Borrowed is implemented
mod owned_eq_borrowed {
impl_types!();
impl PartialEq<Borrowed> for Owned {
fn eq(&self, _: &Borrowed) -> bool {
true
}
}
fn compare() {
let owned = Owned {};
let borrowed = Borrowed {};
if owned == borrowed {}
if owned == borrowed {}
}
}
mod issue_4874 {
impl_types!();
// NOTE: PartialEq<Borrowed> for T can't be implemented due to the orphan rules
impl<T> PartialEq<T> for Borrowed
where
T: AsRef<str> + ?Sized,
{
fn eq(&self, _: &T) -> bool {
true
}
}
impl std::fmt::Display for Borrowed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "borrowed")
}
}
fn compare() {
let borrowed = Borrowed {};
if borrowed == "Hi" {}
if borrowed == "Hi" {}
}
}
fn main() {}

View file

@ -0,0 +1,93 @@
// run-rustfix
#![allow(unused, clippy::redundant_clone)] // See #5700
// Define the types in each module to avoid trait impls leaking between modules.
macro_rules! impl_types {
() => {
#[derive(PartialEq)]
pub struct Owned;
pub struct Borrowed;
impl ToOwned for Borrowed {
type Owned = Owned;
fn to_owned(&self) -> Owned {
Owned {}
}
}
impl std::borrow::Borrow<Borrowed> for Owned {
fn borrow(&self) -> &Borrowed {
static VALUE: Borrowed = Borrowed {};
&VALUE
}
}
};
}
// Only Borrowed == Owned is implemented
mod borrowed_eq_owned {
impl_types!();
impl PartialEq<Owned> for Borrowed {
fn eq(&self, _: &Owned) -> bool {
true
}
}
pub fn compare() {
let owned = Owned {};
let borrowed = Borrowed {};
if borrowed.to_owned() == owned {}
if owned == borrowed.to_owned() {}
}
}
// Only Owned == Borrowed is implemented
mod owned_eq_borrowed {
impl_types!();
impl PartialEq<Borrowed> for Owned {
fn eq(&self, _: &Borrowed) -> bool {
true
}
}
fn compare() {
let owned = Owned {};
let borrowed = Borrowed {};
if owned == borrowed.to_owned() {}
if borrowed.to_owned() == owned {}
}
}
mod issue_4874 {
impl_types!();
// NOTE: PartialEq<Borrowed> for T can't be implemented due to the orphan rules
impl<T> PartialEq<T> for Borrowed
where
T: AsRef<str> + ?Sized,
{
fn eq(&self, _: &T) -> bool {
true
}
}
impl std::fmt::Display for Borrowed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "borrowed")
}
}
fn compare() {
let borrowed = Borrowed {};
if "Hi" == borrowed.to_string() {}
if borrowed.to_string() == "Hi" {}
}
}
fn main() {}

View file

@ -0,0 +1,46 @@
error: this creates an owned instance just for comparison
--> $DIR/asymmetric_partial_eq.rs:42:12
|
LL | if borrowed.to_owned() == owned {}
| ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
|
= note: `-D clippy::cmp-owned` implied by `-D warnings`
error: this creates an owned instance just for comparison
--> $DIR/asymmetric_partial_eq.rs:43:21
|
LL | if owned == borrowed.to_owned() {}
| ---------^^^^^^^^^^^^^^^^^^^
| |
| help: try: `borrowed == owned`
error: this creates an owned instance just for comparison
--> $DIR/asymmetric_partial_eq.rs:61:21
|
LL | if owned == borrowed.to_owned() {}
| ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
error: this creates an owned instance just for comparison
--> $DIR/asymmetric_partial_eq.rs:62:12
|
LL | if borrowed.to_owned() == owned {}
| ^^^^^^^^^^^^^^^^^^^---------
| |
| help: try: `owned == borrowed`
error: this creates an owned instance just for comparison
--> $DIR/asymmetric_partial_eq.rs:88:20
|
LL | if "Hi" == borrowed.to_string() {}
| --------^^^^^^^^^^^^^^^^^^^^
| |
| help: try: `borrowed == "Hi"`
error: this creates an owned instance just for comparison
--> $DIR/asymmetric_partial_eq.rs:89:12
|
LL | if borrowed.to_string() == "Hi" {}
| ^^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
error: aborting due to 6 previous errors

View file

@ -10,7 +10,7 @@ LL | | }
| |_____^ | |_____^
| |
= note: `-D clippy::collapsible-if` implied by `-D warnings` = note: `-D clippy::collapsible-if` implied by `-D warnings`
help: try help: collapse nested if block
| |
LL | } else if y == "world" { LL | } else if y == "world" {
LL | println!("world!") LL | println!("world!")
@ -28,7 +28,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | } else if let Some(42) = Some(42) { LL | } else if let Some(42) = Some(42) {
LL | println!("world!") LL | println!("world!")
@ -48,7 +48,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | } else if y == "world" { LL | } else if y == "world" {
LL | println!("world") LL | println!("world")
@ -71,7 +71,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | } else if let Some(42) = Some(42) { LL | } else if let Some(42) = Some(42) {
LL | println!("world") LL | println!("world")
@ -94,7 +94,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | } else if let Some(42) = Some(42) { LL | } else if let Some(42) = Some(42) {
LL | println!("world") LL | println!("world")
@ -117,7 +117,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | } else if x == "hello" { LL | } else if x == "hello" {
LL | println!("world") LL | println!("world")
@ -140,7 +140,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | } else if let Some(42) = Some(42) { LL | } else if let Some(42) = Some(42) {
LL | println!("world") LL | println!("world")

View file

@ -9,7 +9,7 @@ LL | | }
| |_____^ | |_____^
| |
= note: `-D clippy::collapsible-if` implied by `-D warnings` = note: `-D clippy::collapsible-if` implied by `-D warnings`
help: try help: collapse nested if block
| |
LL | if x == "hello" && y == "world" { LL | if x == "hello" && y == "world" {
LL | println!("Hello world!"); LL | println!("Hello world!");
@ -26,7 +26,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") { LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
LL | println!("Hello world!"); LL | println!("Hello world!");
@ -43,7 +43,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") { LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") {
LL | println!("Hello world!"); LL | println!("Hello world!");
@ -60,7 +60,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" { LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" {
LL | println!("Hello world!"); LL | println!("Hello world!");
@ -77,7 +77,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | if x == "hello" && x == "world" && y == "world" && y == "hello" { LL | if x == "hello" && x == "world" && y == "world" && y == "hello" {
LL | println!("Hello world!"); LL | println!("Hello world!");
@ -94,7 +94,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | if 42 == 1337 && 'a' != 'A' { LL | if 42 == 1337 && 'a' != 'A' {
LL | println!("world!") LL | println!("world!")
@ -111,7 +111,7 @@ LL | | }
LL | | } LL | | }
| |_____^ | |_____^
| |
help: try help: collapse nested if block
| |
LL | if x == "hello" && y == "world" { // Collapsible LL | if x == "hello" && y == "world" { // Collapsible
LL | println!("Hello world!"); LL | println!("Hello world!");

View file

@ -7,5 +7,6 @@
#[warn(clippy::invalid_ref)] #[warn(clippy::invalid_ref)]
#[warn(clippy::into_iter_on_array)] #[warn(clippy::into_iter_on_array)]
#[warn(clippy::unused_label)] #[warn(clippy::unused_label)]
#[warn(clippy::regex_macro)]
fn main() {} fn main() {}

View file

@ -54,11 +54,17 @@ error: lint `clippy::unused_label` has been removed: `this lint has been uplifte
LL | #[warn(clippy::unused_label)] LL | #[warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::regex_macro` has been removed: `the regex! macro has been removed from the regex crate in 2018`
--> $DIR/deprecated.rs:10:8
|
LL | #[warn(clippy::regex_macro)]
| ^^^^^^^^^^^^^^^^^^^
error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
--> $DIR/deprecated.rs:1:8 --> $DIR/deprecated.rs:1:8
| |
LL | #[warn(clippy::str_to_string)] LL | #[warn(clippy::str_to_string)]
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 10 previous errors error: aborting due to 11 previous errors

View file

@ -19,6 +19,7 @@ fn main() {
let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap()); let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
#[allow(clippy::match_like_matches_macro)]
let _: Option<Flavor> = desserts_of_the_week let _: Option<Flavor> = desserts_of_the_week
.iter() .iter()
.find(|dessert| match *dessert { .find(|dessert| match *dessert {

View file

@ -8,7 +8,7 @@ LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s
= help: this is more succinctly expressed by calling `.find_map(..)` instead = help: this is more succinctly expressed by calling `.find_map(..)` instead
error: called `find(p).map(q)` on an `Iterator` error: called `find(p).map(q)` on an `Iterator`
--> $DIR/find_map.rs:22:29 --> $DIR/find_map.rs:23:29
| |
LL | let _: Option<Flavor> = desserts_of_the_week LL | let _: Option<Flavor> = desserts_of_the_week
| _____________________________^ | _____________________________^

View file

@ -0,0 +1,14 @@
// run-rustfix
#![warn(clippy::imprecise_flops)]
fn main() {
let x = 3f32;
let y = 4f32;
let _ = x.hypot(y);
let _ = (x + 1f32).hypot(y);
let _ = x.hypot(y);
// Cases where the lint shouldn't be applied
// TODO: linting this adds some complexity, but could be done
let _ = x.mul_add(x, y * y).sqrt();
let _ = (x * 4f32 + y * y).sqrt();
}

View file

@ -0,0 +1,14 @@
// run-rustfix
#![warn(clippy::imprecise_flops)]
fn main() {
let x = 3f32;
let y = 4f32;
let _ = (x * x + y * y).sqrt();
let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
let _ = (x.powi(2) + y.powi(2)).sqrt();
// Cases where the lint shouldn't be applied
// TODO: linting this adds some complexity, but could be done
let _ = x.mul_add(x, y * y).sqrt();
let _ = (x * 4f32 + y * y).sqrt();
}

View file

@ -0,0 +1,22 @@
error: hypotenuse can be computed more accurately
--> $DIR/floating_point_hypot.rs:7:13
|
LL | let _ = (x * x + y * y).sqrt();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
|
= note: `-D clippy::imprecise-flops` implied by `-D warnings`
error: hypotenuse can be computed more accurately
--> $DIR/floating_point_hypot.rs:8:13
|
LL | let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)`
error: hypotenuse can be computed more accurately
--> $DIR/floating_point_hypot.rs:9:13
|
LL | let _ = (x.powi(2) + y.powi(2)).sqrt();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
error: aborting due to 3 previous errors

View file

@ -25,11 +25,11 @@ fn check_ln1p() {
let _ = 2.0f32.ln_1p(); let _ = 2.0f32.ln_1p();
let _ = x.ln_1p(); let _ = x.ln_1p();
let _ = (x / 2.0).ln_1p(); let _ = (x / 2.0).ln_1p();
let _ = x.powi(2).ln_1p(); let _ = x.powi(3).ln_1p();
let _ = (x.powi(2) / 2.0).ln_1p(); let _ = (x.powi(3) / 2.0).ln_1p();
let _ = ((std::f32::consts::E - 1.0)).ln_1p(); let _ = ((std::f32::consts::E - 1.0)).ln_1p();
let _ = x.ln_1p(); let _ = x.ln_1p();
let _ = x.powi(2).ln_1p(); let _ = x.powi(3).ln_1p();
let _ = (x + 2.0).ln_1p(); let _ = (x + 2.0).ln_1p();
let _ = (x / 2.0).ln_1p(); let _ = (x / 2.0).ln_1p();
// Cases where the lint shouldn't be applied // Cases where the lint shouldn't be applied
@ -43,9 +43,9 @@ fn check_ln1p() {
let _ = 2.0f64.ln_1p(); let _ = 2.0f64.ln_1p();
let _ = x.ln_1p(); let _ = x.ln_1p();
let _ = (x / 2.0).ln_1p(); let _ = (x / 2.0).ln_1p();
let _ = x.powi(2).ln_1p(); let _ = x.powi(3).ln_1p();
let _ = x.ln_1p(); let _ = x.ln_1p();
let _ = x.powi(2).ln_1p(); let _ = x.powi(3).ln_1p();
let _ = (x + 2.0).ln_1p(); let _ = (x + 2.0).ln_1p();
let _ = (x / 2.0).ln_1p(); let _ = (x / 2.0).ln_1p();
// Cases where the lint shouldn't be applied // Cases where the lint shouldn't be applied

View file

@ -25,11 +25,11 @@ fn check_ln1p() {
let _ = (1f32 + 2.0).ln(); let _ = (1f32 + 2.0).ln();
let _ = (1.0 + x).ln(); let _ = (1.0 + x).ln();
let _ = (1.0 + x / 2.0).ln(); let _ = (1.0 + x / 2.0).ln();
let _ = (1.0 + x.powi(2)).ln(); let _ = (1.0 + x.powi(3)).ln();
let _ = (1.0 + x.powi(2) / 2.0).ln(); let _ = (1.0 + x.powi(3) / 2.0).ln();
let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
let _ = (x + 1.0).ln(); let _ = (x + 1.0).ln();
let _ = (x.powi(2) + 1.0).ln(); let _ = (x.powi(3) + 1.0).ln();
let _ = (x + 2.0 + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln();
let _ = (x / 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln();
// Cases where the lint shouldn't be applied // Cases where the lint shouldn't be applied
@ -43,9 +43,9 @@ fn check_ln1p() {
let _ = (1f64 + 2.0).ln(); let _ = (1f64 + 2.0).ln();
let _ = (1.0 + x).ln(); let _ = (1.0 + x).ln();
let _ = (1.0 + x / 2.0).ln(); let _ = (1.0 + x / 2.0).ln();
let _ = (1.0 + x.powi(2)).ln(); let _ = (1.0 + x.powi(3)).ln();
let _ = (x + 1.0).ln(); let _ = (x + 1.0).ln();
let _ = (x.powi(2) + 1.0).ln(); let _ = (x.powi(3) + 1.0).ln();
let _ = (x + 2.0 + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln();
let _ = (x / 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln();
// Cases where the lint shouldn't be applied // Cases where the lint shouldn't be applied

View file

@ -77,14 +77,14 @@ LL | let _ = (1.0 + x / 2.0).ln();
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:28:13 --> $DIR/floating_point_log.rs:28:13
| |
LL | let _ = (1.0 + x.powi(2)).ln(); LL | let _ = (1.0 + x.powi(3)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:29:13 --> $DIR/floating_point_log.rs:29:13
| |
LL | let _ = (1.0 + x.powi(2) / 2.0).ln(); LL | let _ = (1.0 + x.powi(3) / 2.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:30:13 --> $DIR/floating_point_log.rs:30:13
@ -101,8 +101,8 @@ LL | let _ = (x + 1.0).ln();
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:32:13 --> $DIR/floating_point_log.rs:32:13
| |
LL | let _ = (x.powi(2) + 1.0).ln(); LL | let _ = (x.powi(3) + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:33:13 --> $DIR/floating_point_log.rs:33:13
@ -143,8 +143,8 @@ LL | let _ = (1.0 + x / 2.0).ln();
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:46:13 --> $DIR/floating_point_log.rs:46:13
| |
LL | let _ = (1.0 + x.powi(2)).ln(); LL | let _ = (1.0 + x.powi(3)).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:47:13 --> $DIR/floating_point_log.rs:47:13
@ -155,8 +155,8 @@ LL | let _ = (x + 1.0).ln();
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:48:13 --> $DIR/floating_point_log.rs:48:13
| |
LL | let _ = (x.powi(2) + 1.0).ln(); LL | let _ = (x.powi(3) + 1.0).ln();
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
error: ln(1 + x) can be computed more accurately error: ln(1 + x) can be computed more accurately
--> $DIR/floating_point_log.rs:49:13 --> $DIR/floating_point_log.rs:49:13

View file

@ -0,0 +1,16 @@
// run-rustfix
#![warn(clippy::suboptimal_flops)]
fn main() {
let x = 3f32;
let y = 5f32;
let _ = x.log(y);
let _ = x.log(y);
let _ = x.log(y);
let _ = x.log(y);
// Cases where the lint shouldn't be applied
let _ = x.ln() / y.powf(3.2);
let _ = x.powf(3.2) / y.powf(3.2);
let _ = x.powf(3.2) / y.ln();
let _ = x.log(5f32) / y.log(7f32);
}

View file

@ -0,0 +1,16 @@
// run-rustfix
#![warn(clippy::suboptimal_flops)]
fn main() {
let x = 3f32;
let y = 5f32;
let _ = x.ln() / y.ln();
let _ = x.log2() / y.log2();
let _ = x.log10() / y.log10();
let _ = x.log(5f32) / y.log(5f32);
// Cases where the lint shouldn't be applied
let _ = x.ln() / y.powf(3.2);
let _ = x.powf(3.2) / y.powf(3.2);
let _ = x.powf(3.2) / y.ln();
let _ = x.log(5f32) / y.log(7f32);
}

View file

@ -0,0 +1,28 @@
error: log base can be expressed more clearly
--> $DIR/floating_point_logbase.rs:7:13
|
LL | let _ = x.ln() / y.ln();
| ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
|
= note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: log base can be expressed more clearly
--> $DIR/floating_point_logbase.rs:8:13
|
LL | let _ = x.log2() / y.log2();
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
error: log base can be expressed more clearly
--> $DIR/floating_point_logbase.rs:9:13
|
LL | let _ = x.log10() / y.log10();
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
error: log base can be expressed more clearly
--> $DIR/floating_point_logbase.rs:10:13
|
LL | let _ = x.log(5f32) / y.log(5f32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
error: aborting due to 4 previous errors

View file

@ -18,4 +18,9 @@ fn main() {
let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c; let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64); let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
let _ = a.mul_add(a, b).sqrt();
// Cases where the lint shouldn't be applied
let _ = (a * a + b * b).sqrt();
} }

View file

@ -18,4 +18,9 @@ fn main() {
let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
let _ = (a * a + b).sqrt();
// Cases where the lint shouldn't be applied
let _ = (a * a + b * b).sqrt();
} }

View file

@ -54,5 +54,11 @@ error: multiply and add expressions can be calculated more efficiently and accur
LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
error: aborting due to 9 previous errors error: multiply and add expressions can be calculated more efficiently and accurately
--> $DIR/floating_point_mul_add.rs:22:13
|
LL | let _ = (a * a + b).sqrt();
| ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
error: aborting due to 10 previous errors

View file

@ -11,7 +11,7 @@ fn main() {
let _ = (-3.1f32).exp(); let _ = (-3.1f32).exp();
let _ = x.sqrt(); let _ = x.sqrt();
let _ = x.cbrt(); let _ = x.cbrt();
let _ = x.powi(2); let _ = x.powi(3);
let _ = x.powi(-2); let _ = x.powi(-2);
let _ = x.powi(16_777_215); let _ = x.powi(16_777_215);
let _ = x.powi(-16_777_215); let _ = x.powi(-16_777_215);
@ -30,7 +30,7 @@ fn main() {
let _ = (-3.1f64).exp(); let _ = (-3.1f64).exp();
let _ = x.sqrt(); let _ = x.sqrt();
let _ = x.cbrt(); let _ = x.cbrt();
let _ = x.powi(2); let _ = x.powi(3);
let _ = x.powi(-2); let _ = x.powi(-2);
let _ = x.powi(-2_147_483_648); let _ = x.powi(-2_147_483_648);
let _ = x.powi(2_147_483_647); let _ = x.powi(2_147_483_647);

View file

@ -11,7 +11,7 @@ fn main() {
let _ = std::f32::consts::E.powf(-3.1); let _ = std::f32::consts::E.powf(-3.1);
let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 2.0);
let _ = x.powf(1.0 / 3.0); let _ = x.powf(1.0 / 3.0);
let _ = x.powf(2.0); let _ = x.powf(3.0);
let _ = x.powf(-2.0); let _ = x.powf(-2.0);
let _ = x.powf(16_777_215.0); let _ = x.powf(16_777_215.0);
let _ = x.powf(-16_777_215.0); let _ = x.powf(-16_777_215.0);
@ -30,7 +30,7 @@ fn main() {
let _ = std::f64::consts::E.powf(-3.1); let _ = std::f64::consts::E.powf(-3.1);
let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 2.0);
let _ = x.powf(1.0 / 3.0); let _ = x.powf(1.0 / 3.0);
let _ = x.powf(2.0); let _ = x.powf(3.0);
let _ = x.powf(-2.0); let _ = x.powf(-2.0);
let _ = x.powf(-2_147_483_648.0); let _ = x.powf(-2_147_483_648.0);
let _ = x.powf(2_147_483_647.0); let _ = x.powf(2_147_483_647.0);

View file

@ -53,8 +53,8 @@ LL | let _ = x.powf(1.0 / 3.0);
error: exponentiation with integer powers can be computed more efficiently error: exponentiation with integer powers can be computed more efficiently
--> $DIR/floating_point_powf.rs:14:13 --> $DIR/floating_point_powf.rs:14:13
| |
LL | let _ = x.powf(2.0); LL | let _ = x.powf(3.0);
| ^^^^^^^^^^^ help: consider using: `x.powi(2)` | ^^^^^^^^^^^ help: consider using: `x.powi(3)`
error: exponentiation with integer powers can be computed more efficiently error: exponentiation with integer powers can be computed more efficiently
--> $DIR/floating_point_powf.rs:15:13 --> $DIR/floating_point_powf.rs:15:13
@ -125,8 +125,8 @@ LL | let _ = x.powf(1.0 / 3.0);
error: exponentiation with integer powers can be computed more efficiently error: exponentiation with integer powers can be computed more efficiently
--> $DIR/floating_point_powf.rs:33:13 --> $DIR/floating_point_powf.rs:33:13
| |
LL | let _ = x.powf(2.0); LL | let _ = x.powf(3.0);
| ^^^^^^^^^^^ help: consider using: `x.powi(2)` | ^^^^^^^^^^^ help: consider using: `x.powi(3)`
error: exponentiation with integer powers can be computed more efficiently error: exponentiation with integer powers can be computed more efficiently
--> $DIR/floating_point_powf.rs:34:13 --> $DIR/floating_point_powf.rs:34:13

View file

@ -0,0 +1,19 @@
// run-rustfix
#![warn(clippy::suboptimal_flops)]
fn main() {
let one = 1;
let x = 3f32;
let _ = x * x;
let _ = x * x;
let y = 4f32;
let _ = x.mul_add(x, y);
let _ = y.mul_add(y, x);
let _ = x.mul_add(x, y).sqrt();
let _ = y.mul_add(y, x).sqrt();
// Cases where the lint shouldn't be applied
let _ = x.powi(3);
let _ = x.powi(one + 1);
let _ = (x.powi(2) + y.powi(2)).sqrt();
}

View file

@ -0,0 +1,19 @@
// run-rustfix
#![warn(clippy::suboptimal_flops)]
fn main() {
let one = 1;
let x = 3f32;
let _ = x.powi(2);
let _ = x.powi(1 + 1);
let y = 4f32;
let _ = x.powi(2) + y;
let _ = x + y.powi(2);
let _ = (x.powi(2) + y).sqrt();
let _ = (x + y.powi(2)).sqrt();
// Cases where the lint shouldn't be applied
let _ = x.powi(3);
let _ = x.powi(one + 1);
let _ = (x.powi(2) + y.powi(2)).sqrt();
}

View file

@ -0,0 +1,40 @@
error: square can be computed more efficiently
--> $DIR/floating_point_powi.rs:7:13
|
LL | let _ = x.powi(2);
| ^^^^^^^^^ help: consider using: `x * x`
|
= note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: square can be computed more efficiently
--> $DIR/floating_point_powi.rs:8:13
|
LL | let _ = x.powi(1 + 1);
| ^^^^^^^^^^^^^ help: consider using: `x * x`
error: square can be computed more efficiently
--> $DIR/floating_point_powi.rs:11:13
|
LL | let _ = x.powi(2) + y;
| ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
error: square can be computed more efficiently
--> $DIR/floating_point_powi.rs:12:13
|
LL | let _ = x + y.powi(2);
| ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
error: square can be computed more efficiently
--> $DIR/floating_point_powi.rs:13:13
|
LL | let _ = (x.powi(2) + y).sqrt();
| ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
error: square can be computed more efficiently
--> $DIR/floating_point_powi.rs:14:13
|
LL | let _ = (x + y.powi(2)).sqrt();
| ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
error: aborting due to 6 previous errors

View file

@ -0,0 +1,13 @@
// run-rustfix
#![warn(clippy::suboptimal_flops)]
fn main() {
let x = 3f32;
let _ = x.to_degrees();
let _ = x.to_radians();
// Cases where the lint shouldn't be applied
let _ = x * 90f32 / std::f32::consts::PI;
let _ = x * std::f32::consts::PI / 90f32;
let _ = x * 180f32 / std::f32::consts::E;
let _ = x * std::f32::consts::E / 180f32;
}

View file

@ -0,0 +1,13 @@
// run-rustfix
#![warn(clippy::suboptimal_flops)]
fn main() {
let x = 3f32;
let _ = x * 180f32 / std::f32::consts::PI;
let _ = x * std::f32::consts::PI / 180f32;
// Cases where the lint shouldn't be applied
let _ = x * 90f32 / std::f32::consts::PI;
let _ = x * std::f32::consts::PI / 90f32;
let _ = x * 180f32 / std::f32::consts::E;
let _ = x * std::f32::consts::E / 180f32;
}

View file

@ -0,0 +1,16 @@
error: conversion to degrees can be done more accurately
--> $DIR/floating_point_rad.rs:6:13
|
LL | let _ = x * 180f32 / std::f32::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
|
= note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: conversion to radians can be done more accurately
--> $DIR/floating_point_rad.rs:7:13
|
LL | let _ = x * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
error: aborting due to 2 previous errors

View file

@ -2,6 +2,7 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::missing_docs_in_private_items)]
#![allow(clippy::map_identity)]
fn main() { fn main() {
let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();

Some files were not shown because too many files have changed in this diff Show more