@@ -88,7 +88,33 @@ struct SimilarNamesLocalVisitor<'a, 'tcx: 'a> {
8888 names : Vec < ExistingName > ,
8989 cx : & ' a EarlyContext < ' tcx > ,
9090 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+ }
92118}
93119
94120// 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
109135impl < ' a , ' tcx : ' a , ' b > Visitor < ' tcx > for SimilarNamesNameVisitor < ' a , ' tcx , ' b > {
110136 fn visit_pat ( & mut self , pat : & ' tcx Pat ) {
111137 match pat. node {
112- PatKind :: Ident ( _, ident, _) => self . check_name ( ident. span , ident . name ) ,
138+ PatKind :: Ident ( _, ident, _) => self . check_ident ( ident) ,
113139 PatKind :: Struct ( _, ref fields, _) => {
114140 for field in fields {
115141 if !field. node . is_shorthand {
@@ -140,44 +166,40 @@ fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
140166}
141167
142168impl < ' 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+ {
146178 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) ;
159181 }
160182 }
183+
161184 #[ 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 ( ) ;
164187 if interned_name. chars ( ) . any ( char:: is_uppercase) {
165188 return ;
166189 }
167190 if interned_name. chars ( ) . all ( |c| c. is_digit ( 10 ) || c == '_' ) {
168191 span_lint (
169192 self . 0 . cx ,
170193 JUST_UNDERSCORES_AND_DIGITS ,
171- span,
194+ ident . span ,
172195 "consider choosing a more descriptive name" ,
173196 ) ;
174197 return ;
175198 }
176199 let count = interned_name. chars ( ) . count ( ) ;
177200 if count < 3 {
178201 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) ;
181203 }
182204 return ;
183205 }
@@ -247,13 +269,13 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
247269 span_lint_and_then (
248270 self . 0 . cx ,
249271 SIMILAR_NAMES ,
250- span,
272+ ident . span ,
251273 "binding's name is too similar to existing binding" ,
252274 |diag| {
253275 diag. span_note ( existing_name. span , "existing binding defined here" ) ;
254276 if let Some ( split) = split_at {
255277 diag. span_help (
256- span,
278+ ident . span ,
257279 & format ! (
258280 "separate the discriminating character by an \
259281 underscore like: `{}_{}`",
@@ -269,7 +291,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
269291 self . 0 . names . push ( ExistingName {
270292 whitelist : get_whitelist ( & interned_name) . unwrap_or ( & [ ] ) ,
271293 interned : interned_name,
272- span,
294+ span : ident . span ,
273295 len : count,
274296 } ) ;
275297 }
@@ -297,15 +319,25 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
297319 SimilarNamesNameVisitor ( self ) . visit_pat ( & * local. pat ) ;
298320 }
299321 fn visit_block ( & mut self , blk : & ' tcx Block ) {
322+ self . single_char_names . push ( vec ! [ ] ) ;
323+
300324 self . apply ( |this| walk_block ( this, blk) ) ;
325+
326+ self . check_single_char_names ( ) ;
327+ self . single_char_names . pop ( ) ;
301328 }
302329 fn visit_arm ( & mut self , arm : & ' tcx Arm ) {
330+ self . single_char_names . push ( vec ! [ ] ) ;
331+
303332 self . apply ( |this| {
304333 // just go through the first pattern, as either all patterns
305334 // bind the same bindings or rustc would have errored much earlier
306335 SimilarNamesNameVisitor ( this) . visit_pat ( & arm. pats [ 0 ] ) ;
307336 this. apply ( |this| walk_expr ( this, & arm. body ) ) ;
308337 } ) ;
338+
339+ self . check_single_char_names ( ) ;
340+ self . single_char_names . pop ( ) ;
309341 }
310342 fn visit_item ( & mut self , _: & Item ) {
311343 // do not recurse into inner items
@@ -335,14 +367,17 @@ fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attri
335367 names : Vec :: new ( ) ,
336368 cx,
337369 lint,
338- single_char_names : Vec :: new ( ) ,
370+ single_char_names : vec ! [ vec! [ ] ] ,
339371 } ;
372+
340373 // initialize with function arguments
341374 for arg in & decl. inputs {
342375 SimilarNamesNameVisitor ( & mut visitor) . visit_pat ( & arg. pat ) ;
343376 }
344377 // walk all other bindings
345378 walk_block ( & mut visitor, blk) ;
379+
380+ visitor. check_single_char_names ( ) ;
346381 }
347382}
348383
0 commit comments