diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index c55c5ec2f51a..c39e4a4cc956 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -9,7 +9,7 @@ use rustc_ast::{ FormatPlaceholder, FormatTrait, }; use rustc_errors::Applicability; -use rustc_hir::{Expr, Impl, Item, ItemKind}; +use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Span}; @@ -240,7 +240,8 @@ declare_clippy_lint! { pub struct Write { format_args: FormatArgsStorage, - in_debug_impl: bool, + // The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated + outermost_debug_impl: Option, allow_print_in_tests: bool, } @@ -248,10 +249,14 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - in_debug_impl: false, + outermost_debug_impl: None, allow_print_in_tests: conf.allow_print_in_tests, } } + + fn in_debug_impl(&self) -> bool { + self.outermost_debug_impl.is_some() + } } impl_lint_pass!(Write => [ @@ -268,14 +273,16 @@ impl_lint_pass!(Write => [ impl<'tcx> LateLintPass<'tcx> for Write { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_debug_impl(cx, item) { - self.in_debug_impl = true; + // Only check for `impl Debug`s if we're not already in one + if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) { + self.outermost_debug_impl = Some(item.owner_id); } } - fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_debug_impl(cx, item) { - self.in_debug_impl = false; + fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { + // Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl + if self.outermost_debug_impl == Some(item.owner_id) { + self.outermost_debug_impl = None; } } @@ -329,7 +336,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { check_literal(cx, format_args, name); - if !self.in_debug_impl { + if !self.in_debug_impl() { for piece in &format_args.template { if let &FormatArgsPiece::Placeholder(FormatPlaceholder { span: Some(span), diff --git a/tests/ui/print.rs b/tests/ui/print.rs deleted file mode 100644 index ee3d9dc0de03..000000000000 --- a/tests/ui/print.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![allow(clippy::print_literal, clippy::write_literal)] -#![warn(clippy::print_stdout, clippy::use_debug)] - -use std::fmt::{Debug, Display, Formatter, Result}; - -#[allow(dead_code)] -struct Foo; - -impl Display for Foo { - fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{:?}", 43.1415) - //~^ use_debug - } -} - -impl Debug for Foo { - fn fmt(&self, f: &mut Formatter) -> Result { - // ok, we can use `Debug` formatting in `Debug` implementations - write!(f, "{:?}", 42.718) - } -} - -fn main() { - println!("Hello"); - //~^ print_stdout - - print!("Hello"); - //~^ print_stdout - - print!("Hello {}", "World"); - //~^ print_stdout - - print!("Hello {:?}", "World"); - //~^ print_stdout - //~| use_debug - - print!("Hello {:#?}", "#orld"); - //~^ print_stdout - //~| use_debug - - assert_eq!(42, 1337); - - vec![1, 2]; -} diff --git a/tests/ui/print.stderr b/tests/ui/print.stderr deleted file mode 100644 index 9dd216bd1449..000000000000 --- a/tests/ui/print.stderr +++ /dev/null @@ -1,56 +0,0 @@ -error: use of `Debug`-based formatting - --> tests/ui/print.rs:11:20 - | -LL | write!(f, "{:?}", 43.1415) - | ^^^^ - | - = note: `-D clippy::use-debug` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::use_debug)]` - -error: use of `println!` - --> tests/ui/print.rs:24:5 - | -LL | println!("Hello"); - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::print-stdout` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::print_stdout)]` - -error: use of `print!` - --> tests/ui/print.rs:27:5 - | -LL | print!("Hello"); - | ^^^^^^^^^^^^^^^ - -error: use of `print!` - --> tests/ui/print.rs:30:5 - | -LL | print!("Hello {}", "World"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `print!` - --> tests/ui/print.rs:33:5 - | -LL | print!("Hello {:?}", "World"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `Debug`-based formatting - --> tests/ui/print.rs:33:19 - | -LL | print!("Hello {:?}", "World"); - | ^^^^ - -error: use of `print!` - --> tests/ui/print.rs:37:5 - | -LL | print!("Hello {:#?}", "#orld"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: use of `Debug`-based formatting - --> tests/ui/print.rs:37:19 - | -LL | print!("Hello {:#?}", "#orld"); - | ^^^^^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/print_stdout.rs b/tests/ui/print_stdout.rs new file mode 100644 index 000000000000..a379457f1869 --- /dev/null +++ b/tests/ui/print_stdout.rs @@ -0,0 +1,23 @@ +#![expect(clippy::print_literal)] +#![warn(clippy::print_stdout)] + +fn main() { + println!("Hello"); + //~^ print_stdout + + print!("Hello"); + //~^ print_stdout + + print!("Hello {}", "World"); + //~^ print_stdout + + print!("Hello {:?}", "World"); + //~^ print_stdout + + print!("Hello {:#?}", "#orld"); + //~^ print_stdout + + assert_eq!(42, 1337); + + vec![1, 2]; +} diff --git a/tests/ui/print_stdout.stderr b/tests/ui/print_stdout.stderr new file mode 100644 index 000000000000..e1360e59b412 --- /dev/null +++ b/tests/ui/print_stdout.stderr @@ -0,0 +1,35 @@ +error: use of `println!` + --> tests/ui/print_stdout.rs:5:5 + | +LL | println!("Hello"); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stdout` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::print_stdout)]` + +error: use of `print!` + --> tests/ui/print_stdout.rs:8:5 + | +LL | print!("Hello"); + | ^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:11:5 + | +LL | print!("Hello {}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:14:5 + | +LL | print!("Hello {:?}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> tests/ui/print_stdout.rs:17:5 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/use_debug.rs b/tests/ui/use_debug.rs new file mode 100644 index 000000000000..89c127a12faf --- /dev/null +++ b/tests/ui/use_debug.rs @@ -0,0 +1,50 @@ +#![warn(clippy::use_debug)] + +use std::fmt::{Debug, Display, Formatter, Result}; + +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}", 43.1415) + //~^ use_debug + } +} + +impl Debug for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } +} + +fn main() { + print!("Hello {:?}", "World"); + //~^ use_debug + + print!("Hello {:#?}", "#orld"); + //~^ use_debug + + assert_eq!(42, 1337); + + vec![1, 2]; +} + +// don't get confused by nested impls +fn issue15942() { + struct Bar; + impl Debug for Bar { + fn fmt(&self, f: &mut Formatter) -> Result { + struct Baz; + impl Debug for Baz { + fn fmt(&self, f: &mut Formatter) -> Result { + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } + } + + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } + } +} diff --git a/tests/ui/use_debug.stderr b/tests/ui/use_debug.stderr new file mode 100644 index 000000000000..85168d3bc341 --- /dev/null +++ b/tests/ui/use_debug.stderr @@ -0,0 +1,23 @@ +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:9:20 + | +LL | write!(f, "{:?}", 43.1415) + | ^^^^ + | + = note: `-D clippy::use-debug` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::use_debug)]` + +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:22:19 + | +LL | print!("Hello {:?}", "World"); + | ^^^^ + +error: use of `Debug`-based formatting + --> tests/ui/use_debug.rs:25:19 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^ + +error: aborting due to 3 previous errors +