Rollup merge of #126883 - dtolnay:breakvalue, r=fmease
Parenthesize break values containing leading label The AST pretty printer previously produced invalid syntax in the case of `break` expressions with a value that begins with a loop or block label. ```rust macro_rules! expr { ($e:expr) => { $e }; } fn main() { loop { break expr!('a: loop { break 'a 1; } + 1); }; } ``` `rustc -Zunpretty=expanded main.rs `: ```console #![feature(prelude_import)] #![no_std] #[prelude_import] use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; macro_rules! expr { ($e:expr) => { $e }; } fn main() { loop { break 'a: loop { break 'a 1; } + 1; }; } ``` The expanded code is not valid Rust syntax. Printing invalid syntax is bad because it blocks `cargo expand` from being able to format the output as Rust syntax using rustfmt. ```console error: parentheses are required around this expression to avoid confusion with a labeled break expression --> <anon>:9:26 | 9 | fn main() { loop { break 'a: loop { break 'a 1; } + 1; }; } | ^^^^^^^^^^^^^^^^^^^^^^^^ | help: wrap the expression in parentheses | 9 | fn main() { loop { break ('a: loop { break 'a 1; }) + 1; }; } | + + ``` This PR updates the AST pretty-printer to insert parentheses around the value of a `break` expression as required to avoid this edge case.
This commit is contained in:
commit
f8f67b2969
4 changed files with 117 additions and 3 deletions
|
@ -1,7 +1,8 @@
|
|||
//! Routines the parser and pretty-printer use to classify AST nodes.
|
||||
|
||||
use crate::ast::ExprKind::*;
|
||||
use crate::{ast, token::Delimiter};
|
||||
use crate::ast::{self, MatchKind};
|
||||
use crate::token::Delimiter;
|
||||
|
||||
/// This classification determines whether various syntactic positions break out
|
||||
/// of parsing the current expression (true) or continue parsing more of the
|
||||
|
@ -81,6 +82,82 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether the leftmost token of the given expression is the label of a
|
||||
/// labeled loop or block, such as in `'inner: loop { break 'inner 1 } + 1`.
|
||||
///
|
||||
/// Such expressions are not allowed as the value of an unlabeled break.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// 'outer: {
|
||||
/// break 'inner: loop { break 'inner 1 } + 1; // invalid syntax
|
||||
///
|
||||
/// break 'outer 'inner: loop { break 'inner 1 } + 1; // okay
|
||||
///
|
||||
/// break ('inner: loop { break 'inner 1 } + 1); // okay
|
||||
///
|
||||
/// break ('inner: loop { break 'inner 1 }) + 1; // okay
|
||||
/// }
|
||||
/// ```
|
||||
pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
|
||||
loop {
|
||||
match &expr.kind {
|
||||
Block(_, label) | ForLoop { label, .. } | Loop(_, label, _) | While(_, _, label) => {
|
||||
return label.is_some();
|
||||
}
|
||||
|
||||
Assign(e, _, _)
|
||||
| AssignOp(_, e, _)
|
||||
| Await(e, _)
|
||||
| Binary(_, e, _)
|
||||
| Call(e, _)
|
||||
| Cast(e, _)
|
||||
| Field(e, _)
|
||||
| Index(e, _, _)
|
||||
| Match(e, _, MatchKind::Postfix)
|
||||
| Range(Some(e), _, _)
|
||||
| Try(e) => {
|
||||
expr = e;
|
||||
}
|
||||
MethodCall(method_call) => {
|
||||
expr = &method_call.receiver;
|
||||
}
|
||||
|
||||
AddrOf(..)
|
||||
| Array(..)
|
||||
| Become(..)
|
||||
| Break(..)
|
||||
| Closure(..)
|
||||
| ConstBlock(..)
|
||||
| Continue(..)
|
||||
| FormatArgs(..)
|
||||
| Gen(..)
|
||||
| If(..)
|
||||
| IncludedBytes(..)
|
||||
| InlineAsm(..)
|
||||
| Let(..)
|
||||
| Lit(..)
|
||||
| MacCall(..)
|
||||
| Match(_, _, MatchKind::Prefix)
|
||||
| OffsetOf(..)
|
||||
| Paren(..)
|
||||
| Path(..)
|
||||
| Range(None, _, _)
|
||||
| Repeat(..)
|
||||
| Ret(..)
|
||||
| Struct(..)
|
||||
| TryBlock(..)
|
||||
| Tup(..)
|
||||
| Type(..)
|
||||
| Unary(..)
|
||||
| Underscore
|
||||
| Yeet(..)
|
||||
| Yield(..)
|
||||
| Err(..)
|
||||
| Dummy => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TrailingBrace<'a> {
|
||||
/// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
|
||||
/// We will suggest changing the macro call to a different delimiter.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue