@@ -88,7 +88,33 @@ struct SimilarNamesLocalVisitor<'a, 'tcx: 'a> {
88
88
names : Vec < ExistingName > ,
89
89
cx : & ' a EarlyContext < ' tcx > ,
90
90
lint : & ' a NonExpressiveNames ,
91
- single_char_names : Vec < char > ,
91
+
92
+ /// A stack of scopes containing the single-character bindings in each scope.
93
+ single_char_names : Vec < Vec < Ident > > ,
94
+ }
95
+
96
+ impl < ' a , ' tcx : ' a > SimilarNamesLocalVisitor < ' a , ' tcx > {
97
+ fn check_single_char_names ( & self ) {
98
+ let num_single_char_names = self . single_char_names . iter ( ) . flatten ( ) . count ( ) ;
99
+ let threshold = self . lint . single_char_binding_names_threshold ;
100
+ if num_single_char_names as u64 >= threshold {
101
+ let span = self
102
+ . single_char_names
103
+ . iter ( )
104
+ . flatten ( )
105
+ . map ( |ident| ident. span )
106
+ . collect :: < Vec < _ > > ( ) ;
107
+ span_lint (
108
+ self . cx ,
109
+ MANY_SINGLE_CHAR_NAMES ,
110
+ span,
111
+ & format ! (
112
+ "{} bindings with single-character names in scope" ,
113
+ num_single_char_names
114
+ ) ,
115
+ ) ;
116
+ }
117
+ }
92
118
}
93
119
94
120
// this list contains lists of names that are allowed to be similar
@@ -109,7 +135,7 @@ struct SimilarNamesNameVisitor<'a: 'b, 'tcx: 'a, 'b>(&'b mut SimilarNamesLocalVi
109
135
impl < ' a , ' tcx : ' a , ' b > Visitor < ' tcx > for SimilarNamesNameVisitor < ' a , ' tcx , ' b > {
110
136
fn visit_pat ( & mut self , pat : & ' tcx Pat ) {
111
137
match pat. node {
112
- PatKind :: Ident ( _, ident, _) => self . check_name ( ident. span , ident . name ) ,
138
+ PatKind :: Ident ( _, ident, _) => self . check_ident ( ident) ,
113
139
PatKind :: Struct ( _, ref fields, _) => {
114
140
for field in fields {
115
141
if !field. node . is_shorthand {
@@ -140,44 +166,40 @@ fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
140
166
}
141
167
142
168
impl < ' a , ' tcx , ' b > SimilarNamesNameVisitor < ' a , ' tcx , ' b > {
143
- fn check_short_name ( & mut self , c : char , span : Span ) {
144
- // make sure we ignore shadowing
145
- if self . 0 . single_char_names . contains ( & c) {
169
+ fn check_short_ident ( & mut self , ident : Ident ) {
170
+ // Ignore shadowing
171
+ if self
172
+ . 0
173
+ . single_char_names
174
+ . iter ( )
175
+ . flatten ( )
176
+ . any ( |id| id. name == ident. name )
177
+ {
146
178
return ;
147
- }
148
- self . 0 . single_char_names . push ( c) ;
149
- if self . 0 . single_char_names . len ( ) as u64 >= self . 0 . lint . single_char_binding_names_threshold {
150
- span_lint (
151
- self . 0 . cx ,
152
- MANY_SINGLE_CHAR_NAMES ,
153
- span,
154
- & format ! (
155
- "{}th binding whose name is just one char" ,
156
- self . 0 . single_char_names. len( )
157
- ) ,
158
- ) ;
179
+ } else if let Some ( scope) = & mut self . 0 . single_char_names . last_mut ( ) {
180
+ scope. push ( ident) ;
159
181
}
160
182
}
183
+
161
184
#[ allow( clippy:: too_many_lines) ]
162
- fn check_name ( & mut self , span : Span , name : Name ) {
163
- let interned_name = name. as_str ( ) ;
185
+ fn check_ident ( & mut self , ident : Ident ) {
186
+ let interned_name = ident . name . as_str ( ) ;
164
187
if interned_name. chars ( ) . any ( char:: is_uppercase) {
165
188
return ;
166
189
}
167
190
if interned_name. chars ( ) . all ( |c| c. is_digit ( 10 ) || c == '_' ) {
168
191
span_lint (
169
192
self . 0 . cx ,
170
193
JUST_UNDERSCORES_AND_DIGITS ,
171
- span,
194
+ ident . span ,
172
195
"consider choosing a more descriptive name" ,
173
196
) ;
174
197
return ;
175
198
}
176
199
let count = interned_name. chars ( ) . count ( ) ;
177
200
if count < 3 {
178
201
if count == 1 {
179
- let c = interned_name. chars ( ) . next ( ) . expect ( "already checked" ) ;
180
- self . check_short_name ( c, span) ;
202
+ self . check_short_ident ( ident) ;
181
203
}
182
204
return ;
183
205
}
@@ -247,13 +269,13 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
247
269
span_lint_and_then (
248
270
self . 0 . cx ,
249
271
SIMILAR_NAMES ,
250
- span,
272
+ ident . span ,
251
273
"binding's name is too similar to existing binding" ,
252
274
|diag| {
253
275
diag. span_note ( existing_name. span , "existing binding defined here" ) ;
254
276
if let Some ( split) = split_at {
255
277
diag. span_help (
256
- span,
278
+ ident . span ,
257
279
& format ! (
258
280
"separate the discriminating character by an \
259
281
underscore like: `{}_{}`",
@@ -269,7 +291,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
269
291
self . 0 . names . push ( ExistingName {
270
292
whitelist : get_whitelist ( & interned_name) . unwrap_or ( & [ ] ) ,
271
293
interned : interned_name,
272
- span,
294
+ span : ident . span ,
273
295
len : count,
274
296
} ) ;
275
297
}
@@ -297,15 +319,25 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
297
319
SimilarNamesNameVisitor ( self ) . visit_pat ( & * local. pat ) ;
298
320
}
299
321
fn visit_block ( & mut self , blk : & ' tcx Block ) {
322
+ self . single_char_names . push ( vec ! [ ] ) ;
323
+
300
324
self . apply ( |this| walk_block ( this, blk) ) ;
325
+
326
+ self . check_single_char_names ( ) ;
327
+ self . single_char_names . pop ( ) ;
301
328
}
302
329
fn visit_arm ( & mut self , arm : & ' tcx Arm ) {
330
+ self . single_char_names . push ( vec ! [ ] ) ;
331
+
303
332
self . apply ( |this| {
304
333
// just go through the first pattern, as either all patterns
305
334
// bind the same bindings or rustc would have errored much earlier
306
335
SimilarNamesNameVisitor ( this) . visit_pat ( & arm. pats [ 0 ] ) ;
307
336
this. apply ( |this| walk_expr ( this, & arm. body ) ) ;
308
337
} ) ;
338
+
339
+ self . check_single_char_names ( ) ;
340
+ self . single_char_names . pop ( ) ;
309
341
}
310
342
fn visit_item ( & mut self , _: & Item ) {
311
343
// do not recurse into inner items
@@ -335,14 +367,17 @@ fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attri
335
367
names : Vec :: new ( ) ,
336
368
cx,
337
369
lint,
338
- single_char_names : Vec :: new ( ) ,
370
+ single_char_names : vec ! [ vec! [ ] ] ,
339
371
} ;
372
+
340
373
// initialize with function arguments
341
374
for arg in & decl. inputs {
342
375
SimilarNamesNameVisitor ( & mut visitor) . visit_pat ( & arg. pat ) ;
343
376
}
344
377
// walk all other bindings
345
378
walk_block ( & mut visitor, blk) ;
379
+
380
+ visitor. check_single_char_names ( ) ;
346
381
}
347
382
}
348
383
0 commit comments