@@ -64,8 +64,8 @@ struct AstValidator<'a> {
64
64
/// certain positions.
65
65
is_assoc_ty_bound_banned : bool ,
66
66
67
- /// Used to allow `let` expressions in certain syntactic locations.
68
- is_let_allowed : bool ,
67
+ /// See [ForbiddenLetReason]
68
+ forbidden_let_reason : Option < ForbiddenLetReason > ,
69
69
70
70
lint_buffer : & ' a mut LintBuffer ,
71
71
}
@@ -103,20 +103,28 @@ impl<'a> AstValidator<'a> {
103
103
self . is_tilde_const_allowed = old;
104
104
}
105
105
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) ;
108
112
f ( self , old) ;
109
- self . is_let_allowed = old;
113
+ self . forbidden_let_reason = old;
110
114
}
111
115
112
116
/// 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 > ) {
114
118
let sess = & self . session ;
115
119
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 ( ) ;
120
128
} else {
121
129
sess. struct_span_err ( expr. span , "expected expression, found statement (`let`)" )
122
130
. note ( "variable declaration using `let` is a statement" )
@@ -988,39 +996,52 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
988
996
}
989
997
990
998
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
+ } ) ;
1008
1029
return ;
1009
1030
}
1010
1031
}
1011
1032
}
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) ,
1012
1044
}
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) ,
1024
1045
} ) ;
1025
1046
}
1026
1047
@@ -1772,10 +1793,19 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
1772
1793
is_tilde_const_allowed : false ,
1773
1794
is_impl_trait_banned : false ,
1774
1795
is_assoc_ty_bound_banned : false ,
1775
- is_let_allowed : false ,
1796
+ forbidden_let_reason : Some ( ForbiddenLetReason :: GenericForbidden ) ,
1776
1797
lint_buffer : lints,
1777
1798
} ;
1778
1799
visit:: walk_crate ( & mut validator, krate) ;
1779
1800
1780
1801
validator. has_proc_macro_decls
1781
1802
}
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