1
1
use clippy_utils:: diagnostics:: span_lint_and_help;
2
2
use rustc_ast:: {
3
- node_id:: NodeId ,
4
- ptr:: P ,
5
- visit:: { FnKind , Visitor } ,
6
- Arm , AssocItemKind , Block , Expr , ExprKind , Inline , Item , ItemKind , Local , LocalKind , ModKind , ModSpans , Pat ,
7
- PatKind , Stmt , StmtKind ,
3
+ visit:: { walk_block, walk_item, Visitor } ,
4
+ Block , Crate , Inline , Item , ItemKind , ModKind ,
8
5
} ;
9
6
use rustc_lint:: { EarlyContext , EarlyLintPass , LintContext } ;
10
7
use rustc_middle:: lint:: in_external_macro;
11
8
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
12
9
use rustc_span:: Span ;
13
- use thin_vec:: ThinVec ;
14
10
15
11
declare_clippy_lint ! {
16
12
/// ### What it does
@@ -22,11 +18,6 @@ declare_clippy_lint! {
22
18
/// It can severely hinder readability. The default is very generous; if you
23
19
/// exceed this, it's a sign you should refactor.
24
20
///
25
- /// ### Known issues
26
- ///
27
- /// Nested inline modules will all be linted, rather than just the outermost one
28
- /// that applies. This makes the output a bit verbose.
29
- ///
30
21
/// ### Example
31
22
/// An example clippy.toml configuration:
32
23
/// ```toml
@@ -74,14 +65,17 @@ pub struct ExcessiveNesting {
74
65
}
75
66
76
67
impl EarlyLintPass for ExcessiveNesting {
77
- fn check_item ( & mut self , cx : & EarlyContext < ' _ > , item : & Item ) {
68
+ fn check_crate ( & mut self , cx : & EarlyContext < ' _ > , krate : & Crate ) {
78
69
let conf = self ;
79
- NestingVisitor {
70
+ let mut visitor = NestingVisitor {
80
71
conf,
81
72
cx,
82
73
nest_level : 0 ,
74
+ } ;
75
+
76
+ for item in & krate. items {
77
+ visitor. visit_item ( item) ;
83
78
}
84
- . visit_item ( item) ;
85
79
}
86
80
}
87
81
@@ -91,239 +85,52 @@ struct NestingVisitor<'conf, 'cx> {
91
85
nest_level : u64 ,
92
86
}
93
87
94
- impl < ' conf , ' cx > Visitor < ' _ > for NestingVisitor < ' conf , ' cx > {
95
- fn visit_local ( & mut self , local : & Local ) {
96
- self . visit_pat ( & local. pat ) ;
97
-
98
- match & local. kind {
99
- LocalKind :: Init ( expr) => self . visit_expr ( expr) ,
100
- LocalKind :: InitElse ( expr, block) => {
101
- self . visit_expr ( expr) ;
102
- self . visit_block ( block) ;
103
- } ,
104
- LocalKind :: Decl => ( ) ,
88
+ impl NestingVisitor < ' _ , ' _ > {
89
+ fn check_indent ( & self , span : Span ) -> bool {
90
+ if self . nest_level > self . conf . excessive_nesting_threshold && !in_external_macro ( self . cx . sess ( ) , span) {
91
+ span_lint_and_help (
92
+ self . cx ,
93
+ EXCESSIVE_NESTING ,
94
+ span,
95
+ "this block is too nested" ,
96
+ None ,
97
+ "try refactoring your code to minimize nesting" ,
98
+ ) ;
99
+
100
+ return true ;
105
101
}
102
+
103
+ false
106
104
}
105
+ }
107
106
107
+ impl < ' conf , ' cx > Visitor < ' _ > for NestingVisitor < ' conf , ' cx > {
108
108
fn visit_block ( & mut self , block : & Block ) {
109
- // TODO: Can we use some RAII guard instead? Borrow checker seems to hate that
110
- // idea but it would be a lot cleaner.
111
109
self . nest_level += 1 ;
112
110
113
- if !check_indent ( self , block. span ) {
114
- for stmt in & block. stmts {
115
- self . visit_stmt ( stmt) ;
116
- }
111
+ if !self . check_indent ( block. span ) {
112
+ walk_block ( self , block) ;
117
113
}
118
114
119
115
self . nest_level -= 1 ;
120
116
}
121
117
122
- fn visit_stmt ( & mut self , stmt : & Stmt ) {
123
- match & stmt. kind {
124
- StmtKind :: Local ( local) => self . visit_local ( local) ,
125
- StmtKind :: Item ( item) => self . visit_item ( item) ,
126
- StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => self . visit_expr ( expr) ,
127
- _ => ( ) ,
128
- }
129
- }
130
-
131
- fn visit_arm ( & mut self , arm : & Arm ) {
132
- self . visit_pat ( & arm. pat ) ;
133
- if let Some ( expr) = & arm. guard {
134
- self . visit_expr ( expr) ;
135
- }
136
- self . visit_expr ( & arm. body ) ;
137
- }
138
-
139
- // TODO: Is this necessary?
140
- fn visit_pat ( & mut self , pat : & Pat ) {
141
- match & pat. kind {
142
- PatKind :: Box ( pat) | PatKind :: Ref ( pat, ..) | PatKind :: Paren ( pat) => self . visit_pat ( pat) ,
143
- PatKind :: Lit ( expr) => self . visit_expr ( expr) ,
144
- PatKind :: Range ( start, end, ..) => {
145
- if let Some ( expr) = start {
146
- self . visit_expr ( expr) ;
147
- }
148
- if let Some ( expr) = end {
149
- self . visit_expr ( expr) ;
150
- }
151
- } ,
152
- PatKind :: Ident ( .., pat) if let Some ( pat) = pat => {
153
- self . visit_pat ( pat) ;
154
- } ,
155
- PatKind :: Struct ( .., pat_fields, _) => {
156
- for pat_field in pat_fields {
157
- self . visit_pat ( & pat_field. pat ) ;
158
- }
159
- } ,
160
- PatKind :: TupleStruct ( .., pats) | PatKind :: Or ( pats) | PatKind :: Tuple ( pats) | PatKind :: Slice ( pats) => {
161
- for pat in pats {
162
- self . visit_pat ( pat) ;
163
- }
164
- } ,
165
- _ => ( ) ,
166
- }
167
- }
168
-
169
- fn visit_expr ( & mut self , expr : & Expr ) {
170
- // This is a mess, but really all it does is extract every expression from every applicable variant
171
- // of ExprKind until it finds a Block.
172
- // TODO: clippy_utils has the two functions for_each_expr and for_each_expr_with_closures, can those
173
- // be used here or are they not applicable for this case?
174
- match & expr. kind {
175
- ExprKind :: ConstBlock ( anon_const) => self . visit_expr ( & anon_const. value ) ,
176
- ExprKind :: Call ( .., args) => {
177
- for expr in args {
178
- self . visit_expr ( expr) ;
179
- }
180
- } ,
181
- ExprKind :: MethodCall ( method_call) => {
182
- for expr in & method_call. args {
183
- self . visit_expr ( expr) ;
184
- }
185
- } ,
186
- ExprKind :: Tup ( exprs) | ExprKind :: Array ( exprs) => {
187
- for expr in exprs {
188
- self . visit_expr ( expr) ;
189
- }
190
- } ,
191
- ExprKind :: Binary ( .., left, right)
192
- | ExprKind :: Assign ( left, right, ..)
193
- | ExprKind :: AssignOp ( .., left, right)
194
- | ExprKind :: Index ( left, right) => {
195
- self . visit_expr ( left) ;
196
- self . visit_expr ( right) ;
197
- } ,
198
- ExprKind :: Let ( pat, expr, ..) => {
199
- self . visit_pat ( pat) ;
200
- self . visit_expr ( expr) ;
201
- } ,
202
- ExprKind :: Unary ( .., expr)
203
- | ExprKind :: Await ( expr)
204
- | ExprKind :: Field ( expr, ..)
205
- | ExprKind :: AddrOf ( .., expr)
206
- | ExprKind :: Try ( expr) => {
207
- self . visit_expr ( expr) ;
208
- } ,
209
- ExprKind :: Repeat ( expr, anon_const) => {
210
- self . visit_expr ( expr) ;
211
- self . visit_expr ( & anon_const. value ) ;
212
- } ,
213
- ExprKind :: If ( expr, block, else_expr) => {
214
- self . visit_expr ( expr) ;
215
- self . visit_block ( block) ;
216
-
217
- if let Some ( expr) = else_expr {
218
- self . visit_expr ( expr) ;
219
- }
220
- } ,
221
- ExprKind :: While ( expr, block, ..) => {
222
- self . visit_expr ( expr) ;
223
- self . visit_block ( block) ;
224
- } ,
225
- ExprKind :: ForLoop ( pat, expr, block, ..) => {
226
- self . visit_pat ( pat) ;
227
- self . visit_expr ( expr) ;
228
- self . visit_block ( block) ;
229
- } ,
230
- ExprKind :: Loop ( block, ..)
231
- | ExprKind :: Block ( block, ..)
232
- | ExprKind :: Async ( .., block)
233
- | ExprKind :: TryBlock ( block) => {
234
- self . visit_block ( block) ;
235
- } ,
236
- ExprKind :: Match ( expr, arms) => {
237
- self . visit_expr ( expr) ;
238
-
239
- for arm in arms {
240
- self . visit_arm ( arm) ;
241
- }
242
- } ,
243
- ExprKind :: Closure ( closure) => self . visit_expr ( & closure. body ) ,
244
- ExprKind :: Range ( start, end, ..) => {
245
- if let Some ( expr) = start {
246
- self . visit_expr ( expr) ;
247
- }
248
- if let Some ( expr) = end {
249
- self . visit_expr ( expr) ;
250
- }
251
- } ,
252
- ExprKind :: Break ( .., expr) | ExprKind :: Ret ( expr) | ExprKind :: Yield ( expr) | ExprKind :: Yeet ( expr) => {
253
- if let Some ( expr) = expr {
254
- self . visit_expr ( expr) ;
255
- }
256
- } ,
257
- ExprKind :: Struct ( struct_expr) => {
258
- for field in & struct_expr. fields {
259
- self . visit_expr ( & field. expr ) ;
260
- }
261
- } ,
262
- _ => ( ) ,
263
- }
264
- }
265
-
266
- fn visit_fn ( & mut self , fk : FnKind < ' _ > , _: Span , _: NodeId ) {
267
- match fk {
268
- FnKind :: Fn ( .., block) if let Some ( block) = block => self . visit_block ( block) ,
269
- FnKind :: Closure ( .., expr) => self . visit_expr ( expr) ,
270
- // :/
271
- FnKind :: Fn ( ..) => ( ) ,
272
- }
273
- }
274
-
275
118
fn visit_item ( & mut self , item : & Item ) {
276
119
match & item. kind {
277
- ItemKind :: Static ( static_item) if let Some ( expr) = static_item. expr . as_ref ( ) => self . visit_expr ( expr) ,
278
- ItemKind :: Const ( const_item) if let Some ( expr) = const_item. expr . as_ref ( ) => self . visit_expr ( expr) ,
279
- ItemKind :: Fn ( fk) if let Some ( block) = fk. body . as_ref ( ) => self . visit_block ( block) ,
280
- ItemKind :: Mod ( .., mod_kind)
281
- if let ModKind :: Loaded ( items, Inline :: Yes , ModSpans { inner_span, ..} ) = mod_kind =>
282
- {
120
+ ItemKind :: Trait ( _) | ItemKind :: Impl ( _) | ItemKind :: Mod ( .., ModKind :: Loaded ( _, Inline :: Yes , _) ) => {
283
121
self . nest_level += 1 ;
284
122
285
- check_indent ( self , * inner_span) ;
123
+ if !self . check_indent ( item. span ) {
124
+ walk_item ( self , item) ;
125
+ }
286
126
287
127
self . nest_level -= 1 ;
288
- }
289
- ItemKind :: Trait ( trit) => check_trait_and_impl ( self , item, & trit. items ) ,
290
- ItemKind :: Impl ( imp) => check_trait_and_impl ( self , item, & imp. items ) ,
291
- _ => ( ) ,
292
- }
293
- }
294
- }
295
-
296
- fn check_trait_and_impl ( visitor : & mut NestingVisitor < ' _ , ' _ > , item : & Item , items : & ThinVec < P < Item < AssocItemKind > > > ) {
297
- visitor. nest_level += 1 ;
298
-
299
- if !check_indent ( visitor, item. span ) {
300
- for item in items {
301
- match & item. kind {
302
- AssocItemKind :: Const ( const_item) if let Some ( expr) = const_item. expr . as_ref ( ) => {
303
- visitor. visit_expr ( expr) ;
304
- } ,
305
- AssocItemKind :: Fn ( fk) if let Some ( block) = fk. body . as_ref ( ) => visitor. visit_block ( block) ,
306
- _ => ( ) ,
307
- }
128
+ } ,
129
+ // Mod: Don't visit non-inline modules
130
+ // ForeignMod: I don't think this is necessary, but just incase let's not take any chances (don't want to
131
+ // cause any false positives)
132
+ ItemKind :: Mod ( ..) | ItemKind :: ForeignMod ( ..) => { } ,
133
+ _ => walk_item ( self , item) ,
308
134
}
309
135
}
310
-
311
- visitor. nest_level -= 1 ;
312
- }
313
-
314
- fn check_indent ( visitor : & NestingVisitor < ' _ , ' _ > , span : Span ) -> bool {
315
- if visitor. nest_level > visitor. conf . excessive_nesting_threshold && !in_external_macro ( visitor. cx . sess ( ) , span) {
316
- span_lint_and_help (
317
- visitor. cx ,
318
- EXCESSIVE_NESTING ,
319
- span,
320
- "this block is too nested" ,
321
- None ,
322
- "try refactoring your code, extraction is often both easier to read and less nested" ,
323
- ) ;
324
-
325
- return true ;
326
- }
327
-
328
- false
329
136
}
0 commit comments