1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
- use clippy_utils:: macros:: root_macro_call_first_node ;
2
+ use clippy_utils:: macros:: { macro_backtrace , MacroCall } ;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
4
use clippy_utils:: { is_in_cfg_test, is_in_test_function} ;
5
+ use rustc_data_structures:: fx:: FxHashSet ;
5
6
use rustc_errors:: Applicability ;
6
- use rustc_hir:: { Expr , ExprKind , Node } ;
7
+ use rustc_hir:: { Expr , ExprKind , HirId , Node } ;
7
8
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
9
+ use rustc_middle:: lint:: in_external_macro;
8
10
use rustc_session:: impl_lint_pass;
9
- use rustc_span:: sym;
11
+ use rustc_span:: { sym, Span , SyntaxContext } ;
10
12
11
13
declare_clippy_lint ! {
12
14
/// ### What it does
@@ -31,31 +33,38 @@ declare_clippy_lint! {
31
33
"`dbg!` macro is intended as a debugging tool"
32
34
}
33
35
34
- #[ derive( Copy , Clone ) ]
36
+ #[ derive( Clone ) ]
35
37
pub struct DbgMacro {
36
38
allow_dbg_in_tests : bool ,
39
+ /// Tracks the `dbg!` macro callsites that are already checked.
40
+ checked_dbg_call_site : FxHashSet < Span > ,
41
+ /// Tracks the previous `SyntaxContext`, to avoid walking the same context chain.
42
+ prev_ctxt : SyntaxContext ,
37
43
}
38
44
39
45
impl_lint_pass ! ( DbgMacro => [ DBG_MACRO ] ) ;
40
46
41
47
impl DbgMacro {
42
48
pub fn new ( allow_dbg_in_tests : bool ) -> Self {
43
- DbgMacro { allow_dbg_in_tests }
49
+ DbgMacro {
50
+ allow_dbg_in_tests,
51
+ checked_dbg_call_site : FxHashSet :: default ( ) ,
52
+ prev_ctxt : SyntaxContext :: root ( ) ,
53
+ }
44
54
}
45
55
}
46
56
47
57
impl LateLintPass < ' _ > for DbgMacro {
48
58
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
49
- let Some ( macro_call) = root_macro_call_first_node ( cx, expr) else {
50
- return ;
51
- } ;
52
- if cx. tcx . is_diagnostic_item ( sym:: dbg_macro, macro_call. def_id ) {
59
+ let cur_syntax_ctxt = expr. span . ctxt ( ) ;
60
+
61
+ if cur_syntax_ctxt != self . prev_ctxt &&
62
+ let Some ( macro_call) = first_dbg_macro_in_expansion ( cx, expr. span ) &&
63
+ !in_external_macro ( cx. sess ( ) , macro_call. span ) &&
64
+ self . checked_dbg_call_site . insert ( macro_call. span ) &&
53
65
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
54
- if self . allow_dbg_in_tests
55
- && ( is_in_test_function ( cx. tcx , expr. hir_id ) || is_in_cfg_test ( cx. tcx , expr. hir_id ) )
56
- {
57
- return ;
58
- }
66
+ !( self . allow_dbg_in_tests && is_in_test ( cx, expr. hir_id ) )
67
+ {
59
68
let mut applicability = Applicability :: MachineApplicable ;
60
69
61
70
let ( sugg_span, suggestion) = match expr. peel_drop_temps ( ) . kind {
@@ -101,6 +110,8 @@ impl LateLintPass<'_> for DbgMacro {
101
110
_ => return ,
102
111
} ;
103
112
113
+ self . prev_ctxt = cur_syntax_ctxt;
114
+
104
115
span_lint_and_sugg (
105
116
cx,
106
117
DBG_MACRO ,
@@ -112,4 +123,16 @@ impl LateLintPass<'_> for DbgMacro {
112
123
) ;
113
124
}
114
125
}
126
+
127
+ fn check_crate_post ( & mut self , _: & LateContext < ' _ > ) {
128
+ self . checked_dbg_call_site = FxHashSet :: default ( ) ;
129
+ }
130
+ }
131
+
132
+ fn is_in_test ( cx : & LateContext < ' _ > , hir_id : HirId ) -> bool {
133
+ is_in_test_function ( cx. tcx , hir_id) || is_in_cfg_test ( cx. tcx , hir_id)
134
+ }
135
+
136
+ fn first_dbg_macro_in_expansion ( cx : & LateContext < ' _ > , span : Span ) -> Option < MacroCall > {
137
+ macro_backtrace ( span) . find ( |mc| cx. tcx . is_diagnostic_item ( sym:: dbg_macro, mc. def_id ) )
115
138
}
0 commit comments