Skip to content

Commit 86b0baf

Browse files
committed
Auto merge of rust-lang#84995 - petrochenkov:tcollect, r=Aaron1011
parser: Ensure that all nonterminals have tokens after parsing `parse_nonterminal` should always result in something with tokens. This requirement wasn't satisfied in two cases: - `stmt` nonterminal with expression statements (e.g. `0`, or `{}`, or `path + 1`) because `fn parse_stmt_without_recovery` forgot to propagate `force_collect` in some cases. - `expr` nonterminal with expressions with built-in attributes (e.g. `#[allow(warnings)] 0`) due to an incorrect optimization in `fn parse_expr_force_collect`, it assumed that all expressions starting with `#` have their tokens collected during parsing, but that's not true if all the attributes on that expression are built-in and inert. (Discovered when trying to implement eager `cfg` expansion for all attributes rust-lang#83824 (comment).) r? `@Aaron1011`
2 parents f57d5ba + cbdfa1e commit 86b0baf

File tree

8 files changed

+612
-31
lines changed

8 files changed

+612
-31
lines changed

compiler/rustc_ast/src/ast_like.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ impl AstLike for crate::token::Nonterminal {
8282
Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(),
8383
Nonterminal::NtPath(path) => path.tokens_mut(),
8484
Nonterminal::NtVis(vis) => vis.tokens_mut(),
85-
_ => panic!("Called tokens_mut on {:?}", self),
85+
Nonterminal::NtBlock(block) => block.tokens_mut(),
86+
Nonterminal::NtIdent(..) | Nonterminal::NtLifetime(..) | Nonterminal::NtTT(..) => None,
8687
}
8788
}
8889
}

