@@ -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 , or_span : Option < Span > ) {
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 Some ( elem) = or_span {
125+ diag. span_note ( elem, "`||` 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,52 @@ 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+ let or_span = match elem {
1014+ ForbiddenLetReason :: ForbiddenWithOr ( span) => Some ( span) ,
1015+ ForbiddenLetReason :: GenericForbidden => None ,
1016+ } ;
1017+ this. ban_let_expr ( expr, or_span) ;
1018+ } ,
1019+ ExprKind :: Match ( scrutinee, arms) => {
1020+ this. visit_expr ( scrutinee) ;
1021+ for arm in arms {
1022+ this. visit_expr ( & arm. body ) ;
1023+ this. visit_pat ( & arm. pat ) ;
1024+ walk_list ! ( this, visit_attribute, & arm. attrs) ;
1025+ if let Some ( guard) = & arm. guard && let ExprKind :: Let ( _, guard_expr, _) = & guard. kind {
1026+ this. with_let_management ( None , |this, _| {
1027+ this. visit_expr ( guard_expr)
1028+ } ) ;
10081029 return ;
10091030 }
10101031 }
10111032 }
1033+ ExprKind :: Paren ( _) | ExprKind :: Binary ( Spanned { node : BinOpKind :: And , .. } , ..) => {
1034+ this. with_let_management ( forbidden_let_reason, |this, _| visit:: walk_expr ( this, expr) ) ;
1035+ return ;
1036+ }
1037+ ExprKind :: While ( cond, then, opt_label) => {
1038+ walk_list ! ( this, visit_label, opt_label) ;
1039+ this. visit_block ( then) ;
1040+ this. with_let_management ( None , |this, _| this. visit_expr ( cond) ) ;
1041+ return ;
1042+ }
1043+ _ => visit:: walk_expr ( this, expr) ,
10121044 }
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) ,
10241045 } ) ;
10251046 }
10261047
@@ -1772,10 +1793,19 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
17721793 is_tilde_const_allowed : false ,
17731794 is_impl_trait_banned : false ,
17741795 is_assoc_ty_bound_banned : false ,
1775- is_let_allowed : false ,
1796+ forbidden_let_reason : Some ( ForbiddenLetReason :: GenericForbidden ) ,
17761797 lint_buffer : lints,
17771798 } ;
17781799 visit:: walk_crate ( & mut validator, krate) ;
17791800
17801801 validator. has_proc_macro_decls
17811802}
1803+
1804+ /// Used to forbid `let` expressions in certain syntactic locations.
1805+ #[ derive( Clone , Copy ) ]
1806+ enum ForbiddenLetReason {
1807+ /// A let chain with the `||` operator
1808+ ForbiddenWithOr ( Span ) ,
1809+ /// `let` is not valid and the source environment is not important
1810+ GenericForbidden ,
1811+ }
0 commit comments