@@ -6,6 +6,7 @@ use ast::Label;
66use rustc_ast as ast;
77use rustc_ast:: token:: { self , Delimiter , InvisibleOrigin , MetaVarKind , TokenKind } ;
88use rustc_ast:: util:: classify:: { self , TrailingBrace } ;
9+ use rustc_ast:: visit:: { Visitor , walk_expr} ;
910use rustc_ast:: {
1011 AttrStyle , AttrVec , Block , BlockCheckMode , DUMMY_NODE_ID , Expr , ExprKind , HasAttrs , Local ,
1112 LocalKind , MacCall , MacCallStmt , MacStmtStyle , Recovered , Stmt , StmtKind ,
@@ -783,6 +784,71 @@ impl<'a> Parser<'a> {
783784 Ok ( self . mk_block ( stmts, s, lo. to ( self . prev_token . span ) ) )
784785 }
785786
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+
786852 fn recover_missing_dot ( & mut self , err : & mut Diag < ' _ > ) {
787853 let Some ( ( ident, _) ) = self . token . ident ( ) else {
788854 return ;
@@ -977,6 +1043,7 @@ impl<'a> Parser<'a> {
9771043 self . check_mistyped_turbofish_with_multiple_type_params ( e, expr) . map_err (
9781044 |mut e| {
9791045 self . recover_missing_dot ( & mut e) ;
1046+ self . recover_missing_let_else ( & mut e, & local. pat , stmt. span ) ;
9801047 e
9811048 } ,
9821049 ) ?;
@@ -1065,3 +1132,30 @@ impl<'a> Parser<'a> {
10651132 self . mk_block ( thin_vec ! [ self . mk_stmt_err( span, guar) ] , BlockCheckMode :: Default , span)
10661133 }
10671134}
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