@@ -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,29 @@ 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+ target_visitor. identifiers . sort ( ) ;
686+ target_visitor. identifiers . dedup ( ) ;
687+ let mut origin_visitor = BindingVisitor :: default ( ) ;
688+ for ( _, pat) in & origin {
689+ origin_visitor. visit_pat ( pat) ;
690+ }
691+ origin_visitor. identifiers . sort ( ) ;
692+ origin_visitor. identifiers . dedup ( ) ;
693+ // Find if the binding could have been a typo
694+ let mut suggested_typo = false ;
695+ if let Some ( typo) =
696+ find_best_match_for_name ( & target_visitor. identifiers , name. name , None )
697+ && !origin_visitor. identifiers . contains ( & typo)
698+ {
699+ err. subdiagnostic ( errors:: PatternBindingTypo { spans : origin_sp, typo } ) ;
700+ suggested_typo = true ;
676701 }
677702 if could_be_path {
678703 let import_suggestions = self . lookup_import_candidates (
@@ -693,7 +718,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
693718 } ,
694719 ) ;
695720
696- if import_suggestions. is_empty ( ) {
721+ if import_suggestions. is_empty ( ) && !suggested_typo {
697722 let help_msg = format ! (
698723 "if you meant to match on a variant or a `const` item, consider \
699724 making the path in the pattern qualified: `path::to::ModOrType::{name}`",
@@ -3395,7 +3420,7 @@ impl UsePlacementFinder {
33953420 }
33963421}
33973422
3398- impl < ' tcx > visit :: Visitor < ' tcx > for UsePlacementFinder {
3423+ impl < ' tcx > Visitor < ' tcx > for UsePlacementFinder {
33993424 fn visit_crate ( & mut self , c : & Crate ) {
34003425 if self . target_module == CRATE_NODE_ID {
34013426 let inject = c. spans . inject_use_span ;
@@ -3423,6 +3448,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
34233448 }
34243449}
34253450
3451+ #[ derive( Default ) ]
3452+ struct BindingVisitor {
3453+ identifiers : Vec < Symbol > ,
3454+ spans : FxHashMap < Symbol , Vec < Span > > ,
3455+ }
3456+
3457+ impl < ' tcx > Visitor < ' tcx > for BindingVisitor {
3458+ fn visit_pat ( & mut self , pat : & ast:: Pat ) {
3459+ if let ast:: PatKind :: Ident ( _, ident, _) = pat. kind {
3460+ self . identifiers . push ( ident. name ) ;
3461+ self . spans . entry ( ident. name ) . or_default ( ) . push ( ident. span ) ;
3462+ }
3463+ visit:: walk_pat ( self , pat) ;
3464+ }
3465+ }
3466+
34263467fn search_for_any_use_in_items ( items : & [ Box < ast:: Item > ] ) -> Option < Span > {
34273468 for item in items {
34283469 if let ItemKind :: Use ( ..) = item. kind
0 commit comments