@@ -661,8 +661,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
661
661
ResolutionError :: VariableNotBoundInPattern ( binding_error, parent_scope) => {
662
662
let BindingError { name, target, origin, could_be_path } = binding_error;
663
663
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 ( ) ;
666
670
667
671
let msp = MultiSpan :: from_spans ( target_sp. clone ( ) ) ;
668
672
let mut err = self
@@ -671,8 +675,29 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
671
675
for sp in target_sp {
672
676
err. subdiagnostic ( errors:: PatternDoesntBindName { span : sp, name } ) ;
673
677
}
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 ;
676
701
}
677
702
if could_be_path {
678
703
let import_suggestions = self . lookup_import_candidates (
@@ -693,7 +718,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
693
718
} ,
694
719
) ;
695
720
696
- if import_suggestions. is_empty ( ) {
721
+ if import_suggestions. is_empty ( ) && !suggested_typo {
697
722
let help_msg = format ! (
698
723
"if you meant to match on a variant or a `const` item, consider \
699
724
making the path in the pattern qualified: `path::to::ModOrType::{name}`",
@@ -3395,7 +3420,7 @@ impl UsePlacementFinder {
3395
3420
}
3396
3421
}
3397
3422
3398
- impl < ' tcx > visit :: Visitor < ' tcx > for UsePlacementFinder {
3423
+ impl < ' tcx > Visitor < ' tcx > for UsePlacementFinder {
3399
3424
fn visit_crate ( & mut self , c : & Crate ) {
3400
3425
if self . target_module == CRATE_NODE_ID {
3401
3426
let inject = c. spans . inject_use_span ;
@@ -3423,6 +3448,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
3423
3448
}
3424
3449
}
3425
3450
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
+
3426
3467
fn search_for_any_use_in_items ( items : & [ Box < ast:: Item > ] ) -> Option < Span > {
3427
3468
for item in items {
3428
3469
if let ItemKind :: Use ( ..) = item. kind
0 commit comments