diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2025-08-24 19:22:51 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2025-08-25 15:16:25 +0000 |
| commit | 8dbdb1760b23112f87aedad37e4dad97559bc750 (patch) | |
| tree | dd2cd17a9704e56eac5416f6c5c9883ebd6deaf5 /compiler/rustc_resolve/src/diagnostics.rs | |
| parent | 41a79f1862aa6b81bac674598e275e80e9f09eb9 (diff) | |
| download | rust-8dbdb1760b23112f87aedad37e4dad97559bc750.tar.gz rust-8dbdb1760b23112f87aedad37e4dad97559bc750.zip | |
On binding not present in all patterns, suggest potential typo
```
error[E0408]: variable `Ban` is not bound in all patterns
--> f12.rs:9:9
|
9 | (Foo,Bar)|(Ban,Foo) => {}
| ^^^^^^^^^ --- variable not in all patterns
| |
| pattern doesn't bind `Ban`
|
help: you might have meant to use the similarly named previously used binding `Bar`
|
9 - (Foo,Bar)|(Ban,Foo) => {}
9 + (Foo,Bar)|(Bar,Foo) => {}
|
```
Diffstat (limited to 'compiler/rustc_resolve/src/diagnostics.rs')
| -rw-r--r-- | compiler/rustc_resolve/src/diagnostics.rs | 53 |
1 files changed, 47 insertions, 6 deletions
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 337e7d2dd86..324310ff48b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -661,8 +661,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { let BindingError { name, target, origin, could_be_path } = binding_error; - let target_sp = target.iter().copied().collect::<Vec<_>>(); - let origin_sp = origin.iter().copied().collect::<Vec<_>>(); + let mut target_sp = target.iter().map(|pat| pat.span).collect::<Vec<_>>(); + target_sp.sort(); + target_sp.dedup(); + let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::<Vec<_>>(); + origin_sp.sort(); + origin_sp.dedup(); let msp = MultiSpan::from_spans(target_sp.clone()); let mut err = self @@ -671,8 +675,29 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for sp in target_sp { err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name }); } - for sp in origin_sp { - err.subdiagnostic(errors::VariableNotInAllPatterns { span: sp }); + for sp in &origin_sp { + err.subdiagnostic(errors::VariableNotInAllPatterns { span: *sp }); + } + let mut target_visitor = BindingVisitor::default(); + for pat in &target { + target_visitor.visit_pat(pat); + } + target_visitor.identifiers.sort(); + target_visitor.identifiers.dedup(); + let mut origin_visitor = BindingVisitor::default(); + for (_, pat) in &origin { + origin_visitor.visit_pat(pat); + } + origin_visitor.identifiers.sort(); + origin_visitor.identifiers.dedup(); + // Find if the binding could have been a typo + let mut suggested_typo = false; + if let Some(typo) = + find_best_match_for_name(&target_visitor.identifiers, name.name, None) + && !origin_visitor.identifiers.contains(&typo) + { + err.subdiagnostic(errors::PatternBindingTypo { spans: origin_sp, typo }); + suggested_typo = true; } if could_be_path { let import_suggestions = self.lookup_import_candidates( @@ -693,7 +718,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, ); - if import_suggestions.is_empty() { + if import_suggestions.is_empty() && !suggested_typo { let help_msg = format!( "if you meant to match on a variant or a `const` item, consider \ making the path in the pattern qualified: `path::to::ModOrType::{name}`", @@ -3395,7 +3420,7 @@ impl UsePlacementFinder { } } -impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { +impl<'tcx> Visitor<'tcx> for UsePlacementFinder { fn visit_crate(&mut self, c: &Crate) { if self.target_module == CRATE_NODE_ID { let inject = c.spans.inject_use_span; @@ -3423,6 +3448,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { } } +#[derive(Default)] +struct BindingVisitor { + identifiers: Vec<Symbol>, + spans: FxHashMap<Symbol, Vec<Span>>, +} + +impl<'tcx> Visitor<'tcx> for BindingVisitor { + fn visit_pat(&mut self, pat: &ast::Pat) { + if let ast::PatKind::Ident(_, ident, _) = pat.kind { + self.identifiers.push(ident.name); + self.spans.entry(ident.name).or_default().push(ident.span); + } + visit::walk_pat(self, pat); + } +} + fn search_for_any_use_in_items(items: &[Box<ast::Item>]) -> Option<Span> { for item in items { if let ItemKind::Use(..) = item.kind |
