diff options
118 files changed, 2418 insertions, 1031 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f4da9a3827..d66e6cf7fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -981,7 +981,7 @@ Released 2021-03-25 [#6532](https://github.com/rust-lang/rust-clippy/pull/6532) * [`single_match`] Suggest `if` over `if let` when possible [#6574](https://github.com/rust-lang/rust-clippy/pull/6574) -* [`ref_in_deref`] Use parentheses correctly in suggestion +* `ref_in_deref` Use parentheses correctly in suggestion [#6609](https://github.com/rust-lang/rust-clippy/pull/6609) * [`stable_sort_primitive`] Clarify error message [#6611](https://github.com/rust-lang/rust-clippy/pull/6611) @@ -3049,6 +3049,7 @@ Released 2018-09-13 [`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero +[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero [`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits @@ -3226,7 +3227,6 @@ Released 2018-09-13 [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference -[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`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 diff --git a/COPYRIGHT b/COPYRIGHT index 238c919b69d..a6be75b5e31 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,4 +1,4 @@ -Copyright 2014-2021 The Rust Project Developers +Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 04169a42b8b..0d62c37278e 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2014-2021 The Rust Project Developers +Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index 90a2d3950d1..b724b24aa83 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2014-2021 The Rust Project Developers +Copyright (c) 2014-2022 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/README.md b/README.md index f001a42d917..edbc626e354 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT ## License -Copyright 2014-2021 The Rust Project Developers +Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index daf0fcc993b..dcc2502e4c5 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -9,9 +9,14 @@ use walkdir::WalkDir; use crate::clippy_project_root; +#[cfg(not(windows))] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; +#[cfg(windows)] +static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; + static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| { let mut path = std::env::current_exe().unwrap(); - path.set_file_name("cargo-clippy"); + path.set_file_name(CARGO_CLIPPY_EXE); fs::metadata(path).ok()?.modified().ok() }); diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index dfd16f71054..b8287980a4b 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -7,7 +7,6 @@ pub fn run(filename: &str) { .args(["-Z", "no-codegen"]) .args(["--edition", "2021"]) .arg(filename) - .env("__CLIPPY_INTERNAL_TESTS", "true") .status() .expect("failed to run cargo") .code(); diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index 0977cf22b2c..ca4af66cad1 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/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/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs index b8f5217af2b..265cdeab094 100644 --- a/clippy_lints/src/borrow_as_ptr.rs +++ b/clippy_lints/src/borrow_as_ptr.rs @@ -5,7 +5,7 @@ use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -94,4 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 73ce656ad15..f44d370a8fd 100644 --- a/clippy_lints/src/copies.rs +++ b/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/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index bf077a212fd..70f45646596 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::peel_mid_ty_refs; use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local}; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; @@ -10,11 +11,10 @@ use rustc_hir::{ Pat, PatKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; -use std::iter; declare_clippy_lint! { /// ### What it does @@ -131,8 +131,6 @@ pub struct Dereferencing { struct StateData { /// Span of the top level expression span: Span, - /// The required mutability - target_mut: Mutability, } enum State { @@ -141,9 +139,13 @@ enum State { // The number of calls in a sequence which changed the referenced type ty_changed_count: usize, is_final_ufcs: bool, + /// The required mutability + target_mut: Mutability, }, DerefedBorrow { - count: u32, + count: usize, + required_precedence: i8, + msg: &'static str, }, } @@ -214,59 +216,98 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { 1 }, is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), - }, - StateData { - span: expr.span, target_mut, }, + StateData { span: expr.span }, )); }, RefOp::AddrOf => { // Find the number of times the borrow is auto-derefed. let mut iter = find_adjustments(cx.tcx, typeck, expr).iter(); - if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| { - if !matches!(adjust.kind, Adjust::Deref(_)) { - Some((i, adjust)) - } else if !adjust.target.is_ref() { - // Add one to the number of references found. - Some((i + 1, adjust)) + let mut deref_count = 0usize; + let next_adjust = loop { + match iter.next() { + Some(adjust) => { + if !matches!(adjust.kind, Adjust::Deref(_)) { + break Some(adjust); + } else if !adjust.target.is_ref() { + deref_count += 1; + break iter.next(); + } + deref_count += 1; + }, + None => break None, + }; + }; + + // Determine the required number of references before any can be removed. In all cases the + // reference made by the current expression will be removed. After that there are four cases to + // handle. + // + // 1. Auto-borrow will trigger in the current position, so no further references are required. + // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to + // handle the automatically inserted re-borrow. + // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to + // start auto-deref. + // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow + // adjustments will not be inserted automatically, then leave one further reference to avoid + // moving a mutable borrow. + // e.g. + // fn foo<T>(x: &mut Option<&mut T>, y: &mut T) { + // let x = match x { + // // Removing the borrow will cause `x` to be moved + // Some(x) => &mut *x, + // None => y + // }; + // } + let deref_msg = + "this expression creates a reference which is immediately dereferenced by the compiler"; + let borrow_msg = "this expression borrows a value the compiler would automatically borrow"; + + let (required_refs, required_precedence, msg) = if is_auto_borrow_position(parent, expr.hir_id) + { + (1, PREC_POSTFIX, if deref_count == 1 { borrow_msg } else { deref_msg }) + } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) = + next_adjust.map(|a| &a.kind) + { + if matches!(mutability, AutoBorrowMutability::Mut { .. }) + && !is_auto_reborrow_position(parent) + { + (3, 0, deref_msg) } else { - None - } - }) { - // Found two consecutive derefs. At least one can be removed. - if i > 1 { - let target_mut = iter::once(adjust) - .chain(iter) - .find_map(|adjust| match adjust.kind { - Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()), - _ => None, - }) - // This default should never happen. Auto-deref always reborrows. - .unwrap_or(Mutability::Not); - self.state = Some(( - // Subtract one for the current borrow expression, and one to cover the last - // reference which can't be removed (it's either reborrowed, or needed for - // auto-deref to happen). - State::DerefedBorrow { - count: - // Truncation here would require more than a `u32::MAX` level reference. The compiler - // does not support this. - #[allow(clippy::cast_possible_truncation)] - { i as u32 - 2 } - }, - StateData { - span: expr.span, - target_mut, - }, - )); + (2, 0, deref_msg) } + } else { + (2, 0, deref_msg) + }; + + if deref_count >= required_refs { + self.state = Some(( + State::DerefedBorrow { + // One of the required refs is for the current borrow expression, the remaining ones + // can't be removed without breaking the code. See earlier comment. + count: deref_count - required_refs, + required_precedence, + msg, + }, + StateData { span: expr.span }, + )); } }, _ => (), } }, - (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => { + ( + Some(( + State::DerefMethod { + target_mut, + ty_changed_count, + .. + }, + data, + )), + RefOp::Method(_), + ) => { self.state = Some(( State::DerefMethod { ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { @@ -275,12 +316,30 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { ty_changed_count + 1 }, is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + target_mut, }, data, )); }, - (Some((State::DerefedBorrow { count }, data)), RefOp::AddrOf) if count != 0 => { - self.state = Some((State::DerefedBorrow { count: count - 1 }, data)); + ( + Some(( + State::DerefedBorrow { + count, + required_precedence, + msg, + }, + data, + )), + RefOp::AddrOf, + ) if count != 0 => { + self.state = Some(( + State::DerefedBorrow { + count: count - 1, + required_precedence, + msg, + }, + data, + )); }, (Some((state, data)), _) => report(cx, expr, state, data), @@ -456,6 +515,30 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, } } +/// Checks if the given expression is in a position which can be auto-reborrowed. +/// Note: This is only correct assuming auto-deref is already occurring. +fn is_auto_reborrow_position(parent: Option<Node<'_>>) -> bool { + match parent { + Some(Node::Expr(parent)) => matches!(parent.kind, ExprKind::MethodCall(..) | ExprKind::Call(..)), + Some(Node::Local(_)) => true, + _ => false, + } +} + +/// Checks if the given expression is a position which can auto-borrow. +fn is_auto_borrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool { + if let Some(Node::Expr(parent)) = parent { + match parent.kind { + ExprKind::MethodCall(_, _, [self_arg, ..], _) => self_arg.hir_id == child_id, + ExprKind::Field(..) => true, + ExprKind::Call(f, _) => f.hir_id == child_id, + _ => false, + } + } else { + false + } +} + /// Adjustments are sometimes made in the parent block rather than the expression itself. fn find_adjustments<'tcx>( tcx: TyCtxt<'tcx>, @@ -504,6 +587,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) State::DerefMethod { ty_changed_count, is_final_ufcs, + target_mut, } => { let mut app = Applicability::MachineApplicable; let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); @@ -518,12 +602,12 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) }; let addr_of_str = if ty_changed_count < ref_count { // Check if a reborrow from &mut T -> &T is required. - if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { "&*" } else { "" } - } else if data.target_mut == Mutability::Mut { + } else if target_mut == Mutability::Mut { "&mut " } else { "&" @@ -539,7 +623,7 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) cx, EXPLICIT_DEREF_METHODS, data.span, - match data.target_mut { + match target_mut { Mutability::Not => "explicit `deref` method call", Mutability::Mut => "explicit `deref_mut` method call", }, @@ -548,19 +632,24 @@ fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) app, ); }, - State::DerefedBorrow { .. } => { + State::DerefedBorrow { + required_precedence, + msg, + .. + } => { let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; span_lint_and_sugg( cx, NEEDLESS_BORROW, data.span, - &format!( - "this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler", - cx.typeck_results().expr_ty(expr), - ), + msg, "change this to", - snip.into(), + if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) { + format!("({})", snip) + } else { + snip.into() + }, app, ); }, diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 4f89e567430..1f4353fa4f7 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -172,6 +172,9 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n let name = var.ident.name.as_str(); let variant_split = camel_case_split(name); + if variant_split.len() == 1 { + return; + } pre = pre .iter() diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index df75b815436..24d7613e6f8 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,12 +1,16 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; +use clippy_utils::get_enclosing_block; use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; +use rustc_hir::{ + def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, Ty, TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -146,6 +150,13 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { let rty = cx.typeck_results().expr_ty(r); let lcpy = is_copy(cx, lty); let rcpy = is_copy(cx, rty); + if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { + if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) + || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) + { + return; // Don't lint + } + } // either operator autorefs or both args are copyable if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) { span_lint_and_then( @@ -206,6 +217,14 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { // &foo == bar (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => { let lty = cx.typeck_results().expr_ty(l); + if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { + let rty = cx.typeck_results().expr_ty(right); + if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) + || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) + { + return; // Don't lint + } + } let lcpy = is_copy(cx, lty); if (requires_ref || lcpy) && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()]) @@ -230,6 +249,14 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { // foo == &bar (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let rty = cx.typeck_results().expr_ty(r); + if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) { + let lty = cx.typeck_results().expr_ty(left); + if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty)) + || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty)) + { + return; // Don't lint + } + } let rcpy = is_copy(cx, rty); if (requires_ref || rcpy) && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) @@ -251,3 +278,43 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } } } + +fn in_impl<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, bin_op: DefId) -> Option<(&'tcx Ty<'tcx>, &'tcx Ty<'tcx>)> { + if_chain! { + if let Some(block) = get_enclosing_block(cx, e.hir_id); + if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()); + let item = cx.tcx.hir().expect_item(impl_def_id.expect_local()); + if let ItemKind::Impl(item) = &item.kind; + if let Some(of_trait) = &item.of_trait; + if let Some(seg) = of_trait.path.segments.last(); + if let Some(Res::Def(_, trait_id)) = seg.res; + if trait_id == bin_op; + if let Some(generic_args) = seg.args; + if let Some(GenericArg::Type(other_ty)) = generic_args.args.last(); + + then { + Some((item.self_ty, other_ty)) + } + else { + None + } + } +} + +fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: &TyS<'_>, hir_ty: &Ty<'_>) -> bool { + if_chain! { + if let ty::Adt(adt_def, _) = middle_ty.kind(); + if let Some(local_did) = adt_def.did.as_local(); + let item = cx.tcx.hir().expect_item(local_did); + let middle_ty_id = item.def_id.to_def_id(); + if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; + if let Res::Def(_, hir_ty_id) = path.res; + + then { + hir_ty_id == middle_ty_id + } + else { + false + } + } +} diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 688d8f8630f..395c920c997 100644 --- a/clippy_lints/src/format.rs +++ b/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/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 6358228dd47..2818b3e006a 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -179,7 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ) .and_then(|snip| { let i = snip.find("fn")?; - Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32)) + Some(item.span.lo() + BytePos((i + snip[i..].find('(')?) as u32)) }) .expect("failed to create span for type parameters"); Span::new(pos, pos, item.span.ctxt(), item.span.parent()) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 26fb4259952..4721b7f2b47 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/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), @@ -246,10 +247,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(redundant_slicing::REDUNDANT_SLICING), LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(reference::DEREF_ADDROF), - 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/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a21ddf73a11..bd5ff613447 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -71,7 +71,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_slicing::REDUNDANT_SLICING), LintId::of(reference::DEREF_ADDROF), - LintId::of(reference::REF_IN_DEREF), LintId::of(repeat_once::REPEAT_ONCE), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 746bdb19c3d..2d2693832e9 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/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, @@ -422,7 +423,6 @@ store.register_lints(&[ redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, ref_option_ref::REF_OPTION_REF, reference::DEREF_ADDROF, - reference::REF_IN_DEREF, regex::INVALID_REGEX, regex::TRIVIAL_REGEX, repeat_once::REPEAT_ONCE, diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 1744b7c8250..1292675f4a9 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/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/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index f9ffd4cf829..c44ef124bfa 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/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/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 8594338ffa5..10f8ae4b7f7 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/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/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 79e9882fef4..f2a7e925dd3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -581,6 +581,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark)); store.register_late_pass(move || Box::new(casts::Casts::new(msrv))); store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); + store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv))); store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|| Box::new(same_name_method::SameNameMethod)); @@ -591,7 +592,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: msrv, )) }); - store.register_late_pass(|| Box::new(map_clone::MapClone)); store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore)); store.register_late_pass(|| Box::new(shadow::Shadow::default())); store.register_late_pass(|| Box::new(unit_types::UnitTypes)); @@ -703,7 +703,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(mut_key::MutableKeyType)); store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic)); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); - store.register_early_pass(|| Box::new(reference::RefInDeref)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new())); store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); @@ -935,6 +934,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok"); ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types"); ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods"); + ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow"); // uplifted lints ls.register_renamed("clippy::invalid_ref", "invalid_value"); diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 0e2b78609c2..618cf4b8b22 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; declare_clippy_lint! { /// ### What it does @@ -85,7 +85,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { - check_fn_inner(cx, sig.decl, Some(id), generics, item.span, true); + check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); } } @@ -96,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { cx, sig.decl, Some(id), + None, &item.generics, item.span, report_extra_lifetimes, @@ -105,11 +106,11 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if let TraitItemKind::Fn(ref sig, ref body) = item.kind { - let body = match *body { - TraitFn::Required(_) => None, - TraitFn::Provided(id) => Some(id), + let (body, trait_sig) = match *body { + TraitFn::Required(sig) => (None, Some(sig)), + TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, &item.generics, item.span, true); + check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true); } } } @@ -126,6 +127,7 @@ fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: Option<BodyId>, + trait_sig: Option<&[Ident]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, @@ -167,7 +169,7 @@ fn check_fn_inner<'tcx>( } } } - if could_use_elision(cx, decl, body, generics.params) { + if could_use_elision(cx, decl, body, trait_sig, generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -181,10 +183,31 @@ fn check_fn_inner<'tcx>( } } +// elision doesn't work for explicit self types, see rust-lang/rust#69064 +fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool { + if_chain! { + if let Some(ident) = ident; + if ident.name == kw::SelfLower; + if !func.implicit_self.has_implicit_self(); + + if let Some(self_ty) = func.inputs.first(); + then { + let mut visitor = RefVisitor::new(cx); + visitor.visit_ty(self_ty); + + !visitor.all_lts().is_empty() + } + else { + false + } + } +} + fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option<BodyId>, + trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], ) -> bool { // There are two scenarios where elision works: @@ -235,11 +258,24 @@ fn could_use_elision<'tcx>( let input_lts = input_visitor.lts; let output_lts = output_visitor.lts; + if let Some(trait_sig) = trait_sig { + if explicit_self_type(cx, func, trait_sig.first().copied()) { + return false; + } + } + if let Some(body_id) = body { + let body = cx.tcx.hir().body(body_id); + + let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); + if explicit_self_type(cx, func, first_ident) { + return false; + } + let mut checker = BodyLifetimeChecker { lifetimes_used_in_body: false, }; - checker.visit_expr(&cx.tcx.hir().body(body_id).value); + checker.visit_expr(&body.value); if checker.lifetimes_used_in_body { return false; } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 50bf2527e39..a72ef1d2de2 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -4,7 +4,7 @@ use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -72,6 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { } } } + + extract_msrv_attr!(LateContext); } fn get_one_size_of_ty<'tcx>( diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 174c7da28d3..5b203fd3d92 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -1,15 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_trait_method, peel_blocks}; +use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; @@ -42,7 +43,17 @@ declare_clippy_lint! { "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types" } -declare_lint_pass!(MapClone => [MAP_CLONE]); +pub struct MapClone { + msrv: Option<RustcVersion>, +} + +impl_lint_pass!(MapClone => [MAP_CLONE]); + +impl MapClone { + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for MapClone { fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { @@ -65,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { hir::BindingAnnotation::Unannotated, .., name, None ) = inner.kind { if ident_eq(name, closure_expr) { - lint(cx, e.span, args[0].span, true); + self.lint_explicit_closure(cx, e.span, args[0].span, true); } }, hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { @@ -73,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { if ident_eq(name, inner) { if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { - lint(cx, e.span, args[0].span, true); + self.lint_explicit_closure(cx, e.span, args[0].span, true); } } }, @@ -90,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if let ty::Ref(_, ty, mutability) = obj_ty.kind() { if matches!(mutability, Mutability::Not) { let copy = is_copy(cx, ty); - lint(cx, e.span, args[0].span, copy); + self.lint_explicit_closure(cx, e.span, args[0].span, copy); } } else { lint_needless_cloning(cx, e.span, args[0].span); @@ -105,6 +116,8 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } } } + + extract_msrv_attr!(LateContext); } fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { @@ -127,31 +140,30 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } -fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { - let mut applicability = Applicability::MachineApplicable; - if copied { - span_lint_and_sugg( - cx, - MAP_CLONE, - replace, - "you are using an explicit closure for copying elements", - "consider calling the dedicated `copied` method", - format!( - "{}.copied()", - snippet_with_applicability(cx, root, "..", &mut applicability) - ), - applicability, - ); - } else { +impl MapClone { + fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { + let mut applicability = Applicability::MachineApplicable; + let message = if is_copy { + "you are using an explicit closure for copying elements" + } else { + "you are using an explicit closure for cloning elements" + }; + let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + "copied" + } else { + "cloned" + }; + span_lint_and_sugg( cx, MAP_CLONE, replace, - "you are using an explicit closure for cloning elements", - "consider calling the dedicated `cloned` method", + message, + &format!("consider calling the dedicated `{}` method", sugg_method), format!( - "{}.cloned()", - snippet_with_applicability(cx, root, "..", &mut applicability) + "{}.{}()", + snippet_with_applicability(cx, root, "..", &mut applicability), + sugg_method, ), applicability, ); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index be9eff4237b..dc6e4f96969 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Ty, TyS, VariantDef}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::sym; +use rustc_span::{sym, symbol::kw}; use std::cmp::{max, Ordering}; use std::collections::hash_map::Entry; @@ -1063,13 +1063,13 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); if path_str == "Err" { let mut matching_wild = inner.iter().any(is_wild); - let mut ident_bind_name = String::from("_"); + let mut ident_bind_name = kw::Underscore; if !matching_wild { // Looking for unused bindings (i.e.: `_e`) for pat in inner.iter() { if let PatKind::Binding(_, id, ident, None) = pat.kind { if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) { - ident_bind_name = ident.name.as_str().to_string(); + ident_bind_name = ident.name; matching_wild = true; } } @@ -1084,7 +1084,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm span_lint_and_note(cx, MATCH_WILD_ERR_ARM, arm.pat.span, - &format!("`Err({})` matches all errors", &ident_bind_name), + &format!("`Err({})` matches all errors", ident_bind_name), None, "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable", ); diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs new file mode 100644 index 00000000000..ca33bfc643d --- /dev/null +++ b/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/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a3cfdd3d4ba..d75ab0c4b1b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/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,6 +2384,13 @@ 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); }, diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 4e4653dadca..448dc4e6147 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/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/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 21b3f81d5d9..8db71d1e967 100644 --- a/clippy_lints/src/misc.rs +++ b/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/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 0e7ae43ce2d..d4c823d1c1a 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; -use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyS; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -88,7 +88,26 @@ impl LateLintPass<'_> for NeedlessQuestionMark { } fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - check(cx, body.value.peel_blocks()); + if let Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) = body.generator_kind { + if let ExprKind::Block( + Block { + expr: + Some(Expr { + kind: ExprKind::DropTemps(async_body), + .. + }), + .. + }, + _, + ) = body.value.kind + { + if let ExprKind::Block(Block { expr: Some(expr), .. }, ..) = async_body.kind { + check(cx, expr); + } + } + } else { + check(cx, body.value.peel_blocks()); + } } } diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index e0da12f77fc..b446cf9b6c2 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -101,7 +101,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // construct a replacement escape // the maximum value is \077, or \x3f, so u8 is sufficient here if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) { - write!(&mut suggest_1, "\\x{:02x}", n).unwrap(); + write!(suggest_1, "\\x{:02x}", n).unwrap(); } // append the null byte as \x00 and the following digits literally diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c08a19d520b..c2ade4f0db7 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -1,30 +1,36 @@ //! Checks for usage of `&Vec[_]` and `&String`. use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::ptr::get_spans; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::walk_ptrs_hir_ty; -use clippy_utils::{expr_path_res, is_lint_allowed, match_any_diagnostic_items, paths}; +use clippy_utils::ty::expr_sig; +use clippy_utils::{ + expr_path_res, get_expr_use_or_unification_node, is_lint_allowed, match_any_diagnostic_items, path_to_local, paths, +}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::hir_id::HirIdMap; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, Impl, ImplItem, ImplItemKind, Item, ItemKind, - Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, + self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, + ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, + TraitItem, TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, AssocItems, AssocKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; use rustc_span::{sym, MultiSpan}; -use std::borrow::Cow; +use std::fmt; +use std::iter; declare_clippy_lint! { /// ### What it does - /// This lint checks for function arguments of type `&String` - /// or `&Vec` unless the references are mutable. It will also suggest you - /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()` - /// calls. + /// This lint checks for function arguments of type `&String`, `&Vec`, + /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls + /// with the appropriate `.to_owned()`/`to_string()` calls. /// /// ### Why is this bad? /// Requiring the argument to be of the specific size @@ -32,28 +38,7 @@ declare_clippy_lint! { /// or `&str` usually suffice and can be obtained from other types, too. /// /// ### Known problems - /// The lint does not follow data. So if you have an - /// argument `x` and write `let y = x; y.clone()` the lint will not suggest - /// changing that `.clone()` to `.to_owned()`. - /// - /// Other functions called from this function taking a `&String` or `&Vec` - /// argument may also fail to compile if you change the argument. Applying - /// this lint on them will fix the problem, but they may be in other crates. - /// - /// One notable example of a function that may cause issues, and which cannot - /// easily be changed due to being in the standard library is `Vec::contains`. - /// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then - /// it will compile, but if a `&[T]` is passed then it will not compile. - /// - /// ```ignore - /// fn cannot_take_a_slice(v: &Vec<u8>) -> bool { - /// let vec_of_vecs: Vec<Vec<u8>> = some_other_fn(); - /// - /// vec_of_vecs.contains(v) - /// } - /// ``` - /// - /// Also there may be `fn(&Vec)`-typed references pointing to your function. + /// There may be `fn(&Vec)`-typed references pointing to your function. /// If you have them, you will get a compiler error after applying this lint's /// suggestions. You then have the choice to undo your changes or change the /// type of the reference. @@ -155,32 +140,86 @@ declare_clippy_lint! { declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); impl<'tcx> LateLintPass<'tcx> for Ptr { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Fn(ref sig, _, body_id) = item.kind { - check_fn(cx, sig.decl, Some(body_id)); - } - } + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(sig, trait_method) = &item.kind { + if matches!(trait_method, TraitFn::Provided(_)) { + // Handled by check body. + return; + } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Fn(ref sig, body_id) = item.kind { - let parent_item = cx.tcx.hir().get_parent_item(item.hir_id()); - if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) { - if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = it.kind { - return; // ignore trait impls - } + check_mut_from_ref(cx, sig.decl); + for arg in check_fn_args( + cx, + cx.tcx.fn_sig(item.def_id).skip_binder().inputs(), + sig.decl.inputs, + &[], + ) { + span_lint_and_sugg( + cx, + PTR_ARG, + arg.span, + &arg.build_msg(), + "change this to", + format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)), + Applicability::Unspecified, + ); } - check_fn(cx, sig.decl, Some(body_id)); } } - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind { - let body_id = if let TraitFn::Provided(b) = *trait_method { - Some(b) - } else { - None - }; - check_fn(cx, sig.decl, body_id); + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + let hir = cx.tcx.hir(); + let mut parents = hir.parent_iter(body.value.hir_id); + let (item_id, decl) = match parents.next() { + Some((_, Node::Item(i))) => { + if let ItemKind::Fn(sig, ..) = &i.kind { + (i.def_id, sig.decl) + } else { + return; + } + }, + Some((_, Node::ImplItem(i))) => { + if !matches!(parents.next(), + Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none()) + ) { + return; + } + if let ImplItemKind::Fn(sig, _) = &i.kind { + (i.def_id, sig.decl) + } else { + return; + } + }, + Some((_, Node::TraitItem(i))) => { + if let TraitItemKind::Fn(sig, _) = &i.kind { + (i.def_id, sig.decl) + } else { + return; + } + }, + _ => return, + }; + + check_mut_from_ref(cx, decl); + let sig = cx.tcx.fn_sig(item_id).skip_binder(); + let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params).collect(); + let results = check_ptr_arg_usage(cx, body, &lint_args); + + for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) { + span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| { + diag.multipart_suggestion( + "change this to", + iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx)))) + .chain(result.replacements.iter().map(|r| { + ( + r.expr_span, + format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement), + ) + })) + .collect(), + Applicability::Unspecified, + ); + }); } } @@ -247,154 +286,206 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } } -#[allow(clippy::too_many_lines)] -fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, opt_body_id: Option<BodyId>) { - let body = opt_body_id.map(|id| cx.tcx.hir().body(id)); - - for (idx, arg) in decl.inputs.iter().enumerate() { - // Honor the allow attribute on parameters. See issue 5644. - if let Some(body) = &body { - if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) { - continue; - } - } +#[derive(Default)] +struct PtrArgResult { + skip: bool, + replacements: Vec<PtrArgReplacement>, +} - let (item_name, path) = if_chain! { - if let TyKind::Rptr(_, MutTy { ty, mutbl: Mutability::Not }) = arg.kind; - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - if let Res::Def(_, did) = path.res; - if let Some(item_name) = cx.tcx.get_diagnostic_name(did); - then { - (item_name, path) - } else { - continue - } - }; +struct PtrArgReplacement { + expr_span: Span, + self_span: Span, + replacement: &'static str, +} - match item_name { - sym::Vec => { - if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) { - span_lint_and_then( - cx, - PTR_ARG, - arg.span, - "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ - with non-Vec-based slices", - |diag| { - if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) { - diag.span_suggestion( - arg.span, - "change this to", - format!("&[{}]", snippet), - Applicability::Unspecified, - ); - } - for (clonespan, suggestion) in spans { - diag.span_suggestion( - clonespan, - &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { - Cow::Owned(format!("change `{}` to", x)) - }), - suggestion.into(), - Applicability::Unspecified, - ); - } - }, - ); - } +struct PtrArg<'tcx> { + idx: usize, + span: Span, + ty_did: DefId, + ty_name: Symbol, + method_renames: &'static [(&'static str, &'static str)], + ref_prefix: RefPrefix, + deref_ty: DerefTy<'tcx>, + deref_assoc_items: Option<(DefId, &'tcx AssocItems<'tcx>)>, +} +impl PtrArg<'_> { + fn build_msg(&self) -> String { + format!( + "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do", + self.ref_prefix.mutability.prefix_str(), + self.ty_name, + self.ref_prefix.mutability.prefix_str(), + self.deref_ty.argless_str(), + ) + } +} + +struct RefPrefix { + lt: LifetimeName, + mutability: Mutability, +} +impl fmt::Display for RefPrefix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use fmt::Write; + f.write_char('&')?; + match self.lt { + LifetimeName::Param(ParamName::Plain(name)) => { + name.fmt(f)?; + f.write_char(' ')?; }, - sym::String => { - if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) { - span_lint_and_then( - cx, - PTR_ARG, - arg.span, - "writing `&String` instead of `&str` involves a new object where a slice will do", - |diag| { - diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified); - for (clonespan, suggestion) in spans { - diag.span_suggestion_short( - clonespan, - &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { - Cow::Owned(format!("change `{}` to", x)) - }), - suggestion.into(), - Applicability::Unspecified, - ); - } - }, - ); + LifetimeName::Underscore => f.write_str("'_ ")?, + LifetimeName::Static => f.write_str("'static ")?, + _ => (), + } + f.write_str(self.mutability.prefix_str()) + } +} + +struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>); +impl fmt::Display for DerefTyDisplay<'_, '_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use std::fmt::Write; + match self.1 { + DerefTy::Str => f.write_str("str"), + DerefTy::Path => f.write_str("Path"), + DerefTy::Slice(hir_ty, ty) => { + f.write_char('[')?; + match hir_ty.and_then(|s| snippet_opt(self.0, s)) { + Some(s) => f.write_str(&s)?, + None => ty.fmt(f)?, } + f.write_char(']') }, - sym::PathBuf => { - if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) { - span_lint_and_then( - cx, - PTR_ARG, - arg.span, - "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do", - |diag| { - diag.span_suggestion( - arg.span, + } + } +} + +enum DerefTy<'tcx> { + Str, + Path, + Slice(Option<Span>, Ty<'tcx>), +} +impl<'tcx> DerefTy<'tcx> { + fn argless_str(&self) -> &'static str { + match *self { + Self::Str => "str", + Self::Path => "Path", + Self::Slice(..) => "[_]", + } + } + + fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> { + DerefTyDisplay(cx, self) + } +} + +fn check_fn_args<'cx, 'tcx: 'cx>( + cx: &'cx LateContext<'tcx>, + tys: &'tcx [Ty<'_>], + hir_tys: &'tcx [hir::Ty<'_>], + params: &'tcx [Param<'_>], +) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx { + tys.iter() + .zip(hir_tys.iter()) + .enumerate() + .filter_map(|(i, (ty, hir_ty))| { + if_chain! { + if let ty::Ref(_, ty, mutability) = *ty.kind(); + if let ty::Adt(adt, substs) = *ty.kind(); + + if let TyKind::Rptr(lt, ref ty) = hir_ty.kind; + if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind; + + // Check that the name as typed matches the actual name of the type. + // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec` + if let [.., name] = path.segments; + if cx.tcx.item_name(adt.did) == name.ident.name; + + if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id); + if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id)); + + then { + let (method_renames, deref_ty, deref_impl_id) = match cx.tcx.get_diagnostic_name(adt.did) { + Some(sym::Vec) => ( + [("clone", ".to_owned()")].as_slice(), + DerefTy::Slice( + name.args + .and_then(|args| args.args.first()) + .and_then(|arg| if let GenericArg::Type(ty) = arg { + Some(ty.span) + } else { + None + }), + substs.type_at(0), + ), + cx.tcx.lang_items().slice_impl() + ), + Some(sym::String) => ( + [("clone", ".to_owned()"), ("as_str", "")].as_slice(), + DerefTy::Str, + cx.tcx.lang_items().str_impl() + ), + Some(sym::PathBuf) => ( + [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(), + DerefTy::Path, + None, + ), + Some(sym::Cow) => { + let ty_name = name.args + .and_then(|args| { + args.args.iter().find_map(|a| match a { + GenericArg::Type(x) => Some(x), + _ => None, + }) + }) + .and_then(|arg| snippet_opt(cx, arg.span)) + .unwrap_or_else(|| substs.type_at(1).to_string()); + span_lint_and_sugg( + cx, + PTR_ARG, + hir_ty.span, + "using a reference to `Cow` is not recommended", "change this to", - "&Path".into(), + format!("&{}{}", mutability.prefix_str(), ty_name), Applicability::Unspecified, ); - for (clonespan, suggestion) in spans { - diag.span_suggestion_short( - clonespan, - &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { - Cow::Owned(format!("change `{}` to", x)) - }), - suggestion.into(), - Applicability::Unspecified, - ); - } + return None; }, - ); - } - }, - sym::Cow => { - if_chain! { - if let [ref bx] = *path.segments; - if let Some(params) = bx.args; - if !params.parenthesized; - if let Some(inner) = params.args.iter().find_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, + _ => return None, + }; + return Some(PtrArg { + idx: i, + span: hir_ty.span, + ty_did: adt.did, + ty_name: name.ident.name, + method_renames, + ref_prefix: RefPrefix { + lt: lt.name, + mutability, + }, + deref_ty, + deref_assoc_items: deref_impl_id.map(|id| (id, cx.tcx.associated_items(id))), }); - let replacement = snippet_opt(cx, inner.span); - if let Some(r) = replacement; - then { - span_lint_and_sugg( - cx, - PTR_ARG, - arg.span, - "using a reference to `Cow` is not recommended", - "change this to", - "&".to_owned() + &r, - Applicability::Unspecified, - ); - } } - }, - _ => {}, - } - } + } + None + }) +} +fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) { if let FnRetTy::Return(ty) = decl.output { if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { let mut immutables = vec![]; - for (_, ref mutbl, ref argspan) in decl + for (_, mutbl, argspan) in decl .inputs .iter() .filter_map(get_rptr_lm) .filter(|&(lt, _, _)| lt.name == out.name) { - if *mutbl == Mutability::Mut { + if mutbl == Mutability::Mut { return; } - immutables.push(*argspan); + immutables.push(argspan); } if immutables.is_empty() { return; @@ -413,24 +504,158 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, opt_body_id: Option<BodyId> } } -fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> { - if_chain! { - if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind; - if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last(); - let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).collect(); - if types.len() == 1; - then { - snippet_opt(cx, types[0].span) - } else { - None +#[allow(clippy::too_many_lines)] +fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + /// Map from a local id to which argument it came from (index into `Self::args` and + /// `Self::results`) + bindings: HirIdMap<usize>, + /// The arguments being checked. + args: &'cx [PtrArg<'tcx>], + /// The results for each argument (len should match args.len) + results: Vec<PtrArgResult>, + /// The number of arguments which can't be linted. Used to return early. + skip_count: usize, + } + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } + + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.skip_count == self.args.len() { + return; + } + + // Check if this is local we care about + let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) { + Some(&i) => i, + None => return walk_expr(self, e), + }; + let args = &self.args[args_idx]; + let result = &mut self.results[args_idx]; + + // Helper function to handle early returns. + let mut set_skip_flag = || { + if result.skip { + self.skip_count += 1; + } + result.skip = true; + }; + + match get_expr_use_or_unification_node(self.cx.tcx, e) { + Some((Node::Stmt(_), _)) => (), + Some((Node::Local(l), _)) => { + // Only trace simple bindings. e.g `let x = y;` + if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind { + self.bindings.insert(id, args_idx); + } else { + set_skip_flag(); + } + }, + Some((Node::Expr(e), child_id)) => match e.kind { + ExprKind::Call(f, expr_args) => { + let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); + if expr_sig(self.cx, f) + .map(|sig| sig.input(i).skip_binder().peel_refs()) + .map_or(true, |ty| match *ty.kind() { + ty::Param(_) => true, + ty::Adt(def, _) => def.did == args.ty_did, + _ => false, + }) + { + // Passed to a function taking the non-dereferenced type. + set_skip_flag(); + } + }, + ExprKind::MethodCall(name, _, expr_args @ [self_arg, ..], _) => { + let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); + if i == 0 { + // Check if the method can be renamed. + let name = name.ident.as_str(); + if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) { + result.replacements.push(PtrArgReplacement { + expr_span: e.span, + self_span: self_arg.span, + replacement, + }); + return; + } + } + + let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) { + x + } else { + set_skip_flag(); + return; + }; + + match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() { + ty::Param(_) => { + set_skip_flag(); + }, + // If the types match check for methods which exist on both types. e.g. `Vec::len` and + // `slice::len` + ty::Adt(def, _) + if def.did == args.ty_did + && (i != 0 + || self.cx.tcx.trait_of_item(id).is_some() + || !args.deref_assoc_items.map_or(false, |(id, items)| { + items + .find_by_name_and_kind(self.cx.tcx, name.ident, AssocKind::Fn, id) + .is_some() + })) => + { + set_skip_flag(); + }, + _ => (), + } + }, + // Indexing is fine for currently supported types. + ExprKind::Index(e, _) if e.hir_id == child_id => (), + _ => set_skip_flag(), + }, + _ => set_skip_flag(), + } } } + + let mut skip_count = 0; + let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>(); + let mut v = V { + cx, + bindings: args + .iter() + .enumerate() + .filter_map(|(i, arg)| { + let param = &body.params[arg.idx]; + match param.pat.kind { + PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) + if !is_lint_allowed(cx, PTR_ARG, param.hir_id) => + { + Some((id, i)) + }, + _ => { + skip_count += 1; + results[arg.idx].skip = true; + None + }, + } + }) + .collect(), + args, + results, + skip_count, + }; + v.visit_expr(&body.value); + v.results } -fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { +fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(ref lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) } else { diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index b2448372370..811a7bb9c15 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; @@ -104,59 +103,3 @@ impl EarlyLintPass for DerefAddrOf { } } } - -declare_clippy_lint! { - /// ### What it does - /// Checks for references in expressions that use - /// auto dereference. - /// - /// ### Why is this bad? - /// The reference is a no-op and is automatically - /// dereferenced by the compiler and makes the code less clear. - /// - /// ### Example - /// ```rust - /// struct Point(u32, u32); - /// let point = Point(30, 20); - /// let x = (&point).0; - /// ``` - /// Use instead: - /// ```rust - /// # struct Point(u32, u32); - /// # let point = Point(30, 20); - /// let x = point.0; - /// ``` - #[clippy::version = "pre 1.29.0"] - pub REF_IN_DEREF, - complexity, - "Use of reference in auto dereference expression." -} - -declare_lint_pass!(RefInDeref => [REF_IN_DEREF]); - -impl EarlyLintPass for RefInDeref { - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { - if_chain! { - if let ExprKind::Field(ref object, _) = e.kind; - if let ExprKind::Paren(ref parened) = object.kind; - if let ExprKind::AddrOf(_, _, ref inner) = parened.kind; - then { - let applicability = if inner.span.from_expansion() { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - let sugg = Sugg::ast(cx, inner, "_").maybe_par(); - span_lint_and_sugg( - cx, - REF_IN_DEREF, - object.span, - "creating a reference that is immediately dereferenced", - "try this", - sugg.to_string(), - applicability, - ); - } - } - } -} diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 5dafd08cf3b..79f104eac0b 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/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`" } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 6369aafe3f9..5257f5302cd 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{SpanlessEq, SpanlessHash}; use core::hash::{Hash, Hasher}; use if_chain::if_chain; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { let Generics { where_clause, .. } = &item.generics; - let mut self_bounds_set = FxHashSet::default(); + let mut self_bounds_map = FxHashMap::default(); for predicate in where_clause.predicates { if_chain! { @@ -108,27 +108,29 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { ) ) = cx.tcx.hir().get_if_local(*def_id); then { - if self_bounds_set.is_empty() { + if self_bounds_map.is_empty() { for bound in self_bounds.iter() { - let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue }; - self_bounds_set.insert(self_res); + let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue }; + self_bounds_map.insert(self_res, self_segments); } } bound_predicate .bounds .iter() - .filter_map(get_trait_res_span_from_bound) - .for_each(|(trait_item_res, span)| { - if self_bounds_set.get(&trait_item_res).is_some() { - span_lint_and_help( - cx, - TRAIT_DUPLICATION_IN_BOUNDS, - span, - "this trait bound is already specified in trait declaration", - None, - "consider removing this trait bound", - ); + .filter_map(get_trait_info_from_bound) + .for_each(|(trait_item_res, trait_item_segments, span)| { + if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { + if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); + } } }); } @@ -137,14 +139,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } -fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { - if let GenericBound::Trait(t, _) = bound { - Some((t.trait_ref.path.res, t.span)) - } else { - None - } -} - impl TraitBounds { fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { struct SpanlessTy<'cx, 'tcx> { @@ -231,7 +225,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { let res = param .bounds .iter() - .filter_map(get_trait_res_span_from_bound) + .filter_map(get_trait_info_from_bound) .collect::<Vec<_>>(); map.insert(*ident, res); } @@ -245,10 +239,10 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if let Some(segment) = segments.first(); if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { - for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) { - if let Some((_, span_direct)) = trait_resolutions_direct + for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { + if let Some((_, _, span_direct)) = trait_resolutions_direct .iter() - .find(|(res_direct, _)| *res_direct == res_where) { + .find(|(res_direct, _, _)| *res_direct == res_where) { span_lint_and_help( cx, TRAIT_DUPLICATION_IN_BOUNDS, @@ -263,3 +257,11 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { } } } + +fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> { + if let GenericBound::Trait(t, _) = bound { + Some((t.trait_ref.path.res, t.trait_ref.path.segments, t.span)) + } else { + None + } +} diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 0bd151fed91..1d4fe9cfd3c 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -291,7 +291,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) fn extend_with_struct_pat( qself1: &Option<ast::QSelf>, path1: &ast::Path, - fps1: &mut Vec<ast::PatField>, + fps1: &mut [ast::PatField], rest1: bool, start: usize, alternatives: &mut Vec<P<Pat>>, @@ -332,7 +332,7 @@ fn extend_with_struct_pat( /// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), /// where `~` denotes semantic equality. fn extend_with_matching_product( - targets: &mut Vec<P<Pat>>, + targets: &mut [P<Pat>], start: usize, alternatives: &mut Vec<P<Pat>>, predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index d6deb50cc90..c9d99617c1e 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -156,7 +156,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS. /// /// The minimum rust version that the project supports (msrv: Option<String> = None), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5c97535dcdf..86603132d53 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1850,7 +1850,8 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } /// Gets the node where an expression is either used, or it's type is unified with another branch. -pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> { +/// Returns both the node and the `HirId` of the closest child node. +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> { let mut child_id = expr.hir_id; let mut iter = tcx.hir().parent_iter(child_id); loop { @@ -1862,9 +1863,9 @@ pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_> ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id, ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id, ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None, - _ => break Some(Node::Expr(expr)), + _ => break Some((Node::Expr(expr), child_id)), }, - Some((_, node)) => break Some(node), + Some((_, node)) => break Some((node, child_id)), } } } @@ -1873,18 +1874,21 @@ pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_> pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { !matches!( get_expr_use_or_unification_node(tcx, expr), - None | Some(Node::Stmt(Stmt { - kind: StmtKind::Expr(_) - | StmtKind::Semi(_) - | StmtKind::Local(Local { - pat: Pat { - kind: PatKind::Wild, + None | Some(( + Node::Stmt(Stmt { + kind: StmtKind::Expr(_) + | StmtKind::Semi(_) + | StmtKind::Local(Local { + pat: Pat { + kind: PatKind::Wild, + .. + }, .. - }, - .. - }), - .. - })) + }), + .. + }), + _ + )) ) } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 87bc8232dde..be1928045b8 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -388,7 +388,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { } /// Return `true` if `sugg` is enclosed in parenthesis. -fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool { +pub fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool { let mut chars = sugg.as_ref().chars(); if chars.next() == Some('(') { let mut depth = 1; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 72317447159..dc24bc8e252 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -5,19 +5,22 @@ use rustc_ast::ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{TyKind, Unsafety}; +use rustc_hir::{Expr, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; +use rustc_middle::ty::{ + self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, +}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; -use crate::{match_def_path, must_use_attr}; +use crate::{expr_path_res, match_def_path, must_use_attr}; // Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -410,3 +413,105 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P }) .flatten() } + +/// A signature for a function like type. +#[derive(Clone, Copy)] +pub enum ExprFnSig<'tcx> { + Sig(Binder<'tcx, FnSig<'tcx>>), + Closure(Binder<'tcx, FnSig<'tcx>>), + Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>), +} +impl<'tcx> ExprFnSig<'tcx> { + /// Gets the argument type at the given offset. + pub fn input(self, i: usize) -> Binder<'tcx, Ty<'tcx>> { + match self { + Self::Sig(sig) => sig.input(i), + Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_element_ty(i).unwrap()), + Self::Trait(inputs, _) => inputs.map_bound(|ty| ty.tuple_element_ty(i).unwrap()), + } + } + + /// Gets the result type, if one could be found. Note that the result type of a trait may not be + /// specified. + pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> { + match self { + Self::Sig(sig) | Self::Closure(sig) => Some(sig.output()), + Self::Trait(_, output) => output, + } + } +} + +/// If the expression is function like, get the signature for it. +pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> { + if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr_path_res(cx, expr) { + Some(ExprFnSig::Sig(cx.tcx.fn_sig(id))) + } else { + let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs(); + match *ty.kind() { + ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())), + ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))), + ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)), + ty::Dynamic(bounds, _) => { + let lang_items = cx.tcx.lang_items(); + match bounds.principal() { + Some(bound) + if Some(bound.def_id()) == lang_items.fn_trait() + || Some(bound.def_id()) == lang_items.fn_once_trait() + || Some(bound.def_id()) == lang_items.fn_mut_trait() => + { + let output = bounds + .projection_bounds() + .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id())) + .map(|p| p.map_bound(|p| p.ty)); + Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output)) + }, + _ => None, + } + }, + ty::Param(_) | ty::Projection(..) => { + let mut inputs = None; + let mut output = None; + let lang_items = cx.tcx.lang_items(); + + for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) { + let mut is_input = false; + if let Some(ty) = pred + .kind() + .map_bound(|pred| match pred { + PredicateKind::Trait(p) + if (lang_items.fn_trait() == Some(p.def_id()) + || lang_items.fn_mut_trait() == Some(p.def_id()) + || lang_items.fn_once_trait() == Some(p.def_id())) + && p.self_ty() == ty => + { + is_input = true; + Some(p.trait_ref.substs.type_at(1)) + }, + PredicateKind::Projection(p) + if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() + && p.projection_ty.self_ty() == ty => + { + is_input = false; + Some(p.ty) + }, + _ => None, + }) + .transpose() + { + if is_input && inputs.is_none() { + inputs = Some(ty); + } else if !is_input && output.is_none() { + output = Some(ty); + } else { + // Multiple different fn trait impls. Is this even allowed? + return None; + } + } + } + + inputs.map(|ty| ExprFnSig::Trait(ty, output)) + }, + _ => None, + } + } +} diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index 6027538dc4a..01891b51d3b 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -53,7 +53,7 @@ This gives the following output in clippy: ## License -Copyright 2014-2020 The Rust Project Developers +Copyright 2014-2022 The Rust Project Developers Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license diff --git a/src/driver.rs b/src/driver.rs index a8aa3a76abc..8f8f1140a3d 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -331,11 +331,10 @@ pub fn main() { // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN // - IF `--no-deps` is not set (`!no_deps`) OR // - IF `--no-deps` is set and Clippy is run on the specified primary package - let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true"); let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); - let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package)); + let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package); if clippy_enabled { args.extend(clippy_args); } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 531890c863f..6505028db9f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -328,15 +328,9 @@ fn run_ui_cargo(config: &mut compiletest::Config) { } } -fn prepare_env() { - set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); - set_var("__CLIPPY_INTERNAL_TESTS", "true"); - //set_var("RUST_BACKTRACE", "0"); -} - #[test] fn compile_test() { - prepare_env(); + set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); let mut config = default_config(); run_ui(&mut config); run_ui_test(&mut config); diff --git a/tests/ui-toml/min_rust_version/min_rust_version.rs b/tests/ui-toml/min_rust_version/min_rust_version.rs index 8e104926524..1e3ec123a3c 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.rs +++ b/tests/ui-toml/min_rust_version/min_rust_version.rs @@ -1,6 +1,7 @@ -#![allow(clippy::redundant_clone)] -#![warn(clippy::manual_non_exhaustive)] +#![allow(clippy::redundant_clone, clippy::unnecessary_operation)] +#![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)] +use std::mem::{size_of, size_of_val}; use std::ops::Deref; mod enums { @@ -68,6 +69,24 @@ fn check_index_refutable_slice() { } } +fn map_clone_suggest_copied() { + // This should still trigger the lint but suggest `cloned()` instead of `copied()` + let _: Option<u64> = Some(&16).map(|b| *b); +} + +fn borrow_as_ptr() { + let val = 1; + let _p = &val as *const i32; + + let mut val_mut = 1; + let _p_mut = &mut val_mut as *mut i32; +} + +fn manual_bits() { + size_of::<i8>() * 8; + size_of_val(&0u32) * 8; +} + fn main() { option_as_ref_deref(); match_like_matches(); @@ -75,4 +94,5 @@ fn main() { match_same_arms2(); manual_strip_msrv(); check_index_refutable_slice(); + borrow_as_ptr(); } diff --git a/tests/ui-toml/min_rust_version/min_rust_version.stderr b/tests/ui-toml/min_rust_version/min_rust_version.stderr new file mode 100644 index 00000000000..a1e7361c0cb --- /dev/null +++ b/tests/ui-toml/min_rust_version/min_rust_version.stderr @@ -0,0 +1,10 @@ +error: you are using an explicit closure for copying elements + --> $DIR/min_rust_version.rs:74:26 + | +LL | let _: Option<u64> = Some(&16).map(|b| *b); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `Some(&16).cloned()` + | + = note: `-D clippy::map-clone` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index 4327f12c37c..eefeb1decb6 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -1,9 +1,5 @@ #![warn(clippy::borrow_interior_mutable_const)] -#![allow( - clippy::declare_interior_mutable_const, - clippy::ref_in_deref, - clippy::needless_borrow -)] +#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)] #![allow(const_item_mutation)] use std::borrow::Cow; diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index f146b97cf61..9a908cf30e9 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:58:5 + --> $DIR/others.rs:54:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:59:16 + --> $DIR/others.rs:55:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:62:22 + --> $DIR/others.rs:58:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:63:25 + --> $DIR/others.rs:59:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:64:27 + --> $DIR/others.rs:60:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:65:26 + --> $DIR/others.rs:61:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:76:14 + --> $DIR/others.rs:72:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:77:14 + --> $DIR/others.rs:73:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:78:19 + --> $DIR/others.rs:74:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:79:14 + --> $DIR/others.rs:75:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:80:13 + --> $DIR/others.rs:76:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:86:13 + --> $DIR/others.rs:82:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:91:5 + --> $DIR/others.rs:87:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:92:16 + --> $DIR/others.rs:88:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ diff --git a/tests/ui/bytecount.rs b/tests/ui/bytecount.rs index c724ee21be3..d3ad26921bf 100644 --- a/tests/ui/bytecount.rs +++ b/tests/ui/bytecount.rs @@ -1,3 +1,5 @@ +#![allow(clippy::needless_borrow)] + #[deny(clippy::naive_bytecount)] fn main() { let x = vec![0_u8; 16]; diff --git a/tests/ui/bytecount.stderr b/tests/ui/bytecount.stderr index 1dc37fc8b25..68d838c1f82 100644 --- a/tests/ui/bytecount.stderr +++ b/tests/ui/bytecount.stderr @@ -1,23 +1,23 @@ error: you appear to be counting bytes the naive way - --> $DIR/bytecount.rs:5:13 + --> $DIR/bytecount.rs:7:13 | LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)` | note: the lint level is defined here - --> $DIR/bytecount.rs:1:8 + --> $DIR/bytecount.rs:3:8 | LL | #[deny(clippy::naive_bytecount)] | ^^^^^^^^^^^^^^^^^^^^^^^ error: you appear to be counting bytes the naive way - --> $DIR/bytecount.rs:7:13 + --> $DIR/bytecount.rs:9:13 | LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)` error: you appear to be counting bytes the naive way - --> $DIR/bytecount.rs:19:13 + --> $DIR/bytecount.rs:21:13 | LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)` diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index 8d43f64768d..dc062762604 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -7,7 +7,8 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::vec_init_then_push, - clippy::toplevel_ref_arg + clippy::toplevel_ref_arg, + clippy::needless_borrow )] use std::cell::RefCell; diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index f15501f7184..8c39d0d55dd 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -7,7 +7,8 @@ clippy::no_effect, clippy::unnecessary_operation, clippy::vec_init_then_push, - clippy::toplevel_ref_arg + clippy::toplevel_ref_arg, + clippy::needless_borrow )] use std::cell::RefCell; diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index e7d28b4320b..861543d0aa9 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:24:5 + --> $DIR/clone_on_copy.rs:25:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -7,43 +7,43 @@ LL | 42.clone(); = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:28:5 + --> $DIR/clone_on_copy.rs:29:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:31:5 + --> $DIR/clone_on_copy.rs:32:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `u32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:34:5 + --> $DIR/clone_on_copy.rs:35:5 | LL | x.clone().rotate_left(1); | ^^^^^^^^^ help: try removing the `clone` call: `x` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:48:5 + --> $DIR/clone_on_copy.rs:49:5 | LL | m!(42).clone(); | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` error: using `clone` on type `[u32; 2]` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:58:5 + --> $DIR/clone_on_copy.rs:59:5 | LL | x.clone()[0]; | ^^^^^^^^^ help: try dereferencing it: `(*x)` error: using `clone` on type `char` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:68:14 + --> $DIR/clone_on_copy.rs:69:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:72:14 + --> $DIR/clone_on_copy.rs:73:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` diff --git a/tests/ui/cmp_owned/comparison_flip.fixed b/tests/ui/cmp_owned/comparison_flip.fixed new file mode 100644 index 00000000000..44e41bdd114 --- /dev/null +++ b/tests/ui/cmp_owned/comparison_flip.fixed @@ -0,0 +1,29 @@ +// run-rustfix + +use std::fmt::{self, Display}; + +fn main() { + let a = Foo; + + if a != "bar" { + println!("foo"); + } + + if a != "bar" { + println!("foo"); + } +} + +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "foo") + } +} + +impl PartialEq<&str> for Foo { + fn eq(&self, other: &&str) -> bool { + "foo" == *other + } +} diff --git a/tests/ui/cmp_owned/comparison_flip.rs b/tests/ui/cmp_owned/comparison_flip.rs new file mode 100644 index 00000000000..662673abb62 --- /dev/null +++ b/tests/ui/cmp_owned/comparison_flip.rs @@ -0,0 +1,29 @@ +// run-rustfix + +use std::fmt::{self, Display}; + +fn main() { + let a = Foo; + + if a.to_string() != "bar" { + println!("foo"); + } + + if "bar" != a.to_string() { + println!("foo"); + } +} + +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "foo") + } +} + +impl PartialEq<&str> for Foo { + fn eq(&self, other: &&str) -> bool { + "foo" == *other + } +} diff --git a/tests/ui/cmp_owned/comparison_flip.stderr b/tests/ui/cmp_owned/comparison_flip.stderr new file mode 100644 index 00000000000..e4d0d822bb1 --- /dev/null +++ b/tests/ui/cmp_owned/comparison_flip.stderr @@ -0,0 +1,18 @@ +error: this creates an owned instance just for comparison + --> $DIR/comparison_flip.rs:8:8 + | +LL | if a.to_string() != "bar" { + | ^^^^^^^^^^^^^ help: try: `a` + | + = note: `-D clippy::cmp-owned` implied by `-D warnings` + +error: this creates an owned instance just for comparison + --> $DIR/comparison_flip.rs:12:17 + | +LL | if "bar" != a.to_string() { + | ---------^^^^^^^^^^^^^ + | | + | help: try: `a != "bar"` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/duration_subsec.fixed b/tests/ui/duration_subsec.fixed index ee5c7863eff..d92b8998e88 100644 --- a/tests/ui/duration_subsec.fixed +++ b/tests/ui/duration_subsec.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code)] +#![allow(dead_code, clippy::needless_borrow)] #![warn(clippy::duration_subsec)] use std::time::Duration; diff --git a/tests/ui/duration_subsec.rs b/tests/ui/duration_subsec.rs index 3c9d2a28621..08da804996d 100644 --- a/tests/ui/duration_subsec.rs +++ b/tests/ui/duration_subsec.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code)] +#![allow(dead_code, clippy::needless_borrow)] #![warn(clippy::duration_subsec)] use std::time::Duration; diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index d3662a0a213..b2bf7c4e360 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -151,4 +151,11 @@ enum North { NoRight, } +// #8324 +enum Phase { + PreLookup, + Lookup, + PostLookup, +} + fn main() {} diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index f938f710688..618f80cdcf8 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -5,13 +5,10 @@ clippy::no_effect, clippy::redundant_closure_call, clippy::needless_pass_by_value, - clippy::option_map_unit_fn -)] -#![warn( - clippy::redundant_closure, - clippy::redundant_closure_for_method_calls, + clippy::option_map_unit_fn, clippy::needless_borrow )] +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] use std::path::{Path, PathBuf}; @@ -34,7 +31,7 @@ fn main() { Some(1).map(closure_mac!()); // don't lint closure in macro expansion let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec! let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted? - all(&[1, 2, 3], &2, below); //is adjusted + all(&[1, 2, 3], &&2, below); //is adjusted unsafe { Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn } diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 075bbc74922..a759e6eb514 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -5,13 +5,10 @@ clippy::no_effect, clippy::redundant_closure_call, clippy::needless_pass_by_value, - clippy::option_map_unit_fn -)] -#![warn( - clippy::redundant_closure, - clippy::redundant_closure_for_method_calls, + clippy::option_map_unit_fn, clippy::needless_borrow )] +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 8092f04c3fc..cda84982c9b 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> $DIR/eta.rs:31:27 + --> $DIR/eta.rs:28:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -7,45 +7,37 @@ LL | let a = Some(1u8).map(|a| foo(a)); = note: `-D clippy::redundant-closure` implied by `-D warnings` error: redundant closure - --> $DIR/eta.rs:35:40 + --> $DIR/eta.rs:32:40 | LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> $DIR/eta.rs:36:35 + --> $DIR/eta.rs:33:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` -error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler - --> $DIR/eta.rs:37:21 - | -LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted - | ^^^ help: change this to: `&2` - | - = note: `-D clippy::needless-borrow` implied by `-D warnings` - error: redundant closure - --> $DIR/eta.rs:37:26 + --> $DIR/eta.rs:34:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> $DIR/eta.rs:43:27 + --> $DIR/eta.rs:40:27 | LL | let e = Some(1u8).map(|a| divergent(a)); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` error: redundant closure - --> $DIR/eta.rs:44:27 + --> $DIR/eta.rs:41:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> $DIR/eta.rs:90:51 + --> $DIR/eta.rs:87:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -53,82 +45,82 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` error: redundant closure - --> $DIR/eta.rs:91:51 + --> $DIR/eta.rs:88:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> $DIR/eta.rs:93:42 + --> $DIR/eta.rs:90:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> $DIR/eta.rs:97:29 + --> $DIR/eta.rs:94:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> $DIR/eta.rs:98:27 + --> $DIR/eta.rs:95:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> $DIR/eta.rs:100:65 + --> $DIR/eta.rs:97:65 | LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> $DIR/eta.rs:163:22 + --> $DIR/eta.rs:160:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> $DIR/eta.rs:170:27 + --> $DIR/eta.rs:167:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> $DIR/eta.rs:175:27 + --> $DIR/eta.rs:172:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> $DIR/eta.rs:207:28 + --> $DIR/eta.rs:204:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:208:28 + --> $DIR/eta.rs:205:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:209:28 + --> $DIR/eta.rs:206:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> $DIR/eta.rs:216:21 + --> $DIR/eta.rs:213:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:235:21 + --> $DIR/eta.rs:232:21 | LL | map_str_to_path(|s| s.as_ref()); | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 48e2aae75d0..3de2a51ffa5 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref)] +#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index d8c8c0c5ca3..a08d7596422 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref)] +#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index f373e905d05..aa69781d15a 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -23,7 +23,12 @@ impl Unrelated { clippy::iter_next_loop, clippy::for_kv_map )] -#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] +#[allow( + clippy::linkedlist, + clippy::unnecessary_mut_passed, + clippy::similar_names, + clippy::needless_borrow +)] #[allow(unused_variables)] fn main() { let mut vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 3814583bb6e..7c063d99511 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -23,7 +23,12 @@ impl Unrelated { clippy::iter_next_loop, clippy::for_kv_map )] -#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] +#[allow( + clippy::linkedlist, + clippy::unnecessary_mut_passed, + clippy::similar_names, + clippy::needless_borrow +)] #[allow(unused_variables)] fn main() { let mut vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_fixable.stderr b/tests/ui/for_loop_fixable.stderr index 009dbe1a0bf..ddfe66d675f 100644 --- a/tests/ui/for_loop_fixable.stderr +++ b/tests/ui/for_loop_fixable.stderr @@ -1,5 +1,5 @@ error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:38:15 + --> $DIR/for_loop_fixable.rs:43:15 | LL | for _v in vec.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` @@ -7,13 +7,13 @@ LL | for _v in vec.iter() {} = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:40:15 + --> $DIR/for_loop_fixable.rs:45:15 | LL | for _v in vec.iter_mut() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:43:15 + --> $DIR/for_loop_fixable.rs:48:15 | LL | for _v in out_vec.into_iter() {} | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` @@ -21,73 +21,73 @@ LL | for _v in out_vec.into_iter() {} = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:48:15 + --> $DIR/for_loop_fixable.rs:53:15 | LL | for _v in [1, 2, 3].iter() {} | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:52:15 + --> $DIR/for_loop_fixable.rs:57:15 | LL | for _v in [0; 32].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:57:15 + --> $DIR/for_loop_fixable.rs:62:15 | LL | for _v in ll.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&ll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:60:15 + --> $DIR/for_loop_fixable.rs:65:15 | LL | for _v in vd.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&vd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:63:15 + --> $DIR/for_loop_fixable.rs:68:15 | LL | for _v in bh.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bh` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:66:15 + --> $DIR/for_loop_fixable.rs:71:15 | LL | for _v in hm.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hm` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:69:15 + --> $DIR/for_loop_fixable.rs:74:15 | LL | for _v in bt.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bt` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:72:15 + --> $DIR/for_loop_fixable.rs:77:15 | LL | for _v in hs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:75:15 + --> $DIR/for_loop_fixable.rs:80:15 | LL | for _v in bs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bs` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:250:18 + --> $DIR/for_loop_fixable.rs:255:18 | LL | for i in iterator.into_iter() { | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:270:18 + --> $DIR/for_loop_fixable.rs:275:18 | LL | for _ in t.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t` error: it is more concise to loop over containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:272:18 + --> $DIR/for_loop_fixable.rs:277:18 | LL | for _ in r.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 64cb7b1cfb8..d08f8f52495 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,6 +1,11 @@ // run-rustfix -#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] +#![allow( + clippy::print_literal, + clippy::redundant_clone, + clippy::to_string_in_format_args, + clippy::needless_borrow +)] #![warn(clippy::useless_format)] struct Foo(pub String); @@ -73,4 +78,10 @@ fn main() { let _s: String = (&*v.join("\n")).to_string(); format!("prepend {:+}", "s"); + + // Issue #8290 + let x = "foo"; + let _ = x.to_string(); + let _ = format!("{x:?}"); // Don't lint on debug + let _ = x.to_string(); } diff --git a/tests/ui/format.rs b/tests/ui/format.rs index a065b1b5683..4a10b580d26 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,6 +1,11 @@ // run-rustfix -#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)] +#![allow( + clippy::print_literal, + clippy::redundant_clone, + clippy::to_string_in_format_args, + clippy::needless_borrow +)] #![warn(clippy::useless_format)] struct Foo(pub String); @@ -75,4 +80,10 @@ fn main() { let _s: String = format!("{}", &*v.join("\n")); format!("prepend {:+}", "s"); + + // Issue #8290 + let x = "foo"; + let _ = format!("{x}"); + let _ = format!("{x:?}"); // Don't lint on debug + let _ = format!("{y}", y = x); } diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 58ad7499bb2..f25c7fb1ff1 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -1,5 +1,5 @@ error: useless use of `format!` - --> $DIR/format.rs:13:5 + --> $DIR/format.rs:18:5 | LL | format!("foo"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` @@ -7,19 +7,19 @@ LL | format!("foo"); = note: `-D clippy::useless-format` implied by `-D warnings` error: useless use of `format!` - --> $DIR/format.rs:14:5 + --> $DIR/format.rs:19:5 | LL | format!("{{}}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:15:5 + --> $DIR/format.rs:20:5 | LL | format!("{{}} abc {{}}"); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:16:5 + --> $DIR/format.rs:21:5 | LL | / format!( LL | | r##"foo {{}} @@ -34,70 +34,82 @@ LL ~ " bar"##.to_string(); | error: useless use of `format!` - --> $DIR/format.rs:21:13 + --> $DIR/format.rs:26:13 | LL | let _ = format!(""); | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` error: useless use of `format!` - --> $DIR/format.rs:23:5 + --> $DIR/format.rs:28:5 | LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:27:5 + --> $DIR/format.rs:32:5 | LL | format!("{:+}", "foo"); // Warn when the format makes no difference. | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:28:5 + --> $DIR/format.rs:33:5 | LL | format!("{:<}", "foo"); // Warn when the format makes no difference. | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:33:5 + --> $DIR/format.rs:38:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:37:5 + --> $DIR/format.rs:42:5 | LL | format!("{:+}", arg); // Warn when the format makes no difference. | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:38:5 + --> $DIR/format.rs:43:5 | LL | format!("{:<}", arg); // Warn when the format makes no difference. | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:65:5 + --> $DIR/format.rs:70:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> $DIR/format.rs:67:5 + --> $DIR/format.rs:72:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> $DIR/format.rs:71:18 + --> $DIR/format.rs:76:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> $DIR/format.rs:75:22 + --> $DIR/format.rs:80:22 | LL | let _s: String = format!("{}", &*v.join("/n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` -error: aborting due to 15 previous errors +error: useless use of `format!` + --> $DIR/format.rs:86:13 + | +LL | let _ = format!("{x}"); + | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` + +error: useless use of `format!` + --> $DIR/format.rs:88:13 + | +LL | let _ = format!("{y}", y = x); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 69189d9e0c0..0016009a02f 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -138,6 +138,23 @@ fn if_same_then_else2() -> Result<&'static str, ()> { let (y, x) = (1, 2); return Ok(&foo[x..y]); } + + // Issue #7579 + let _ = if let Some(0) = None { 0 } else { 0 }; + + if true { + return Err(()); + } else if let Some(0) = None { + return Err(()); + } + + let _ = if let Some(0) = None { + 0 + } else if let Some(1) = None { + 0 + } else { + 0 + }; } fn main() {} diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed new file mode 100644 index 00000000000..a9041671101 --- /dev/null +++ b/tests/ui/iter_overeager_cloned.fixed @@ -0,0 +1,45 @@ +// run-rustfix +#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] + +fn main() { + let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; + + let _: Option<String> = vec.iter().last().cloned(); + + let _: Option<String> = vec.iter().chain(vec.iter()).next().cloned(); + + let _: usize = vec.iter().filter(|x| x == &"2").count(); + + let _: Vec<_> = vec.iter().take(2).cloned().collect(); + + let _: Vec<_> = vec.iter().skip(2).cloned().collect(); + + let _ = vec.iter().filter(|x| x == &"2").nth(2).cloned(); + + let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] + .iter().flatten().cloned(); + + // Not implemented yet + let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); + + // Not implemented yet + let _ = vec.iter().cloned().map(|x| x.len()); + + // This would fail if changed. + let _ = vec.iter().cloned().map(|x| x + "2"); + + // Not implemented yet + let _ = vec.iter().cloned().find(|x| x == "2"); + + // Not implemented yet + let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); + + // Not implemented yet + let _ = vec.iter().cloned().all(|x| x.len() == 1); + + // Not implemented yet + let _ = vec.iter().cloned().any(|x| x.len() == 1); + + // Should probably stay as it is. + let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); +} diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs new file mode 100644 index 00000000000..dd04e33a4b3 --- /dev/null +++ b/tests/ui/iter_overeager_cloned.rs @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] + +fn main() { + let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; + + let _: Option<String> = vec.iter().cloned().last(); + + let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next(); + + let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); + + let _: Vec<_> = vec.iter().cloned().take(2).collect(); + + let _: Vec<_> = vec.iter().cloned().skip(2).collect(); + + let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); + + let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] + .iter() + .cloned() + .flatten(); + + // Not implemented yet + let _ = vec.iter().cloned().filter(|x| x.starts_with('2')); + + // Not implemented yet + let _ = vec.iter().cloned().map(|x| x.len()); + + // This would fail if changed. + let _ = vec.iter().cloned().map(|x| x + "2"); + + // Not implemented yet + let _ = vec.iter().cloned().find(|x| x == "2"); + + // Not implemented yet + let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); + + // Not implemented yet + let _ = vec.iter().cloned().all(|x| x.len() == 1); + + // Not implemented yet + let _ = vec.iter().cloned().any(|x| x.len() == 1); + + // Should probably stay as it is. + let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); +} diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr new file mode 100644 index 00000000000..e36b0e36fbd --- /dev/null +++ b/tests/ui/iter_overeager_cloned.stderr @@ -0,0 +1,58 @@ +error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead + --> $DIR/iter_overeager_cloned.rs:7:29 + | +LL | let _: Option<String> = vec.iter().cloned().last(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()` + | + = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings` + +error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead + --> $DIR/iter_overeager_cloned.rs:9:29 + | +LL | let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()` + +error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead + --> $DIR/iter_overeager_cloned.rs:11:20 + | +LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()` + | + = note: `-D clippy::redundant-clone` implied by `-D warnings` + +error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead + --> $DIR/iter_overeager_cloned.rs:13:21 + | +LL | let _: Vec<_> = vec.iter().cloned().take(2).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()` + +error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead + --> $DIR/iter_overeager_cloned.rs:15:21 + | +LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()` + +error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead + --> $DIR/iter_overeager_cloned.rs:17:13 + | +LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()` + +error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead + --> $DIR/iter_overeager_cloned.rs:19:13 + | +LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] + | _____________^ +LL | | .iter() +LL | | .cloned() +LL | | .flatten(); + | |__________________^ + | +help: try this + | +LL ~ let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))] +LL ~ .iter().flatten().cloned(); + | + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 9e37fb92559..b856f1375d3 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -45,6 +45,33 @@ fn main() { let b = &mut b; x(b); } + + // Issue #8191 + let mut x = 5; + let mut x = &mut x; + + mut_ref(x); + mut_ref(x); + let y: &mut i32 = x; + let y: &mut i32 = x; + + let y = match 0 { + // Don't lint. Removing the borrow would move 'x' + 0 => &mut x, + _ => &mut *x, + }; + + *x = 5; + + let s = String::new(); + let _ = s.len(); + let _ = s.capacity(); + let _ = s.capacity(); + + let x = (1, 2); + let _ = x.0; + let x = &x as *const (i32, i32); + let _ = unsafe { (*x).0 }; } #[allow(clippy::needless_borrowed_reference)] diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 093277784be..0bfe222a3dc 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -45,6 +45,33 @@ fn main() { let b = &mut b; x(&b); } + + // Issue #8191 + let mut x = 5; + let mut x = &mut x; + + mut_ref(&mut x); + mut_ref(&mut &mut x); + let y: &mut i32 = &mut x; + let y: &mut i32 = &mut &mut x; + + let y = match 0 { + // Don't lint. Removing the borrow would move 'x' + 0 => &mut x, + _ => &mut *x, + }; + + *x = 5; + + let s = String::new(); + let _ = (&s).len(); + let _ = (&s).capacity(); + let _ = (&&s).capacity(); + + let x = (1, 2); + let _ = (&x).0; + let x = &x as *const (i32, i32); + let _ = unsafe { (&*x).0 }; } #[allow(clippy::needless_borrowed_reference)] diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 03a5b3d260e..b90e8448db0 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,4 +1,4 @@ -error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:9:15 | LL | let _ = x(&&a); // warn @@ -6,59 +6,113 @@ LL | let _ = x(&&a); // warn | = note: `-D clippy::needless-borrow` implied by `-D warnings` -error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:13:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` -error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:25:13 | LL | &&a | ^^^ help: change this to: `&a` -error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:27:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` -error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:33:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` -error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:40:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` -error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:41:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` -error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:42:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` -error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:43:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` -error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler +error: this expression creates a reference which is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:46:11 | LL | x(&b); | ^^ help: change this to: `b` -error: aborting due to 10 previous errors +error: this expression creates a reference which is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:53:13 + | +LL | mut_ref(&mut x); + | ^^^^^^ help: change this to: `x` + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:54:13 + | +LL | mut_ref(&mut &mut x); + | ^^^^^^^^^^^ help: change this to: `x` + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:55:23 + | +LL | let y: &mut i32 = &mut x; + | ^^^^^^ help: change this to: `x` + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:56:23 + | +LL | let y: &mut i32 = &mut &mut x; + | ^^^^^^^^^^^ help: change this to: `x` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:67:13 + | +LL | let _ = (&s).len(); + | ^^^^ help: change this to: `s` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:68:13 + | +LL | let _ = (&s).capacity(); + | ^^^^ help: change this to: `s` + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:69:13 + | +LL | let _ = (&&s).capacity(); + | ^^^^^ help: change this to: `s` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:72:13 + | +LL | let _ = (&x).0; + | ^^^^ help: change this to: `x` + +error: this expression borrows a value the compiler would automatically borrow + --> $DIR/needless_borrow.rs:74:22 + | +LL | let _ = unsafe { (&*x).0 }; + | ^^^^^ help: change this to: `(*x)` + +error: aborting due to 19 previous errors diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index b07c4a23810..f3eafe8e927 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,5 +1,11 @@ #![warn(clippy::needless_lifetimes)] -#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop)] +#![allow( + dead_code, + clippy::boxed_local, + clippy::needless_pass_by_value, + clippy::unnecessary_wraps, + dyn_drop +)] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} @@ -369,4 +375,47 @@ mod issue6159 { } } +mod issue7296 { + use std::rc::Rc; + use std::sync::Arc; + + struct Foo; + impl Foo { + fn implicit<'a>(&'a self) -> &'a () { + &() + } + fn implicit_mut<'a>(&'a mut self) -> &'a () { + &() + } + + fn explicit<'a>(self: &'a Arc<Self>) -> &'a () { + &() + } + fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () { + &() + } + + fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () { + &() + } + } + + trait Bar { + fn implicit<'a>(&'a self) -> &'a (); + fn implicit_provided<'a>(&'a self) -> &'a () { + &() + } + + fn explicit<'a>(self: &'a Arc<Self>) -> &'a (); + fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () { + &() + } + + fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a (); + fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () { + &() + } + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 4114e6f1832..ffa152427a9 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:4:1 + --> $DIR/needless_lifetimes.rs:10:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,148 +7,190 @@ LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:6:1 + --> $DIR/needless_lifetimes.rs:12:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:16:1 + --> $DIR/needless_lifetimes.rs:22:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:50:1 + --> $DIR/needless_lifetimes.rs:56:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:55:1 + --> $DIR/needless_lifetimes.rs:61:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:67:1 + --> $DIR/needless_lifetimes.rs:73:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:91:1 + --> $DIR/needless_lifetimes.rs:97:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:121:5 + --> $DIR/needless_lifetimes.rs:127:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:130:5 + --> $DIR/needless_lifetimes.rs:136:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:149:1 + --> $DIR/needless_lifetimes.rs:155:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:179:1 + --> $DIR/needless_lifetimes.rs:185:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:185:1 + --> $DIR/needless_lifetimes.rs:191:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:204:1 + --> $DIR/needless_lifetimes.rs:210:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:212:1 + --> $DIR/needless_lifetimes.rs:218:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:248:1 + --> $DIR/needless_lifetimes.rs:254:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:255:9 + --> $DIR/needless_lifetimes.rs:261:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:259:9 + --> $DIR/needless_lifetimes.rs:265:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:272:9 + --> $DIR/needless_lifetimes.rs:278:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:301:5 + --> $DIR/needless_lifetimes.rs:307:5 | LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:304:5 + --> $DIR/needless_lifetimes.rs:310:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:313:5 + --> $DIR/needless_lifetimes.rs:319:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:325:5 + --> $DIR/needless_lifetimes.rs:331:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:340:5 + --> $DIR/needless_lifetimes.rs:346:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:353:5 + --> $DIR/needless_lifetimes.rs:359:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:356:5 + --> $DIR/needless_lifetimes.rs:362:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 25 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:384:9 + | +LL | fn implicit<'a>(&'a self) -> &'a () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:387:9 + | +LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:398:9 + | +LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:404:9 + | +LL | fn implicit<'a>(&'a self) -> &'a (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:405:9 + | +LL | fn implicit_provided<'a>(&'a self) -> &'a () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:414:9 + | +LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:415:9 + | +LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 32 previous errors diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index f1fc81aa12b..ba9d15e59d0 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -125,3 +125,16 @@ pub fn test2() { let x = Some(3); let _x = some_and_qmark_in_macro!(x?); } + +async fn async_option_bad(to: TO) -> Option<usize> { + let _ = Some(3); + to.magic +} + +async fn async_deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + +async fn async_result_bad(s: TR) -> Result<usize, bool> { + s.magic +} diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 44a0c5f61b5..3a6523e8fe8 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -125,3 +125,16 @@ pub fn test2() { let x = Some(3); let _x = some_and_qmark_in_macro!(x?); } + +async fn async_option_bad(to: TO) -> Option<usize> { + let _ = Some(3); + Some(to.magic?) +} + +async fn async_deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + +async fn async_result_bad(s: TR) -> Result<usize, bool> { + Ok(s.magic?) +} diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 57c3d48c761..f8308e24e77 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -77,5 +77,17 @@ LL | let _x = some_and_qmark_in_macro!(x?); | = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 12 previous errors +error: question mark operator is useless here + --> $DIR/needless_question_mark.rs:131:5 + | +LL | Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` + +error: question mark operator is useless here + --> $DIR/needless_question_mark.rs:139:5 + | +LL | Ok(s.magic?) + | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` + +error: aborting due to 14 previous errors diff --git a/tests/ui/op_ref.rs b/tests/ui/op_ref.rs index ab9c4d34c88..d8bf66603d9 100644 --- a/tests/ui/op_ref.rs +++ b/tests/ui/op_ref.rs @@ -1,7 +1,7 @@ #![allow(unused_variables, clippy::blacklisted_name)] #![warn(clippy::op_ref)] use std::collections::HashSet; -use std::ops::BitAnd; +use std::ops::{BitAnd, Mul}; fn main() { let tracked_fds: HashSet<i32> = HashSet::new(); @@ -55,3 +55,40 @@ fn main() { let y = Y(2); let z = x & &y; } + +#[derive(Clone, Copy)] +struct A(i32); +#[derive(Clone, Copy)] +struct B(i32); + +impl Mul<&A> for B { + type Output = i32; + fn mul(self, rhs: &A) -> Self::Output { + self.0 * rhs.0 + } +} +impl Mul<A> for B { + type Output = i32; + fn mul(self, rhs: A) -> Self::Output { + // Should not lint because removing the reference would lead to unconditional recursion + self * &rhs + } +} +impl Mul<&A> for A { + type Output = i32; + fn mul(self, rhs: &A) -> Self::Output { + self.0 * rhs.0 + } +} +impl Mul<A> for A { + type Output = i32; + fn mul(self, rhs: A) -> Self::Output { + let one = B(1); + let two = 2; + let three = 3; + let _ = one * &self; + let _ = two + &three; + // Removing the reference would lead to unconditional recursion + self * &rhs + } +} diff --git a/tests/ui/op_ref.stderr b/tests/ui/op_ref.stderr index 992417084bd..fe36c01166f 100644 --- a/tests/ui/op_ref.stderr +++ b/tests/ui/op_ref.stderr @@ -18,5 +18,21 @@ LL | let z = x & &y; | | | help: use the right value directly: `y` -error: aborting due to 2 previous errors +error: taken reference of right operand + --> $DIR/op_ref.rs:89:17 + | +LL | let _ = one * &self; + | ^^^^^^----- + | | + | help: use the right value directly: `self` + +error: taken reference of right operand + --> $DIR/op_ref.rs:90:17 + | +LL | let _ = two + &three; + | ^^^^^^------ + | | + | help: use the right value directly: `three` + +error: aborting due to 4 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 87cdb3ace47..3208048e0d5 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -176,4 +176,52 @@ mod issue6675 { } } +mod issue8239 { + fn more_than_max_suggestion_highest_lines_0() { + let frames = Vec::new(); + frames + .iter() + .map(|f: &String| f.to_lowercase()) + .reduce(|mut acc, f| { + acc.push_str(&f); + acc + }) + .unwrap_or_default(); + } + + fn more_to_max_suggestion_highest_lines_1() { + let frames = Vec::new(); + let iter = frames.iter(); + iter.map(|f: &String| f.to_lowercase()) + .reduce(|mut acc, f| { + let _ = ""; + let _ = ""; + acc.push_str(&f); + acc + }) + .unwrap_or_default(); + } + + fn equal_to_max_suggestion_highest_lines() { + let frames = Vec::new(); + let iter = frames.iter(); + iter.map(|f: &String| f.to_lowercase()) + .reduce(|mut acc, f| { + let _ = ""; + acc.push_str(&f); + acc + }).unwrap_or_default(); + } + + fn less_than_max_suggestion_highest_lines() { + let frames = Vec::new(); + let iter = frames.iter(); + let map = iter.map(|f: &String| f.to_lowercase()); + map.reduce(|mut acc, f| { + acc.push_str(&f); + acc + }).unwrap_or_default(); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 3f69cef301c..57ab5f03ee2 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -176,4 +176,54 @@ mod issue6675 { } } +mod issue8239 { + fn more_than_max_suggestion_highest_lines_0() { + let frames = Vec::new(); + frames + .iter() + .map(|f: &String| f.to_lowercase()) + .reduce(|mut acc, f| { + acc.push_str(&f); + acc + }) + .unwrap_or(String::new()); + } + + fn more_to_max_suggestion_highest_lines_1() { + let frames = Vec::new(); + let iter = frames.iter(); + iter.map(|f: &String| f.to_lowercase()) + .reduce(|mut acc, f| { + let _ = ""; + let _ = ""; + acc.push_str(&f); + acc + }) + .unwrap_or(String::new()); + } + + fn equal_to_max_suggestion_highest_lines() { + let frames = Vec::new(); + let iter = frames.iter(); + iter.map(|f: &String| f.to_lowercase()) + .reduce(|mut acc, f| { + let _ = ""; + acc.push_str(&f); + acc + }) + .unwrap_or(String::new()); + } + + fn less_than_max_suggestion_highest_lines() { + let frames = Vec::new(); + let iter = frames.iter(); + let map = iter.map(|f: &String| f.to_lowercase()); + map.reduce(|mut acc, f| { + acc.push_str(&f); + acc + }) + .unwrap_or(String::new()); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 9d0c42b10c2..549b00ae3c4 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -108,5 +108,57 @@ error: use of `unwrap_or` followed by a function call LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` -error: aborting due to 18 previous errors +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:189:14 + | +LL | .unwrap_or(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:202:14 + | +LL | .unwrap_or(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` + +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:208:9 + | +LL | / iter.map(|f: &String| f.to_lowercase()) +LL | | .reduce(|mut acc, f| { +LL | | let _ = ""; +LL | | acc.push_str(&f); +LL | | acc +LL | | }) +LL | | .unwrap_or(String::new()); + | |_____________________________________^ + | +help: try this + | +LL ~ iter.map(|f: &String| f.to_lowercase()) +LL + .reduce(|mut acc, f| { +LL + let _ = ""; +LL + acc.push_str(&f); +LL + acc +LL ~ }).unwrap_or_default(); + | + +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:221:9 + | +LL | / map.reduce(|mut acc, f| { +LL | | acc.push_str(&f); +LL | | acc +LL | | }) +LL | | .unwrap_or(String::new()); + | |_________________________________^ + | +help: try this + | +LL ~ map.reduce(|mut acc, f| { +LL + acc.push_str(&f); +LL + acc +LL ~ }).unwrap_or_default(); + | + +error: aborting due to 22 previous errors diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 67bfef06a05..ed724237808 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -9,7 +9,6 @@ fn do_vec(x: &Vec<i64>) { } fn do_vec_mut(x: &mut Vec<i64>) { - // no error here //Nothing here } @@ -18,7 +17,6 @@ fn do_str(x: &String) { } fn do_str_mut(x: &mut String) { - // no error here //Nothing here either } @@ -27,7 +25,6 @@ fn do_path(x: &PathBuf) { } fn do_path_mut(x: &mut PathBuf) { - // no error here //Nothing here either } @@ -52,7 +49,7 @@ fn cloned(x: &Vec<u8>) -> Vec<u8> { let e = x.clone(); let f = e.clone(); // OK let g = x; - let h = g.clone(); // Alas, we cannot reliably detect this without following data. + let h = g.clone(); let i = (e).clone(); x.clone() } @@ -156,6 +153,30 @@ mod issue6509 { } } +fn mut_vec_slice_methods(v: &mut Vec<u32>) { + v.copy_within(1..5, 10); +} + +fn mut_vec_vec_methods(v: &mut Vec<u32>) { + v.clear(); +} + +fn vec_contains(v: &Vec<u32>) -> bool { + [vec![], vec![0]].as_slice().contains(v) +} + +fn fn_requires_vec(v: &Vec<u32>) -> bool { + vec_contains(v) +} + +fn impl_fn_requires_vec(v: &Vec<u32>, f: impl Fn(&Vec<u32>)) { + f(v); +} + +fn dyn_fn_requires_vec(v: &Vec<u32>, f: &dyn Fn(&Vec<u32>)) { + f(v); +} + // No error for types behind an alias (#7699) type A = Vec<u8>; fn aliased(a: &A) {} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 64594eb870c..a9613daadde 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -1,4 +1,4 @@ -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do --> $DIR/ptr_arg.rs:7:14 | LL | fn do_vec(x: &Vec<i64>) { @@ -6,170 +6,154 @@ LL | fn do_vec(x: &Vec<i64>) { | = note: `-D clippy::ptr-arg` implied by `-D warnings` +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:11:18 + | +LL | fn do_vec_mut(x: &mut Vec<i64>) { + | ^^^^^^^^^^^^^ help: change this to: `&mut [i64]` + error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:16:14 + --> $DIR/ptr_arg.rs:15:14 | LL | fn do_str(x: &String) { | ^^^^^^^ help: change this to: `&str` +error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:19:18 + | +LL | fn do_str_mut(x: &mut String) { + | ^^^^^^^^^^^ help: change this to: `&mut str` + error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:25:15 + --> $DIR/ptr_arg.rs:23:15 | LL | fn do_path(x: &PathBuf) { | ^^^^^^^^ help: change this to: `&Path` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices - --> $DIR/ptr_arg.rs:38:18 +error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:27:19 + | +LL | fn do_path_mut(x: &mut PathBuf) { + | ^^^^^^^^^^^^ help: change this to: `&mut Path` + +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:35:18 | LL | fn do_vec(x: &Vec<i64>); | ^^^^^^^^^ help: change this to: `&[i64]` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices - --> $DIR/ptr_arg.rs:51:14 +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:48:14 | LL | fn cloned(x: &Vec<u8>) -> Vec<u8> { | ^^^^^^^^ | help: change this to | -LL | fn cloned(x: &[u8]) -> Vec<u8> { - | ~~~~~ -help: change `x.clone()` to - | -LL | let e = x.to_owned(); - | ~~~~~~~~~~~~ -help: change `x.clone()` to - | -LL | x.to_owned() - | +LL ~ fn cloned(x: &[u8]) -> Vec<u8> { +LL ~ let e = x.to_owned(); +LL | let f = e.clone(); // OK +LL | let g = x; +LL ~ let h = g.to_owned(); +LL | let i = (e).clone(); + ... error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:60:18 + --> $DIR/ptr_arg.rs:57:18 | LL | fn str_cloned(x: &String) -> String { | ^^^^^^^ | help: change this to | -LL | fn str_cloned(x: &str) -> String { - | ~~~~ -help: change `x.clone()` to - | -LL | let a = x.to_string(); - | ~~~~~~~~~~~~~ -help: change `x.clone()` to - | -LL | let b = x.to_string(); - | ~~~~~~~~~~~~~ -help: change `x.clone()` to - | -LL | x.to_string() +LL ~ fn str_cloned(x: &str) -> String { +LL ~ let a = x.to_owned(); +LL ~ let b = x.to_owned(); +LL | let c = b.clone(); +LL | let d = a.clone().clone().clone(); +LL ~ x.to_owned() | error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:68:19 + --> $DIR/ptr_arg.rs:65:19 | LL | fn path_cloned(x: &PathBuf) -> PathBuf { | ^^^^^^^^ | help: change this to | -LL | fn path_cloned(x: &Path) -> PathBuf { - | ~~~~~ -help: change `x.clone()` to - | -LL | let a = x.to_path_buf(); - | ~~~~~~~~~~~~~~~ -help: change `x.clone()` to - | -LL | let b = x.to_path_buf(); - | ~~~~~~~~~~~~~~~ -help: change `x.clone()` to - | -LL | x.to_path_buf() +LL ~ fn path_cloned(x: &Path) -> PathBuf { +LL ~ let a = x.to_path_buf(); +LL ~ let b = x.to_path_buf(); +LL | let c = b.clone(); +LL | let d = a.clone().clone().clone(); +LL ~ x.to_path_buf() | error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:76:44 + --> $DIR/ptr_arg.rs:73:44 | LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) { | ^^^^^^^ | help: change this to | -LL | fn false_positive_capacity(x: &Vec<u8>, y: &str) { - | ~~~~ -help: change `y.clone()` to +LL ~ fn false_positive_capacity(x: &Vec<u8>, y: &str) { +LL | let a = x.capacity(); +LL ~ let b = y.to_owned(); +LL ~ let c = y; | -LL | let b = y.to_string(); - | ~~~~~~~~~~~~~ -help: change `y.as_str()` to - | -LL | let c = y; - | ~ error: using a reference to `Cow` is not recommended - --> $DIR/ptr_arg.rs:90:25 + --> $DIR/ptr_arg.rs:87:25 | LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices - --> $DIR/ptr_arg.rs:143:21 +error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:140:21 | LL | fn foo_vec(vec: &Vec<u8>) { | ^^^^^^^^ | help: change this to | -LL | fn foo_vec(vec: &[u8]) { - | ~~~~~ -help: change `vec.clone()` to - | -LL | let _ = vec.to_owned().pop(); - | ~~~~~~~~~~~~~~ -help: change `vec.clone()` to +LL ~ fn foo_vec(vec: &[u8]) { +LL ~ let _ = vec.to_owned().pop(); +LL ~ let _ = vec.to_owned().clone(); | -LL | let _ = vec.to_owned().clone(); - | ~~~~~~~~~~~~~~ error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:148:23 + --> $DIR/ptr_arg.rs:145:23 | LL | fn foo_path(path: &PathBuf) { | ^^^^^^^^ | help: change this to | -LL | fn foo_path(path: &Path) { - | ~~~~~ -help: change `path.clone()` to +LL ~ fn foo_path(path: &Path) { +LL ~ let _ = path.to_path_buf().pop(); +LL ~ let _ = path.to_path_buf().clone(); | -LL | let _ = path.to_path_buf().pop(); - | ~~~~~~~~~~~~~~~~~~ -help: change `path.clone()` to - | -LL | let _ = path.to_path_buf().clone(); - | ~~~~~~~~~~~~~~~~~~ error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do - --> $DIR/ptr_arg.rs:153:21 + --> $DIR/ptr_arg.rs:150:21 | LL | fn foo_str(str: &PathBuf) { | ^^^^^^^^ | help: change this to | -LL | fn foo_str(str: &Path) { - | ~~~~~ -help: change `str.clone()` to +LL ~ fn foo_str(str: &Path) { +LL ~ let _ = str.to_path_buf().pop(); +LL ~ let _ = str.to_path_buf().clone(); | -LL | let _ = str.to_path_buf().pop(); - | ~~~~~~~~~~~~~~~~~ -help: change `str.clone()` to + +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:156:29 | -LL | let _ = str.to_path_buf().clone(); - | ~~~~~~~~~~~~~~~~~ +LL | fn mut_vec_slice_methods(v: &mut Vec<u32>) { + | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` -error: aborting due to 12 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index cc93859269c..a89845c1dd3 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -81,7 +81,7 @@ const fn issue6067() { None::<()>.is_none(); } -#[allow(clippy::deref_addrof, dead_code)] +#[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)] fn issue7921() { if (&None::<()>).is_none() {} if (&None::<()>).is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 280dca40c01..d6f44403487 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -96,7 +96,7 @@ const fn issue6067() { }; } -#[allow(clippy::deref_addrof, dead_code)] +#[allow(clippy::deref_addrof, dead_code, clippy::needless_borrow)] fn issue7921() { if let None = *(&None::<()>) {} if let None = *&None::<()> {} diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index b9425733a8b..8bddec576ed 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -54,6 +54,7 @@ #![warn(clippy::match_result_ok)] #![warn(clippy::disallowed_types)] #![warn(clippy::disallowed_methods)] +#![warn(clippy::needless_borrow)] // uplifted lints #![warn(invalid_value)] #![warn(array_into_iter)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 341c003b9df..d2010d71d2c 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -54,6 +54,7 @@ #![warn(clippy::if_let_some_result)] #![warn(clippy::disallowed_type)] #![warn(clippy::disallowed_method)] +#![warn(clippy::ref_in_deref)] // uplifted lints #![warn(clippy::invalid_ref)] #![warn(clippy::into_iter_on_array)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index cdec2808f1d..45cb8b786f5 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -138,59 +138,65 @@ error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_ LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` + --> $DIR/rename.rs:57:9 + | +LL | #![warn(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` + error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` -error: aborting due to 32 previous errors +error: aborting due to 33 previous errors diff --git a/tests/ui/return_self_not_must_use.rs b/tests/ui/return_self_not_must_use.rs index 7dd5742dae9..9b33ad6d3f6 100644 --- a/tests/ui/return_self_not_must_use.rs +++ b/tests/ui/return_self_not_must_use.rs @@ -1,4 +1,5 @@ #![crate_type = "lib"] +#![warn(clippy::return_self_not_must_use)] #[derive(Clone)] pub struct Bar; diff --git a/tests/ui/return_self_not_must_use.stderr b/tests/ui/return_self_not_must_use.stderr index 8af10cb65c4..94be87dfa31 100644 --- a/tests/ui/return_self_not_must_use.stderr +++ b/tests/ui/return_self_not_must_use.stderr @@ -1,5 +1,5 @@ error: missing `#[must_use]` attribute on a method returning `Self` - --> $DIR/return_self_not_must_use.rs:7:5 + --> $DIR/return_self_not_must_use.rs:8:5 | LL | fn what(&self) -> Self; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn what(&self) -> Self; = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` - --> $DIR/return_self_not_must_use.rs:17:5 + --> $DIR/return_self_not_must_use.rs:18:5 | LL | / pub fn foo(&self) -> Self { LL | | Self @@ -18,7 +18,7 @@ LL | | } = help: consider adding the `#[must_use]` attribute to the method or directly to the `Self` type error: missing `#[must_use]` attribute on a method returning `Self` - --> $DIR/return_self_not_must_use.rs:20:5 + --> $DIR/return_self_not_must_use.rs:21:5 | LL | / pub fn bar(self) -> Self { LL | | self diff --git a/tests/ui/search_is_some_fixable_none.rs b/tests/ui/search_is_some_fixable_none.rs index 778f4f6fa25..767518ab0c0 100644 --- a/tests/ui/search_is_some_fixable_none.rs +++ b/tests/ui/search_is_some_fixable_none.rs @@ -189,7 +189,7 @@ mod issue7392 { let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); } - fn test_string_1(s: &String) -> bool { + fn test_string_1(s: &str) -> bool { s.is_empty() } diff --git a/tests/ui/search_is_some_fixable_none.stderr b/tests/ui/search_is_some_fixable_none.stderr index 7c5e5eb589c..933ce5cf42d 100644 --- a/tests/ui/search_is_some_fixable_none.stderr +++ b/tests/ui/search_is_some_fixable_none.stderr @@ -251,14 +251,6 @@ error: called `is_none()` after searching an `Iterator` with `find` LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` -error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/search_is_some_fixable_none.rs:192:25 - | -LL | fn test_string_1(s: &String) -> bool { - | ^^^^^^^ help: change this to: `&str` - | - = note: `-D clippy::ptr-arg` implied by `-D warnings` - error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:208:17 | @@ -289,5 +281,5 @@ error: called `is_none()` after searching an `Iterator` with `find` LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))` -error: aborting due to 44 previous errors +error: aborting due to 43 previous errors diff --git a/tests/ui/search_is_some_fixable_some.rs b/tests/ui/search_is_some_fixable_some.rs index 241641fceae..77fd52e4ce7 100644 --- a/tests/ui/search_is_some_fixable_some.rs +++ b/tests/ui/search_is_some_fixable_some.rs @@ -188,7 +188,7 @@ mod issue7392 { let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); } - fn test_string_1(s: &String) -> bool { + fn test_string_1(s: &str) -> bool { s.is_empty() } diff --git a/tests/ui/search_is_some_fixable_some.stderr b/tests/ui/search_is_some_fixable_some.stderr index 9212c6e71ff..8b424f18ef5 100644 --- a/tests/ui/search_is_some_fixable_some.stderr +++ b/tests/ui/search_is_some_fixable_some.stderr @@ -234,14 +234,6 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)` -error: writing `&String` instead of `&str` involves a new object where a slice will do - --> $DIR/search_is_some_fixable_some.rs:191:25 - | -LL | fn test_string_1(s: &String) -> bool { - | ^^^^^^^ help: change this to: `&str` - | - = note: `-D clippy::ptr-arg` implied by `-D warnings` - error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:207:26 | @@ -272,5 +264,5 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))` -error: aborting due to 44 previous errors +error: aborting due to 43 previous errors diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index c5ae3ff769b..7e3d357ae50 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -53,7 +53,7 @@ fn resize_vector() { vec1.resize(10, 0); } -fn do_stuff(vec: &mut Vec<u8>) {} +fn do_stuff(vec: &mut [u8]) {} fn extend_vector_with_manipulations_between() { let len = 300; diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index 2edb202892a..21de19a2601 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -1,5 +1,6 @@ #![deny(clippy::trait_duplication_in_bounds)] +use std::collections::BTreeMap; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) @@ -73,4 +74,25 @@ impl U for Life { fn f() {} } +// should not warn +trait Iter: Iterator { + fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>> + where + Self: Iterator<Item = (K, V)> + Sized, + K: Ord + Eq, + { + unimplemented!(); + } +} + +struct Foo {} + +trait FooIter: Iterator<Item = Foo> { + fn bar() + where + Self: Iterator<Item = Foo>, + { + } +} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index e0c7a7ec618..6f8c8e47dfb 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -1,5 +1,5 @@ error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:5:15 + --> $DIR/trait_duplication_in_bounds.rs:6:15 | LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) | ^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::trait_duplication_in_bounds)] = help: consider removing this trait bound error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:5:23 + --> $DIR/trait_duplication_in_bounds.rs:6:23 | LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) | ^^^^^^^ @@ -20,7 +20,7 @@ LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) = help: consider removing this trait bound error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:34:15 + --> $DIR/trait_duplication_in_bounds.rs:35:15 | LL | Self: Default; | ^^^^^^^ @@ -28,7 +28,7 @@ LL | Self: Default; = help: consider removing this trait bound error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:48:15 + --> $DIR/trait_duplication_in_bounds.rs:49:15 | LL | Self: Default + Clone; | ^^^^^^^ @@ -36,7 +36,7 @@ LL | Self: Default + Clone; = help: consider removing this trait bound error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:54:15 + --> $DIR/trait_duplication_in_bounds.rs:55:15 | LL | Self: Default + Clone; | ^^^^^^^ @@ -44,7 +44,7 @@ LL | Self: Default + Clone; = help: consider removing this trait bound error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:54:25 + --> $DIR/trait_duplication_in_bounds.rs:55:25 | LL | Self: Default + Clone; | ^^^^^ @@ -52,12 +52,20 @@ LL | Self: Default + Clone; = help: consider removing this trait bound error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:57:15 + --> $DIR/trait_duplication_in_bounds.rs:58:15 | LL | Self: Default; | ^^^^^^^ | = help: consider removing this trait bound -error: aborting due to 7 previous errors +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds.rs:93:15 + | +LL | Self: Iterator<Item = Foo>, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 8 previous errors diff --git a/tests/ui/unnecessary_ref.fixed b/tests/ui/unnecessary_ref.fixed deleted file mode 100644 index d927bae976f..00000000000 --- a/tests/ui/unnecessary_ref.fixed +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix - -#![feature(stmt_expr_attributes)] -#![allow(unused_variables, dead_code)] - -struct Outer { - inner: u32, -} - -#[deny(clippy::ref_in_deref)] -fn main() { - let outer = Outer { inner: 0 }; - let inner = outer.inner; -} - -struct Apple; -impl Apple { - fn hello(&self) {} -} -struct Package(pub *const Apple); -fn foobar(package: *const Package) { - unsafe { &*(*package).0 }.hello(); -} diff --git a/tests/ui/unnecessary_ref.rs b/tests/ui/unnecessary_ref.rs deleted file mode 100644 index 86bfb76ec26..00000000000 --- a/tests/ui/unnecessary_ref.rs +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix - -#![feature(stmt_expr_attributes)] -#![allow(unused_variables, dead_code)] - -struct Outer { - inner: u32, -} - -#[deny(clippy::ref_in_deref)] -fn main() { - let outer = Outer { inner: 0 }; - let inner = (&outer).inner; -} - -struct Apple; -impl Apple { - fn hello(&self) {} -} -struct Package(pub *const Apple); -fn foobar(package: *const Package) { - unsafe { &*(&*package).0 }.hello(); -} diff --git a/tests/ui/unnecessary_ref.stderr b/tests/ui/unnecessary_ref.stderr deleted file mode 100644 index 436f4bcf738..00000000000 --- a/tests/ui/unnecessary_ref.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: creating a reference that is immediately dereferenced - --> $DIR/unnecessary_ref.rs:13:17 - | -LL | let inner = (&outer).inner; - | ^^^^^^^^ help: try this: `outer` - | -note: the lint level is defined here - --> $DIR/unnecessary_ref.rs:10:8 - | -LL | #[deny(clippy::ref_in_deref)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: creating a reference that is immediately dereferenced - --> $DIR/unnecessary_ref.rs:22:16 - | -LL | unsafe { &*(&*package).0 }.hello(); - | ^^^^^^^^^^^ help: try this: `(*package)` - | - = note: `-D clippy::ref-in-deref` implied by `-D warnings` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index e356f13d087..e431661d180 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -67,7 +67,7 @@ fn not_ok() { foo_rslice(mrrrrrslice); foo_rslice(mrrrrrslice); } - #[allow(unused_parens, clippy::double_parens)] + #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)] foo_rrrrmr((&&&&MoreRef)); generic_not_ok(mrslice); diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index 2a80291f5d8..6ae931d7aa4 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -67,7 +67,7 @@ fn not_ok() { foo_rslice(mrrrrrslice.as_ref()); foo_rslice(mrrrrrslice); } - #[allow(unused_parens, clippy::double_parens)] + #[allow(unused_parens, clippy::double_parens, clippy::needless_borrow)] foo_rrrrmr((&&&&MoreRef).as_ref()); generic_not_ok(mrslice); diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index 0a127858def..44669174411 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -7,37 +7,37 @@ fn main() { let mut v = Vec::new(); // these should be fine - write!(&mut v, "Hello"); - writeln!(&mut v, "Hello"); + write!(v, "Hello"); + writeln!(v, "Hello"); let world = "world"; - writeln!(&mut v, "Hello {}", world); - writeln!(&mut v, "Hello {world}", world = world); - writeln!(&mut v, "3 in hex is {:X}", 3); - writeln!(&mut v, "2 + 1 = {:.4}", 3); - writeln!(&mut v, "2 + 1 = {:5.4}", 3); - writeln!(&mut v, "Debug test {:?}", "hello, world"); - writeln!(&mut v, "{0:8} {1:>8}", "hello", "world"); - writeln!(&mut v, "{1:8} {0:>8}", "hello", "world"); - writeln!(&mut v, "{foo:8} {bar:>8}", foo = "hello", bar = "world"); - writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world"); - writeln!(&mut v, "{number:>width$}", number = 1, width = 6); - writeln!(&mut v, "{number:>0width$}", number = 1, width = 6); - writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); - writeln!(&mut v, "10 / 4 is {}", 2.5); - writeln!(&mut v, "2 + 1 = {}", 3); + writeln!(v, "Hello {}", world); + writeln!(v, "Hello {world}", world = world); + writeln!(v, "3 in hex is {:X}", 3); + writeln!(v, "2 + 1 = {:.4}", 3); + writeln!(v, "2 + 1 = {:5.4}", 3); + writeln!(v, "Debug test {:?}", "hello, world"); + writeln!(v, "{0:8} {1:>8}", "hello", "world"); + writeln!(v, "{1:8} {0:>8}", "hello", "world"); + writeln!(v, "{foo:8} {bar:>8}", foo = "hello", bar = "world"); + writeln!(v, "{bar:8} {foo:>8}", foo = "hello", bar = "world"); + writeln!(v, "{number:>width$}", number = 1, width = 6); + writeln!(v, "{number:>0width$}", number = 1, width = 6); + writeln!(v, "{} of {:b} people know binary, the other half doesn't", 1, 2); + writeln!(v, "10 / 4 is {}", 2.5); + writeln!(v, "2 + 1 = {}", 3); // these should throw warnings - write!(&mut v, "Hello {}", "world"); - writeln!(&mut v, "Hello {} {}", world, "world"); - writeln!(&mut v, "Hello {}", "world"); + write!(v, "Hello {}", "world"); + writeln!(v, "Hello {} {}", world, "world"); + writeln!(v, "Hello {}", "world"); // positional args don't change the fact // that we're using a literal -- this should // throw a warning - writeln!(&mut v, "{0} {1}", "hello", "world"); - writeln!(&mut v, "{1} {0}", "hello", "world"); + writeln!(v, "{0} {1}", "hello", "world"); + writeln!(v, "{1} {0}", "hello", "world"); // named args shouldn't change anything either - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); + writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); + writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); } diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index e0297c00231..593e9493ec5 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -1,134 +1,134 @@ error: literal with an empty format string - --> $DIR/write_literal.rs:30:32 + --> $DIR/write_literal.rs:30:27 | -LL | write!(&mut v, "Hello {}", "world"); - | ^^^^^^^ +LL | write!(v, "Hello {}", "world"); + | ^^^^^^^ | = note: `-D clippy::write-literal` implied by `-D warnings` help: try this | -LL - write!(&mut v, "Hello {}", "world"); -LL + write!(&mut v, "Hello world"); +LL - write!(v, "Hello {}", "world"); +LL + write!(v, "Hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:31:44 + --> $DIR/write_literal.rs:31:39 | -LL | writeln!(&mut v, "Hello {} {}", world, "world"); - | ^^^^^^^ +LL | writeln!(v, "Hello {} {}", world, "world"); + | ^^^^^^^ | help: try this | -LL - writeln!(&mut v, "Hello {} {}", world, "world"); -LL + writeln!(&mut v, "Hello {} world", world); +LL - writeln!(v, "Hello {} {}", world, "world"); +LL + writeln!(v, "Hello {} world", world); | error: literal with an empty format string - --> $DIR/write_literal.rs:32:34 + --> $DIR/write_literal.rs:32:29 | -LL | writeln!(&mut v, "Hello {}", "world"); - | ^^^^^^^ +LL | writeln!(v, "Hello {}", "world"); + | ^^^^^^^ | help: try this | -LL - writeln!(&mut v, "Hello {}", "world"); -LL + writeln!(&mut v, "Hello world"); +LL - writeln!(v, "Hello {}", "world"); +LL + writeln!(v, "Hello world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:37:33 + --> $DIR/write_literal.rs:37:28 | -LL | writeln!(&mut v, "{0} {1}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1}", "hello", "world"); + | ^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{0} {1}", "hello", "world"); -LL + writeln!(&mut v, "hello {1}", "world"); +LL - writeln!(v, "{0} {1}", "hello", "world"); +LL + writeln!(v, "hello {1}", "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:37:42 + --> $DIR/write_literal.rs:37:37 | -LL | writeln!(&mut v, "{0} {1}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{0} {1}", "hello", "world"); + | ^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{0} {1}", "hello", "world"); -LL + writeln!(&mut v, "{0} world", "hello"); +LL - writeln!(v, "{0} {1}", "hello", "world"); +LL + writeln!(v, "{0} world", "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:38:33 + --> $DIR/write_literal.rs:38:28 | -LL | writeln!(&mut v, "{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{1} {0}", "hello", "world"); + | ^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{1} {0}", "hello", "world"); -LL + writeln!(&mut v, "{1} hello", "world"); +LL - writeln!(v, "{1} {0}", "hello", "world"); +LL + writeln!(v, "{1} hello", "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:38:42 + --> $DIR/write_literal.rs:38:37 | -LL | writeln!(&mut v, "{1} {0}", "hello", "world"); - | ^^^^^^^ +LL | writeln!(v, "{1} {0}", "hello", "world"); + | ^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{1} {0}", "hello", "world"); -LL + writeln!(&mut v, "world {0}", "hello"); +LL - writeln!(v, "{1} {0}", "hello", "world"); +LL + writeln!(v, "world {0}", "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:41:37 + --> $DIR/write_literal.rs:41:32 | -LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ +LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); -LL + writeln!(&mut v, "hello {bar}", bar = "world"); +LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); +LL + writeln!(v, "hello {bar}", bar = "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:41:52 + --> $DIR/write_literal.rs:41:47 | -LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ +LL | writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); -LL + writeln!(&mut v, "{foo} world", foo = "hello"); +LL - writeln!(v, "{foo} {bar}", foo = "hello", bar = "world"); +LL + writeln!(v, "{foo} world", foo = "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:42:37 + --> $DIR/write_literal.rs:42:32 | -LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ +LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(&mut v, "{bar} hello", bar = "world"); +LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); +LL + writeln!(v, "{bar} hello", bar = "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:42:52 + --> $DIR/write_literal.rs:42:47 | -LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^^^^^^^ +LL | writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^^^^^^^ | help: try this | -LL - writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); -LL + writeln!(&mut v, "world {foo}", foo = "hello"); +LL - writeln!(v, "{bar} {foo}", foo = "hello", bar = "world"); +LL + writeln!(v, "world {foo}", foo = "hello"); | error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs index f341e8215e1..ba0d7be5eaa 100644 --- a/tests/ui/write_literal_2.rs +++ b/tests/ui/write_literal_2.rs @@ -6,20 +6,20 @@ use std::io::Write; fn main() { let mut v = Vec::new(); - writeln!(&mut v, "{}", "{hello}"); - writeln!(&mut v, r"{}", r"{hello}"); - writeln!(&mut v, "{}", '\''); - writeln!(&mut v, "{}", '"'); - writeln!(&mut v, r"{}", '"'); // don't lint - writeln!(&mut v, r"{}", '\''); + writeln!(v, "{}", "{hello}"); + writeln!(v, r"{}", r"{hello}"); + writeln!(v, "{}", '\''); + writeln!(v, "{}", '"'); + writeln!(v, r"{}", '"'); // don't lint + writeln!(v, r"{}", '\''); writeln!( - &mut v, + v, "some {}", "hello \ world!" ); writeln!( - &mut v, + v, "some {}\ {} \\ {}", "1", "2", "3", diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr index 73c6b885813..fc40fbfa9e2 100644 --- a/tests/ui/write_literal_2.stderr +++ b/tests/ui/write_literal_2.stderr @@ -1,62 +1,62 @@ error: literal with an empty format string - --> $DIR/write_literal_2.rs:9:28 + --> $DIR/write_literal_2.rs:9:23 | -LL | writeln!(&mut v, "{}", "{hello}"); - | ^^^^^^^^^ +LL | writeln!(v, "{}", "{hello}"); + | ^^^^^^^^^ | = note: `-D clippy::write-literal` implied by `-D warnings` help: try this | -LL - writeln!(&mut v, "{}", "{hello}"); -LL + writeln!(&mut v, "{{hello}}"); +LL - writeln!(v, "{}", "{hello}"); +LL + writeln!(v, "{{hello}}"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:10:29 + --> $DIR/write_literal_2.rs:10:24 | -LL | writeln!(&mut v, r"{}", r"{hello}"); - | ^^^^^^^^^^ +LL | writeln!(v, r"{}", r"{hello}"); + | ^^^^^^^^^^ | help: try this | -LL - writeln!(&mut v, r"{}", r"{hello}"); -LL + writeln!(&mut v, r"{{hello}}"); +LL - writeln!(v, r"{}", r"{hello}"); +LL + writeln!(v, r"{{hello}}"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:11:28 + --> $DIR/write_literal_2.rs:11:23 | -LL | writeln!(&mut v, "{}", '/''); - | ^^^^ +LL | writeln!(v, "{}", '/''); + | ^^^^ | help: try this | -LL - writeln!(&mut v, "{}", '/''); -LL + writeln!(&mut v, "'"); +LL - writeln!(v, "{}", '/''); +LL + writeln!(v, "'"); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:12:28 + --> $DIR/write_literal_2.rs:12:23 | -LL | writeln!(&mut v, "{}", '"'); - | ^^^ +LL | writeln!(v, "{}", '"'); + | ^^^ | help: try this | -LL - writeln!(&mut v, "{}", '"'); -LL + writeln!(&mut v, "/""); +LL - writeln!(v, "{}", '"'); +LL + writeln!(v, "/""); | error: literal with an empty format string - --> $DIR/write_literal_2.rs:14:29 + --> $DIR/write_literal_2.rs:14:24 | -LL | writeln!(&mut v, r"{}", '/''); - | ^^^^ +LL | writeln!(v, r"{}", '/''); + | ^^^^ | help: try this | -LL - writeln!(&mut v, r"{}", '/''); -LL + writeln!(&mut v, r"'"); +LL - writeln!(v, r"{}", '/''); +LL + writeln!(v, r"'"); | error: literal with an empty format string diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs index 1c1b1b58402..446d6914d34 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -10,50 +10,50 @@ fn main() { let mut v = Vec::new(); // These should fail - write!(&mut v, "Hello\n"); - write!(&mut v, "Hello {}\n", "world"); - write!(&mut v, "Hello {} {}\n", "world", "#2"); - write!(&mut v, "{}\n", 1265); - write!(&mut v, "\n"); + write!(v, "Hello\n"); + write!(v, "Hello {}\n", "world"); + write!(v, "Hello {} {}\n", "world", "#2"); + write!(v, "{}\n", 1265); + write!(v, "\n"); // These should be fine - write!(&mut v, ""); - write!(&mut v, "Hello"); - writeln!(&mut v, "Hello"); - writeln!(&mut v, "Hello\n"); - writeln!(&mut v, "Hello {}\n", "world"); - write!(&mut v, "Issue\n{}", 1265); - write!(&mut v, "{}", 1265); - write!(&mut v, "\n{}", 1275); - write!(&mut v, "\n\n"); - write!(&mut v, "like eof\n\n"); - write!(&mut v, "Hello {} {}\n\n", "world", "#2"); - writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 - writeln!(&mut v, "\nbla\n\n"); // #3126 + write!(v, ""); + write!(v, "Hello"); + writeln!(v, "Hello"); + writeln!(v, "Hello\n"); + writeln!(v, "Hello {}\n", "world"); + write!(v, "Issue\n{}", 1265); + write!(v, "{}", 1265); + write!(v, "\n{}", 1275); + write!(v, "\n\n"); + write!(v, "like eof\n\n"); + write!(v, "Hello {} {}\n\n", "world", "#2"); + writeln!(v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + writeln!(v, "\nbla\n\n"); // #3126 // Escaping - write!(&mut v, "\\n"); // #3514 - write!(&mut v, "\\\n"); // should fail - write!(&mut v, "\\\\n"); + write!(v, "\\n"); // #3514 + write!(v, "\\\n"); // should fail + write!(v, "\\\\n"); // Raw strings - write!(&mut v, r"\n"); // #3778 + write!(v, r"\n"); // #3778 // Literal newlines should also fail write!( - &mut v, + v, " " ); write!( - &mut v, + v, r" " ); // Don't warn on CRLF (#4208) - write!(&mut v, "\r\n"); - write!(&mut v, "foo\r\n"); - write!(&mut v, "\\r\n"); //~ ERROR - write!(&mut v, "foo\rbar\n"); + write!(v, "\r\n"); + write!(v, "foo\r\n"); + write!(v, "\\r\n"); //~ ERROR + write!(v, "foo\rbar\n"); } diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index 186459e50b6..3314a2a6e24 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -1,81 +1,81 @@ error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:13:5 | -LL | write!(&mut v, "Hello/n"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "Hello/n"); + | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::write-with-newline` implied by `-D warnings` help: use `writeln!()` instead | -LL - write!(&mut v, "Hello/n"); -LL + writeln!(&mut v, "Hello"); +LL - write!(v, "Hello/n"); +LL + writeln!(v, "Hello"); | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:14:5 | -LL | write!(&mut v, "Hello {}/n", "world"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: use `writeln!()` instead | -LL - write!(&mut v, "Hello {}/n", "world"); -LL + writeln!(&mut v, "Hello {}", "world"); +LL - write!(v, "Hello {}/n", "world"); +LL + writeln!(v, "Hello {}", "world"); | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:15:5 | -LL | write!(&mut v, "Hello {} {}/n", "world", "#2"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: use `writeln!()` instead | -LL - write!(&mut v, "Hello {} {}/n", "world", "#2"); -LL + writeln!(&mut v, "Hello {} {}", "world", "#2"); +LL - write!(v, "Hello {} {}/n", "world", "#2"); +LL + writeln!(v, "Hello {} {}", "world", "#2"); | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:16:5 | -LL | write!(&mut v, "{}/n", 1265); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^^^^ | help: use `writeln!()` instead | -LL - write!(&mut v, "{}/n", 1265); -LL + writeln!(&mut v, "{}", 1265); +LL - write!(v, "{}/n", 1265); +LL + writeln!(v, "{}", 1265); | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:17:5 | -LL | write!(&mut v, "/n"); - | ^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "/n"); + | ^^^^^^^^^^^^^^^ | help: use `writeln!()` instead | -LL - write!(&mut v, "/n"); -LL + writeln!(&mut v); +LL - write!(v, "/n"); +LL + writeln!(v); | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:36:5 | -LL | write!(&mut v, "//n"); // should fail - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "//n"); // should fail + | ^^^^^^^^^^^^^^^^^ | help: use `writeln!()` instead | -LL - write!(&mut v, "//n"); // should fail -LL + writeln!(&mut v, "/"); // should fail +LL - write!(v, "//n"); // should fail +LL + writeln!(v, "/"); // should fail | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:43:5 | LL | / write!( -LL | | &mut v, +LL | | v, LL | | " LL | | " LL | | ); @@ -84,7 +84,7 @@ LL | | ); help: use `writeln!()` instead | LL ~ writeln!( -LL | &mut v, +LL | v, LL ~ "" | @@ -92,7 +92,7 @@ error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:48:5 | LL | / write!( -LL | | &mut v, +LL | | v, LL | | r" LL | | " LL | | ); @@ -101,32 +101,32 @@ LL | | ); help: use `writeln!()` instead | LL ~ writeln!( -LL | &mut v, +LL | v, LL ~ r"" | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:57:5 | -LL | write!(&mut v, "/r/n"); //~ ERROR - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^^^ | help: use `writeln!()` instead | -LL - write!(&mut v, "/r/n"); //~ ERROR -LL + writeln!(&mut v, "/r"); //~ ERROR +LL - write!(v, "/r/n"); //~ ERROR +LL + writeln!(v, "/r"); //~ ERROR | error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:58:5 | -LL | write!(&mut v, "foo/rbar/n"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | write!(v, "foo/rbar/n"); + | ^^^^^^^^^^^^^^^^^^^^^^^ | help: use `writeln!()` instead | -LL - write!(&mut v, "foo/rbar/n"); -LL + writeln!(&mut v, "foo/rbar"); +LL - write!(v, "foo/rbar/n"); +LL + writeln!(v, "foo/rbar"); | error: aborting due to 10 previous errors diff --git a/tests/ui/writeln_empty_string.fixed b/tests/ui/writeln_empty_string.fixed index c3ac15b0375..e7d94acd130 100644 --- a/tests/ui/writeln_empty_string.fixed +++ b/tests/ui/writeln_empty_string.fixed @@ -8,13 +8,13 @@ fn main() { let mut v = Vec::new(); // These should fail - writeln!(&mut v); + writeln!(v); let mut suggestion = Vec::new(); - writeln!(&mut suggestion); + writeln!(suggestion); // These should be fine - writeln!(&mut v); - writeln!(&mut v, " "); - write!(&mut v, ""); + writeln!(v); + writeln!(v, " "); + write!(v, ""); } diff --git a/tests/ui/writeln_empty_string.rs b/tests/ui/writeln_empty_string.rs index 9a8894b6c0d..662c62f0211 100644 --- a/tests/ui/writeln_empty_string.rs +++ b/tests/ui/writeln_empty_string.rs @@ -8,13 +8,13 @@ fn main() { let mut v = Vec::new(); // These should fail - writeln!(&mut v, ""); + writeln!(v, ""); let mut suggestion = Vec::new(); - writeln!(&mut suggestion, ""); + writeln!(suggestion, ""); // These should be fine - writeln!(&mut v); - writeln!(&mut v, " "); - write!(&mut v, ""); + writeln!(v); + writeln!(v, " "); + write!(v, ""); } diff --git a/tests/ui/writeln_empty_string.stderr b/tests/ui/writeln_empty_string.stderr index 99635229b3e..ac65aadfc0e 100644 --- a/tests/ui/writeln_empty_string.stderr +++ b/tests/ui/writeln_empty_string.stderr @@ -1,16 +1,16 @@ -error: using `writeln!(&mut v, "")` +error: using `writeln!(v, "")` --> $DIR/writeln_empty_string.rs:11:5 | -LL | writeln!(&mut v, ""); - | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut v)` +LL | writeln!(v, ""); + | ^^^^^^^^^^^^^^^ help: replace it with: `writeln!(v)` | = note: `-D clippy::writeln-empty-string` implied by `-D warnings` -error: using `writeln!(&mut suggestion, "")` +error: using `writeln!(suggestion, "")` --> $DIR/writeln_empty_string.rs:14:5 | -LL | writeln!(&mut suggestion, ""); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut suggestion)` +LL | writeln!(suggestion, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(suggestion)` error: aborting due to 2 previous errors diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index f175700a3f4..83a200ca3c4 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -368,7 +368,7 @@ Otherwise, have a great day =^.^= <img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on Github"/> </a> - <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/7.0.0/markdown-it.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/languages/rust.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script> @@ -522,6 +522,11 @@ Otherwise, have a great day =^.^= } scrollToLintByURL($scope); + + setTimeout(function () { + var el = document.getElementById('filter-input'); + if (el) { el.focus() } + }, 0); }) .error(function (data) { $scope.error = data; |
