@@ -17,13 +17,11 @@ use syntax::attr;
17
17
use syntax:: feature_gate:: is_builtin_attr;
18
18
use syntax:: source_map:: Spanned ;
19
19
use syntax:: symbol:: { kw, sym} ;
20
- use syntax:: ptr:: P ;
21
20
use syntax:: visit:: { self , Visitor } ;
22
21
use syntax:: { span_err, struct_span_err, walk_list} ;
23
22
use syntax_ext:: proc_macro_decls:: is_proc_macro_attr;
24
23
use syntax_pos:: { Span , MultiSpan } ;
25
24
use errors:: { Applicability , FatalError } ;
26
- use log:: debug;
27
25
28
26
#[ derive( Copy , Clone , Debug ) ]
29
27
struct OuterImplTrait {
@@ -76,20 +74,34 @@ struct AstValidator<'a> {
76
74
/// these booleans.
77
75
warning_period_57979_didnt_record_next_impl_trait : bool ,
78
76
warning_period_57979_impl_trait_in_proj : bool ,
77
+
78
+ /// Used to ban `let` expressions in inappropriate places.
79
+ is_let_allowed : bool ,
80
+ }
81
+
82
+ /// With the `new` value in `store`,
83
+ /// runs and returns the `scoped` computation,
84
+ /// resetting the old value of `store` after,
85
+ /// and returning the result of `scoped`.
86
+ fn with < C , T , S > (
87
+ this : & mut C ,
88
+ new : S ,
89
+ store : impl Fn ( & mut C ) -> & mut S ,
90
+ scoped : impl FnOnce ( & mut C ) -> T
91
+ ) -> T {
92
+ let old = mem:: replace ( store ( this) , new) ;
93
+ let ret = scoped ( this) ;
94
+ * store ( this) = old;
95
+ ret
79
96
}
80
97
81
98
impl < ' a > AstValidator < ' a > {
82
99
fn with_impl_trait_in_proj_warning < T > ( & mut self , v : bool , f : impl FnOnce ( & mut Self ) -> T ) -> T {
83
- let old = mem:: replace ( & mut self . warning_period_57979_impl_trait_in_proj , v) ;
84
- let ret = f ( self ) ;
85
- self . warning_period_57979_impl_trait_in_proj = old;
86
- ret
100
+ with ( self , v, |this| & mut this. warning_period_57979_impl_trait_in_proj , f)
87
101
}
88
102
89
103
fn with_banned_impl_trait ( & mut self , f : impl FnOnce ( & mut Self ) ) {
90
- let old = mem:: replace ( & mut self . is_impl_trait_banned , true ) ;
91
- f ( self ) ;
92
- self . is_impl_trait_banned = old;
104
+ with ( self , true , |this| & mut this. is_impl_trait_banned , f)
93
105
}
94
106
95
107
fn with_banned_assoc_ty_bound ( & mut self , f : impl FnOnce ( & mut Self ) ) {
@@ -99,9 +111,11 @@ impl<'a> AstValidator<'a> {
99
111
}
100
112
101
113
fn with_impl_trait ( & mut self , outer : Option < OuterImplTrait > , f : impl FnOnce ( & mut Self ) ) {
102
- let old = mem:: replace ( & mut self . outer_impl_trait , outer) ;
103
- f ( self ) ;
104
- self . outer_impl_trait = old;
114
+ with ( self , outer, |this| & mut this. outer_impl_trait , f)
115
+ }
116
+
117
+ fn with_let_allowed ( & mut self , v : bool , f : impl FnOnce ( & mut Self ) ) {
118
+ with ( self , v, |this| & mut this. is_let_allowed , f)
105
119
}
106
120
107
121
fn visit_assoc_ty_constraint_from_generic_args ( & mut self , constraint : & ' a AssocTyConstraint ) {
@@ -319,52 +333,36 @@ impl<'a> AstValidator<'a> {
319
333
}
320
334
}
321
335
322
- /// With eRFC 2497, we need to check whether an expression is ambiguous and warn or error
323
- /// depending on the edition, this function handles that.
324
- fn while_if_let_ambiguity ( & self , expr : & P < Expr > ) {
325
- if let Some ( ( span, op_kind) ) = self . while_if_let_expr_ambiguity ( & expr) {
326
- let mut err = self . err_handler ( ) . struct_span_err (
327
- span, & format ! ( "ambiguous use of `{}`" , op_kind. to_string( ) )
328
- ) ;
329
-
330
- err. note (
331
- "this will be a error until the `let_chains` feature is stabilized"
332
- ) ;
333
- err. note (
334
- "see rust-lang/rust#53668 for more information"
335
- ) ;
336
-
337
- if let Ok ( snippet) = self . session . source_map ( ) . span_to_snippet ( span) {
338
- err. span_suggestion (
339
- span, "consider adding parentheses" , format ! ( "({})" , snippet) ,
340
- Applicability :: MachineApplicable ,
341
- ) ;
342
- }
343
-
344
- err. emit ( ) ;
345
- }
346
- }
347
-
348
- /// With eRFC 2497 adding if-let chains, there is a requirement that the parsing of
349
- /// `&&` and `||` in a if-let statement be unambiguous. This function returns a span and
350
- /// a `BinOpKind` (either `&&` or `||` depending on what was ambiguous) if it is determined
351
- /// that the current expression parsed is ambiguous and will break in future.
352
- fn while_if_let_expr_ambiguity ( & self , expr : & P < Expr > ) -> Option < ( Span , BinOpKind ) > {
353
- debug ! ( "while_if_let_expr_ambiguity: expr.node: {:?}" , expr. node) ;
336
+ /// Visits the `expr` and adjusts whether `let $pat = $expr` is allowed in decendants.
337
+ /// Returns whether we walked into `expr` or not.
338
+ /// If we did, walking should not happen again.
339
+ fn visit_expr_with_let_maybe_allowed ( & mut self , expr : & ' a Expr ) -> bool {
354
340
match & expr. node {
355
- ExprKind :: Binary ( op, _, _) if op. node == BinOpKind :: And || op. node == BinOpKind :: Or => {
356
- Some ( ( expr. span , op. node ) )
357
- } ,
358
- ExprKind :: Range ( ref lhs, ref rhs, _) => {
359
- let lhs_ambiguous = lhs. as_ref ( )
360
- . and_then ( |lhs| self . while_if_let_expr_ambiguity ( lhs) ) ;
361
- let rhs_ambiguous = rhs. as_ref ( )
362
- . and_then ( |rhs| self . while_if_let_expr_ambiguity ( rhs) ) ;
363
-
364
- lhs_ambiguous. or ( rhs_ambiguous)
341
+ // Assuming the context permits, `($expr)` does not impose additional constraints.
342
+ ExprKind :: Paren ( _) => visit:: walk_expr ( self , expr) ,
343
+ // Assuming the context permits,
344
+ // l && r` allows decendants in `l` and `r` to be `let` expressions.
345
+ ExprKind :: Binary ( op, ..) if op. node == BinOpKind :: And => visit:: walk_expr ( self , expr) ,
346
+ // However, we do allow it in the condition of the `if` expression.
347
+ // We do not allow `let` in `then` and `opt_else` directly.
348
+ ExprKind :: If ( ref cond, ref then, ref opt_else) => {
349
+ self . with_let_allowed ( false , |this| {
350
+ this. visit_block ( then) ;
351
+ walk_list ! ( this, visit_expr, opt_else) ;
352
+ } ) ;
353
+ self . with_let_allowed ( true , |this| this. visit_expr ( cond) ) ;
365
354
}
366
- _ => None ,
355
+ // The same logic applies to `While`.
356
+ ExprKind :: While ( ref cond, ref then, ref opt_label) => {
357
+ walk_list ! ( self , visit_label, opt_label) ;
358
+ self . with_let_allowed ( false , |this| this. visit_block ( then) ) ;
359
+ self . with_let_allowed ( true , |this| this. visit_expr ( cond) ) ;
360
+ }
361
+ // Don't walk into `expr` and defer further checks to the caller.
362
+ _ => return false ,
367
363
}
364
+
365
+ true
368
366
}
369
367
370
368
fn check_fn_decl ( & self , fn_decl : & FnDecl ) {
@@ -494,18 +492,27 @@ fn validate_generics_order<'a>(
494
492
impl < ' a > Visitor < ' a > for AstValidator < ' a > {
495
493
fn visit_expr ( & mut self , expr : & ' a Expr ) {
496
494
match expr. node {
495
+ ExprKind :: Let ( _, _) if !self . is_let_allowed => {
496
+ self . err_handler ( )
497
+ . struct_span_err ( expr. span , "`let` expressions are not supported here" )
498
+ . note ( "only supported directly in conditions of `if`- and `while`-expressions" )
499
+ . note ( "as well as when nested within `&&` and parenthesis in those conditions" )
500
+ . emit ( ) ;
501
+ }
502
+ _ if self . visit_expr_with_let_maybe_allowed ( & expr) => {
503
+ // Prevent `walk_expr` to happen since we've already done that.
504
+ return ;
505
+ }
497
506
ExprKind :: Closure ( _, _, _, ref fn_decl, _, _) => {
498
507
self . check_fn_decl ( fn_decl) ;
499
508
}
500
- ExprKind :: IfLet ( _, ref expr, _, _) | ExprKind :: WhileLet ( _, ref expr, _, _) =>
501
- self . while_if_let_ambiguity ( & expr) ,
502
509
ExprKind :: InlineAsm ( ..) if !self . session . target . target . options . allow_asm => {
503
510
span_err ! ( self . session, expr. span, E0472 , "asm! is unsupported on this target" ) ;
504
511
}
505
512
_ => { }
506
513
}
507
514
508
- visit:: walk_expr ( self , expr)
515
+ self . with_let_allowed ( false , |this| visit:: walk_expr ( this , expr) ) ;
509
516
}
510
517
511
518
fn visit_ty ( & mut self , ty : & ' a Ty ) {
@@ -917,6 +924,7 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
917
924
is_assoc_ty_bound_banned : false ,
918
925
warning_period_57979_didnt_record_next_impl_trait : false ,
919
926
warning_period_57979_impl_trait_in_proj : false ,
927
+ is_let_allowed : false ,
920
928
} ;
921
929
visit:: walk_crate ( & mut validator, krate) ;
922
930
0 commit comments