Skip to content

Commit d349b20

Browse files
committed
fix(use_debug): don't get confused by nested Debug impls
1 parent 21d3b9d commit d349b20

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

clippy_lints/src/write.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_ast::{
99
FormatPlaceholder, FormatTrait,
1010
};
1111
use rustc_errors::Applicability;
12-
use rustc_hir::{Expr, Impl, Item, ItemKind};
12+
use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId};
1313
use rustc_lint::{LateContext, LateLintPass, LintContext};
1414
use rustc_session::impl_lint_pass;
1515
use rustc_span::{BytePos, Span};
@@ -240,18 +240,23 @@ declare_clippy_lint! {
240240

241241
pub struct Write {
242242
format_args: FormatArgsStorage,
243-
in_debug_impl: bool,
243+
// The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated
244+
outermost_debug_impl: Option<OwnerId>,
244245
allow_print_in_tests: bool,
245246
}
246247

247248
impl Write {
248249
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
249250
Self {
250251
format_args,
251-
in_debug_impl: false,
252+
outermost_debug_impl: None,
252253
allow_print_in_tests: conf.allow_print_in_tests,
253254
}
254255
}
256+
257+
fn in_debug_impl(&self) -> bool {
258+
self.outermost_debug_impl.is_some()
259+
}
255260
}
256261

257262
impl_lint_pass!(Write => [
@@ -268,14 +273,16 @@ impl_lint_pass!(Write => [
268273

269274
impl<'tcx> LateLintPass<'tcx> for Write {
270275
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
271-
if is_debug_impl(cx, item) {
272-
self.in_debug_impl = true;
276+
// Only check for `impl Debug`s if we're not already in one
277+
if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) {
278+
self.outermost_debug_impl = Some(item.owner_id);
273279
}
274280
}
275281

276-
fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
277-
if is_debug_impl(cx, item) {
278-
self.in_debug_impl = false;
282+
fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
283+
// Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl
284+
if self.outermost_debug_impl == Some(item.owner_id) {
285+
self.outermost_debug_impl = None;
279286
}
280287
}
281288

@@ -329,7 +336,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
329336

330337
check_literal(cx, format_args, name);
331338

332-
if !self.in_debug_impl {
339+
if !self.in_debug_impl() {
333340
for piece in &format_args.template {
334341
if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
335342
span: Some(span),

tests/ui/use_debug.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,22 @@ fn main() {
2929

3030
vec![1, 2];
3131
}
32+
33+
// don't get confused by nested impls
34+
fn issue15942() {
35+
struct Bar;
36+
impl Debug for Bar {
37+
fn fmt(&self, f: &mut Formatter) -> Result {
38+
struct Baz;
39+
impl Debug for Baz {
40+
fn fmt(&self, f: &mut Formatter) -> Result {
41+
// ok, we can use `Debug` formatting in `Debug` implementations
42+
write!(f, "{:?}", 42.718)
43+
}
44+
}
45+
46+
// ok, we can use `Debug` formatting in `Debug` implementations
47+
write!(f, "{:?}", 42.718)
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)