@@ -41,15 +41,25 @@ impl LintPass for NonExpressiveNames {
41
41
}
42
42
}
43
43
44
+ struct ExistingName {
45
+ interned : InternedString ,
46
+ span : Span ,
47
+ len : usize ,
48
+ whitelist : & ' static [ & ' static str ] ,
49
+ }
50
+
44
51
struct SimilarNamesLocalVisitor < ' a , ' b : ' a > {
45
- names : Vec < ( InternedString , Span , usize ) > ,
52
+ names : Vec < ExistingName > ,
46
53
cx : & ' a EarlyContext < ' b > ,
47
54
lint : & ' a NonExpressiveNames ,
48
55
single_char_names : Vec < char > ,
49
56
}
50
57
51
- const WHITELIST : & ' static [ & ' static str ] = & [
52
- "lhs" , "rhs" ,
58
+ // this list contains lists of names that are allowed to be similar
59
+ // the assumption is that no name is ever contained in multiple lists.
60
+ const WHITELIST : & ' static [ & ' static [ & ' static str ] ] = & [
61
+ & [ "parsed" , "parser" ] ,
62
+ & [ "lhs" , "rhs" ] ,
53
63
] ;
54
64
55
65
struct SimilarNamesNameVisitor < ' a , ' b : ' a , ' c : ' b > ( & ' a mut SimilarNamesLocalVisitor < ' b , ' c > ) ;
@@ -63,21 +73,27 @@ impl<'v, 'a, 'b, 'c> visit::Visitor<'v> for SimilarNamesNameVisitor<'a, 'b, 'c>
63
73
}
64
74
}
65
75
66
- fn whitelisted ( interned_name : & str ) -> bool {
76
+ fn get_whitelist ( interned_name : & str ) -> Option < & ' static [ & ' static str ] > {
67
77
for & allow in WHITELIST {
68
- if interned_name == allow {
69
- return true ;
78
+ if whitelisted ( interned_name, allow) {
79
+ return Some ( allow ) ;
70
80
}
71
- if interned_name. len ( ) <= allow. len ( ) {
72
- continue ;
73
- }
74
- // allow_*
75
- let allow_start = allow. chars ( ) . chain ( Some ( '_' ) ) ;
81
+ }
82
+ None
83
+ }
84
+
85
+ fn whitelisted ( interned_name : & str , list : & [ & str ] ) -> bool {
86
+ if list. iter ( ) . any ( |& name| interned_name == name) {
87
+ return true ;
88
+ }
89
+ for name in list {
90
+ // name_*
91
+ let allow_start = name. chars ( ) . chain ( Some ( '_' ) ) ;
76
92
if interned_name. chars ( ) . zip ( allow_start) . all ( |( l, r) | l == r) {
77
93
return true ;
78
94
}
79
- // *_allow
80
- let allow_end = Some ( '_' ) . into_iter ( ) . chain ( allow . chars ( ) ) ;
95
+ // *_name
96
+ let allow_end = Some ( '_' ) . into_iter ( ) . chain ( name . chars ( ) ) ;
81
97
if interned_name. chars ( ) . rev ( ) . zip ( allow_end. rev ( ) ) . all ( |( l, r) | l == r) {
82
98
return true ;
83
99
}
@@ -110,83 +126,66 @@ impl<'a, 'b, 'c> SimilarNamesNameVisitor<'a, 'b, 'c> {
110
126
}
111
127
let count = interned_name. chars ( ) . count ( ) ;
112
128
if count < 3 {
113
- if count != 1 {
114
- return ;
129
+ if count == 1 {
130
+ let c = interned_name. chars ( ) . next ( ) . expect ( "already checked" ) ;
131
+ self . check_short_name ( c, span) ;
115
132
}
116
- let c = interned_name. chars ( ) . next ( ) . expect ( "already checked" ) ;
117
- self . check_short_name ( c, span) ;
118
- return ;
119
- }
120
- if whitelisted ( & interned_name) {
121
133
return ;
122
134
}
123
- for & ( ref existing_name, sp, existing_len) in & self . 0 . names {
135
+ for existing_name in & self . 0 . names {
136
+ if whitelisted ( & interned_name, existing_name. whitelist ) {
137
+ continue ;
138
+ }
124
139
let mut split_at = None ;
125
- if existing_len > count {
126
- if existing_len - count != 1 {
127
- continue ;
128
- }
129
- if levenstein_not_1 ( & interned_name, & existing_name) {
140
+ if existing_name. len > count {
141
+ if existing_name. len - count != 1 || levenstein_not_1 ( & interned_name, & existing_name. interned ) {
130
142
continue ;
131
143
}
132
- } else if existing_len < count {
133
- if count - existing_len != 1 {
134
- continue ;
135
- }
136
- if levenstein_not_1 ( & existing_name, & interned_name) {
144
+ } else if existing_name. len < count {
145
+ if count - existing_name. len != 1 || levenstein_not_1 ( & existing_name. interned , & interned_name) {
137
146
continue ;
138
147
}
139
148
} else {
140
149
let mut interned_chars = interned_name. chars ( ) ;
141
- let mut existing_chars = existing_name. chars ( ) ;
150
+ let mut existing_chars = existing_name. interned . chars ( ) ;
151
+ let first_i = interned_chars. next ( ) . expect ( "we know we have at least one char" ) ;
152
+ let first_e = existing_chars. next ( ) . expect ( "we know we have at least one char" ) ;
153
+ let eq_or_numeric = |a : char , b : char | a == b || a. is_numeric ( ) && b. is_numeric ( ) ;
142
154
143
- if interned_chars. next ( ) != existing_chars. next ( ) {
144
- let i = interned_chars. next ( ) . expect ( "we know we have more than 1 char" ) ;
145
- let e = existing_chars. next ( ) . expect ( "we know we have more than 1 char" ) ;
146
- if i == e {
147
- if i == '_' {
148
- // allowed similarity x_foo, y_foo
149
- // or too many chars differ (x_foo, y_boo)
155
+ if eq_or_numeric ( first_i, first_e) {
156
+ let last_i = interned_chars. next_back ( ) . expect ( "we know we have at least two chars" ) ;
157
+ let last_e = existing_chars. next_back ( ) . expect ( "we know we have at least two chars" ) ;
158
+ if eq_or_numeric ( last_i, last_e) {
159
+ if interned_chars. zip ( existing_chars) . filter ( |& ( i, e) | !eq_or_numeric ( i, e) ) . count ( ) != 1 {
150
160
continue ;
151
- } else if interned_chars. ne ( existing_chars) {
152
- // too many chars differ
153
- continue
154
161
}
155
162
} else {
156
- // too many chars differ
157
- continue ;
158
- }
159
- split_at = interned_name. chars ( ) . next ( ) . map ( |c| c. len_utf8 ( ) ) ;
160
- } else if interned_chars. next_back ( ) == existing_chars. next_back ( ) {
161
- if interned_chars. zip ( existing_chars) . filter ( |& ( i, e) | i != e) . count ( ) != 1 {
162
- // too many chars differ, or none differ (aka shadowing)
163
- continue ;
164
- }
165
- } else {
166
- let i = interned_chars. next_back ( ) . expect ( "we know we have more than 2 chars" ) ;
167
- let e = existing_chars. next_back ( ) . expect ( "we know we have more than 2 chars" ) ;
168
- if i == e {
169
- if i == '_' {
170
- // allowed similarity foo_x, foo_x
171
- // or too many chars differ (foo_x, boo_x)
163
+ let second_last_i = interned_chars. next_back ( ) . expect ( "we know we have at least three chars" ) ;
164
+ let second_last_e = existing_chars. next_back ( ) . expect ( "we know we have at least three chars" ) ;
165
+ if !eq_or_numeric ( second_last_i, second_last_e) || second_last_i == '_' || !interned_chars. zip ( existing_chars) . all ( |( i, e) | eq_or_numeric ( i, e) ) {
166
+ // allowed similarity foo_x, foo_y
167
+ // or too many chars differ (foo_x, boo_y) or (foox, booy)
172
168
continue ;
173
- } else if interned_chars. ne ( existing_chars) {
174
- // too many chars differ
175
- continue
176
169
}
177
- } else {
178
- // too many chars differ
170
+ split_at = interned_name. char_indices ( ) . rev ( ) . next ( ) . map ( |( i, _) | i) ;
171
+ }
172
+ } else {
173
+ let second_i = interned_chars. next ( ) . expect ( "we know we have at least two chars" ) ;
174
+ let second_e = existing_chars. next ( ) . expect ( "we know we have at least two chars" ) ;
175
+ if !eq_or_numeric ( second_i, second_e) || second_i == '_' || !interned_chars. zip ( existing_chars) . all ( |( i, e) | eq_or_numeric ( i, e) ) {
176
+ // allowed similarity x_foo, y_foo
177
+ // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
179
178
continue ;
180
179
}
181
- split_at = interned_name. char_indices ( ) . rev ( ) . next ( ) . map ( |( i , _ ) | i ) ;
180
+ split_at = interned_name. chars ( ) . next ( ) . map ( |c| c . len_utf8 ( ) ) ;
182
181
}
183
182
}
184
183
span_lint_and_then ( self . 0 . cx ,
185
184
SIMILAR_NAMES ,
186
185
span,
187
186
"binding's name is too similar to existing binding" ,
188
187
|diag| {
189
- diag. span_note ( sp , "existing binding defined here" ) ;
188
+ diag. span_note ( existing_name . span , "existing binding defined here" ) ;
190
189
if let Some ( split) = split_at {
191
190
diag. span_help ( span, & format ! ( "separate the discriminating character \
192
191
by an underscore like: `{}_{}`",
@@ -196,7 +195,12 @@ impl<'a, 'b, 'c> SimilarNamesNameVisitor<'a, 'b, 'c> {
196
195
} ) ;
197
196
return ;
198
197
}
199
- self . 0 . names . push ( ( interned_name, span, count) ) ;
198
+ self . 0 . names . push ( ExistingName {
199
+ whitelist : get_whitelist ( & interned_name) . unwrap_or ( & [ ] ) ,
200
+ interned : interned_name,
201
+ span : span,
202
+ len : count,
203
+ } ) ;
200
204
}
201
205
}
202
206
0 commit comments