]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #88690 - m-ou-se:macro-braces-dot-question-expr-parse, r=nagisa
authorManish Goregaokar <manishsmail@gmail.com>
Wed, 15 Sep 2021 21:56:57 +0000 (14:56 -0700)
committerGitHub <noreply@github.com>
Wed, 15 Sep 2021 21:56:57 +0000 (14:56 -0700)
Accept `m!{ .. }.method()` and `m!{ .. }?` statements.

This PR fixes something that I keep running into when using `quote!{}.into()` in a proc macro to convert the `proc_macro2::TokenStream` to a `proc_macro::TokenStream`:

Before:

```
error: expected expression, found `.`
 --> src/lib.rs:6:6
  |
4 |     quote! {
5 |         ...
6 |     }.into()
  |      ^ expected expression
```

After:
```
```
(No output, compiles fine.)

---

Context:

For expressions like `{ 1 }` and `if true { 1 } else { 2 }`, we accept them as full statements without a trailing `;`, which means the following is not accepted:

```rust
{ 1 } - 1 // error
```

since that is parsed as two statements: `{ 1 }` and `-1`. Syntactically correct, but the type of `{ 1 }` should be `()` as there is no `;`.

However, for specifically `.` and `?` after the `}`, we do [continue parsing it as an expression](https://github.com/rust-lang/rust/blob/13db8440bbbe42870bc828d4ec3e965b38670277/compiler/rustc_parse/src/parser/expr.rs#L864-L876):

```rust
{ "abc" }.len(); // ok
```

For braced macro invocations, we do not do this:

```rust
vec![1, 2, 3].len(); // ok
vec!{1, 2, 3}.len(); // error
```

(It parses `vec!{1, 2, 3}` as a full statement, and then complains about `.len()` not being a valid expression.)

This PR changes this to also look for a `.` and `?` after a braced macro invocation. We can be sure the macro is an expression and not a full statement in those cases, since no statement can start with a `.` or `?`.

compiler/rustc_parse/src/parser/stmt.rs
src/test/ui/parser/macro-braces-dot-question.rs [new file with mode: 0644]

index 25dcb4a112de1c7e030498a7167e97be22526055..9ec6effeb4e03a9e2ed2368204ed75511a38eb3d 100644 (file)
@@ -155,17 +155,20 @@ fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResu
 
         let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
 
-        let kind = if delim == token::Brace || self.token == token::Semi || self.token == token::Eof
-        {
-            StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
-        } else {
-            // Since none of the above applied, this is an expression statement macro.
-            let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
-            let e = self.maybe_recover_from_bad_qpath(e, true)?;
-            let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
-            let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
-            StmtKind::Expr(e)
-        };
+        let kind =
+            if (delim == token::Brace && self.token != token::Dot && self.token != token::Question)
+                || self.token == token::Semi
+                || self.token == token::Eof
+            {
+                StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
+            } else {
+                // Since none of the above applied, this is an expression statement macro.
+                let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
+                let e = self.maybe_recover_from_bad_qpath(e, true)?;
+                let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
+                let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
+                StmtKind::Expr(e)
+            };
         Ok(self.mk_stmt(lo.to(hi), kind))
     }
 
diff --git a/src/test/ui/parser/macro-braces-dot-question.rs b/src/test/ui/parser/macro-braces-dot-question.rs
new file mode 100644 (file)
index 0000000..016b434
--- /dev/null
@@ -0,0 +1,11 @@
+// check-pass
+
+use std::io::Write;
+
+fn main() -> Result<(), std::io::Error> {
+    vec! { 1, 2, 3 }.len();
+    write! { vec![], "" }?;
+    println!{""}
+    [0]; // separate statement, not indexing into the result of println.
+    Ok(())
+}