Skip to content

Commit 0d50d44

Browse files
committed
use a multispan for MANY_SINGLE_CHAR_NAMES
1 parent 17e04ac commit 0d50d44

File tree

5 files changed

+142
-45
lines changed

5 files changed

+142
-45
lines changed

clippy_lints/src/non_expressive_names.rs

+61-26
Original file line numberDiff line numberDiff line change
@@ -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
109135
impl<'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

142168
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+
{
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

clippy_lints/src/utils/diagnostics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc::lint::{LateContext, Lint, LintContext};
55
use rustc_errors::{Applicability, CodeSuggestion, Substitution, SubstitutionPart, SuggestionStyle};
66
use std::env;
77
use syntax::errors::DiagnosticBuilder;
8-
use syntax::source_map::Span;
8+
use syntax::source_map::{MultiSpan, Span};
99

1010
/// Wrapper around `DiagnosticBuilder` that adds a link to Clippy documentation for the emitted lint
1111
struct DiagnosticWrapper<'a>(DiagnosticBuilder<'a>);
@@ -48,7 +48,7 @@ impl<'a> DiagnosticWrapper<'a> {
4848
/// 17 | std::mem::forget(seven);
4949
/// | ^^^^^^^^^^^^^^^^^^^^^^^
5050
/// ```
51-
pub fn span_lint<'a, T: LintContext<'a>>(cx: &T, lint: &'static Lint, sp: Span, msg: &str) {
51+
pub fn span_lint<'a, T: LintContext<'a>>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
5252
DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg)).docs_link(lint);
5353
}
5454

tests/ui/non_expressive_names.rs

+39
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,45 @@ fn bla() {
4949
}
5050
}
5151

52+
fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
53+
54+
fn bindings2() {
55+
let (a, b, c, d, e, f, g, h) = unimplemented!();
56+
}
57+
58+
fn shadowing() {
59+
let a = 0i32;
60+
let a = 0i32;
61+
let a = 0i32;
62+
let a = 0i32;
63+
let a = 0i32;
64+
let a = 0i32;
65+
{
66+
let a = 0i32;
67+
}
68+
}
69+
70+
fn patterns() {
71+
enum Z {
72+
A(i32),
73+
B(i32),
74+
C(i32),
75+
D(i32),
76+
E(i32),
77+
F(i32),
78+
}
79+
80+
// These should not trigger a warning, since the pattern bindings are a new scope.
81+
match Z::A(0) {
82+
Z::A(a) => {},
83+
Z::B(b) => {},
84+
Z::C(c) => {},
85+
Z::D(d) => {},
86+
Z::E(e) => {},
87+
Z::F(f) => {},
88+
}
89+
}
90+
5291
fn underscores_and_numbers() {
5392
let _1 = 1; //~ERROR Consider a more descriptive name
5493
let ____1 = 1; //~ERROR Consider a more descriptive name

tests/ui/non_expressive_names.stderr

+40-17
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,89 @@
1-
error: 5th binding whose name is just one char
2-
--> $DIR/non_expressive_names.rs:35:17
1+
error: 5 bindings with single-character names in scope
2+
--> $DIR/non_expressive_names.rs:27:9
33
|
4+
LL | let a: i32;
5+
| ^
6+
LL | let (b, c, d): (i32, i64, i16);
7+
| ^ ^ ^
8+
...
49
LL | let e: i32;
510
| ^
611
|
712
= note: `-D clippy::many-single-char-names` implied by `-D warnings`
813

9-
error: 5th binding whose name is just one char
10-
--> $DIR/non_expressive_names.rs:38:17
14+
error: 6 bindings with single-character names in scope
15+
--> $DIR/non_expressive_names.rs:27:9
1116
|
17+
LL | let a: i32;
18+
| ^
19+
LL | let (b, c, d): (i32, i64, i16);
20+
| ^ ^ ^
21+
...
1222
LL | let e: i32;
1323
| ^
14-
15-
error: 6th binding whose name is just one char
16-
--> $DIR/non_expressive_names.rs:39:17
17-
|
1824
LL | let f: i32;
1925
| ^
2026

21-
error: 5th binding whose name is just one char
22-
--> $DIR/non_expressive_names.rs:43:13
27+
error: 5 bindings with single-character names in scope
28+
--> $DIR/non_expressive_names.rs:27:9
2329
|
30+
LL | let a: i32;
31+
| ^
32+
LL | let (b, c, d): (i32, i64, i16);
33+
| ^ ^ ^
34+
...
2435
LL | e => panic!(),
2536
| ^
2637

38+
error: 8 bindings with single-character names in scope
39+
--> $DIR/non_expressive_names.rs:52:13
40+
|
41+
LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
42+
| ^ ^ ^ ^ ^ ^ ^ ^
43+
44+
error: 8 bindings with single-character names in scope
45+
--> $DIR/non_expressive_names.rs:55:10
46+
|
47+
LL | let (a, b, c, d, e, f, g, h) = unimplemented!();
48+
| ^ ^ ^ ^ ^ ^ ^ ^
49+
2750
error: consider choosing a more descriptive name
28-
--> $DIR/non_expressive_names.rs:53:9
51+
--> $DIR/non_expressive_names.rs:92:9
2952
|
3053
LL | let _1 = 1; //~ERROR Consider a more descriptive name
3154
| ^^
3255
|
3356
= note: `-D clippy::just-underscores-and-digits` implied by `-D warnings`
3457

3558
error: consider choosing a more descriptive name
36-
--> $DIR/non_expressive_names.rs:54:9
59+
--> $DIR/non_expressive_names.rs:93:9
3760
|
3861
LL | let ____1 = 1; //~ERROR Consider a more descriptive name
3962
| ^^^^^
4063

4164
error: consider choosing a more descriptive name
42-
--> $DIR/non_expressive_names.rs:55:9
65+
--> $DIR/non_expressive_names.rs:94:9
4366
|
4467
LL | let __1___2 = 12; //~ERROR Consider a more descriptive name
4568
| ^^^^^^^
4669

4770
error: consider choosing a more descriptive name
48-
--> $DIR/non_expressive_names.rs:75:13
71+
--> $DIR/non_expressive_names.rs:114:13
4972
|
5073
LL | let _1 = 1;
5174
| ^^
5275

5376
error: consider choosing a more descriptive name
54-
--> $DIR/non_expressive_names.rs:76:13
77+
--> $DIR/non_expressive_names.rs:115:13
5578
|
5679
LL | let ____1 = 1;
5780
| ^^^^^
5881

5982
error: consider choosing a more descriptive name
60-
--> $DIR/non_expressive_names.rs:77:13
83+
--> $DIR/non_expressive_names.rs:116:13
6184
|
6285
LL | let __1___2 = 12;
6386
| ^^^^^^^
6487

65-
error: aborting due to 10 previous errors
88+
error: aborting due to 11 previous errors
6689

tests/ui/non_expressive_names.stdout

Whitespace-only changes.

0 commit comments

Comments
 (0)