@@ -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,28 @@ 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
+ 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 ;
676
700
}
677
701
if could_be_path {
678
702
let import_suggestions = self . lookup_import_candidates (
@@ -693,7 +717,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
693
717
} ,
694
718
) ;
695
719
696
- if import_suggestions. is_empty ( ) {
720
+ if import_suggestions. is_empty ( ) && !suggested_typo {
697
721
let help_msg = format ! (
698
722
"if you meant to match on a variant or a `const` item, consider \
699
723
making the path in the pattern qualified: `path::to::ModOrType::{name}`",
@@ -3395,7 +3419,7 @@ impl UsePlacementFinder {
3395
3419
}
3396
3420
}
3397
3421
3398
- impl < ' tcx > visit :: Visitor < ' tcx > for UsePlacementFinder {
3422
+ impl < ' tcx > Visitor < ' tcx > for UsePlacementFinder {
3399
3423
fn visit_crate ( & mut self , c : & Crate ) {
3400
3424
if self . target_module == CRATE_NODE_ID {
3401
3425
let inject = c. spans . inject_use_span ;
@@ -3423,6 +3447,22 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
3423
3447
}
3424
3448
}
3425
3449
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
+
3426
3466
fn search_for_any_use_in_items ( items : & [ Box < ast:: Item > ] ) -> Option < Span > {
3427
3467
for item in items {
3428
3468
if let ItemKind :: Use ( ..) = item. kind
0 commit comments