@@ -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,70 @@ 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+ // 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+
786851 fn recover_missing_dot ( & mut self , err : & mut Diag < ' _ > ) {
787852 let Some ( ( ident, _) ) = self . token . ident ( ) else {
788853 return ;
@@ -977,6 +1042,7 @@ impl<'a> Parser<'a> {
9771042 self . check_mistyped_turbofish_with_multiple_type_params ( e, expr) . map_err (
9781043 |mut e| {
9791044 self . recover_missing_dot ( & mut e) ;
1045+ self . recover_missing_let_else ( & mut e, & local. pat , stmt. span ) ;
9801046 e
9811047 } ,
9821048 ) ?;
@@ -1065,3 +1131,30 @@ impl<'a> Parser<'a> {
10651131 self . mk_block ( thin_vec ! [ self . mk_stmt_err( span, guar) ] , BlockCheckMode :: Default , span)
10661132 }
10671133}
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