@@ -661,8 +661,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
661661 ResolutionError :: VariableNotBoundInPattern ( binding_error, parent_scope) => {
662662 let BindingError { name, target, origin, could_be_path } = binding_error;
663663
664- let target_sp = target. iter ( ) . copied ( ) . collect :: < Vec < _ > > ( ) ;
665- let origin_sp = origin. iter ( ) . copied ( ) . collect :: < Vec < _ > > ( ) ;
664+ let mut target_sp = target. iter ( ) . map ( |pat| pat. span ) . collect :: < Vec < _ > > ( ) ;
665+ target_sp. sort ( ) ;
666+ target_sp. dedup ( ) ;
667+ let mut origin_sp = origin. iter ( ) . map ( |( span, _) | * span) . collect :: < Vec < _ > > ( ) ;
668+ origin_sp. sort ( ) ;
669+ origin_sp. dedup ( ) ;
666670
667671 let msp = MultiSpan :: from_spans ( target_sp. clone ( ) ) ;
668672 let mut err = self
@@ -671,8 +675,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
671675 for sp in target_sp {
672676 err. subdiagnostic ( errors:: PatternDoesntBindName { span : sp, name } ) ;
673677 }
674- for sp in origin_sp {
675- err. subdiagnostic ( errors:: VariableNotInAllPatterns { span : sp } ) ;
678+ for sp in & origin_sp {
679+ err. subdiagnostic ( errors:: VariableNotInAllPatterns { span : * sp } ) ;
680+ }
681+ let mut target_visitor = BindingVisitor :: default ( ) ;
682+ for pat in & target {
683+ target_visitor. visit_pat ( pat) ;
684+ }
685+ let mut origin_visitor = BindingVisitor :: default ( ) ;
686+ for ( _, pat) in & origin {
687+ origin_visitor. visit_pat ( pat) ;
688+ }
689+ // Find if the binding could have been a typo
690+ let mut suggested_typo = false ;
691+ if let Some ( typo) = find_best_match_for_name (
692+ #[ allow( rustc:: potential_query_instability) ]
693+ & target_visitor. identifiers . iter ( ) . map ( |name| * name) . collect :: < Vec < Symbol > > ( ) ,
694+ name. name ,
695+ None ,
696+ ) && !origin_visitor. identifiers . contains ( & typo)
697+ {
698+ err. subdiagnostic ( errors:: PatternBindingTypo { spans : origin_sp, typo } ) ;
699+ suggested_typo = true ;
676700 }
677701 if could_be_path {
678702 let import_suggestions = self . lookup_import_candidates (
@@ -693,7 +717,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
693717 } ,
694718 ) ;
695719
696- if import_suggestions. is_empty ( ) {
720+ if import_suggestions. is_empty ( ) && !suggested_typo {
697721 let help_msg = format ! (
698722 "if you meant to match on a variant or a `const` item, consider \
699723 making the path in the pattern qualified: `path::to::ModOrType::{name}`",
@@ -3395,7 +3419,7 @@ impl UsePlacementFinder {
33953419 }
33963420}
33973421
3398- impl < ' tcx > visit :: Visitor < ' tcx > for UsePlacementFinder {
3422+ impl < ' tcx > Visitor < ' tcx > for UsePlacementFinder {
33993423 fn visit_crate ( & mut self , c : & Crate ) {
34003424 if self . target_module == CRATE_NODE_ID {
34013425 let inject = c. spans . inject_use_span ;
@@ -3423,6 +3447,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
34233447 }
34243448}
34253449
3450+ #[ derive( Default ) ]
3451+ struct BindingVisitor {
3452+ identifiers : FxHashSet < Symbol > ,
3453+ spans : FxHashMap < Symbol , Vec < Span > > ,
3454+ }
3455+
3456+ impl < ' tcx > Visitor < ' tcx > for BindingVisitor {
3457+ fn visit_pat ( & mut self , pat : & ast:: Pat ) {
3458+ if let ast:: PatKind :: Ident ( _, ident, _) = pat. kind {
3459+ self . identifiers . insert ( ident. name ) ;
3460+ self . spans . entry ( ident. name ) . or_default ( ) . push ( ident. span ) ;
3461+ }
3462+ visit:: walk_pat ( self , pat) ;
3463+ }
3464+ }
3465+
34263466fn search_for_any_use_in_items ( items : & [ Box < ast:: Item > ] ) -> Option < Span > {
34273467 for item in items {
34283468 if let ItemKind :: Use ( ..) = item. kind
0 commit comments