@@ -64,8 +64,8 @@ struct AstValidator<'a> {
6464 /// certain positions.
6565 is_assoc_ty_bound_banned : bool ,
6666
67- /// Used to allow `let` expressions in certain syntactic locations.
68- is_let_allowed : bool ,
67+ /// See [ForbiddenLetReason]
68+ forbidden_let_reason : Option < ForbiddenLetReason > ,
6969
7070 lint_buffer : & ' a mut LintBuffer ,
7171}
@@ -103,20 +103,28 @@ impl<'a> AstValidator<'a> {
103103 self . is_tilde_const_allowed = old;
104104 }
105105
106- fn with_let_allowed ( & mut self , allowed : bool , f : impl FnOnce ( & mut Self , bool ) ) {
107- let old = mem:: replace ( & mut self . is_let_allowed , allowed) ;
106+ fn with_let_management (
107+ & mut self ,
108+ forbidden_let_reason : Option < ForbiddenLetReason > ,
109+ f : impl FnOnce ( & mut Self , Option < ForbiddenLetReason > ) ,
110+ ) {
111+ let old = mem:: replace ( & mut self . forbidden_let_reason , forbidden_let_reason) ;
108112 f ( self , old) ;
109- self . is_let_allowed = old;
113+ self . forbidden_let_reason = old;
110114 }
111115
112116 /// Emits an error banning the `let` expression provided in the given location.
113- fn ban_let_expr ( & self , expr : & ' a Expr ) {
117+ fn ban_let_expr ( & self , expr : & ' a Expr , forbidden_let_reason : ForbiddenLetReason ) {
114118 let sess = & self . session ;
115119 if sess. opts . unstable_features . is_nightly_build ( ) {
116- sess. struct_span_err ( expr. span , "`let` expressions are not supported here" )
117- . note ( "only supported directly in conditions of `if`- and `while`-expressions" )
118- . note ( "as well as when nested within `&&` and parentheses in those conditions" )
119- . emit ( ) ;
120+ let err = "`let` expressions are not supported here" ;
121+ let mut diag = sess. struct_span_err ( expr. span , err) ;
122+ diag. note ( "only supported directly in conditions of `if` and `while` expressions" ) ;
123+ diag. note ( "as well as when nested within `&&` and parentheses in those conditions" ) ;
124+ if let ForbiddenLetReason :: ForbiddenWithOr ( span) = forbidden_let_reason {
125+ diag. span_note ( span, "`||` operators are not allowed in let chain expressions" ) ;
126+ }
127+ diag. emit ( ) ;
120128 } else {
121129 sess. struct_span_err ( expr. span , "expected expression, found statement (`let`)" )
122130 . note ( "variable declaration using `let` is a statement" )
@@ -988,39 +996,48 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
988996 }
989997
990998 fn visit_expr ( & mut self , expr : & ' a Expr ) {
991- self . with_let_allowed ( false , |this, let_allowed| match & expr. kind {
992- ExprKind :: If ( cond, then, opt_else) => {
993- this. visit_block ( then) ;
994- walk_list ! ( this, visit_expr, opt_else) ;
995- this. with_let_allowed ( true , |this, _| this. visit_expr ( cond) ) ;
996- return ;
997- }
998- ExprKind :: Let ( ..) if !let_allowed => this. ban_let_expr ( expr) ,
999- ExprKind :: Match ( expr, arms) => {
1000- this. visit_expr ( expr) ;
1001- for arm in arms {
1002- this. visit_expr ( & arm. body ) ;
1003- this. visit_pat ( & arm. pat ) ;
1004- walk_list ! ( this, visit_attribute, & arm. attrs) ;
1005- if let Some ( ref guard) = arm. guard {
1006- if let ExprKind :: Let ( _, ref expr, _) = guard. kind {
1007- this. with_let_allowed ( true , |this, _| this. visit_expr ( expr) ) ;
999+ self . with_let_management ( Some ( ForbiddenLetReason :: GenericForbidden ) , |this, forbidden_let_reason| {
1000+ match & expr. kind {
1001+ ExprKind :: Binary ( Spanned { node : BinOpKind :: Or , span } , lhs, rhs) => {
1002+ let forbidden_let_reason = Some ( ForbiddenLetReason :: ForbiddenWithOr ( * span) ) ;
1003+ this. with_let_management ( forbidden_let_reason, |this, _| this. visit_expr ( lhs) ) ;
1004+ this. with_let_management ( forbidden_let_reason, |this, _| this. visit_expr ( rhs) ) ;
1005+ }
1006+ ExprKind :: If ( cond, then, opt_else) => {
1007+ this. visit_block ( then) ;
1008+ walk_list ! ( this, visit_expr, opt_else) ;
1009+ this. with_let_management ( None , |this, _| this. visit_expr ( cond) ) ;
1010+ return ;
1011+ }
1012+ ExprKind :: Let ( ..) if let Some ( elem) = forbidden_let_reason => {
1013+ this. ban_let_expr ( expr, elem) ;
1014+ } ,
1015+ ExprKind :: Match ( scrutinee, arms) => {
1016+ this. visit_expr ( scrutinee) ;
1017+ for arm in arms {
1018+ this. visit_expr ( & arm. body ) ;
1019+ this. visit_pat ( & arm. pat ) ;
1020+ walk_list ! ( this, visit_attribute, & arm. attrs) ;
1021+ if let Some ( guard) = & arm. guard && let ExprKind :: Let ( _, guard_expr, _) = & guard. kind {
1022+ this. with_let_management ( None , |this, _| {
1023+ this. visit_expr ( guard_expr)
1024+ } ) ;
10081025 return ;
10091026 }
10101027 }
10111028 }
1029+ ExprKind :: Paren ( _) | ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , ..) => {
1030+ this. with_let_management ( forbidden_let_reason, |this, _| visit:: walk_expr ( this, expr) ) ;
1031+ return ;
1032+ }
1033+ ExprKind :: While ( cond, then, opt_label) => {
1034+ walk_list ! ( this, visit_label, opt_label) ;
1035+ this. visit_block ( then) ;
1036+ this. with_let_management ( None , |this, _| this. visit_expr ( cond) ) ;
1037+ return ;
1038+ }
1039+ _ => visit:: walk_expr ( this, expr) ,
10121040 }
1013- ExprKind :: Paren ( _) | ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , ..) => {
1014- this. with_let_allowed ( let_allowed, |this, _| visit:: walk_expr ( this, expr) ) ;
1015- return ;
1016- }
1017- ExprKind :: While ( cond, then, opt_label) => {
1018- walk_list ! ( this, visit_label, opt_label) ;
1019- this. visit_block ( then) ;
1020- this. with_let_allowed ( true , |this, _| this. visit_expr ( cond) ) ;
1021- return ;
1022- }
1023- _ => visit:: walk_expr ( this, expr) ,
10241041 } ) ;
10251042 }
10261043
@@ -1772,10 +1789,19 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
17721789 is_tilde_const_allowed : false ,
17731790 is_impl_trait_banned : false ,
17741791 is_assoc_ty_bound_banned : false ,
1775- is_let_allowed : false ,
1792+ forbidden_let_reason : Some ( ForbiddenLetReason :: GenericForbidden ) ,
17761793 lint_buffer : lints,
17771794 } ;
17781795 visit:: walk_crate ( & mut validator, krate) ;
17791796
17801797 validator. has_proc_macro_decls
17811798}
1799+
1800+ /// Used to forbid `let` expressions in certain syntactic locations.
1801+ #[ derive( Clone , Copy ) ]
1802+ enum ForbiddenLetReason {
1803+ /// A let chain with the `||` operator
1804+ ForbiddenWithOr ( Span ) ,
1805+ /// `let` is not valid and the source environment is not important
1806+ GenericForbidden ,
1807+ }
0 commit comments