]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #78376 - Aaron1011:feature/consistent-empty-expr, r=petrochenkov
authorYuki Okushi <huyuumi.dev@gmail.com>
Tue, 3 Nov 2020 06:27:03 +0000 (15:27 +0900)
committerGitHub <noreply@github.com>
Tue, 3 Nov 2020 06:27:03 +0000 (15:27 +0900)
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.

compiler/rustc_ast/src/ast.rs
compiler/rustc_expand/src/placeholders.rs
compiler/rustc_lint/src/redundant_semicolon.rs
src/test/ui/macros/empty-trailing-stmt.rs [new file with mode: 0644]
src/test/ui/macros/empty-trailing-stmt.stderr [new file with mode: 0644]
src/test/ui/proc-macro/meta-macro-hygiene.stdout

index 7d5e235c885269523a50bf7e3f4da7826ad31e7e..f13d67b9c15840178eb35adf6e9fd90fd87c47e1 100644 (file)
@@ -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),
index 1bc14ae41bf29106372e22776ea96705ebff1983..0cffca17271247d3d1f5e622c04b5fb586608623 100644 (file)
@@ -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);
             }
         }
 
index a31deb87ff0d062fd37d7503a80570074642195b..84cc7b68d4ca9ef45bc9f14b8395b54347638fab 100644 (file)
@@ -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")
diff --git a/src/test/ui/macros/empty-trailing-stmt.rs b/src/test/ui/macros/empty-trailing-stmt.rs
new file mode 100644 (file)
index 0000000..3d78ed4
--- /dev/null
@@ -0,0 +1,10 @@
+macro_rules! empty {
+    () => { }
+}
+
+fn foo() -> bool { //~ ERROR mismatched
+    { true } //~ ERROR mismatched
+    empty!();
+}
+
+fn main() {}
diff --git a/src/test/ui/macros/empty-trailing-stmt.stderr b/src/test/ui/macros/empty-trailing-stmt.stderr
new file mode 100644 (file)
index 0000000..e88b127
--- /dev/null
@@ -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`.
index 81cebae17aeba4d1690326f5831650a86638be59..a067b7b5411dd87e05f1b5c0df6f7b506e78f7f7 100644 (file)
@@ -40,7 +40,7 @@ macro_rules! produce_it
     }
 }
 
-fn main /* 0#0 */() { }
+fn main /* 0#0 */() { }
 
 /*
 Expansions: