Skip to content

Commit 1295c6a

Browse files
committed
Extend is_case_difference to handle digit-letter confusables
Signed-off-by: xizheyin <[email protected]>
1 parent cb6785f commit 1295c6a

File tree

3 files changed

+123
-10
lines changed

3 files changed

+123
-10
lines changed

compiler/rustc_errors/src/emitter.rs

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3519,23 +3519,68 @@ pub fn is_different(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
35193519

35203520
/// Whether the original and suggested code are visually similar enough to warrant extra wording.
35213521
pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
3522-
// FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode.
35233522
let found = match sm.span_to_snippet(sp) {
35243523
Ok(snippet) => snippet,
35253524
Err(e) => {
35263525
warn!(error = ?e, "Invalid span {:?}", sp);
35273526
return false;
35283527
}
35293528
};
3530-
let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
3531-
// All the chars that differ in capitalization are confusable (above):
3532-
let confusable = iter::zip(found.chars(), suggested.chars())
3533-
.filter(|(f, s)| f != s)
3534-
.all(|(f, s)| ascii_confusables.contains(&f) || ascii_confusables.contains(&s));
3535-
confusable && found.to_lowercase() == suggested.to_lowercase()
3536-
// FIXME: We sometimes suggest the same thing we already have, which is a
3537-
// bug, but be defensive against that here.
3538-
&& found != suggested
3529+
3530+
// Check if the strings are identical after case normalization
3531+
if found.to_lowercase() == suggested.to_lowercase() {
3532+
// Check if they differ only in case
3533+
let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
3534+
let confusable = iter::zip(found.chars(), suggested.chars())
3535+
.filter(|(f, s)| f != s)
3536+
.all(|(f, s)| ascii_confusables.contains(&f) || ascii_confusables.contains(&s));
3537+
3538+
if confusable
3539+
// FIXME: We sometimes suggest the same thing we already have, which is a
3540+
// bug, but be defensive against that here.
3541+
&& found != suggested
3542+
{
3543+
return true;
3544+
}
3545+
}
3546+
3547+
// Check for digit-letter confusables (like 0 vs O, 1 vs l, etc.)
3548+
if found.len() == suggested.len() {
3549+
let digit_letter_confusables = [
3550+
('0', 'O'),
3551+
('O', '0'),
3552+
('1', 'l'),
3553+
('l', '1'),
3554+
('5', 'S'),
3555+
('S', '5'),
3556+
('8', 'B'),
3557+
('B', '8'),
3558+
('9', 'g'),
3559+
('g', '9'),
3560+
];
3561+
3562+
let mut has_confusable = false;
3563+
// To ensure all the differences are confusables,
3564+
// we need to check that all the differences
3565+
let mut all_confusable = true;
3566+
3567+
for (f, s) in iter::zip(found.chars(), suggested.chars()) {
3568+
if f != s {
3569+
if digit_letter_confusables.contains(&(f, s)) {
3570+
has_confusable = true;
3571+
} else {
3572+
all_confusable = false;
3573+
break;
3574+
}
3575+
}
3576+
}
3577+
3578+
if has_confusable && all_confusable && found != suggested {
3579+
return true;
3580+
}
3581+
}
3582+
3583+
false
35393584
}
35403585

35413586
pub(crate) fn should_show_source_code(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
fn main() {
2+
// Test case difference suggestions
3+
{
4+
let FOO = 42;
5+
// Test 1: Case difference (should show "notice the capitalization")
6+
println!("{}", Foo); //~ ERROR cannot find value `Foo` in this scope
7+
8+
}
9+
10+
// Test 2: Digit-letter confusion (0 vs O)
11+
let FO0 = 100;
12+
println!("{}", FOO); //~ ERROR cannot find value `FOO` in this scope
13+
14+
// Test 3: Digit-letter confusion (1 vs l)
15+
let l1st = vec![1, 2, 3];
16+
println!("{}", list); //~ ERROR cannot find value `list` in this scope
17+
18+
// Test 4: Digit-letter confusion (5 vs S)
19+
let S5 = "test";
20+
println!("{}", SS); //~ ERROR cannot find value `SS` in this scope
21+
22+
// Test 5: Digit-letter confusion (8 vs B)
23+
let B8 = 8;
24+
println!("{}", BB); //~ ERROR cannot find value `BB` in this scope
25+
26+
// Test 6: Digit-letter confusion (9 vs g)
27+
let g9 = 9;
28+
println!("{}", gg); //~ ERROR cannot find value `gg` in this scope
29+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0425]: cannot find value `Foo` in this scope
2+
--> $DIR/case-difference-suggestions.rs:6:24
3+
|
4+
LL | println!("{}", Foo);
5+
| ^^^ help: a local variable with a similar name exists (notice the capitalization): `FOO`
6+
7+
error[E0425]: cannot find value `FOO` in this scope
8+
--> $DIR/case-difference-suggestions.rs:12:20
9+
|
10+
LL | println!("{}", FOO);
11+
| ^^^ help: a local variable with a similar name exists (notice the capitalization): `FO0`
12+
13+
error[E0425]: cannot find value `list` in this scope
14+
--> $DIR/case-difference-suggestions.rs:16:20
15+
|
16+
LL | println!("{}", list);
17+
| ^^^^ help: a local variable with a similar name exists: `l1st`
18+
19+
error[E0425]: cannot find value `SS` in this scope
20+
--> $DIR/case-difference-suggestions.rs:20:20
21+
|
22+
LL | println!("{}", SS);
23+
| ^^ help: a local variable with a similar name exists (notice the capitalization): `S5`
24+
25+
error[E0425]: cannot find value `BB` in this scope
26+
--> $DIR/case-difference-suggestions.rs:24:20
27+
|
28+
LL | println!("{}", BB);
29+
| ^^ help: a local variable with a similar name exists (notice the capitalization): `B8`
30+
31+
error[E0425]: cannot find value `gg` in this scope
32+
--> $DIR/case-difference-suggestions.rs:28:20
33+
|
34+
LL | println!("{}", gg);
35+
| ^^ help: a local variable with a similar name exists (notice the capitalization): `g9`
36+
37+
error: aborting due to 6 previous errors
38+
39+
For more information about this error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)