@@ -6,6 +6,7 @@ use ast::Label;
6
6
use rustc_ast as ast;
7
7
use rustc_ast:: token:: { self , Delimiter , InvisibleOrigin , MetaVarKind , TokenKind } ;
8
8
use rustc_ast:: util:: classify:: { self , TrailingBrace } ;
9
+ use rustc_ast:: visit:: { Visitor , walk_expr} ;
9
10
use rustc_ast:: {
10
11
AttrStyle , AttrVec , Block , BlockCheckMode , DUMMY_NODE_ID , Expr , ExprKind , HasAttrs , Local ,
11
12
LocalKind , MacCall , MacCallStmt , MacStmtStyle , Recovered , Stmt , StmtKind ,
@@ -783,6 +784,71 @@ impl<'a> Parser<'a> {
783
784
Ok ( self . mk_block ( stmts, s, lo. to ( self . prev_token . span ) ) )
784
785
}
785
786
787
+ fn recover_missing_let_else ( & mut self , err : & mut Diag < ' _ > , pat : & ast:: Pat , stmt_span : Span ) {
788
+ if self . token . kind != token:: OpenBrace {
789
+ return ;
790
+ }
791
+ match pat. kind {
792
+ ast:: PatKind :: Ident ( ..) | ast:: PatKind :: Missing | ast:: PatKind :: Wild => {
793
+ // Not if let or let else
794
+ return ;
795
+ }
796
+ _ => { }
797
+ }
798
+ let snapshot = self . create_snapshot_for_diagnostic ( ) ;
799
+ let block_span = self . token . span ;
800
+ let ( if_let, let_else) = match self . parse_block ( ) {
801
+ Ok ( block) => {
802
+ let mut idents = vec ! [ ] ;
803
+ pat. walk ( & mut |pat : & ast:: Pat | {
804
+ if let ast:: PatKind :: Ident ( _, ident, _) = pat. kind {
805
+ idents. push ( ident) ;
806
+ }
807
+ true
808
+ } ) ;
809
+ // Collect all bindings in pattern and see if they appear in the block. Likely meant
810
+ // to write `if let`. See if the block has a return. Likely meant to write
811
+ // `let else`.
812
+ let mut visitor = IdentFinder { idents, .. } ;
813
+ visitor. visit_block ( & block) ;
814
+
815
+ ( visitor. references_ident , visitor. has_return )
816
+ }
817
+ Err ( e) => {
818
+ e. cancel ( ) ;
819
+ self . restore_snapshot ( snapshot) ;
820
+ ( false , false )
821
+ }
822
+ } ;
823
+
824
+ let mut alternatively = "" ;
825
+ if if_let || !let_else {
826
+ alternatively = "alternatively, " ;
827
+ err. span_suggestion_verbose (
828
+ stmt_span. shrink_to_lo ( ) ,
829
+ "you might have meant to use `if let`" ,
830
+ "if " . to_string ( ) ,
831
+ if if_let {
832
+ Applicability :: MachineApplicable
833
+ } else {
834
+ Applicability :: MaybeIncorrect
835
+ } ,
836
+ ) ;
837
+ }
838
+ if let_else || !if_let {
839
+ err. span_suggestion_verbose (
840
+ block_span. shrink_to_lo ( ) ,
841
+ format ! ( "{alternatively}you might have meant to use `let else`" ) ,
842
+ "else " . to_string ( ) ,
843
+ if let_else {
844
+ Applicability :: MachineApplicable
845
+ } else {
846
+ Applicability :: MaybeIncorrect
847
+ } ,
848
+ ) ;
849
+ }
850
+ }
851
+
786
852
fn recover_missing_dot ( & mut self , err : & mut Diag < ' _ > ) {
787
853
let Some ( ( ident, _) ) = self . token . ident ( ) else {
788
854
return ;
@@ -977,6 +1043,7 @@ impl<'a> Parser<'a> {
977
1043
self . check_mistyped_turbofish_with_multiple_type_params ( e, expr) . map_err (
978
1044
|mut e| {
979
1045
self . recover_missing_dot ( & mut e) ;
1046
+ self . recover_missing_let_else ( & mut e, & local. pat , stmt. span ) ;
980
1047
e
981
1048
} ,
982
1049
) ?;
@@ -1065,3 +1132,30 @@ impl<'a> Parser<'a> {
1065
1132
self . mk_block ( thin_vec ! [ self . mk_stmt_err( span, guar) ] , BlockCheckMode :: Default , span)
1066
1133
}
1067
1134
}
1135
+
1136
+ struct IdentFinder {
1137
+ idents : Vec < Ident > ,
1138
+ /// If a block references one of the bindings introduced by the let pattern, we likely meant to
1139
+ /// use `if let`.
1140
+ /// This is pre-expansion, so if we encounter `let Some(x) = foo() { println!("{x}") }` we won't
1141
+ /// find it.
1142
+ references_ident : bool = false,
1143
+ /// If a block has a `return`, then we know with high certainty that the
1144
+ has_return : bool = false,
1145
+ }
1146
+
1147
+ impl < ' a > Visitor < ' a > for IdentFinder {
1148
+ fn visit_ident ( & mut self , ident : & Ident ) {
1149
+ for i in & self . idents {
1150
+ if ident. name == i. name {
1151
+ self . references_ident = true ;
1152
+ }
1153
+ }
1154
+ }
1155
+ fn visit_expr ( & mut self , node : & ' a Expr ) {
1156
+ if let ExprKind :: Ret ( ..) = node. kind {
1157
+ self . has_return = true ;
1158
+ }
1159
+ walk_expr ( self , node) ;
1160
+ }
1161
+ }
0 commit comments