diff --git a/CHANGELOG.md b/CHANGELOG.md index 1276ab3d4bd3..816147076179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7393,6 +7393,7 @@ Released 2018-09-13 [`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_borrows_in_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_borrows_in_formatting [`useless_concat`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_concat [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c164241673a3..837bf29f5f39 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -176,6 +176,7 @@ crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO, crate::format_args::UNNECESSARY_TRAILING_COMMA_INFO, crate::format_args::UNUSED_FORMAT_SPECS_INFO, + crate::format_args::USELESS_BORROWS_IN_FORMATTING_INFO, crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, crate::format_push_string::FORMAT_PUSH_STRING_INFO, diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 12cf82916739..232aa35df088 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -10,13 +10,13 @@ }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_opt}; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_from_proc_macro, is_in_test, sym, trait_ref_of_method}; +use clippy_utils::{is_from_proc_macro, is_in_test, peel_hir_expr_while, sym, trait_ref_of_method}; use itertools::Itertools; use rustc_ast::{ - FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, - FormatPlaceholder, FormatTrait, + BorrowKind, FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, + FormatOptions, FormatPlaceholder, FormatTrait, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -257,6 +257,34 @@ "use of a format specifier that has no effect" } +declare_clippy_lint! { + /// ### What it does + /// Detects format!-style macros (e.g. `format!`, `println!`, `write!`) where an argument + /// is passed with an explicit `&` but the value is already a reference, resulting in a + /// double reference (e.g. `&&T`). + /// + /// ### Why is this bad? + /// The extra `&` is redundant and can make the code less clear. Format macros take + /// references to the arguments internally, so passing `&x` when `x` is already a + /// reference produces a double reference. The compiler is currently unable to + /// optimize double references, which results in about 6% degradation per call. + /// + /// ### Example + /// ```no_run + /// let s: &str = "hello"; + /// println!("{}", &s); + /// ``` + /// Use instead: + /// ```no_run + /// let s: &str = "hello"; + /// println!("{}", s); + /// ``` + #[clippy::version = "1.97.0"] + pub USELESS_BORROWS_IN_FORMATTING, + perf, + "redundant reference in format args causes double reference" +} + impl_lint_pass!(FormatArgs<'_> => [ FORMAT_IN_FORMAT_ARGS, POINTER_FORMAT, @@ -265,6 +293,7 @@ UNNECESSARY_DEBUG_FORMATTING, UNNECESSARY_TRAILING_COMMA, UNUSED_FORMAT_SPECS, + USELESS_BORROWS_IN_FORMATTING, ]); #[expect(clippy::struct_field_names)] @@ -363,6 +392,18 @@ fn check_templates(&mut self) { && let Some(arg_expr) = find_format_arg_expr(self.expr, arg) { self.check_unused_format_specifier(placeholder, arg_expr); + self.check_useless_borrows_in_formatting(placeholder, arg_expr); + + // Check width and precision arguments the same way as the value + for opt in [&placeholder.format_options.width, &placeholder.format_options.precision] { + if let Some(FormatCount::Argument(position)) = opt.as_ref() + && let Ok(pos_index) = position.index + && let Some(pos_arg) = self.format_args.arguments.all_args().get(pos_index) + && let Some(pos_arg_expr) = find_format_arg_expr(self.expr, pos_arg) + { + self.check_useless_borrows_in_formatting(placeholder, pos_arg_expr); + } + } if placeholder.format_trait == FormatTrait::Display && placeholder.format_options == FormatOptions::default() @@ -392,6 +433,43 @@ fn check_templates(&mut self) { } } + fn check_useless_borrows_in_formatting(&self, placeholder: &FormatPlaceholder, arg_expr: &Expr<'tcx>) { + if !arg_expr.span.from_expansion() + && !is_from_proc_macro(self.cx, arg_expr) + && let Some(fmt_trait) = match placeholder.format_trait { + FormatTrait::Display => self.cx.tcx.get_diagnostic_item(sym::Display), + FormatTrait::Debug => self.cx.tcx.get_diagnostic_item(sym::Debug), + _ => None, + } + && let Some(sized_trait) = self.cx.tcx.lang_items().sized_trait() + && let peeled_expr = peel_hir_expr_while(arg_expr, |e| { + // Need to handle `&&&T` to `&T` when a single ref is still required + if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = e.kind + && let ty = self.cx.typeck_results().expr_ty(e) + && implements_trait(self.cx, ty, sized_trait, &[]) + && implements_trait(self.cx, ty, fmt_trait, &[]) + { + Some(e) + } else { + None + } + }) + && !std::ptr::eq(arg_expr, peeled_expr) + && let Some(peeled_snippet) = snippet_opt(self.cx, peeled_expr.span) + { + let name = self.cx.tcx.item_name(self.macro_call.def_id); + span_lint_and_sugg( + self.cx, + USELESS_BORROWS_IN_FORMATTING, + arg_expr.span, + format!("redundant reference in `{name}!` argument"), + "remove the redundant `&`", + peeled_snippet, + Applicability::MachineApplicable, + ); + } + } + fn check_unused_format_specifier(&self, placeholder: &FormatPlaceholder, arg: &Expr<'_>) { let options = &placeholder.format_options; diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 6c29630dc3a5..35149755a34b 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -3,14 +3,15 @@ #![allow(unused_variables, unused_must_use)] #![allow( clippy::borrow_deref_ref, - suspicious_double_ref_op, - noop_method_call, + clippy::deref_addrof, clippy::explicit_auto_deref, clippy::needless_borrow, clippy::no_effect, + clippy::useless_borrows_in_formatting, clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap, - clippy::deref_addrof + noop_method_call, + suspicious_double_ref_op )] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index f6309cd404b8..215c5450597f 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -3,14 +3,15 @@ #![allow(unused_variables, unused_must_use)] #![allow( clippy::borrow_deref_ref, - suspicious_double_ref_op, - noop_method_call, + clippy::deref_addrof, clippy::explicit_auto_deref, clippy::needless_borrow, clippy::no_effect, + clippy::useless_borrows_in_formatting, clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap, - clippy::deref_addrof + noop_method_call, + suspicious_double_ref_op )] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index e2f2e68720b1..d7e41e4409fa 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:58:19 + --> tests/ui/explicit_deref_methods.rs:59:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try: `&*a` @@ -8,73 +8,73 @@ LL | let b: &str = a.deref(); = help: to override `-D warnings` add `#[allow(clippy::explicit_deref_methods)]` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:61:23 + --> tests/ui/explicit_deref_methods.rs:62:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try: `&mut **a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:39 + --> tests/ui/explicit_deref_methods.rs:66:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:65:50 + --> tests/ui/explicit_deref_methods.rs:66:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:69:20 + --> tests/ui/explicit_deref_methods.rs:70:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:73:11 + --> tests/ui/explicit_deref_methods.rs:74:11 | LL | match a.deref() { | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:78:28 + --> tests/ui/explicit_deref_methods.rs:79:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:81:13 + --> tests/ui/explicit_deref_methods.rs:82:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:84:28 + --> tests/ui/explicit_deref_methods.rs:85:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:124:31 + --> tests/ui/explicit_deref_methods.rs:125:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:154:14 + --> tests/ui/explicit_deref_methods.rs:155:14 | LL | let _ = &Deref::deref(&"foo"); | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:156:14 + --> tests/ui/explicit_deref_methods.rs:157:14 | LL | let _ = &DerefMut::deref_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:157:14 + --> tests/ui/explicit_deref_methods.rs:158:14 | LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index 9f46fef62354..936d56877055 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -3,6 +3,7 @@ clippy::borrow_deref_ref, clippy::deref_addrof, clippy::inherent_to_string_shadow_display, + clippy::useless_borrows_in_formatting, clippy::to_string_in_format_args, clippy::uninlined_format_args )] diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 4361d612bf2a..c813bb349b86 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:31:25 + --> tests/ui/recursive_format_impl.rs:32:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -8,55 +8,55 @@ LL | write!(f, "{}", self.to_string()) = help: to override `-D warnings` add `#[allow(clippy::recursive_format_impl)]` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:76:9 + --> tests/ui/recursive_format_impl.rs:77:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:86:9 + --> tests/ui/recursive_format_impl.rs:87:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:93:9 + --> tests/ui/recursive_format_impl.rs:94:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:103:9 + --> tests/ui/recursive_format_impl.rs:104:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:178:9 + --> tests/ui/recursive_format_impl.rs:179:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:185:9 + --> tests/ui/recursive_format_impl.rs:186:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:202:9 + --> tests/ui/recursive_format_impl.rs:203:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:219:9 + --> tests/ui/recursive_format_impl.rs:220:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> tests/ui/recursive_format_impl.rs:236:9 + --> tests/ui/recursive_format_impl.rs:237:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/useless_borrows_in_formatting.fixed b/tests/ui/useless_borrows_in_formatting.fixed new file mode 100644 index 000000000000..82319252b468 --- /dev/null +++ b/tests/ui/useless_borrows_in_formatting.fixed @@ -0,0 +1,118 @@ +// When testing or blessing this lint, set TESTNAME so only this test runs: +// TESTNAME=useless_borrows_in_formatting cargo uitest +// TESTNAME=useless_borrows_in_formatting cargo uibless +#![warn(clippy::useless_borrows_in_formatting)] +#![allow(unused, clippy::useless_format)] + +fn main() { + let s: &str = "hello"; + println!("{}", s); //~ useless_borrows_in_formatting + println!("{:?}", s); //~ useless_borrows_in_formatting + println!("{}", s); //~ useless_borrows_in_formatting + + let string = String::from("world"); + println!("{}", string); //~ useless_borrows_in_formatting + println!("{:?}", string); //~ useless_borrows_in_formatting + println!("{}", string); //~ useless_borrows_in_formatting + println!("{}", &string[..2]); //~ useless_borrows_in_formatting + println!("{:?}", &string[..2]); //~ useless_borrows_in_formatting + // these are ok + println!("{}", &string[..2]); + println!("{:?}", &string[..2]); + + let n: i32 = 42; + println!("{}", n); //~ useless_borrows_in_formatting + println!("{:?}", n); //~ useless_borrows_in_formatting + println!("{}", n); //~ useless_borrows_in_formatting + + // Reference to slice element + let slice: [i32; 3] = [1, 2, 3]; + println!("{}", slice[0]); //~ useless_borrows_in_formatting + println!("{:?}", slice[0]); //~ useless_borrows_in_formatting + println!("{}", slice[0]); //~ useless_borrows_in_formatting + + // big array: should not suggest removing & because of the size of the output + println!( + "{:?}", + [ + //~^ useless_borrows_in_formatting + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + ] + ); + + println!("{:?}", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + //~^ useless_borrows_in_formatting + + let a: [i32; 2] = [1, 2]; + println!("{:016x?}", [a[0], a[1], a[0], a[1]]); //~ useless_borrows_in_formatting + + // &slice[0..1] with {:?}: inner type [i32] is unsized, so we don't suggest removing & + println!("{:?}", &slice[0..1]); // don't change + println!("{:?}", &slice[0..1]); //~ useless_borrows_in_formatting + + // Pointer formatting ({:p}): never suggest any changes to it + let x: i32 = 0; + println!("{:p}", &x); // don't change + println!("{:p}", &&x); // should change, but out of scope + + struct Wrap(i32); + let w: Wrap = Wrap(42); + println!("{}", w.0); //~ useless_borrows_in_formatting + println!("{:?}", w.0); //~ useless_borrows_in_formatting + println!("{}", w.0); //~ useless_borrows_in_formatting + + struct WrapRef<'a>(&'a i32); + let n: i32 = 42; + let w: WrapRef<'_> = WrapRef(&n); + println!("{}", w.0); //~ useless_borrows_in_formatting + println!("{:?}", w.0); //~ useless_borrows_in_formatting + println!("{}", w.0); //~ useless_borrows_in_formatting + + let a: &mut String = &mut String::from("foo"); + println!("{}", *a); //~ useless_borrows_in_formatting + println!("{:?}", *a); //~ useless_borrows_in_formatting + + // Parenthesized expressions: &(expr) + let n: i32 = 42; + println!("{}", (n)); //~ useless_borrows_in_formatting + println!("{:?}", (n + 1)); //~ useless_borrows_in_formatting + println!("{}", (String::from("paren"))); //~ useless_borrows_in_formatting + + // Block expressions: &{ expr } + println!("{}", { n }); //~ useless_borrows_in_formatting + println!("{:?}", { n + 1 }); //~ useless_borrows_in_formatting + println!("{}", { String::from("block") }); //~ useless_borrows_in_formatting + + let v1 = 42.12345; + let v2 = 20; + let v3 = 10; + println!("{0:1$.2$}", v1, v2, v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$?}", v1, v2, v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, v3); //~ useless_borrows_in_formatting + + // Macro wrapping println! - should not lint (println! call is inside macro expansion) + macro_rules! my_println { + ($($args:tt)*) => { + println!($($args)*); + }; + } + my_println!("{}", &n); + + // Arguments coming from a macro - should not lint (& comes from expansion) + macro_rules! make_ref { + ($e:expr) => { + &$e + }; + } + println!("{}", make_ref!(n)); +} diff --git a/tests/ui/useless_borrows_in_formatting.rs b/tests/ui/useless_borrows_in_formatting.rs new file mode 100644 index 000000000000..6d23c9c8e7bb --- /dev/null +++ b/tests/ui/useless_borrows_in_formatting.rs @@ -0,0 +1,118 @@ +// When testing or blessing this lint, set TESTNAME so only this test runs: +// TESTNAME=useless_borrows_in_formatting cargo uitest +// TESTNAME=useless_borrows_in_formatting cargo uibless +#![warn(clippy::useless_borrows_in_formatting)] +#![allow(unused, clippy::useless_format)] + +fn main() { + let s: &str = "hello"; + println!("{}", &s); //~ useless_borrows_in_formatting + println!("{:?}", &s); //~ useless_borrows_in_formatting + println!("{}", &&s); //~ useless_borrows_in_formatting + + let string = String::from("world"); + println!("{}", &string); //~ useless_borrows_in_formatting + println!("{:?}", &string); //~ useless_borrows_in_formatting + println!("{}", &&string); //~ useless_borrows_in_formatting + println!("{}", &&string[..2]); //~ useless_borrows_in_formatting + println!("{:?}", &&string[..2]); //~ useless_borrows_in_formatting + // these are ok + println!("{}", &string[..2]); + println!("{:?}", &string[..2]); + + let n: i32 = 42; + println!("{}", &n); //~ useless_borrows_in_formatting + println!("{:?}", &n); //~ useless_borrows_in_formatting + println!("{}", &&n); //~ useless_borrows_in_formatting + + // Reference to slice element + let slice: [i32; 3] = [1, 2, 3]; + println!("{}", &slice[0]); //~ useless_borrows_in_formatting + println!("{:?}", &slice[0]); //~ useless_borrows_in_formatting + println!("{}", &&slice[0]); //~ useless_borrows_in_formatting + + // big array: should not suggest removing & because of the size of the output + println!( + "{:?}", + &[ + //~^ useless_borrows_in_formatting + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + ] + ); + + println!("{:?}", &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + //~^ useless_borrows_in_formatting + + let a: [i32; 2] = [1, 2]; + println!("{:016x?}", &[a[0], a[1], a[0], a[1]]); //~ useless_borrows_in_formatting + + // &slice[0..1] with {:?}: inner type [i32] is unsized, so we don't suggest removing & + println!("{:?}", &slice[0..1]); // don't change + println!("{:?}", &&slice[0..1]); //~ useless_borrows_in_formatting + + // Pointer formatting ({:p}): never suggest any changes to it + let x: i32 = 0; + println!("{:p}", &x); // don't change + println!("{:p}", &&x); // should change, but out of scope + + struct Wrap(i32); + let w: Wrap = Wrap(42); + println!("{}", &w.0); //~ useless_borrows_in_formatting + println!("{:?}", &w.0); //~ useless_borrows_in_formatting + println!("{}", &&w.0); //~ useless_borrows_in_formatting + + struct WrapRef<'a>(&'a i32); + let n: i32 = 42; + let w: WrapRef<'_> = WrapRef(&n); + println!("{}", &w.0); //~ useless_borrows_in_formatting + println!("{:?}", &w.0); //~ useless_borrows_in_formatting + println!("{}", &&w.0); //~ useless_borrows_in_formatting + + let a: &mut String = &mut String::from("foo"); + println!("{}", &*a); //~ useless_borrows_in_formatting + println!("{:?}", &*a); //~ useless_borrows_in_formatting + + // Parenthesized expressions: &(expr) + let n: i32 = 42; + println!("{}", &(n)); //~ useless_borrows_in_formatting + println!("{:?}", &(n + 1)); //~ useless_borrows_in_formatting + println!("{}", &(String::from("paren"))); //~ useless_borrows_in_formatting + + // Block expressions: &{ expr } + println!("{}", &{ n }); //~ useless_borrows_in_formatting + println!("{:?}", &{ n + 1 }); //~ useless_borrows_in_formatting + println!("{}", &{ String::from("block") }); //~ useless_borrows_in_formatting + + let v1 = 42.12345; + let v2 = 20; + let v3 = 10; + println!("{0:1$.2$}", &v1, &v2, &v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$?}", &v1, &v2, &v3); + //~^ useless_borrows_in_formatting + //~| useless_borrows_in_formatting + //~| useless_borrows_in_formatting + println!("{0:1$.2$}", &v1, v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, &v2, v3); //~ useless_borrows_in_formatting + println!("{0:1$.2$}", v1, v2, &v3); //~ useless_borrows_in_formatting + + // Macro wrapping println! - should not lint (println! call is inside macro expansion) + macro_rules! my_println { + ($($args:tt)*) => { + println!($($args)*); + }; + } + my_println!("{}", &n); + + // Arguments coming from a macro - should not lint (& comes from expansion) + macro_rules! make_ref { + ($e:expr) => { + &$e + }; + } + println!("{}", make_ref!(n)); +} diff --git a/tests/ui/useless_borrows_in_formatting.stderr b/tests/ui/useless_borrows_in_formatting.stderr new file mode 100644 index 000000000000..e9e028c81ac4 --- /dev/null +++ b/tests/ui/useless_borrows_in_formatting.stderr @@ -0,0 +1,266 @@ +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:9:20 + | +LL | println!("{}", &s); + | ^^ help: remove the redundant `&`: `s` + | + = note: `-D clippy::useless-borrows-in-formatting` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_borrows_in_formatting)]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:10:22 + | +LL | println!("{:?}", &s); + | ^^ help: remove the redundant `&`: `s` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:11:20 + | +LL | println!("{}", &&s); + | ^^^ help: remove the redundant `&`: `s` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:14:20 + | +LL | println!("{}", &string); + | ^^^^^^^ help: remove the redundant `&`: `string` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:15:22 + | +LL | println!("{:?}", &string); + | ^^^^^^^ help: remove the redundant `&`: `string` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:16:20 + | +LL | println!("{}", &&string); + | ^^^^^^^^ help: remove the redundant `&`: `string` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:17:20 + | +LL | println!("{}", &&string[..2]); + | ^^^^^^^^^^^^^ help: remove the redundant `&`: `&string[..2]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:18:22 + | +LL | println!("{:?}", &&string[..2]); + | ^^^^^^^^^^^^^ help: remove the redundant `&`: `&string[..2]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:24:20 + | +LL | println!("{}", &n); + | ^^ help: remove the redundant `&`: `n` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:25:22 + | +LL | println!("{:?}", &n); + | ^^ help: remove the redundant `&`: `n` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:26:20 + | +LL | println!("{}", &&n); + | ^^^ help: remove the redundant `&`: `n` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:30:20 + | +LL | println!("{}", &slice[0]); + | ^^^^^^^^^ help: remove the redundant `&`: `slice[0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:31:22 + | +LL | println!("{:?}", &slice[0]); + | ^^^^^^^^^ help: remove the redundant `&`: `slice[0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:32:20 + | +LL | println!("{}", &&slice[0]); + | ^^^^^^^^^^ help: remove the redundant `&`: `slice[0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:37:9 + | +LL | / &[ +LL | | +LL | | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, +LL | | 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, +LL | | 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +LL | | ] + | |_________^ + | +help: remove the redundant `&` + | +LL ~ [ +LL + +LL + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, +LL + 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, +LL + 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +LL + ] + | + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:45:22 + | +LL | println!("{:?}", &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:49:26 + | +LL | println!("{:016x?}", &[a[0], a[1], a[0], a[1]]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `[a[0], a[1], a[0], a[1]]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:53:22 + | +LL | println!("{:?}", &&slice[0..1]); + | ^^^^^^^^^^^^^ help: remove the redundant `&`: `&slice[0..1]` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:62:20 + | +LL | println!("{}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:63:22 + | +LL | println!("{:?}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:64:20 + | +LL | println!("{}", &&w.0); + | ^^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:69:20 + | +LL | println!("{}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:70:22 + | +LL | println!("{:?}", &w.0); + | ^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:71:20 + | +LL | println!("{}", &&w.0); + | ^^^^^ help: remove the redundant `&`: `w.0` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:74:20 + | +LL | println!("{}", &*a); + | ^^^ help: remove the redundant `&`: `*a` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:75:22 + | +LL | println!("{:?}", &*a); + | ^^^ help: remove the redundant `&`: `*a` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:79:20 + | +LL | println!("{}", &(n)); + | ^^^^ help: remove the redundant `&`: `(n)` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:80:22 + | +LL | println!("{:?}", &(n + 1)); + | ^^^^^^^^ help: remove the redundant `&`: `(n + 1)` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:81:20 + | +LL | println!("{}", &(String::from("paren"))); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `(String::from("paren"))` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:84:20 + | +LL | println!("{}", &{ n }); + | ^^^^^^ help: remove the redundant `&`: `{ n }` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:85:22 + | +LL | println!("{:?}", &{ n + 1 }); + | ^^^^^^^^^^ help: remove the redundant `&`: `{ n + 1 }` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:86:20 + | +LL | println!("{}", &{ String::from("block") }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the redundant `&`: `{ String::from("block") }` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:91:27 + | +LL | println!("{0:1$.2$}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v1` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:91:32 + | +LL | println!("{0:1$.2$}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v2` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:91:37 + | +LL | println!("{0:1$.2$}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v3` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:95:28 + | +LL | println!("{0:1$.2$?}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v1` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:95:33 + | +LL | println!("{0:1$.2$?}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v2` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:95:38 + | +LL | println!("{0:1$.2$?}", &v1, &v2, &v3); + | ^^^ help: remove the redundant `&`: `v3` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:99:27 + | +LL | println!("{0:1$.2$}", &v1, v2, v3); + | ^^^ help: remove the redundant `&`: `v1` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:100:31 + | +LL | println!("{0:1$.2$}", v1, &v2, v3); + | ^^^ help: remove the redundant `&`: `v2` + +error: redundant reference in `println!` argument + --> tests/ui/useless_borrows_in_formatting.rs:101:35 + | +LL | println!("{0:1$.2$}", v1, v2, &v3); + | ^^^ help: remove the redundant `&`: `v3` + +error: aborting due to 41 previous errors +