Skip to content

Commit 226ce31

Browse files
authored
Rollup merge of rust-lang#108200 - jhpratt:restricted-damerau-levenshtein-distance, r=tmiasko
Use restricted Damerau-Levenshtein distance for diagnostics This replaces the existing Levenshtein algorithm with the Damerau-Levenshtein algorithm. This means that "ab" to "ba" is one change (a transposition) instead of two (a deletion and insertion). More specifically, this is a _restricted_ implementation, in that "ca" to "abc" cannot be performed as "ca" → "ac" → "abc", as there is an insertion in the middle of a transposition. I believe that errors like that are sufficiently rare that it's not worth taking into account. This was first brought up [on IRLO](https://internals.rust-lang.org/t/18227) when it was noticed that the diagnostic for `prinltn!` (transposed L and T) was `print!` and not `println!`. Only a single existing UI test was effected, with the result being an objective improvement. ~~I have left the method name and various other references to the Levenshtein algorithm untouched, as the exact manner in which the edit distance is calculated should not be relevant to the caller.~~ r? ``@estebank`` ``@rustbot`` label +A-diagnostics +C-enhancement
2 parents 1f5c2b1 + ab4c0dd commit 226ce31

File tree

21 files changed

+357
-271
lines changed

21 files changed

+357
-271
lines changed

compiler/rustc_ast_lowering/src/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
1313
use rustc_hir::PredicateOrigin;
1414
use rustc_index::vec::{Idx, IndexVec};
1515
use rustc_middle::ty::{DefIdTree, ResolverAstLowering, TyCtxt};
16-
use rustc_span::lev_distance::find_best_match_for_name;
16+
use rustc_span::edit_distance::find_best_match_for_name;
1717
use rustc_span::source_map::DesugaringKind;
1818
use rustc_span::symbol::{kw, sym, Ident};
1919
use rustc_span::{Span, Symbol};

compiler/rustc_hir_analysis/src/astconv/errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId;
77
use rustc_infer::traits::FulfillmentError;
88
use rustc_middle::ty::{self, Ty};
99
use rustc_session::parse::feature_err;
10-
use rustc_span::lev_distance::find_best_match_for_name;
10+
use rustc_span::edit_distance::find_best_match_for_name;
1111
use rustc_span::symbol::{sym, Ident};
1212
use rustc_span::{Span, Symbol, DUMMY_SP};
1313

compiler/rustc_hir_analysis/src/astconv/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ use rustc_middle::ty::DynKind;
3737
use rustc_middle::ty::GenericParamDefKind;
3838
use rustc_middle::ty::{self, Const, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeVisitable};
3939
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
40+
use rustc_span::edit_distance::find_best_match_for_name;
4041
use rustc_span::edition::Edition;
41-
use rustc_span::lev_distance::find_best_match_for_name;
4242
use rustc_span::symbol::{kw, Ident, Symbol};
4343
use rustc_span::{sym, Span, DUMMY_SP};
4444
use rustc_target::spec::abi;

compiler/rustc_hir_typeck/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ use rustc_middle::ty::subst::SubstsRef;
4545
use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
4646
use rustc_session::errors::ExprParenthesesNeeded;
4747
use rustc_session::parse::feature_err;
48+
use rustc_span::edit_distance::find_best_match_for_name;
4849
use rustc_span::hygiene::DesugaringKind;
49-
use rustc_span::lev_distance::find_best_match_for_name;
5050
use rustc_span::source_map::{Span, Spanned};
5151
use rustc_span::symbol::{kw, sym, Ident, Symbol};
5252
use rustc_target::spec::abi::Abi::RustIntrinsic;

