@@ -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,70 @@ 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
+ // FIXME: collect all bindings in pattern and see if they appear in the block. if let
810
+ // FIXME: see if the block has a return. let else
811
+ let mut visitor = IdentFinder { idents, .. } ;
812
+ visitor. visit_block ( & block) ;
813
+
814
+ ( visitor. references_ident , visitor. has_return )
815
+ }
816
+ Err ( e) => {
817
+ e. cancel ( ) ;
818
+ self . restore_snapshot ( snapshot) ;
819
+ ( false , false )
820
+ }
821
+ } ;
822
+
823
+ let mut alternatively = "" ;
824
+ if if_let || !let_else {
825
+ alternatively = "alternatively, " ;
826
+ err. span_suggestion_verbose (
827
+ stmt_span. shrink_to_lo ( ) ,
828
+ "you might have meant to use `if let`" ,
829
+ "if " . to_string ( ) ,
830
+ if if_let {
831
+ Applicability :: MachineApplicable
832
+ } else {
833
+ Applicability :: MaybeIncorrect
834
+ } ,
835
+ ) ;
836
+ }
837
+ if let_else || !if_let {
838
+ err. span_suggestion_verbose (
839
+ block_span. shrink_to_lo ( ) ,
840
+ format ! ( "{alternatively}you might have meant to use `let else`" ) ,
841
+ "else " . to_string ( ) ,
842
+ if let_else {
843
+ Applicability :: MachineApplicable
844
+ } else {
845
+ Applicability :: MaybeIncorrect
846
+ } ,
847
+ ) ;
848
+ }
849
+ }
850
+
786
851
fn recover_missing_dot ( & mut self , err : & mut Diag < ' _ > ) {
787
852
let Some ( ( ident, _) ) = self . token . ident ( ) else {
788
853
return ;
@@ -977,6 +1042,7 @@ impl<'a> Parser<'a> {
977
1042
self . check_mistyped_turbofish_with_multiple_type_params ( e, expr) . map_err (
978
1043
|mut e| {
979
1044
self . recover_missing_dot ( & mut e) ;
1045
+ self . recover_missing_let_else ( & mut e, & local. pat , stmt. span ) ;
980
1046
e
981
1047
} ,
982
1048
) ?;
@@ -1065,3 +1131,30 @@ impl<'a> Parser<'a> {
1065
1131
self . mk_block ( thin_vec ! [ self . mk_stmt_err( span, guar) ] , BlockCheckMode :: Default , span)
1066
1132
}
1067
1133
}
1134
+
1135
+ struct IdentFinder {
1136
+ idents : Vec < Ident > ,
1137
+ /// If a block references one of the bindings introduced by the let pattern, we likely meant to
1138
+ /// use `if let`.
1139
+ /// This is pre-expansion, so if we encounter `let Some(x) = foo() { println!("{x}") }` we won't
1140
+ /// find it.
1141
+ references_ident : bool = false,
1142
+ /// If a block has a `return`, then we know with high certainty that the
1143
+ has_return : bool = false,
1144
+ }
1145
+
1146
+ impl < ' a > Visitor < ' a > for IdentFinder {
1147
+ fn visit_ident ( & mut self , ident : & Ident ) {
1148
+ for i in & self . idents {
1149
+ if ident. name == i. name {
1150
+ self . references_ident = true ;
1151
+ }
1152
+ }
1153
+ }
1154
+ fn visit_expr ( & mut self , node : & ' a Expr ) {
1155
+ if let ExprKind :: Ret ( ..) = node. kind {
1156
+ self . has_return = true ;
1157
+ }
1158
+ walk_expr ( self , node) ;
1159
+ }
1160
+ }
0 commit comments