diff options
| author | bors <bors@rust-lang.org> | 2022-01-18 02:32:36 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-01-18 02:32:36 +0000 |
| commit | e4ff9031acdb3579f61a67ded2cf0faa00bea3fc (patch) | |
| tree | 96621b004fce9d03c1de2b8fdcdb9ff826e665ab /src/tools/clippy/clippy_lints | |
| parent | 86b1581ea0e0e47196e1ea70b2d980b3eee72623 (diff) | |
| parent | 0fc3fda8e79c509273af0f547b5143f97d60343c (diff) | |
Auto merge of #93001 - flip1995:clippyup, r=Manishearth
Out of cycle Clippy update I want to do an out-of-cycle sync for rust-lang/rust-clippy#8295, and possibly backport this to stable together with https://github.com/rust-lang/rust/issues/92938. If this doesn't get backported to stable, then I at least want to backport it to beta. r? `@Manishearth`
Diffstat (limited to 'src/tools/clippy/clippy_lints')
16 files changed, 245 insertions, 64 deletions
diff --git a/src/tools/clippy/clippy_lints/src/bit_mask.rs b/src/tools/clippy/clippy_lints/src/bit_mask.rs index 0977cf22b2c..ca4af66cad1 100644 --- a/src/tools/clippy/clippy_lints/src/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/bit_mask.rs @@ -18,14 +18,14 @@ declare_clippy_lint! { /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following /// table: /// - /// |Comparison |Bit Op|Example |is always|Formula | - /// |------------|------|------------|---------|----------------------| - /// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` | - /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | - /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | - /// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` | - /// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` | - /// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` | + /// |Comparison |Bit Op|Example |is always|Formula | + /// |------------|------|-------------|---------|----------------------| + /// |`==` or `!=`| `&` |`x & 2 == 3` |`false` |`c & m != c` | + /// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | + /// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | + /// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false` |`c \| m != c` | + /// |`<` or `>=`| `\|` |`x \| 1 < 1` |`false` |`m >= c` | + /// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true` |`m > c` | /// /// ### Why is this bad? /// If the bits that the comparison cares about are always @@ -53,10 +53,10 @@ declare_clippy_lint! { /// without changing the outcome. The basic structure can be seen in the /// following table: /// - /// |Comparison| Bit Op |Example |equals | - /// |----------|---------|-----------|-------| - /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`| - /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`| + /// |Comparison| Bit Op |Example |equals | + /// |----------|----------|------------|-------| + /// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`| + /// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`| /// /// ### Why is this bad? /// Not equally evil as [`bad_bit_mask`](#bad_bit_mask), diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 39456e25ff4..8b79f1600ae 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); // Block duplication - lint_same_then_else(cx, &blocks, conds.len() == blocks.len(), expr); + lint_same_then_else(cx, &conds, &blocks, conds.len() == blocks.len(), expr); } } } @@ -192,6 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { /// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal. fn lint_same_then_else<'tcx>( cx: &LateContext<'tcx>, + conds: &[&'tcx Expr<'_>], blocks: &[&Block<'tcx>], has_conditional_else: bool, expr: &'tcx Expr<'_>, @@ -204,7 +205,7 @@ fn lint_same_then_else<'tcx>( // Check if each block has shared code let has_expr = blocks[0].expr.is_some(); - let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) { + let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, conds, blocks) { (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq) } else { return; @@ -316,14 +317,14 @@ struct BlockEqual { /// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to /// abort any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> { +fn scan_block_for_eq(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> Option<BlockEqual> { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; - let mut iter = blocks.windows(2); - while let Some(&[win0, win1]) = iter.next() { - let l_stmts = win0.stmts; - let r_stmts = win1.stmts; + let mut iter = blocks.windows(2).enumerate(); + while let Some((i, &[block0, block1])) = iter.next() { + let l_stmts = block0.stmts; + let r_stmts = block1.stmts; // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. // The comparison therefore needs to be done in a way that builds the correct context. @@ -340,22 +341,26 @@ fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<Bloc it1.zip(it2) .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 }) }; - let block_expr_eq = both(&win0.expr, &win1.expr, |l, r| evaluator.eq_expr(l, r)); + let block_expr_eq = both(&block0.expr, &block1.expr, |l, r| evaluator.eq_expr(l, r)); // IF_SAME_THEN_ELSE if_chain! { if block_expr_eq; if l_stmts.len() == r_stmts.len(); if l_stmts.len() == current_start_eq; - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win0.hir_id); - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win1.hir_id); + // `conds` may have one last item than `blocks`. + // Any `i` from `blocks.windows(2)` will exist in `conds`, but `i+1` may not exist on the last iteration. + if !matches!(conds[i].kind, ExprKind::Let(..)); + if !matches!(conds.get(i + 1).map(|e| &e.kind), Some(ExprKind::Let(..))); + if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block0.hir_id); + if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block1.hir_id); then { span_lint_and_note( cx, IF_SAME_THEN_ELSE, - win0.span, + block0.span, "this `if` has identical blocks", - Some(win1.span), + Some(block1.span), "same as this", ); diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 688d8f8630f..395c920c997 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -84,7 +84,22 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string", _ => false, }; - let sugg = if is_new_string { + let sugg = if format_args.format_string_span.contains(value.span) { + // Implicit argument. e.g. `format!("{x}")` span points to `{x}` + let spdata = value.span.data(); + let span = Span::new( + spdata.lo + BytePos(1), + spdata.hi - BytePos(1), + spdata.ctxt, + spdata.parent + ); + let snip = snippet_with_applicability(cx, span, "..", &mut applicability); + if is_new_string { + snip.into() + } else { + format!("{snip}.to_string()") + } + } else if is_new_string { snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned() } else { let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 8902ee14572..ab3dae4b67f 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -42,8 +42,7 @@ fn check_raw_ptr<'tcx>( let expr = &body.value; if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) { let raw_ptrs = iter_input_pats(decl, body) - .zip(decl.inputs.iter()) - .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty)) + .filter_map(|arg| raw_ptr_arg(cx, arg)) .collect::<HirIdSet>(); if !raw_ptrs.is_empty() { @@ -59,8 +58,12 @@ fn check_raw_ptr<'tcx>( } } -fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option<hir::HirId> { - if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) { +fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<hir::HirId> { + if let (&hir::PatKind::Binding(_, id, _, _), Some(&ty::RawPtr(_))) = ( + &arg.pat.kind, + cx.maybe_typeck_results() + .map(|typeck_results| typeck_results.pat_ty(arg.pat).kind()), + ) { Some(id) } else { None diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index d3bdc819a9f..b56d87c5348 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { if sig.decl.implicit_self.has_implicit_self() { - let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output(); + let ret_ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).output()); let ret_ty = cx .tcx .try_normalize_erasing_regions(cx.param_env, ret_ty) diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 26fb4259952..87fd7f99748 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -156,6 +156,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::ITER_NEXT_SLICE), LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_NTH_ZERO), + LintId::of(methods::ITER_OVEREAGER_CLONED), LintId::of(methods::ITER_SKIP_NEXT), LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), @@ -249,7 +250,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(reference::REF_IN_DEREF), LintId::of(regex::INVALID_REGEX), LintId::of(repeat_once::REPEAT_ONCE), - LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(returns::LET_AND_RETURN), LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_assignment::SELF_ASSIGNMENT), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index 746bdb19c3d..56146a0fd3a 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -288,6 +288,7 @@ store.register_lints(&[ methods::ITER_NEXT_SLICE, methods::ITER_NTH, methods::ITER_NTH_ZERO, + methods::ITER_OVEREAGER_CLONED, methods::ITER_SKIP_NEXT, methods::MANUAL_FILTER_MAP, methods::MANUAL_FIND_MAP, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs index 1744b7c8250..1292675f4a9 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs @@ -81,6 +81,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(ranges::RANGE_PLUS_ONE), LintId::of(redundant_else::REDUNDANT_ELSE), LintId::of(ref_option_ref::REF_OPTION_REF), + LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(strings::STRING_ADD_ASSIGN), LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs index f9ffd4cf829..c44ef124bfa 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs @@ -14,6 +14,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::ITER_NTH), + LintId::of(methods::ITER_OVEREAGER_CLONED), LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::OR_FUN_CALL), LintId::of(methods::SINGLE_CHAR_PATTERN), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs index 8594338ffa5..10f8ae4b7f7 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs @@ -16,7 +16,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), ]) diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 90492ffda3c..865e7702b71 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -1,31 +1,40 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::peel_mid_ty_refs; use clippy_utils::{is_diag_item_method, is_diag_trait_item}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -use rustc_span::{sym, Span}; +use rustc_span::sym; use super::IMPLICIT_CLONE; -pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) { +pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if is_clone_like(cx, method_name, method_def_id); let return_type = cx.typeck_results().expr_ty(expr); - let input_type = cx.typeck_results().expr_ty(recv).peel_refs(); + let input_type = cx.typeck_results().expr_ty(recv); + let (input_type, ref_count) = peel_mid_ty_refs(input_type); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); if TyS::same_type(return_type, input_type); then { + let mut app = Applicability::MachineApplicable; + let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; span_lint_and_sugg( cx, IMPLICIT_CLONE, - span, + expr.span, &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name), "consider using", - "clone".to_string(), - Applicability::MachineApplicable + if ref_count > 1 { + format!("({}{}).clone()", "*".repeat(ref_count - 1), recv_snip) + } else { + format!("{}.clone()", recv_snip) + }, + app, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs new file mode 100644 index 00000000000..ca33bfc643d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -0,0 +1,62 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use itertools::Itertools; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use std::ops::Not; + +use super::ITER_OVEREAGER_CLONED; +use crate::redundant_clone::REDUNDANT_CLONE; + +/// lint overeager use of `cloned()` for `Iterator`s +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + name: &str, + map_arg: &[hir::Expr<'_>], +) { + // Check if it's iterator and get type associated with `Item`. + let inner_ty = match get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) { + Some(ty) => ty, + _ => return, + }; + + match inner_ty.kind() { + ty::Ref(_, ty, _) if !is_copy(cx, ty) => {}, + _ => return, + }; + + let (lint, preserve_cloned) = match name { + "count" => (REDUNDANT_CLONE, false), + _ => (ITER_OVEREAGER_CLONED, true), + }; + let wildcard_params = map_arg.is_empty().not().then(|| "...").unwrap_or_default(); + let msg = format!( + "called `cloned().{}({})` on an `Iterator`. It may be more efficient to call `{}({}){}` instead", + name, + wildcard_params, + name, + wildcard_params, + preserve_cloned.then(|| ".cloned()").unwrap_or_default(), + ); + + span_lint_and_sugg( + cx, + lint, + expr.span, + &msg, + "try this", + format!( + "{}.{}({}){}", + snippet(cx, recv.span, ".."), + name, + map_arg.iter().map(|a| snippet(cx, a.span, "..")).join(", "), + preserve_cloned.then(|| ".cloned()").unwrap_or_default(), + ), + Applicability::MachineApplicable, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index ed5136e7d00..7357219f7b4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -30,6 +30,7 @@ mod iter_count; mod iter_next_slice; mod iter_nth; mod iter_nth_zero; +mod iter_overeager_cloned; mod iter_skip_next; mod iterator_step_by_zero; mod manual_saturating_arithmetic; @@ -108,6 +109,41 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed. + /// + /// ### Why is this bad? + /// It's often inefficient to clone all elements of an iterator, when eventually, only some + /// of them will be consumed. + /// + /// ### Examples + /// ```rust + /// # let vec = vec!["string".to_string()]; + /// + /// // Bad + /// vec.iter().cloned().take(10); + /// + /// // Good + /// vec.iter().take(10).cloned(); + /// + /// // Bad + /// vec.iter().cloned().last(); + /// + /// // Good + /// vec.iter().last().cloned(); + /// + /// ``` + /// ### Known Problems + /// This `lint` removes the side of effect of cloning items in the iterator. + /// A code that relies on that side-effect could fail. + /// + #[clippy::version = "1.59.0"] + pub ITER_OVEREAGER_CLONED, + perf, + "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be /// used instead. /// @@ -1950,6 +1986,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, CLONE_DOUBLE_REF, + ITER_OVEREAGER_CLONED, CLONED_INSTEAD_OF_COPIED, FLAT_MAP_OPTION, INEFFICIENT_TO_STRING, @@ -2243,9 +2280,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("count", []) => match method_call(recv) { - Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { - iter_count::check(cx, expr, recv2, name); + (name @ "count", args @ []) => match method_call(recv) { + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { + iter_count::check(cx, expr, recv2, name2); }, Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), _ => {}, @@ -2266,10 +2304,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); }, - ("flatten", []) => { - if let Some(("map", [recv, map_arg], _)) = method_call(recv) { - map_flatten::check(cx, expr, recv, map_arg); - } + (name @ "flatten", args @ []) => match method_call(recv) { + Some(("map", [recv, map_arg], _)) => map_flatten::check(cx, expr, recv, map_arg), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + _ => {}, }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { @@ -2281,6 +2319,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), + ("last", args @ []) | ("skip", args @ [_]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } + } + }, ("map", [m_arg]) => { if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { @@ -2296,20 +2341,22 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio map_identity::check(cx, expr, recv, m_arg, span); }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), - ("next", []) => { - if let Some((name, [recv, args @ ..], _)) = method_call(recv) { - match (name, args) { - ("filter", [arg]) => filter_next::check(cx, expr, recv, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), - ("iter", []) => iter_next_slice::check(cx, expr, recv), - ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg), + (name @ "next", args @ []) => { + if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { + match (name2, args2) { + ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv), + ("iter", []) => iter_next_slice::check(cx, expr, recv2), + ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), ("skip_while", [_]) => skip_while_next::check(cx, expr), _ => {}, } } }, - ("nth", [n_arg]) => match method_call(recv) { + ("nth", args @ [n_arg]) => match method_call(recv) { Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), _ => iter_nth_zero::check(cx, expr, recv, n_arg), @@ -2337,8 +2384,15 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), + ("take", args @ [_arg]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } + } + }, ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { - implicit_clone::check(cx, name, expr, recv, span); + implicit_clone::check(cx, name, expr, recv); }, ("unwrap", []) => match method_call(recv) { Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index 4e4653dadca..448dc4e6147 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -4,6 +4,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_mac use clippy_utils::ty::{implements_trait, match_type}; use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths}; use if_chain::if_chain; +use rustc_errors::emitter::MAX_SUGGESTION_HIGHLIGHT_LINES; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -23,6 +24,7 @@ pub(super) fn check<'tcx>( args: &'tcx [hir::Expr<'_>], ) { /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. + #[allow(clippy::too_many_arguments)] fn check_unwrap_or_default( cx: &LateContext<'_>, name: &str, @@ -31,6 +33,7 @@ pub(super) fn check<'tcx>( arg: &hir::Expr<'_>, or_has_args: bool, span: Span, + method_span: Span, ) -> bool { let is_default_default = || is_trait_item(cx, fun, sym::Default); @@ -52,16 +55,27 @@ pub(super) fn check<'tcx>( then { let mut applicability = Applicability::MachineApplicable; + let hint = "unwrap_or_default()"; + let mut sugg_span = span; + + let mut sugg: String = format!( + "{}.{}", + snippet_with_applicability(cx, self_expr.span, "..", &mut applicability), + hint + ); + + if sugg.lines().count() > MAX_SUGGESTION_HIGHLIGHT_LINES { + sugg_span = method_span.with_hi(span.hi()); + sugg = hint.to_string(); + } + span_lint_and_sugg( cx, OR_FUN_CALL, - span, + sugg_span, &format!("use of `{}` followed by a call to `{}`", name, path), "try this", - format!( - "{}.unwrap_or_default()", - snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) - ), + sugg, applicability, ); @@ -164,7 +178,7 @@ pub(super) fn check<'tcx>( match inner_arg.kind { hir::ExprKind::Call(fun, or_args) => { let or_has_args = !or_args.is_empty(); - if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) { + if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span, method_span) { let fun_span = if or_has_args { None } else { Some(fun.span) }; check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span); } diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index 21b3f81d5d9..8db71d1e967 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -548,6 +548,7 @@ fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } +#[allow(clippy::too_many_lines)] fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { #[derive(Default)] struct EqImpl { @@ -644,10 +645,26 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: hint = expr_snip; } else { span = expr.span.to(other.span); + + let cmp_span = if other.span < expr.span { + other.span.between(expr.span) + } else { + expr.span.between(other.span) + }; if eq_impl.ty_eq_other { - hint = format!("{} == {}", expr_snip, snippet(cx, other.span, "..")); + hint = format!( + "{}{}{}", + expr_snip, + snippet(cx, cmp_span, ".."), + snippet(cx, other.span, "..") + ); } else { - hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip); + hint = format!( + "{}{}{}", + snippet(cx, other.span, ".."), + snippet(cx, cmp_span, ".."), + expr_snip + ); } } diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 5dafd08cf3b..79f104eac0b 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -60,7 +60,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.59.0"] pub RETURN_SELF_NOT_MUST_USE, - suspicious, + pedantic, "missing `#[must_use]` annotation on a method returning `Self`" } |
