Skip to content

Commit 429a7e7

Browse files
authored
Rollup merge of rust-lang#67849 - cjkenn:check-sorted-words, r=estebank
Add a check for swapped words when we can't find an identifier Fixes rust-lang#66968 Couple things here: 1. The matches take the precedence of case insensitive match, then levenshtein match, then swapped words match. Doing this allows us to not even check for swapped words unless the other checks return `None`. 2. I've assumed that the swapped words check is not held to the limits of the max levenshtein distance threshold (ie. we want to try and find a match even if the levenshtein distance is very high). This means that we cannot perform this check in the `fold` that occurs after the `filter_map` call, because the candidate will be filtered out. So, I've split this into two separate `fold` calls, and had to collect the original iterator into a vec so it can be copied (I don't think we want to change the function signature to take a vec or require the `Copy` trait). An alternative implemenation may be to remove the `filter_map`, `fold` over the entire iterator, and do a check against `max_dist` inside the relevant cases there. r? @estebank
2 parents 256f401 + e01e8b9 commit 429a7e7

File tree

4 files changed

+47
-5
lines changed

4 files changed

+47
-5
lines changed

src/libsyntax/util/lev_distance.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,16 @@ where
5454
T: Iterator<Item = &'a Symbol>,
5555
{
5656
let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d);
57+
let name_vec: Vec<&Symbol> = iter_names.collect();
5758

58-
let (case_insensitive_match, levenstein_match) = iter_names
59+
let (case_insensitive_match, levenshtein_match) = name_vec
60+
.iter()
5961
.filter_map(|&name| {
6062
let dist = lev_distance(lookup, &name.as_str());
6163
if dist <= max_dist { Some((name, dist)) } else { None }
6264
})
6365
// Here we are collecting the next structure:
64-
// (case_insensitive_match, (levenstein_match, levenstein_distance))
66+
// (case_insensitive_match, (levenshtein_match, levenshtein_distance))
6567
.fold((None, None), |result, (candidate, dist)| {
6668
(
6769
if candidate.as_str().to_uppercase() == lookup.to_uppercase() {
@@ -75,10 +77,31 @@ where
7577
},
7678
)
7779
});
78-
80+
// Priority of matches:
81+
// 1. Exact case insensitive match
82+
// 2. Levenshtein distance match
83+
// 3. Sorted word match
7984
if let Some(candidate) = case_insensitive_match {
80-
Some(candidate) // exact case insensitive match has a higher priority
85+
Some(*candidate)
86+
} else if levenshtein_match.is_some() {
87+
levenshtein_match.map(|(candidate, _)| *candidate)
8188
} else {
82-
levenstein_match.map(|(candidate, _)| candidate)
89+
find_match_by_sorted_words(name_vec, lookup)
8390
}
8491
}
92+
93+
fn find_match_by_sorted_words<'a>(iter_names: Vec<&'a Symbol>, lookup: &str) -> Option<Symbol> {
94+
iter_names.iter().fold(None, |result, candidate| {
95+
if sort_by_words(&candidate.as_str()) == sort_by_words(lookup) {
96+
Some(**candidate)
97+
} else {
98+
result
99+
}
100+
})
101+
}
102+
103+
fn sort_by_words(name: &str) -> String {
104+
let mut split_words: Vec<&str> = name.split('_').collect();
105+
split_words.sort();
106+
split_words.join("_")
107+
}

src/libsyntax/util/lev_distance/tests.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,11 @@ fn test_find_best_match_for_name() {
4646
find_best_match_for_name(input.iter(), "aaaa", Some(4)),
4747
Some(Symbol::intern("AAAA"))
4848
);
49+
50+
let input = vec![Symbol::intern("a_longer_variable_name")];
51+
assert_eq!(
52+
find_best_match_for_name(input.iter(), "a_variable_longer_name", None),
53+
Some(Symbol::intern("a_longer_variable_name"))
54+
);
4955
})
5056
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
let a_longer_variable_name = 1;
3+
println!("{}", a_variable_longer_name); //~ ERROR E0425
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0425]: cannot find value `a_variable_longer_name` in this scope
2+
--> $DIR/issue-66968-suggest-sorted-words.rs:3:20
3+
|
4+
LL | println!("{}", a_variable_longer_name);
5+
| ^^^^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `a_longer_variable_name`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)