Rollup merge of #78376 - Aaron1011:feature/consistent-empty-expr, r=petrochenkov
Treat trailing semicolon as a statement in macro call See #61733 (comment) We now preserve the trailing semicolon in a macro invocation, even if the macro expands to nothing. As a result, the following code no longer compiles: ```rust macro_rules! empty { () => { } } fn foo() -> bool { //~ ERROR mismatched { true } //~ ERROR mismatched empty!(); } ``` Previously, `{ true }` would be considered the trailing expression, even though there's a semicolon in `empty!();` This makes macro expansion more token-based.
This commit is contained in:
commit
0716724a0b
@ -905,6 +905,13 @@ pub struct Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Stmt {
|
||||||
|
pub fn has_trailing_semicolon(&self) -> bool {
|
||||||
|
match &self.kind {
|
||||||
|
StmtKind::Semi(_) => true,
|
||||||
|
StmtKind::MacCall(mac) => matches!(mac.style, MacStmtStyle::Semicolon),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn add_trailing_semicolon(mut self) -> Self {
|
pub fn add_trailing_semicolon(mut self) -> Self {
|
||||||
self.kind = match self.kind {
|
self.kind = match self.kind {
|
||||||
StmtKind::Expr(expr) => StmtKind::Semi(expr),
|
StmtKind::Expr(expr) => StmtKind::Semi(expr),
|
||||||
|
@ -310,8 +310,44 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if style == ast::MacStmtStyle::Semicolon {
|
if style == ast::MacStmtStyle::Semicolon {
|
||||||
|
// Implement the proposal described in
|
||||||
|
// https://github.com/rust-lang/rust/issues/61733#issuecomment-509626449
|
||||||
|
//
|
||||||
|
// The macro invocation expands to the list of statements.
|
||||||
|
// If the list of statements is empty, then 'parse'
|
||||||
|
// the trailing semicolon on the original invocation
|
||||||
|
// as an empty statement. That is:
|
||||||
|
//
|
||||||
|
// `empty();` is parsed as a single `StmtKind::Empty`
|
||||||
|
//
|
||||||
|
// If the list of statements is non-empty, see if the
|
||||||
|
// final statement alreayd has a trailing semicolon.
|
||||||
|
//
|
||||||
|
// If it doesn't have a semicolon, then 'parse' the trailing semicolon
|
||||||
|
// from the invocation as part of the final statement,
|
||||||
|
// using `stmt.add_trailing_semicolon()`
|
||||||
|
//
|
||||||
|
// If it does have a semicolon, then 'parse' the trailing semicolon
|
||||||
|
// from the invocation as a new StmtKind::Empty
|
||||||
|
|
||||||
|
// FIXME: We will need to preserve the original
|
||||||
|
// semicolon token and span as part of #15701
|
||||||
|
let empty_stmt = ast::Stmt {
|
||||||
|
id: ast::DUMMY_NODE_ID,
|
||||||
|
kind: ast::StmtKind::Empty,
|
||||||
|
span: DUMMY_SP,
|
||||||
|
tokens: None,
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(stmt) = stmts.pop() {
|
if let Some(stmt) = stmts.pop() {
|
||||||
stmts.push(stmt.add_trailing_semicolon());
|
if stmt.has_trailing_semicolon() {
|
||||||
|
stmts.push(stmt);
|
||||||
|
stmts.push(empty_stmt);
|
||||||
|
} else {
|
||||||
|
stmts.push(stmt.add_trailing_semicolon());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stmts.push(empty_stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,11 @@ impl EarlyLintPass for RedundantSemicolons {
|
|||||||
|
|
||||||
fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) {
|
fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) {
|
||||||
if let Some((span, multiple)) = seq.take() {
|
if let Some((span, multiple)) = seq.take() {
|
||||||
|
// FIXME: Find a better way of ignoring the trailing
|
||||||
|
// semicolon from macro expansion
|
||||||
|
if span == rustc_span::DUMMY_SP {
|
||||||
|
return;
|
||||||
|
}
|
||||||
cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| {
|
cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| {
|
||||||
let (msg, rem) = if multiple {
|
let (msg, rem) = if multiple {
|
||||||
("unnecessary trailing semicolons", "remove these semicolons")
|
("unnecessary trailing semicolons", "remove these semicolons")
|
||||||
|
10
src/test/ui/macros/empty-trailing-stmt.rs
Normal file
10
src/test/ui/macros/empty-trailing-stmt.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
macro_rules! empty {
|
||||||
|
() => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo() -> bool { //~ ERROR mismatched
|
||||||
|
{ true } //~ ERROR mismatched
|
||||||
|
empty!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
17
src/test/ui/macros/empty-trailing-stmt.stderr
Normal file
17
src/test/ui/macros/empty-trailing-stmt.stderr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/empty-trailing-stmt.rs:6:7
|
||||||
|
|
|
||||||
|
LL | { true }
|
||||||
|
| ^^^^ expected `()`, found `bool`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/empty-trailing-stmt.rs:5:13
|
||||||
|
|
|
||||||
|
LL | fn foo() -> bool {
|
||||||
|
| --- ^^^^ expected `bool`, found `()`
|
||||||
|
| |
|
||||||
|
| implicitly returns `()` as its body has no tail or `return` expression
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
@ -40,7 +40,7 @@ macro_rules! produce_it
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main /* 0#0 */() { }
|
fn main /* 0#0 */() { ; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Expansions:
|
Expansions:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user