@@ -16,13 +16,11 @@ use syntax::ast::*;
16
16
use syntax:: attr;
17
17
use syntax:: source_map:: Spanned ;
18
18
use syntax:: symbol:: { keywords, sym} ;
19
- use syntax:: ptr:: P ;
20
19
use syntax:: visit:: { self , Visitor } ;
21
20
use syntax:: { span_err, struct_span_err, walk_list} ;
22
21
use syntax_ext:: proc_macro_decls:: is_proc_macro_attr;
23
22
use syntax_pos:: { Span , MultiSpan } ;
24
23
use errors:: { Applicability , FatalError } ;
25
- use log:: debug;
26
24
27
25
#[ derive( Copy , Clone , Debug ) ]
28
26
struct OuterImplTrait {
@@ -71,26 +69,42 @@ struct AstValidator<'a> {
71
69
/// these booleans.
72
70
warning_period_57979_didnt_record_next_impl_trait : bool ,
73
71
warning_period_57979_impl_trait_in_proj : bool ,
72
+
73
+ /// Used to ban `let` expressions in inappropriate places.
74
+ is_let_allowed : bool ,
75
+ }
76
+
77
+ /// With the `new` value in `store`,
78
+ /// runs and returns the `scoped` computation,
79
+ /// resetting the old value of `store` after,
80
+ /// and returning the result of `scoped`.
81
+ fn with < C , T , S > (
82
+ this : & mut C ,
83
+ new : S ,
84
+ store : impl Fn ( & mut C ) -> & mut S ,
85
+ scoped : impl FnOnce ( & mut C ) -> T
86
+ ) -> T {
87
+ let old = mem:: replace ( store ( this) , new) ;
88
+ let ret = scoped ( this) ;
89
+ * store ( this) = old;
90
+ ret
74
91
}
75
92
76
93
impl < ' a > AstValidator < ' a > {
77
94
fn with_impl_trait_in_proj_warning < T > ( & mut self , v : bool , f : impl FnOnce ( & mut Self ) -> T ) -> T {
78
- let old = mem:: replace ( & mut self . warning_period_57979_impl_trait_in_proj , v) ;
79
- let ret = f ( self ) ;
80
- self . warning_period_57979_impl_trait_in_proj = old;
81
- ret
95
+ with ( self , v, |this| & mut this. warning_period_57979_impl_trait_in_proj , f)
82
96
}
83
97
84
98
fn with_banned_impl_trait ( & mut self , f : impl FnOnce ( & mut Self ) ) {
85
- let old = mem:: replace ( & mut self . is_impl_trait_banned , true ) ;
86
- f ( self ) ;
87
- self . is_impl_trait_banned = old;
99
+ with ( self , true , |this| & mut this. is_impl_trait_banned , f)
88
100
}
89
101
90
102
fn with_impl_trait ( & mut self , outer : Option < OuterImplTrait > , f : impl FnOnce ( & mut Self ) ) {
91
- let old = mem:: replace ( & mut self . outer_impl_trait , outer) ;
92
- f ( self ) ;
93
- self . outer_impl_trait = old;
103
+ with ( self , outer, |this| & mut this. outer_impl_trait , f)
104
+ }
105
+
106
+ fn with_let_allowed ( & mut self , v : bool , f : impl FnOnce ( & mut Self ) ) {
107
+ with ( self , v, |this| & mut this. is_let_allowed , f)
94
108
}
95
109
96
110
fn visit_assoc_type_binding_from_generic_args ( & mut self , type_binding : & ' a TypeBinding ) {
@@ -297,52 +311,60 @@ impl<'a> AstValidator<'a> {
297
311
}
298
312
}
299
313
300
- /// With eRFC 2497, we need to check whether an expression is ambiguous and warn or error
301
- /// depending on the edition, this function handles that.
302
- fn while_if_let_ambiguity ( & self , expr : & P < Expr > ) {
303
- if let Some ( ( span, op_kind) ) = self . while_if_let_expr_ambiguity ( & expr) {
304
- let mut err = self . err_handler ( ) . struct_span_err (
305
- span, & format ! ( "ambiguous use of `{}`" , op_kind. to_string( ) )
306
- ) ;
307
-
308
- err. note (
309
- "this will be a error until the `let_chains` feature is stabilized"
310
- ) ;
311
- err. note (
312
- "see rust-lang/rust#53668 for more information"
313
- ) ;
314
-
315
- if let Ok ( snippet) = self . session . source_map ( ) . span_to_snippet ( span) {
314
+ fn obsolete_in_place ( & self , expr : & Expr , place : & Expr , val : & Expr ) {
315
+ let mut err = self . err_handler ( ) . struct_span_err (
316
+ expr. span ,
317
+ "emplacement syntax is obsolete (for now, anyway)" ,
318
+ ) ;
319
+ err. note (
320
+ "for more information, see \
321
+ <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>"
322
+ ) ;
323
+ match val. node {
324
+ ExprKind :: Lit ( ref v) if v. node . is_numeric ( ) => {
316
325
err. span_suggestion (
317
- span, "consider adding parentheses" , format ! ( "({})" , snippet) ,
318
- Applicability :: MachineApplicable ,
326
+ place. span . between ( val. span ) ,
327
+ "if you meant to write a comparison against a negative value, add a \
328
+ space in between `<` and `-`",
329
+ "< -" . to_string ( ) ,
330
+ Applicability :: MaybeIncorrect
319
331
) ;
320
332
}
321
-
322
- err. emit ( ) ;
333
+ _ => { }
323
334
}
335
+ err. emit ( ) ;
324
336
}
325
337
326
- /// With eRFC 2497 adding if-let chains, there is a requirement that the parsing of
327
- /// `&&` and `||` in a if-let statement be unambiguous. This function returns a span and
328
- /// a `BinOpKind` (either `&&` or `||` depending on what was ambiguous) if it is determined
329
- /// that the current expression parsed is ambiguous and will break in future.
330
- fn while_if_let_expr_ambiguity ( & self , expr : & P < Expr > ) -> Option < ( Span , BinOpKind ) > {
331
- debug ! ( "while_if_let_expr_ambiguity: expr.node: {:?}" , expr. node) ;
338
+ /// Visits the `expr` and adjusts whether `let $pat = $expr` is allowed in decendants.
339
+ /// Returns whether we walked into `expr` or not.
340
+ /// If we did, walking should not happen again.
341
+ fn visit_expr_with_let_maybe_allowed ( & mut self , expr : & ' a Expr ) -> bool {
332
342
match & expr. node {
333
- ExprKind :: Binary ( op, _, _) if op. node == BinOpKind :: And || op. node == BinOpKind :: Or => {
334
- Some ( ( expr. span , op. node ) )
335
- } ,
336
- ExprKind :: Range ( ref lhs, ref rhs, _) => {
337
- let lhs_ambiguous = lhs. as_ref ( )
338
- . and_then ( |lhs| self . while_if_let_expr_ambiguity ( lhs) ) ;
339
- let rhs_ambiguous = rhs. as_ref ( )
340
- . and_then ( |rhs| self . while_if_let_expr_ambiguity ( rhs) ) ;
341
-
342
- lhs_ambiguous. or ( rhs_ambiguous)
343
+ // Assuming the context permits, `($expr)` does not impose additional constraints.
344
+ ExprKind :: Paren ( _) => visit:: walk_expr ( self , expr) ,
345
+ // Assuming the context permits,
346
+ // l && r` allows decendants in `l` and `r` to be `let` expressions.
347
+ ExprKind :: Binary ( op, ..) if op. node == BinOpKind :: And => visit:: walk_expr ( self , expr) ,
348
+ // However, we do allow it in the condition of the `if` expression.
349
+ // We do not allow `let` in `then` and `opt_else` directly.
350
+ ExprKind :: If ( ref cond, ref then, ref opt_else) => {
351
+ self . with_let_allowed ( false , |this| {
352
+ this. visit_block ( then) ;
353
+ walk_list ! ( this, visit_expr, opt_else) ;
354
+ } ) ;
355
+ self . with_let_allowed ( true , |this| this. visit_expr ( cond) ) ;
356
+ }
357
+ // The same logic applies to `While`.
358
+ ExprKind :: While ( ref cond, ref then, ref opt_label) => {
359
+ walk_list ! ( self , visit_label, opt_label) ;
360
+ self . with_let_allowed ( false , |this| this. visit_block ( then) ) ;
361
+ self . with_let_allowed ( true , |this| this. visit_expr ( cond) ) ;
343
362
}
344
- _ => None ,
363
+ // Don't walk into `expr` and defer further checks to the caller.
364
+ _ => return false ,
345
365
}
366
+
367
+ true
346
368
}
347
369
}
348
370
@@ -449,38 +471,27 @@ fn validate_generics_order<'a>(
449
471
impl < ' a > Visitor < ' a > for AstValidator < ' a > {
450
472
fn visit_expr ( & mut self , expr : & ' a Expr ) {
451
473
match expr. node {
452
- ExprKind :: IfLet ( _, ref expr, _, _) | ExprKind :: WhileLet ( _, ref expr, _, _) =>
453
- self . while_if_let_ambiguity ( & expr) ,
474
+ ExprKind :: Let ( _, _) if !self . is_let_allowed => {
475
+ self . err_handler ( )
476
+ . struct_span_err ( expr. span , "`let` expressions are not supported here" )
477
+ . note ( "only supported directly in conditions of `if`- and `while`-expressions" )
478
+ . note ( "as well as when nested within `&&` and parenthesis in those conditions" )
479
+ . emit ( ) ;
480
+ }
481
+ _ if self . visit_expr_with_let_maybe_allowed ( & expr) => {
482
+ // Prevent `walk_expr` to happen since we've already done that.
483
+ return ;
484
+ }
454
485
ExprKind :: InlineAsm ( ..) if !self . session . target . target . options . allow_asm => {
455
486
span_err ! ( self . session, expr. span, E0472 , "asm! is unsupported on this target" ) ;
456
487
}
457
488
ExprKind :: ObsoleteInPlace ( ref place, ref val) => {
458
- let mut err = self . err_handler ( ) . struct_span_err (
459
- expr. span ,
460
- "emplacement syntax is obsolete (for now, anyway)" ,
461
- ) ;
462
- err. note (
463
- "for more information, see \
464
- <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>"
465
- ) ;
466
- match val. node {
467
- ExprKind :: Lit ( ref v) if v. node . is_numeric ( ) => {
468
- err. span_suggestion (
469
- place. span . between ( val. span ) ,
470
- "if you meant to write a comparison against a negative value, add a \
471
- space in between `<` and `-`",
472
- "< -" . to_string ( ) ,
473
- Applicability :: MaybeIncorrect
474
- ) ;
475
- }
476
- _ => { }
477
- }
478
- err. emit ( ) ;
489
+ self . obsolete_in_place ( expr, place, val) ;
479
490
}
480
491
_ => { }
481
492
}
482
493
483
- visit:: walk_expr ( self , expr)
494
+ self . with_let_allowed ( false , |this| visit:: walk_expr ( this , expr) ) ;
484
495
}
485
496
486
497
fn visit_ty ( & mut self , ty : & ' a Ty ) {
@@ -862,6 +873,7 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
862
873
is_impl_trait_banned : false ,
863
874
warning_period_57979_didnt_record_next_impl_trait : false ,
864
875
warning_period_57979_impl_trait_in_proj : false ,
876
+ is_let_allowed : false ,
865
877
} ;
866
878
visit:: walk_crate ( & mut validator, krate) ;
867
879
0 commit comments