Treat trailing semicolon as a statement in macro call
See https://github.com/rust-lang/rust/issues/61733#issuecomment-716188981 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:
parent
499ebcfdf3
commit
e78e9d4a06
@ -905,6 +905,13 @@ pub struct 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 {
|
||||
self.kind = match self.kind {
|
||||
StmtKind::Expr(expr) => StmtKind::Semi(expr),
|
||||
|
@ -310,8 +310,44 @@ fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
|
||||
};
|
||||
|
||||
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() {
|
||||
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 @@ fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
|
||||
|
||||
fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) {
|
||||
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| {
|
||||
let (msg, rem) = if multiple {
|
||||
("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:
|
||||
|
Loading…
Reference in New Issue
Block a user