compiler/rustc_parse/src/parser/attr_wrapper.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -342,16 +342,10 @@ impl<'a> Parser<'a> {
342342

343343
// If we support tokens at all
344344
if let Some(target_tokens) = ret.tokens_mut() {
345-
if let Some(target_tokens) = target_tokens {
346-
assert!(
347-
!self.capture_cfg,
348-
"Encountered existing tokens with capture_cfg set: {:?}",
349-
target_tokens
350-
);
351-
} else {
345+
if target_tokens.is_none() {
352346
// Store se our newly captured tokens into the AST node
353347
*target_tokens = Some(tokens.clone());
354-
};
348+
}
355349
}
356350

357351
let final_attrs = ret.attrs();

compiler/rustc_parse/src/parser/expr.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,7 @@ impl<'a> Parser<'a> {
9494

9595
/// Parses an expression, forcing tokens to be collected
9696
pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P<Expr>> {
97-
// If we have outer attributes, then the call to `collect_tokens_trailing_token`
98-
// will be made for us.
99-
if matches!(self.token.kind, TokenKind::Pound | TokenKind::DocComment(..)) {
100-
self.parse_expr()
101-
} else {
102-
// If we don't have outer attributes, then we need to ensure
103-
// that collection happens by using `collect_tokens_no_attrs`.
104-
// Expression don't support custom inner attributes, so `parse_expr`
105-
// will never try to collect tokens if we don't have outer attributes.
106-
self.collect_tokens_no_attrs(|this| this.parse_expr())
107-
}
97+
self.collect_tokens_no_attrs(|this| this.parse_expr())
10898
}
10999

110100
pub fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> {

compiler/rustc_parse/src/parser/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum BlockMode {
6363

6464
/// Whether or not we should force collection of tokens for an AST node,
6565
/// regardless of whether or not it has attributes
66+
#[derive(Clone, Copy, PartialEq)]
6667
pub enum ForceCollect {
6768
Yes,
6869
No,

compiler/rustc_parse/src/parser/nonterminal.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use rustc_ast::ptr::P;
22
use rustc_ast::token::{self, Nonterminal, NonterminalKind, Token};
3+
use rustc_ast::AstLike;
34
use rustc_ast_pretty::pprust;
45
use rustc_errors::PResult;
56
use rustc_span::symbol::{kw, Ident};
@@ -102,7 +103,7 @@ impl<'a> Parser<'a> {
102103
// which requires having captured tokens available. Since we cannot determine
103104
// in advance whether or not a proc-macro will be (transitively) invoked,
104105
// we always capture tokens for any `Nonterminal` which needs them.
105-
Ok(match kind {
106+
let mut nt = match kind {
106107
NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
107108
Some(item) => token::NtItem(item),
108109
None => {
@@ -169,7 +170,19 @@ impl<'a> Parser<'a> {
169170
return Err(self.struct_span_err(self.token.span, msg));
170171
}
171172
}
172-
})
173+
};
174+
175+
// If tokens are supported at all, they should be collected.
176+
if matches!(nt.tokens_mut(), Some(None)) {
177+
panic!(
178+
"Missing tokens for nt {:?} at {:?}: {:?}",
179+
nt,
180+
nt.span(),
181+
pprust::nonterminal_to_string(&nt)
182+
);
183+
}
184+
185+
Ok(nt)
173186
}
174187
}
175188

compiler/rustc_parse/src/parser/stmt.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ impl<'a> Parser<'a> {
7373
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
7474
// that starts like a path (1 token), but it fact not a path.
7575
// Also, we avoid stealing syntax from `parse_item_`.
76-
self.parse_stmt_path_start(lo, attrs, force_collect)?
76+
if force_collect == ForceCollect::Yes {
77+
self.collect_tokens_no_attrs(|this| this.parse_stmt_path_start(lo, attrs))
78+
} else {
79+
self.parse_stmt_path_start(lo, attrs)
80+
}?
7781
} else if let Some(item) =
7882
self.parse_item_common(attrs.clone(), false, true, |_| true, force_collect)?
7983
{
@@ -85,21 +89,22 @@ impl<'a> Parser<'a> {
8589
self.mk_stmt(lo, StmtKind::Empty)
8690
} else if self.token != token::CloseDelim(token::Brace) {
8791
// Remainder are line-expr stmts.
88-
let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs))?;
92+
let e = if force_collect == ForceCollect::Yes {
93+
self.collect_tokens_no_attrs(|this| {
94+
this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs))
95+
})
96+
} else {
97+
self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs))
98+
}?;
8999
self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
90100
} else {
91101
self.error_outer_attrs(&attrs.take_for_recovery());
92102
return Ok(None);
93103
}))
94104
}
95105

96-
fn parse_stmt_path_start(
97-
&mut self,
98-
lo: Span,
99-
attrs: AttrWrapper,
100-
force_collect: ForceCollect,
101-
) -> PResult<'a, Stmt> {
102-
let stmt = self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
106+
fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
107+
let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
103108
let path = this.parse_path(PathStyle::Expr)?;
104109

105110
if this.eat(&token::Not) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// check-pass
2+
// aux-build:test-macros.rs
3+
4+
#![feature(decl_macro)]
5+
#![feature(stmt_expr_attributes)]
6+
7+
#![no_std] // Don't load unnecessary hygiene information from std
8+
extern crate std;
9+
10+
#[macro_use]
11+
extern crate test_macros;
12+
13+
macro mac {
14+
(expr $expr:expr) => {
15+
#[derive(Print)]
16+
enum E {
17+
V = { let _ = $expr; 0 },
18+
}
19+
},
20+
(stmt $stmt:stmt) => {
21+
#[derive(Print)]
22+
enum E {
23+
V = { let _ = { $stmt }; 0 },
24+
}
25+
},
26+
}
27+
28+
const PATH: u8 = 2;
29+
30+
fn main() {
31+
mac!(expr #[allow(warnings)] 0);
32+
mac!(stmt 0);
33+
mac!(stmt {});
34+
mac!(stmt PATH);
35+
mac!(stmt 0 + 1);
36+
mac!(stmt PATH + 1);
37+
}

0 commit comments

Comments
 (0)