1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
- use clippy_utils:: macros:: root_macro_call ;
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
5
use rustc_data_structures:: fx:: FxHashSet ;
6
6
use rustc_errors:: Applicability ;
7
- use rustc_hir:: { Expr , ExprKind , Node } ;
7
+ use rustc_hir:: { Expr , ExprKind , HirId , Node } ;
8
8
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
9
+ use rustc_middle:: lint:: in_external_macro;
9
10
use rustc_session:: impl_lint_pass;
10
- use rustc_span:: { sym, Span } ;
11
+ use rustc_span:: { sym, Span , SyntaxContext } ;
11
12
12
13
declare_clippy_lint ! {
13
14
/// ### What it does
@@ -35,9 +36,10 @@ declare_clippy_lint! {
35
36
#[ derive( Clone ) ]
36
37
pub struct DbgMacro {
37
38
allow_dbg_in_tests : bool ,
38
- /// Keep tracks the `dbg!` macro callsites that are already checked,
39
- /// so that we can save some performance by skipping any expressions from the same expansion.
39
+ /// Tracks the `dbg!` macro callsites that are already checked.
40
40
checked_dbg_call_site : FxHashSet < Span > ,
41
+ /// Tracks the previous `SyntaxContext`, to avoid walking the same context chain.
42
+ prev_ctxt : SyntaxContext ,
41
43
}
42
44
43
45
impl_lint_pass ! ( DbgMacro => [ DBG_MACRO ] ) ;
@@ -47,80 +49,90 @@ impl DbgMacro {
47
49
DbgMacro {
48
50
allow_dbg_in_tests,
49
51
checked_dbg_call_site : FxHashSet :: default ( ) ,
52
+ prev_ctxt : SyntaxContext :: root ( ) ,
50
53
}
51
54
}
52
55
}
53
56
54
57
impl LateLintPass < ' _ > for DbgMacro {
55
58
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
56
- let Some ( macro_call) =
57
- root_macro_call ( expr. span ) . filter ( |mc| cx. tcx . is_diagnostic_item ( sym:: dbg_macro, mc. def_id ) )
58
- else {
59
- return ;
60
- } ;
61
- if self . checked_dbg_call_site . contains ( & macro_call. span ) {
62
- return ;
63
- }
64
- self . checked_dbg_call_site . insert ( macro_call. span ) ;
59
+ let cur_syntax_ctxt = expr. span . ctxt ( ) ;
65
60
66
- // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
67
- if self . allow_dbg_in_tests && ( is_in_test_function ( cx. tcx , expr. hir_id ) || is_in_cfg_test ( cx. tcx , expr. hir_id ) )
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 ) &&
65
+ // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
66
+ !( self . allow_dbg_in_tests && is_in_test ( cx, expr. hir_id ) )
68
67
{
69
- return ;
70
- }
71
- let mut applicability = Applicability :: MachineApplicable ;
68
+ let mut applicability = Applicability :: MachineApplicable ;
69
+
70
+ let ( sugg_span, suggestion) = match expr. peel_drop_temps ( ) . kind {
71
+ // dbg!()
72
+ ExprKind :: Block ( ..) => {
73
+ // If the `dbg!` macro is a "free" statement and not contained within other expressions,
74
+ // remove the whole statement.
75
+ if let Some ( Node :: Stmt ( _) ) = cx. tcx . hir ( ) . find_parent ( expr. hir_id )
76
+ && let Some ( semi_span) = cx. sess ( ) . source_map ( ) . mac_call_stmt_semi_span ( macro_call. span )
77
+ {
78
+ ( macro_call. span . to ( semi_span) , String :: new ( ) )
79
+ } else {
80
+ ( macro_call. span , String :: from ( "()" ) )
81
+ }
82
+ } ,
83
+ // dbg!(1)
84
+ ExprKind :: Match ( val, ..) => (
85
+ macro_call. span ,
86
+ snippet_with_applicability ( cx, val. span . source_callsite ( ) , ".." , & mut applicability) . to_string ( ) ,
87
+ ) ,
88
+ // dbg!(2, 3)
89
+ ExprKind :: Tup (
90
+ [
91
+ Expr {
92
+ kind : ExprKind :: Match ( first, ..) ,
93
+ ..
94
+ } ,
95
+ ..,
96
+ Expr {
97
+ kind : ExprKind :: Match ( last, ..) ,
98
+ ..
99
+ } ,
100
+ ] ,
101
+ ) => {
102
+ let snippet = snippet_with_applicability (
103
+ cx,
104
+ first. span . source_callsite ( ) . to ( last. span . source_callsite ( ) ) ,
105
+ ".." ,
106
+ & mut applicability,
107
+ ) ;
108
+ ( macro_call. span , format ! ( "({snippet})" ) )
109
+ } ,
110
+ _ => return ,
111
+ } ;
72
112
73
- let ( sugg_span, suggestion) = match expr. peel_drop_temps ( ) . kind {
74
- // dbg!()
75
- ExprKind :: Block ( ..) => {
76
- // If the `dbg!` macro is a "free" statement and not contained within other expressions,
77
- // remove the whole statement.
78
- if let Some ( Node :: Stmt ( _) ) = cx. tcx . hir ( ) . find_parent ( expr. hir_id )
79
- && let Some ( semi_span) = cx. sess ( ) . source_map ( ) . mac_call_stmt_semi_span ( macro_call. span )
80
- {
81
- ( macro_call. span . to ( semi_span) , String :: new ( ) )
82
- } else {
83
- ( macro_call. span , String :: from ( "()" ) )
84
- }
85
- } ,
86
- // dbg!(1)
87
- ExprKind :: Match ( val, ..) => (
88
- macro_call. span ,
89
- snippet_with_applicability ( cx, val. span . source_callsite ( ) , ".." , & mut applicability) . to_string ( ) ,
90
- ) ,
91
- // dbg!(2, 3)
92
- ExprKind :: Tup (
93
- [
94
- Expr {
95
- kind : ExprKind :: Match ( first, ..) ,
96
- ..
97
- } ,
98
- ..,
99
- Expr {
100
- kind : ExprKind :: Match ( last, ..) ,
101
- ..
102
- } ,
103
- ] ,
104
- ) => {
105
- let snippet = snippet_with_applicability (
106
- cx,
107
- first. span . source_callsite ( ) . to ( last. span . source_callsite ( ) ) ,
108
- ".." ,
109
- & mut applicability,
110
- ) ;
111
- ( macro_call. span , format ! ( "({snippet})" ) )
112
- } ,
113
- _ => return ,
114
- } ;
113
+ self . prev_ctxt = cur_syntax_ctxt;
114
+
115
+ span_lint_and_sugg (
116
+ cx,
117
+ DBG_MACRO ,
118
+ sugg_span,
119
+ "the `dbg!` macro is intended as a debugging tool" ,
120
+ "remove the invocation before committing it to a version control system" ,
121
+ suggestion,
122
+ applicability,
123
+ ) ;
124
+ }
125
+ }
115
126
116
- span_lint_and_sugg (
117
- cx,
118
- DBG_MACRO ,
119
- sugg_span,
120
- "the `dbg!` macro is intended as a debugging tool" ,
121
- "remove the invocation before committing it to a version control system" ,
122
- suggestion,
123
- applicability,
124
- ) ;
127
+ fn check_crate_post ( & mut self , _: & LateContext < ' _ > ) {
128
+ self . checked_dbg_call_site = FxHashSet :: default ( ) ;
125
129
}
126
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 ) )
138
+ }
0 commit comments