Skip to content

Commit 4682a8c

Browse files
authored
Rollup merge of rust-lang#88690 - m-ou-se:macro-braces-dot-question-expr-parse, r=nagisa
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 `?`.
2 parents c3c0f80 + 7d8d7a0 commit 4682a8c

File tree

2 files changed

+25
-11
lines changed

2 files changed

+25
-11
lines changed

compiler/rustc_parse/src/parser/stmt.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -155,17 +155,20 @@ impl<'a> Parser<'a> {
155155

156156
let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
157157

158-
let kind = if delim == token::Brace || self.token == token::Semi || self.token == token::Eof
159-
{
160-
StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
161-
} else {
162-
// Since none of the above applied, this is an expression statement macro.
163-
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
164-
let e = self.maybe_recover_from_bad_qpath(e, true)?;
165-
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
166-
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
167-
StmtKind::Expr(e)
168-
};
158+
let kind =
159+
if (delim == token::Brace && self.token != token::Dot && self.token != token::Question)
160+
|| self.token == token::Semi
161+
|| self.token == token::Eof
162+
{
163+
StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
164+
} else {
165+
// Since none of the above applied, this is an expression statement macro.
166+
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
167+
let e = self.maybe_recover_from_bad_qpath(e, true)?;
168+
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
169+
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
170+
StmtKind::Expr(e)
171+
};
169172
Ok(self.mk_stmt(lo.to(hi), kind))
170173
}
171174

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// check-pass
2+
3+
use std::io::Write;
4+
5+
fn main() -> Result<(), std::io::Error> {
6+
vec! { 1, 2, 3 }.len();
7+
write! { vec![], "" }?;
8+
println!{""}
9+
[0]; // separate statement, not indexing into the result of println.
10+
Ok(())
11+
}

0 commit comments

Comments
 (0)