@@ -16,13 +16,11 @@ use syntax::ast::*;
1616use syntax:: attr;
1717use syntax:: source_map:: Spanned ;
1818use syntax:: symbol:: { keywords, sym} ;
19- use syntax:: ptr:: P ;
2019use syntax:: visit:: { self , Visitor } ;
2120use syntax:: { span_err, struct_span_err, walk_list} ;
2221use syntax_ext:: proc_macro_decls:: is_proc_macro_attr;
2322use syntax_pos:: { Span , MultiSpan } ;
2423use errors:: { Applicability , FatalError } ;
25- use log:: debug;
2624
2725#[ derive( Copy , Clone , Debug ) ]
2826struct OuterImplTrait {
@@ -71,26 +69,42 @@ struct AstValidator<'a> {
7169 /// these booleans.
7270 warning_period_57979_didnt_record_next_impl_trait : bool ,
7371 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
7491}
7592
7693impl < ' a > AstValidator < ' a > {
7794 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)
8296 }
8397
8498 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)
88100 }
89101
90102 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)
94108 }
95109
96110 fn visit_assoc_type_binding_from_generic_args ( & mut self , type_binding : & ' a TypeBinding ) {
@@ -297,52 +311,60 @@ impl<'a> AstValidator<'a> {
297311 }
298312 }
299313
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 ( ) => {
316325 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
319331 ) ;
320332 }
321-
322- err. emit ( ) ;
333+ _ => { }
323334 }
335+ err. emit ( ) ;
324336 }
325337
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 {
332342 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) ) ;
343362 }
344- _ => None ,
363+ // Don't walk into `expr` and defer further checks to the caller.
364+ _ => return false ,
345365 }
366+
367+ true
346368 }
347369}
348370
@@ -449,38 +471,27 @@ fn validate_generics_order<'a>(
449471impl < ' a > Visitor < ' a > for AstValidator < ' a > {
450472 fn visit_expr ( & mut self , expr : & ' a Expr ) {
451473 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+ }
454485 ExprKind :: InlineAsm ( ..) if !self . session . target . target . options . allow_asm => {
455486 span_err ! ( self . session, expr. span, E0472 , "asm! is unsupported on this target" ) ;
456487 }
457488 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) ;
479490 }
480491 _ => { }
481492 }
482493
483- visit:: walk_expr ( self , expr)
494+ self . with_let_allowed ( false , |this| visit:: walk_expr ( this , expr) ) ;
484495 }
485496
486497 fn visit_ty ( & mut self , ty : & ' a Ty ) {
@@ -862,6 +873,7 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
862873 is_impl_trait_banned : false ,
863874 warning_period_57979_didnt_record_next_impl_trait : false ,
864875 warning_period_57979_impl_trait_in_proj : false ,
876+ is_let_allowed : false ,
865877 } ;
866878 visit:: walk_crate ( & mut validator, krate) ;
867879
0 commit comments