@@ -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,100 @@ 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
+
810
+ struct IdentFinder {
811
+ idents : Vec < Ident > ,
812
+ /// If a block references one of the bindings introduced by the let pattern,
813
+ /// we likely meant to use `if let`.
814
+ /// This is pre-expansion, so if we encounter
815
+ /// `let Some(x) = foo() { println!("{x}") }` we won't find it.
816
+ references_ident : bool = false,
817
+ /// If a block has a `return`, then we know with high certainty that it was
818
+ /// meant to be let-else.
819
+ has_return : bool = false,
820
+ }
821
+
822
+ impl < ' a > Visitor < ' a > for IdentFinder {
823
+ fn visit_ident ( & mut self , ident : & Ident ) {
824
+ for i in & self . idents {
825
+ if ident. name == i. name {
826
+ self . references_ident = true ;
827
+ }
828
+ }
829
+ }
830
+ fn visit_expr ( & mut self , node : & ' a Expr ) {
831
+ if let ExprKind :: Ret ( ..) = node. kind {
832
+ self . has_return = true ;
833
+ }
834
+ walk_expr ( self , node) ;
835
+ }
836
+ }
837
+
838
+ // Collect all bindings in pattern and see if they appear in the block. Likely meant
839
+ // to write `if let`. See if the block has a return. Likely meant to write
840
+ // `let else`.
841
+ let mut visitor = IdentFinder { idents, .. } ;
842
+ visitor. visit_block ( & block) ;
843
+
844
+ ( visitor. references_ident , visitor. has_return )
845
+ }
846
+ Err ( e) => {
847
+ e. cancel ( ) ;
848
+ self . restore_snapshot ( snapshot) ;
849
+ ( false , false )
850
+ }
851
+ } ;
852
+
853
+ let mut alternatively = "" ;
854
+ if if_let || !let_else {
855
+ alternatively = "alternatively, " ;
856
+ err. span_suggestion_verbose (
857
+ stmt_span. shrink_to_lo ( ) ,
858
+ "you might have meant to use `if let`" ,
859
+ "if " . to_string ( ) ,
860
+ if if_let {
861
+ Applicability :: MachineApplicable
862
+ } else {
863
+ Applicability :: MaybeIncorrect
864
+ } ,
865
+ ) ;
866
+ }
867
+ if let_else || !if_let {
868
+ err. span_suggestion_verbose (
869
+ block_span. shrink_to_lo ( ) ,
870
+ format ! ( "{alternatively}you might have meant to use `let else`" ) ,
871
+ "else " . to_string ( ) ,
872
+ if let_else {
873
+ Applicability :: MachineApplicable
874
+ } else {
875
+ Applicability :: MaybeIncorrect
876
+ } ,
877
+ ) ;
878
+ }
879
+ }
880
+
786
881
fn recover_missing_dot ( & mut self , err : & mut Diag < ' _ > ) {
787
882
let Some ( ( ident, _) ) = self . token . ident ( ) else {
788
883
return ;
@@ -977,6 +1072,7 @@ impl<'a> Parser<'a> {
977
1072
self . check_mistyped_turbofish_with_multiple_type_params ( e, expr) . map_err (
978
1073
|mut e| {
979
1074
self . recover_missing_dot ( & mut e) ;
1075
+ self . recover_missing_let_else ( & mut e, & local. pat , stmt. span ) ;
980
1076
e
981
1077
} ,
982
1078
) ?;
0 commit comments