mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-16 17:03:35 +00:00
Auto merge of #119427 - dtolnay:maccall, r=compiler-errors
Fix, document, and test parser and pretty-printer edge cases related to braced macro calls _Review note: this is a deceptively small PR because it comes with 145 lines of docs and 196 lines of tests, and only 25 lines of compiler code changed. However, I recommend reviewing it 1 commit at a time because much of the effect of the code changes is non-local i.e. affecting code that is not visible in the final state of the PR. I have paid attention that reviewing the PR one commit at a time is as easy as I can make it. All of the code you need to know about is touched in those commits, even if some of those changes disappear by the end of the stack._ This is a follow-up to https://github.com/rust-lang/rust/pull/119105. One case that is not relevant to `-Zunpretty=expanded`, but which came up as I'm porting #119105 and #118726 into `syn`'s printer and `prettyplease`'s printer where it **is** relevant, and is also relevant to rustc's `stringify!`, is statement boundaries in the vicinity of braced macro calls. Rustc's AST pretty-printer produces invalid syntax for statements that begin with a braced macro call: ```rust macro_rules! stringify_item { ($i:item) => { stringify!($i) }; } macro_rules! repro { ($e:expr) => { stringify_item!(fn main() { $e + 1; }) }; } fn main() { println!("{}", repro!(m! {})); } ``` **Before this PR:** output is not valid Rust syntax. ```console fn main() { m! {} + 1; } ``` ```console error: leading `+` is not supported --> <anon>:1:19 | 1 | fn main() { m! {} + 1; } | ^ unexpected `+` | help: try removing the `+` | 1 - fn main() { m! {} + 1; } 1 + fn main() { m! {} 1; } | ``` **After this PR:** valid syntax. ```console fn main() { (m! {}) + 1; } ```
This commit is contained in:
commit
8cc6f34653
@ -1,34 +1,88 @@
|
||||
//! Routines the parser uses to classify AST nodes
|
||||
|
||||
// Predicates on exprs and stmts that the pretty-printer and parser use
|
||||
//! Routines the parser and pretty-printer use to classify AST nodes.
|
||||
|
||||
use crate::ast::ExprKind::*;
|
||||
use crate::{ast, token::Delimiter};
|
||||
|
||||
/// Does this expression require a semicolon to be treated
|
||||
/// as a statement? The negation of this: 'can this expression
|
||||
/// be used as a statement without a semicolon' -- is used
|
||||
/// as an early-bail-out in the parser so that, for instance,
|
||||
/// if true {...} else {...}
|
||||
/// |x| 5
|
||||
/// isn't parsed as (if true {...} else {...} | x) | 5
|
||||
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
||||
!matches!(
|
||||
/// This classification determines whether various syntactic positions break out
|
||||
/// of parsing the current expression (true) or continue parsing more of the
|
||||
/// same expression (false).
|
||||
///
|
||||
/// For example, it's relevant in the parsing of match arms:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// match ... {
|
||||
/// // Is this calling $e as a function, or is it the start of a new arm
|
||||
/// // with a tuple pattern?
|
||||
/// _ => $e (
|
||||
/// ^ )
|
||||
///
|
||||
/// // Is this an Index operation, or new arm with a slice pattern?
|
||||
/// _ => $e [
|
||||
/// ^ ]
|
||||
///
|
||||
/// // Is this a binary operator, or leading vert in a new arm? Same for
|
||||
/// // other punctuation which can either be a binary operator in
|
||||
/// // expression or unary operator in pattern, such as `&` and `-`.
|
||||
/// _ => $e |
|
||||
/// ^
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If $e is something like `{}` or `if … {}`, then terminate the current
|
||||
/// arm and parse a new arm.
|
||||
///
|
||||
/// If $e is something like `path::to` or `(…)`, continue parsing the same
|
||||
/// arm.
|
||||
///
|
||||
/// *Almost* the same classification is used as an early bail-out for parsing
|
||||
/// statements. See `expr_requires_semi_to_be_stmt`.
|
||||
pub fn expr_is_complete(e: &ast::Expr) -> bool {
|
||||
matches!(
|
||||
e.kind,
|
||||
ast::ExprKind::If(..)
|
||||
| ast::ExprKind::Match(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::While(..)
|
||||
| ast::ExprKind::Loop(..)
|
||||
| ast::ExprKind::ForLoop { .. }
|
||||
| ast::ExprKind::TryBlock(..)
|
||||
| ast::ExprKind::ConstBlock(..)
|
||||
If(..)
|
||||
| Match(..)
|
||||
| Block(..)
|
||||
| While(..)
|
||||
| Loop(..)
|
||||
| ForLoop { .. }
|
||||
| TryBlock(..)
|
||||
| ConstBlock(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// Does this expression require a semicolon to be treated as a statement?
|
||||
///
|
||||
/// The negation of this: "can this expression be used as a statement without a
|
||||
/// semicolon" -- is used as an early bail-out when parsing statements so that,
|
||||
/// for instance,
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// if true {...} else {...}
|
||||
/// |x| 5
|
||||
/// ```
|
||||
///
|
||||
/// isn't parsed as `(if true {...} else {...} | x) | 5`.
|
||||
///
|
||||
/// Surprising special case: even though braced macro calls like `m! {}`
|
||||
/// normally do not introduce a boundary when found at the head of a match arm,
|
||||
/// they do terminate the parsing of a statement.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// match ... {
|
||||
/// _ => m! {} (), // macro that expands to a function, which is then called
|
||||
/// }
|
||||
///
|
||||
/// let _ = { m! {} () }; // macro call followed by unit
|
||||
/// ```
|
||||
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
||||
match &e.kind {
|
||||
MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,
|
||||
_ => !expr_is_complete(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
|
||||
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||
use ast::ExprKind::*;
|
||||
|
||||
loop {
|
||||
match &expr.kind {
|
||||
AddrOf(_, _, e)
|
||||
|
@ -780,7 +780,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
_ => {
|
||||
self.end(); // Close the ibox for the pattern.
|
||||
self.print_expr(body, FixupContext::new_stmt());
|
||||
self.print_expr(body, FixupContext::new_match_arm());
|
||||
self.word(",");
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,38 @@ pub(crate) struct FixupContext {
|
||||
/// No parentheses required.
|
||||
leftmost_subexpression_in_stmt: bool,
|
||||
|
||||
/// Print expression such that it can be parsed as a match arm.
|
||||
///
|
||||
/// This is almost equivalent to `stmt`, but the grammar diverges a tiny bit
|
||||
/// between statements and match arms when it comes to braced macro calls.
|
||||
/// Macro calls with brace delimiter terminate a statement without a
|
||||
/// semicolon, but do not terminate a match-arm without comma.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// m! {} - 1; // two statements: a macro call followed by -1 literal
|
||||
///
|
||||
/// match () {
|
||||
/// _ => m! {} - 1, // binary subtraction operator
|
||||
/// }
|
||||
/// ```
|
||||
match_arm: bool,
|
||||
|
||||
/// This is almost equivalent to `leftmost_subexpression_in_stmt`, other
|
||||
/// than for braced macro calls.
|
||||
///
|
||||
/// If we have `m! {} - 1` as an expression, the leftmost subexpression
|
||||
/// `m! {}` will need to be parenthesized in the statement case but not the
|
||||
/// match-arm case.
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// (m! {}) - 1; // subexpression needs parens
|
||||
///
|
||||
/// match () {
|
||||
/// _ => m! {} - 1, // no parens
|
||||
/// }
|
||||
/// ```
|
||||
leftmost_subexpression_in_match_arm: bool,
|
||||
|
||||
/// This is the difference between:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
@ -68,6 +100,8 @@ impl Default for FixupContext {
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
match_arm: false,
|
||||
leftmost_subexpression_in_match_arm: false,
|
||||
parenthesize_exterior_struct_lit: false,
|
||||
}
|
||||
}
|
||||
@ -76,13 +110,16 @@ impl Default for FixupContext {
|
||||
impl FixupContext {
|
||||
/// Create the initial fixup for printing an expression in statement
|
||||
/// position.
|
||||
///
|
||||
/// This is currently also used for printing an expression as a match-arm,
|
||||
/// but this is incorrect and leads to over-parenthesizing.
|
||||
pub fn new_stmt() -> Self {
|
||||
FixupContext { stmt: true, ..FixupContext::default() }
|
||||
}
|
||||
|
||||
/// Create the initial fixup for printing an expression as the right-hand
|
||||
/// side of a match arm.
|
||||
pub fn new_match_arm() -> Self {
|
||||
FixupContext { match_arm: true, ..FixupContext::default() }
|
||||
}
|
||||
|
||||
/// Create the initial fixup for printing an expression as the "condition"
|
||||
/// of an `if` or `while`. There are a few other positions which are
|
||||
/// grammatically equivalent and also use this, such as the iterator
|
||||
@ -106,6 +143,9 @@ impl FixupContext {
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt,
|
||||
match_arm: false,
|
||||
leftmost_subexpression_in_match_arm: self.match_arm
|
||||
|| self.leftmost_subexpression_in_match_arm,
|
||||
..self
|
||||
}
|
||||
}
|
||||
@ -119,7 +159,13 @@ impl FixupContext {
|
||||
/// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or
|
||||
/// `$a.f($b)`.
|
||||
pub fn subsequent_subexpression(self) -> Self {
|
||||
FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..self }
|
||||
FixupContext {
|
||||
stmt: false,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
match_arm: false,
|
||||
leftmost_subexpression_in_match_arm: false,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether parentheses are needed around the given expression to
|
||||
@ -128,7 +174,8 @@ impl FixupContext {
|
||||
/// The documentation on `FixupContext::leftmost_subexpression_in_stmt` has
|
||||
/// examples.
|
||||
pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool {
|
||||
self.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr)
|
||||
(self.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr))
|
||||
|| (self.leftmost_subexpression_in_match_arm && classify::expr_is_complete(expr))
|
||||
}
|
||||
|
||||
/// Determine whether parentheses are needed around the given `let`
|
||||
|
@ -677,6 +677,33 @@ trait UnusedDelimLint {
|
||||
}
|
||||
|
||||
// Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`.
|
||||
//
|
||||
// FIXME: https://github.com/rust-lang/rust/issues/119426
|
||||
// The syntax tree in this code is from after macro expansion, so the
|
||||
// current implementation has both false negatives and false positives
|
||||
// related to expressions containing macros.
|
||||
//
|
||||
// macro_rules! m1 {
|
||||
// () => {
|
||||
// 1
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// fn f1() -> u8 {
|
||||
// // Lint says parens are not needed, but they are.
|
||||
// (m1! {} + 1)
|
||||
// }
|
||||
//
|
||||
// macro_rules! m2 {
|
||||
// () => {
|
||||
// loop { break 1; }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// fn f2() -> u8 {
|
||||
// // Lint says parens are needed, but they are not.
|
||||
// (m2!() + 1)
|
||||
// }
|
||||
{
|
||||
let mut innermost = inner;
|
||||
loop {
|
||||
|
@ -497,8 +497,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
/// Checks if this expression is a successfully parsed statement.
|
||||
fn expr_is_complete(&self, e: &Expr) -> bool {
|
||||
self.restrictions.contains(Restrictions::STMT_EXPR)
|
||||
&& !classify::expr_requires_semi_to_be_stmt(e)
|
||||
self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e)
|
||||
}
|
||||
|
||||
/// Parses `x..y`, `x..=y`, and `x..`/`x..=`.
|
||||
@ -2691,8 +2690,33 @@ impl<'a> Parser<'a> {
|
||||
let first_tok_span = self.token.span;
|
||||
match self.parse_expr() {
|
||||
Ok(cond)
|
||||
// If it's not a free-standing expression, and is followed by a block,
|
||||
// then it's very likely the condition to an `else if`.
|
||||
// Try to guess the difference between a "condition-like" vs
|
||||
// "statement-like" expression.
|
||||
//
|
||||
// We are seeing the following code, in which $cond is neither
|
||||
// ExprKind::Block nor ExprKind::If (the 2 cases wherein this
|
||||
// would be valid syntax).
|
||||
//
|
||||
// if ... {
|
||||
// } else $cond
|
||||
//
|
||||
// If $cond is "condition-like" such as ExprKind::Binary, we
|
||||
// want to suggest inserting `if`.
|
||||
//
|
||||
// if ... {
|
||||
// } else if a == b {
|
||||
// ^^
|
||||
// }
|
||||
//
|
||||
// If $cond is "statement-like" such as ExprKind::While then we
|
||||
// want to suggest wrapping in braces.
|
||||
//
|
||||
// if ... {
|
||||
// } else {
|
||||
// ^
|
||||
// while true {}
|
||||
// }
|
||||
// ^
|
||||
if self.check(&TokenKind::OpenDelim(Delimiter::Brace))
|
||||
&& classify::expr_requires_semi_to_be_stmt(&cond) =>
|
||||
{
|
||||
@ -3136,7 +3160,7 @@ impl<'a> Parser<'a> {
|
||||
err
|
||||
})?;
|
||||
|
||||
let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
|
||||
let require_comma = !classify::expr_is_complete(&expr)
|
||||
&& this.token != token::CloseDelim(Delimiter::Brace);
|
||||
|
||||
if !require_comma {
|
||||
|
@ -46,6 +46,28 @@ pub fn parens_with_keyword(e: &[()]) -> i32 {
|
||||
macro_rules! baz {
|
||||
($($foo:expr),+) => {
|
||||
($($foo),*)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unit {
|
||||
() => {
|
||||
()
|
||||
};
|
||||
}
|
||||
|
||||
struct One;
|
||||
|
||||
impl std::ops::Sub<One> for () {
|
||||
type Output = i32;
|
||||
fn sub(self, _: One) -> Self::Output {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for One {
|
||||
type Output = i32;
|
||||
fn neg(self) -> Self::Output {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,4 +116,13 @@ fn main() {
|
||||
|
||||
let _a = baz!(3, 4);
|
||||
let _b = baz!(3);
|
||||
|
||||
let _ = {
|
||||
unit!() - One //~ ERROR unnecessary parentheses around block return value
|
||||
} + {
|
||||
unit![] - One //~ ERROR unnecessary parentheses around block return value
|
||||
} + {
|
||||
// FIXME: false positive. This parenthesis is required.
|
||||
unit! {} - One //~ ERROR unnecessary parentheses around block return value
|
||||
};
|
||||
}
|
||||
|
@ -46,6 +46,28 @@ pub fn parens_with_keyword(e: &[()]) -> i32 {
|
||||
macro_rules! baz {
|
||||
($($foo:expr),+) => {
|
||||
($($foo),*)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unit {
|
||||
() => {
|
||||
()
|
||||
};
|
||||
}
|
||||
|
||||
struct One;
|
||||
|
||||
impl std::ops::Sub<One> for () {
|
||||
type Output = i32;
|
||||
fn sub(self, _: One) -> Self::Output {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for One {
|
||||
type Output = i32;
|
||||
fn neg(self) -> Self::Output {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,4 +116,13 @@ fn main() {
|
||||
|
||||
let _a = baz!(3, 4);
|
||||
let _b = baz!(3);
|
||||
|
||||
let _ = {
|
||||
(unit!() - One) //~ ERROR unnecessary parentheses around block return value
|
||||
} + {
|
||||
(unit![] - One) //~ ERROR unnecessary parentheses around block return value
|
||||
} + {
|
||||
// FIXME: false positive. This parenthesis is required.
|
||||
(unit! {} - One) //~ ERROR unnecessary parentheses around block return value
|
||||
};
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ LL + return 1;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around assigned value
|
||||
--> $DIR/lint-unnecessary-parens.rs:52:31
|
||||
--> $DIR/lint-unnecessary-parens.rs:74:31
|
||||
|
|
||||
LL | pub const CONST_ITEM: usize = (10);
|
||||
| ^ ^
|
||||
@ -136,7 +136,7 @@ LL + pub const CONST_ITEM: usize = 10;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around assigned value
|
||||
--> $DIR/lint-unnecessary-parens.rs:53:33
|
||||
--> $DIR/lint-unnecessary-parens.rs:75:33
|
||||
|
|
||||
LL | pub static STATIC_ITEM: usize = (10);
|
||||
| ^ ^
|
||||
@ -148,7 +148,7 @@ LL + pub static STATIC_ITEM: usize = 10;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around function argument
|
||||
--> $DIR/lint-unnecessary-parens.rs:57:9
|
||||
--> $DIR/lint-unnecessary-parens.rs:79:9
|
||||
|
|
||||
LL | bar((true));
|
||||
| ^ ^
|
||||
@ -160,7 +160,7 @@ LL + bar(true);
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around `if` condition
|
||||
--> $DIR/lint-unnecessary-parens.rs:59:8
|
||||
--> $DIR/lint-unnecessary-parens.rs:81:8
|
||||
|
|
||||
LL | if (true) {}
|
||||
| ^ ^
|
||||
@ -172,7 +172,7 @@ LL + if true {}
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around `while` condition
|
||||
--> $DIR/lint-unnecessary-parens.rs:60:11
|
||||
--> $DIR/lint-unnecessary-parens.rs:82:11
|
||||
|
|
||||
LL | while (true) {}
|
||||
| ^ ^
|
||||
@ -184,7 +184,7 @@ LL + while true {}
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around `match` scrutinee expression
|
||||
--> $DIR/lint-unnecessary-parens.rs:61:11
|
||||
--> $DIR/lint-unnecessary-parens.rs:83:11
|
||||
|
|
||||
LL | match (true) {
|
||||
| ^ ^
|
||||
@ -196,7 +196,7 @@ LL + match true {
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around `let` scrutinee expression
|
||||
--> $DIR/lint-unnecessary-parens.rs:64:16
|
||||
--> $DIR/lint-unnecessary-parens.rs:86:16
|
||||
|
|
||||
LL | if let 1 = (1) {}
|
||||
| ^ ^
|
||||
@ -208,7 +208,7 @@ LL + if let 1 = 1 {}
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around `let` scrutinee expression
|
||||
--> $DIR/lint-unnecessary-parens.rs:65:19
|
||||
--> $DIR/lint-unnecessary-parens.rs:87:19
|
||||
|
|
||||
LL | while let 1 = (2) {}
|
||||
| ^ ^
|
||||
@ -220,7 +220,7 @@ LL + while let 1 = 2 {}
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around method argument
|
||||
--> $DIR/lint-unnecessary-parens.rs:81:24
|
||||
--> $DIR/lint-unnecessary-parens.rs:103:24
|
||||
|
|
||||
LL | X { y: false }.foo((true));
|
||||
| ^ ^
|
||||
@ -232,7 +232,7 @@ LL + X { y: false }.foo(true);
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around assigned value
|
||||
--> $DIR/lint-unnecessary-parens.rs:83:18
|
||||
--> $DIR/lint-unnecessary-parens.rs:105:18
|
||||
|
|
||||
LL | let mut _a = (0);
|
||||
| ^ ^
|
||||
@ -244,7 +244,7 @@ LL + let mut _a = 0;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around assigned value
|
||||
--> $DIR/lint-unnecessary-parens.rs:84:10
|
||||
--> $DIR/lint-unnecessary-parens.rs:106:10
|
||||
|
|
||||
LL | _a = (0);
|
||||
| ^ ^
|
||||
@ -256,7 +256,7 @@ LL + _a = 0;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around assigned value
|
||||
--> $DIR/lint-unnecessary-parens.rs:85:11
|
||||
--> $DIR/lint-unnecessary-parens.rs:107:11
|
||||
|
|
||||
LL | _a += (1);
|
||||
| ^ ^
|
||||
@ -268,7 +268,7 @@ LL + _a += 1;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around pattern
|
||||
--> $DIR/lint-unnecessary-parens.rs:87:8
|
||||
--> $DIR/lint-unnecessary-parens.rs:109:8
|
||||
|
|
||||
LL | let(mut _a) = 3;
|
||||
| ^ ^
|
||||
@ -280,7 +280,7 @@ LL + let mut _a = 3;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around pattern
|
||||
--> $DIR/lint-unnecessary-parens.rs:88:9
|
||||
--> $DIR/lint-unnecessary-parens.rs:110:9
|
||||
|
|
||||
LL | let (mut _a) = 3;
|
||||
| ^ ^
|
||||
@ -292,7 +292,7 @@ LL + let mut _a = 3;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around pattern
|
||||
--> $DIR/lint-unnecessary-parens.rs:89:8
|
||||
--> $DIR/lint-unnecessary-parens.rs:111:8
|
||||
|
|
||||
LL | let( mut _a) = 3;
|
||||
| ^^ ^
|
||||
@ -304,7 +304,7 @@ LL + let mut _a = 3;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around pattern
|
||||
--> $DIR/lint-unnecessary-parens.rs:91:8
|
||||
--> $DIR/lint-unnecessary-parens.rs:113:8
|
||||
|
|
||||
LL | let(_a) = 3;
|
||||
| ^ ^
|
||||
@ -316,7 +316,7 @@ LL + let _a = 3;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around pattern
|
||||
--> $DIR/lint-unnecessary-parens.rs:92:9
|
||||
--> $DIR/lint-unnecessary-parens.rs:114:9
|
||||
|
|
||||
LL | let (_a) = 3;
|
||||
| ^ ^
|
||||
@ -328,7 +328,7 @@ LL + let _a = 3;
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around pattern
|
||||
--> $DIR/lint-unnecessary-parens.rs:93:8
|
||||
--> $DIR/lint-unnecessary-parens.rs:115:8
|
||||
|
|
||||
LL | let( _a) = 3;
|
||||
| ^^ ^
|
||||
@ -339,5 +339,41 @@ LL - let( _a) = 3;
|
||||
LL + let _a = 3;
|
||||
|
|
||||
|
||||
error: aborting due to 28 previous errors
|
||||
error: unnecessary parentheses around block return value
|
||||
--> $DIR/lint-unnecessary-parens.rs:121:9
|
||||
|
|
||||
LL | (unit!() - One)
|
||||
| ^ ^
|
||||
|
|
||||
help: remove these parentheses
|
||||
|
|
||||
LL - (unit!() - One)
|
||||
LL + unit!() - One
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around block return value
|
||||
--> $DIR/lint-unnecessary-parens.rs:123:9
|
||||
|
|
||||
LL | (unit![] - One)
|
||||
| ^ ^
|
||||
|
|
||||
help: remove these parentheses
|
||||
|
|
||||
LL - (unit![] - One)
|
||||
LL + unit![] - One
|
||||
|
|
||||
|
||||
error: unnecessary parentheses around block return value
|
||||
--> $DIR/lint-unnecessary-parens.rs:126:9
|
||||
|
|
||||
LL | (unit! {} - One)
|
||||
| ^ ^
|
||||
|
|
||||
help: remove these parentheses
|
||||
|
|
||||
LL - (unit! {} - One)
|
||||
LL + unit! {} - One
|
||||
|
|
||||
|
||||
error: aborting due to 31 previous errors
|
||||
|
||||
|
@ -213,6 +213,21 @@ fn test_expr() {
|
||||
"match () { _ => ({ 1 }) - 1, }",
|
||||
"match () { _ => { 1 } - 1 }",
|
||||
);
|
||||
c2_match_arm!(
|
||||
[ m!() - 1 ],
|
||||
"match () { _ => m!() - 1, }",
|
||||
"match () { _ => m!() - 1 }",
|
||||
);
|
||||
c2_match_arm!(
|
||||
[ m![] - 1 ],
|
||||
"match () { _ => m![] - 1, }",
|
||||
"match () { _ => m![] - 1 }",
|
||||
);
|
||||
c2_match_arm!(
|
||||
[ m! {} - 1 ],
|
||||
"match () { _ => m! {} - 1, }",
|
||||
"match () { _ => m! {} - 1 }",
|
||||
);
|
||||
|
||||
// ExprKind::Closure
|
||||
c1!(expr, [ || {} ], "|| {}");
|
||||
@ -720,6 +735,21 @@ fn test_stmt() {
|
||||
"(loop { break 1; }) - 1;",
|
||||
"loop { break 1; } - 1",
|
||||
);
|
||||
c2_minus_one!(
|
||||
[ m!() ],
|
||||
"m!() - 1;",
|
||||
"m!() - 1"
|
||||
);
|
||||
c2_minus_one!(
|
||||
[ m![] ],
|
||||
"m![] - 1;",
|
||||
"m![] - 1"
|
||||
);
|
||||
c2_minus_one!(
|
||||
[ m! {} ],
|
||||
"(m! {}) - 1;",
|
||||
"m! {} - 1"
|
||||
);
|
||||
|
||||
// StmtKind::Empty
|
||||
c1!(stmt, [ ; ], ";");
|
||||
|
@ -1,3 +1,7 @@
|
||||
macro_rules! falsy {
|
||||
() => { false };
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
if true {
|
||||
} else false {
|
||||
@ -25,6 +29,32 @@ fn foo4() {
|
||||
{}
|
||||
}
|
||||
|
||||
fn foo5() {
|
||||
if true {
|
||||
} else falsy!() {
|
||||
//~^ ERROR expected `{`, found `falsy`
|
||||
}
|
||||
}
|
||||
|
||||
fn foo6() {
|
||||
if true {
|
||||
} else falsy!();
|
||||
//~^ ERROR expected `{`, found `falsy`
|
||||
}
|
||||
|
||||
fn foo7() {
|
||||
if true {
|
||||
} else falsy! {} {
|
||||
//~^ ERROR expected `{`, found `falsy`
|
||||
}
|
||||
}
|
||||
|
||||
fn foo8() {
|
||||
if true {
|
||||
} else falsy! {};
|
||||
//~^ ERROR expected `{`, found `falsy`
|
||||
}
|
||||
|
||||
fn falsy() -> bool {
|
||||
false
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: expected `{`, found keyword `false`
|
||||
--> $DIR/else-no-if.rs:3:12
|
||||
--> $DIR/else-no-if.rs:7:12
|
||||
|
|
||||
LL | } else false {
|
||||
| ---- ^^^^^
|
||||
@ -12,7 +12,7 @@ LL | } else if false {
|
||||
| ++
|
||||
|
||||
error: expected `{`, found `falsy`
|
||||
--> $DIR/else-no-if.rs:10:12
|
||||
--> $DIR/else-no-if.rs:14:12
|
||||
|
|
||||
LL | } else falsy() {
|
||||
| ---- ^^^^^
|
||||
@ -25,7 +25,7 @@ LL | } else if falsy() {
|
||||
| ++
|
||||
|
||||
error: expected `{`, found `falsy`
|
||||
--> $DIR/else-no-if.rs:17:12
|
||||
--> $DIR/else-no-if.rs:21:12
|
||||
|
|
||||
LL | } else falsy();
|
||||
| ^^^^^ expected `{`
|
||||
@ -36,7 +36,7 @@ LL | } else { falsy() };
|
||||
| + +
|
||||
|
||||
error: expected `{`, found keyword `loop`
|
||||
--> $DIR/else-no-if.rs:23:12
|
||||
--> $DIR/else-no-if.rs:27:12
|
||||
|
|
||||
LL | } else loop{}
|
||||
| ^^^^ expected `{`
|
||||
@ -46,5 +46,51 @@ help: try placing this code inside a block
|
||||
LL | } else { loop{} }
|
||||
| + +
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: expected `{`, found `falsy`
|
||||
--> $DIR/else-no-if.rs:34:12
|
||||
|
|
||||
LL | } else falsy!() {
|
||||
| ---- ^^^^^
|
||||
| |
|
||||
| expected an `if` or a block after this `else`
|
||||
|
|
||||
help: add an `if` if this is the condition of a chained `else if` statement
|
||||
|
|
||||
LL | } else if falsy!() {
|
||||
| ++
|
||||
|
||||
error: expected `{`, found `falsy`
|
||||
--> $DIR/else-no-if.rs:41:12
|
||||
|
|
||||
LL | } else falsy!();
|
||||
| ^^^^^ expected `{`
|
||||
|
|
||||
help: try placing this code inside a block
|
||||
|
|
||||
LL | } else { falsy!() };
|
||||
| + +
|
||||
|
||||
error: expected `{`, found `falsy`
|
||||
--> $DIR/else-no-if.rs:47:12
|
||||
|
|
||||
LL | } else falsy! {} {
|
||||
| ^^^^^ expected `{`
|
||||
|
|
||||
help: try placing this code inside a block
|
||||
|
|
||||
LL | } else { falsy! {} } {
|
||||
| + +
|
||||
|
||||
error: expected `{`, found `falsy`
|
||||
--> $DIR/else-no-if.rs:54:12
|
||||
|
|
||||
LL | } else falsy! {};
|
||||
| ^^^^^ expected `{`
|
||||
|
|
||||
help: try placing this code inside a block
|
||||
|
|
||||
LL | } else { falsy! {} };
|
||||
| + +
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
104
tests/ui/parser/macro/statement-boundaries.rs
Normal file
104
tests/ui/parser/macro/statement-boundaries.rs
Normal file
@ -0,0 +1,104 @@
|
||||
//@ run-pass
|
||||
//@ edition:2021
|
||||
|
||||
// This is a test of several uses of rustc_ast::util::classify::expr_requires_semi_to_be_stmt
|
||||
// by the Rust parser, which relates to the insertion of statement boundaries
|
||||
// after certain kinds of expressions if they appear at the head of a statement.
|
||||
|
||||
#![allow(unused_braces, unused_unsafe)]
|
||||
|
||||
macro_rules! unit {
|
||||
() => {
|
||||
{ () }
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct X;
|
||||
|
||||
fn main() {
|
||||
let x = X;
|
||||
|
||||
// There is a statement boundary before `|x| x`, so it's a closure.
|
||||
let _: fn(X) -> X = { if true {} |x| x };
|
||||
let _: fn(X) -> X = { if true {} else {} |x| x };
|
||||
let _: fn(X) -> X = { match () { () => {} } |x| x };
|
||||
let _: fn(X) -> X = { { () } |x| x };
|
||||
let _: fn(X) -> X = { unsafe {} |x| x };
|
||||
let _: fn(X) -> X = { while false {} |x| x };
|
||||
let _: fn(X) -> X = { loop { break; } |x| x };
|
||||
let _: fn(X) -> X = { for _ in 0..0 {} |x| x };
|
||||
let _: fn(X) -> X = { const {} |x| x };
|
||||
let _: fn(X) -> X = { unit! {} |x| x };
|
||||
|
||||
// No statement boundary, so `|x| x` is 2× BitOr operation.
|
||||
() = { "" |x| x };
|
||||
() = { ("") |x| x };
|
||||
() = { [""] |x| x };
|
||||
() = { unit!() |x| x };
|
||||
() = { unit![] |x| x };
|
||||
|
||||
// All the same cases, but as a match arm.
|
||||
() = match x {
|
||||
// Statement boundary before `| X`, which becomes a new arm with leading vert.
|
||||
X if false => if true {} | X if false => {}
|
||||
X if false => if true {} else {} | X if false => {}
|
||||
X if false => match () { () => {} } | X if false => {}
|
||||
X if false => { () } | X if false => {}
|
||||
X if false => unsafe {} | X if false => {}
|
||||
X if false => while false {} | X if false => {}
|
||||
X if false => loop { break; } | X if false => {}
|
||||
X if false => for _ in 0..0 {} | X if false => {}
|
||||
X if false => const {} | X if false => {}
|
||||
|
||||
// No statement boundary, so `| X` is BitOr.
|
||||
X if false => "" | X,
|
||||
X if false => ("") | X,
|
||||
X if false => [""] | X,
|
||||
X if false => unit! {} | X, // !! inconsistent with braced mac call in statement position
|
||||
X if false => unit!() | X,
|
||||
X if false => unit![] | X,
|
||||
|
||||
X => {}
|
||||
};
|
||||
|
||||
// Test how the statement boundary logic interacts with macro metavariables /
|
||||
// "invisible delimiters".
|
||||
macro_rules! assert_statement_boundary {
|
||||
($expr:expr) => {
|
||||
let _: fn(X) -> X = { $expr |x| x };
|
||||
|
||||
() = match X {
|
||||
X if false => $expr | X if false => {}
|
||||
X => {}
|
||||
};
|
||||
};
|
||||
}
|
||||
macro_rules! assert_no_statement_boundary {
|
||||
($expr:expr) => {
|
||||
() = { $expr |x| x };
|
||||
|
||||
() = match x {
|
||||
X if false => $expr | X,
|
||||
X => {}
|
||||
};
|
||||
};
|
||||
}
|
||||
assert_statement_boundary!(if true {});
|
||||
assert_no_statement_boundary!("");
|
||||
}
|
||||
|
||||
impl std::ops::BitOr<X> for () {
|
||||
type Output = ();
|
||||
fn bitor(self, _: X) {}
|
||||
}
|
||||
|
||||
impl std::ops::BitOr<X> for &str {
|
||||
type Output = ();
|
||||
fn bitor(self, _: X) {}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> std::ops::BitOr<X> for [T; N] {
|
||||
type Output = ();
|
||||
fn bitor(self, _: X) {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user