compiler/rustc_hir_typeck/src/method/probe.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use rustc_middle::ty::{InternalSubsts, SubstsRef};
2424
use rustc_session::lint;
2525
use rustc_span::def_id::DefId;
2626
use rustc_span::def_id::LocalDefId;
27-
use rustc_span::lev_distance::{
28-
find_best_match_for_name_with_substrings, lev_distance_with_substrings,
27+
use rustc_span::edit_distance::{
28+
edit_distance_with_substrings, find_best_match_for_name_with_substrings,
2929
};
3030
use rustc_span::symbol::sym;
3131
use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
@@ -69,7 +69,7 @@ struct ProbeContext<'a, 'tcx> {
6969
impl_dups: FxHashSet<DefId>,
7070

7171
/// When probing for names, include names that are close to the
72-
/// requested name (by Levenshtein distance)
72+
/// requested name (by edit distance)
7373
allow_similar_names: bool,
7474

7575
/// Some(candidate) if there is a private candidate
@@ -1793,7 +1793,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17931793

17941794
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
17951795
/// candidate method where the method name may have been misspelled. Similarly to other
1796-
/// Levenshtein based suggestions, we provide at most one such suggestion.
1796+
/// edit distance based suggestions, we provide at most one such suggestion.
17971797
fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
17981798
debug!("probing for method names similar to {:?}", self.method_name);
17991799

@@ -2024,8 +2024,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
20242024
if self.matches_by_doc_alias(x.def_id) {
20252025
return true;
20262026
}
2027-
match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
2028-
{
2027+
match edit_distance_with_substrings(
2028+
name.as_str(),
2029+
x.name.as_str(),
2030+
max_dist,
2031+
) {
20292032
Some(d) => d > 0,
20302033
None => false,
20312034
}

compiler/rustc_hir_typeck/src/method/suggest.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitabl
3131
use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
3232
use rustc_span::symbol::{kw, sym, Ident};
3333
use rustc_span::Symbol;
34-
use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
34+
use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span};
3535
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
3636
use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
3737
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1014,7 +1014,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10141014
// that had unsatisfied trait bounds
10151015
if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
10161016
let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
1017-
if let Some(suggestion) = lev_distance::find_best_match_for_name(
1017+
if let Some(suggestion) = edit_distance::find_best_match_for_name(
10181018
&adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
10191019
item_name.name,
10201020
None,

compiler/rustc_hir_typeck/src/pat.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
1414
use rustc_middle::middle::stability::EvalResult;
1515
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitable};
1616
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
17+
use rustc_span::edit_distance::find_best_match_for_name;
1718
use rustc_span::hygiene::DesugaringKind;
18-
use rustc_span::lev_distance::find_best_match_for_name;
1919
use rustc_span::source_map::{Span, Spanned};
2020
use rustc_span::symbol::{kw, sym, Ident};
2121
use rustc_span::{BytePos, DUMMY_SP};

compiler/rustc_interface/src/util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use rustc_session::filesearch::sysroot_candidates;
1414
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
1515
use rustc_session::parse::CrateConfig;
1616
use rustc_session::{early_error, filesearch, output, Session};
17+
use rustc_span::edit_distance::find_best_match_for_name;
1718
use rustc_span::edition::Edition;
18-
use rustc_span::lev_distance::find_best_match_for_name;
1919
use rustc_span::source_map::FileLoader;
2020
use rustc_span::symbol::{sym, Symbol};
2121
use session::CompilerIO;

compiler/rustc_lint/src/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools,
3939
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
4040
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
4141
use rustc_session::Session;
42-
use rustc_span::lev_distance::find_best_match_for_name;
42+
use rustc_span::edit_distance::find_best_match_for_name;
4343
use rustc_span::symbol::{sym, Ident, Symbol};
4444
use rustc_span::{BytePos, Span};
4545
use rustc_target::abi;

compiler/rustc_parse/src/parser/item.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use rustc_errors::{
1919
struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
2020
StashKey,
2121
};
22+
use rustc_span::edit_distance::edit_distance;
2223
use rustc_span::edition::Edition;
23-
use rustc_span::lev_distance::lev_distance;
2424
use rustc_span::source_map::{self, Span};
2525
use rustc_span::symbol::{kw, sym, Ident, Symbol};
2626
use rustc_span::DUMMY_SP;
@@ -459,7 +459,8 @@ impl<'a> Parser<'a> {
459459
// Maybe the user misspelled `macro_rules` (issue #91227)
460460
if self.token.is_ident()
461461
&& path.segments.len() == 1
462-
&& lev_distance("macro_rules", &path.segments[0].ident.to_string(), 3).is_some()
462+
&& edit_distance("macro_rules", &path.segments[0].ident.to_string(), 2)
463+
.is_some()
463464
{
464465
err.span_suggestion(
465466
path.span,

compiler/rustc_resolve/src/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE;
2121
use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS;
2222
use rustc_session::lint::BuiltinLintDiagnostics;
2323
use rustc_session::Session;
24+
use rustc_span::edit_distance::find_best_match_for_name;
2425
use rustc_span::edition::Edition;
2526
use rustc_span::hygiene::MacroKind;
26-
use rustc_span::lev_distance::find_best_match_for_name;
2727
use rustc_span::source_map::SourceMap;
2828
use rustc_span::symbol::{kw, sym, Ident, Symbol};
2929
use rustc_span::{BytePos, Span, SyntaxContext};

compiler/rustc_resolve/src/imports.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use rustc_middle::span_bug;
2121
use rustc_middle::ty;
2222
use rustc_session::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS};
2323
use rustc_session::lint::BuiltinLintDiagnostics;
24+
use rustc_span::edit_distance::find_best_match_for_name;
2425
use rustc_span::hygiene::LocalExpnId;
25-
use rustc_span::lev_distance::find_best_match_for_name;
2626
use rustc_span::symbol::{kw, Ident, Symbol};
2727
use rustc_span::Span;
2828

compiler/rustc_resolve/src/late/diagnostics.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ use rustc_middle::ty::DefIdTree;
2525
use rustc_session::lint;
2626
use rustc_session::parse::feature_err;
2727
use rustc_session::Session;
28+
use rustc_span::edit_distance::find_best_match_for_name;
2829
use rustc_span::edition::Edition;
2930
use rustc_span::hygiene::MacroKind;
30-
use rustc_span::lev_distance::find_best_match_for_name;
3131
use rustc_span::symbol::{kw, sym, Ident, Symbol};
3232
use rustc_span::{BytePos, Span};
3333

@@ -542,7 +542,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
542542
}
543543
}
544544

545-
// Try Levenshtein algorithm.
545+
// Try finding a suitable replacement.
546546
let typo_sugg =
547547
self.lookup_typo_candidate(path, source.namespace(), is_expected).to_opt_suggestion();
548548
if path.len() == 1 && self.self_type_is_available() {
@@ -770,7 +770,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
770770
_ => {}
771771
}
772772

773-
// If the trait has a single item (which wasn't matched by Levenshtein), suggest it
773+
// If the trait has a single item (which wasn't matched by the algorithm), suggest it
774774
let suggestion = self.get_single_associated_item(&path, &source, is_expected);
775775
if !self.r.add_typo_suggestion(err, suggestion, ident_span) {
776776
fallback = !self.let_binding_suggestion(err, ident_span);

0 commit comments

Comments
 (0)