Skip to content

Commit 11b1e37

Browse files
committed
Force token collection to run when parsing nonterminals
Fixes #81007 Previously, we would fail to collect tokens in the proper place when only builtin attributes were present. As a result, we would end up with attribute tokens in the collected `TokenStream`, leading to duplication when we attempted to prepend the attributes from the AST node. We now explicitly track when token collection must be performed due to nomterminal parsing.
1 parent a4cbb44 commit 11b1e37

File tree

13 files changed

+233
-72
lines changed

13 files changed

+233
-72
lines changed

compiler/rustc_builtin_macros/src/source_util.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use rustc_ast::tokenstream::TokenStream;
55
use rustc_ast_pretty::pprust;
66
use rustc_expand::base::{self, *};
77
use rustc_expand::module::DirectoryOwnership;
8-
use rustc_parse::{self, new_parser_from_file, parser::Parser};
8+
use rustc_parse::parser::{ForceCollect, Parser};
9+
use rustc_parse::{self, new_parser_from_file};
910
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
1011
use rustc_span::symbol::Symbol;
1112
use rustc_span::{self, Pos, Span};
@@ -139,7 +140,7 @@ pub fn expand_include<'cx>(
139140
fn make_items(mut self: Box<ExpandResult<'a>>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
140141
let mut ret = SmallVec::new();
141142
while self.p.token != token::Eof {
142-
match self.p.parse_item() {
143+
match self.p.parse_item(ForceCollect::No) {
143144
Err(mut err) => {
144145
err.emit();
145146
break;

compiler/rustc_expand/src/expand.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_data_structures::map_in_place::MapInPlace;
2020
use rustc_data_structures::stack::ensure_sufficient_stack;
2121
use rustc_errors::{struct_span_err, Applicability, PResult};
2222
use rustc_feature::Features;
23-
use rustc_parse::parser::{AttemptLocalParseRecovery, Parser};
23+
use rustc_parse::parser::{AttemptLocalParseRecovery, ForceCollect, Parser};
2424
use rustc_parse::validate_attr;
2525
use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
2626
use rustc_session::lint::BuiltinLintDiagnostics;
@@ -913,7 +913,7 @@ pub fn parse_ast_fragment<'a>(
913913
Ok(match kind {
914914
AstFragmentKind::Items => {
915915
let mut items = SmallVec::new();
916-
while let Some(item) = this.parse_item()? {
916+
while let Some(item) = this.parse_item(ForceCollect::No)? {
917917
items.push(item);
918918
}
919919
AstFragment::Items(items)

compiler/rustc_expand/src/parse/tests.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_ast::{self as ast, PatKind};
88
use rustc_ast_pretty::pprust::item_to_string;
99
use rustc_errors::PResult;
1010
use rustc_parse::new_parser_from_source_str;
11+
use rustc_parse::parser::ForceCollect;
1112
use rustc_session::parse::ParseSess;
1213
use rustc_span::source_map::FilePathMapping;
1314
use rustc_span::symbol::{kw, sym, Symbol};
@@ -29,7 +30,7 @@ fn parse_item_from_source_str(
2930
source: String,
3031
sess: &ParseSess,
3132
) -> PResult<'_, Option<P<ast::Item>>> {
32-
new_parser_from_source_str(sess, name, source).parse_item()
33+
new_parser_from_source_str(sess, name, source).parse_item(ForceCollect::No)
3334
}
3435

3536
// Produces a `rustc_span::span`.
@@ -44,7 +45,7 @@ fn string_to_expr(source_str: String) -> P<ast::Expr> {
4445

4546
/// Parses a string, returns an item.
4647
fn string_to_item(source_str: String) -> Option<P<ast::Item>> {
47-
with_error_checking_parse(source_str, &sess(), |p| p.parse_item())
48+
with_error_checking_parse(source_str, &sess(), |p| p.parse_item(ForceCollect::No))
4849
}
4950

5051
#[should_panic]

compiler/rustc_expand/src/proc_macro.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_data_structures::sync::Lrc;
99
use rustc_errors::{struct_span_err, Applicability, ErrorReported};
1010
use rustc_lexer::is_ident;
1111
use rustc_parse::nt_to_tokenstream;
12+
use rustc_parse::parser::ForceCollect;
1213
use rustc_span::symbol::sym;
1314
use rustc_span::{Span, DUMMY_SP};
1415

@@ -117,7 +118,7 @@ impl MultiItemModifier for ProcMacroDerive {
117118
let mut items = vec![];
118119

119120
loop {
120-
match parser.parse_item() {
121+
match parser.parse_item(ForceCollect::No) {
121122
Ok(None) => break,
122123
Ok(Some(item)) => {
123124
if is_stmt {

compiler/rustc_parse/src/parser/expr.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,11 @@ impl<'a> Parser<'a> {
472472
/// Parses a prefix-unary-operator expr.
473473
fn parse_prefix_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> {
474474
let attrs = self.parse_or_use_outer_attributes(attrs)?;
475-
let needs_tokens = super::attr::maybe_needs_tokens(&attrs);
475+
// FIXME: Use super::attr::maybe_needs_tokens(&attrs) once we come up
476+
// with a good way of passing `force_tokens` through from `parse_nonterminal`.
477+
// Checking !attrs.is_empty() is correct, but will cause us to unnecessarily
478+
// capture tokens in some circumstances.
479+
let needs_tokens = !attrs.is_empty();
476480
let do_parse = |this: &mut Parser<'a>| {
477481
let lo = this.token.span;
478482
// Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()

compiler/rustc_parse/src/parser/item.rs

+46-41
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error};
22
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
3-
use super::{FollowedByType, Parser, PathStyle};
3+
use super::{FollowedByType, ForceCollect, Parser, PathStyle};
44

5-
use crate::maybe_whole;
5+
use crate::{maybe_collect_tokens, maybe_whole};
66

77
use rustc_ast::ptr::P;
88
use rustc_ast::token::{self, TokenKind};
@@ -69,7 +69,7 @@ impl<'a> Parser<'a> {
6969
unsafety: Unsafe,
7070
) -> PResult<'a, Mod> {
7171
let mut items = vec![];
72-
while let Some(item) = self.parse_item()? {
72+
while let Some(item) = self.parse_item(ForceCollect::No)? {
7373
items.push(item);
7474
self.maybe_consume_incorrect_semicolon(&items);
7575
}
@@ -93,13 +93,17 @@ impl<'a> Parser<'a> {
9393
pub(super) type ItemInfo = (Ident, ItemKind);
9494

9595
impl<'a> Parser<'a> {
96-
pub fn parse_item(&mut self) -> PResult<'a, Option<P<Item>>> {
97-
self.parse_item_(|_| true).map(|i| i.map(P))
96+
pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<P<Item>>> {
97+
self.parse_item_(|_| true, force_collect).map(|i| i.map(P))
9898
}
9999

100-
fn parse_item_(&mut self, req_name: ReqName) -> PResult<'a, Option<Item>> {
100+
fn parse_item_(
101+
&mut self,
102+
req_name: ReqName,
103+
force_collect: ForceCollect,
104+
) -> PResult<'a, Option<Item>> {
101105
let attrs = self.parse_outer_attributes()?;
102-
self.parse_item_common(attrs, true, false, req_name)
106+
self.parse_item_common(attrs, true, false, req_name, force_collect)
103107
}
104108

105109
pub(super) fn parse_item_common(
@@ -108,6 +112,7 @@ impl<'a> Parser<'a> {
108112
mac_allowed: bool,
109113
attrs_allowed: bool,
110114
req_name: ReqName,
115+
force_collect: ForceCollect,
111116
) -> PResult<'a, Option<Item>> {
112117
maybe_whole!(self, NtItem, |item| {
113118
let mut item = item;
@@ -116,16 +121,12 @@ impl<'a> Parser<'a> {
116121
Some(item.into_inner())
117122
});
118123

119-
let needs_tokens = super::attr::maybe_needs_tokens(&attrs);
120-
121124
let mut unclosed_delims = vec![];
122-
let parse_item = |this: &mut Self| {
125+
let item = maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Self| {
123126
let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name);
124127
unclosed_delims.append(&mut this.unclosed_delims);
125128
item
126-
};
127-
128-
let item = if needs_tokens { self.collect_tokens(parse_item) } else { parse_item(self) }?;
129+
})?;
129130

130131
self.unclosed_delims.append(&mut unclosed_delims);
131132
Ok(item)
@@ -731,20 +732,22 @@ impl<'a> Parser<'a> {
731732

732733
/// Parses associated items.
733734
fn parse_assoc_item(&mut self, req_name: ReqName) -> PResult<'a, Option<Option<P<AssocItem>>>> {
734-
Ok(self.parse_item_(req_name)?.map(|Item { attrs, id, span, vis, ident, kind, tokens }| {
735-
let kind = match AssocItemKind::try_from(kind) {
736-
Ok(kind) => kind,
737-
Err(kind) => match kind {
738-
ItemKind::Static(a, _, b) => {
739-
self.struct_span_err(span, "associated `static` items are not allowed")
740-
.emit();
741-
AssocItemKind::Const(Defaultness::Final, a, b)
742-
}
743-
_ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"),
744-
},
745-
};
746-
Some(P(Item { attrs, id, span, vis, ident, kind, tokens }))
747-
}))
735+
Ok(self.parse_item_(req_name, ForceCollect::No)?.map(
736+
|Item { attrs, id, span, vis, ident, kind, tokens }| {
737+
let kind = match AssocItemKind::try_from(kind) {
738+
Ok(kind) => kind,
739+
Err(kind) => match kind {
740+
ItemKind::Static(a, _, b) => {
741+
self.struct_span_err(span, "associated `static` items are not allowed")
742+
.emit();
743+
AssocItemKind::Const(Defaultness::Final, a, b)
744+
}
745+
_ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"),
746+
},
747+
};
748+
Some(P(Item { attrs, id, span, vis, ident, kind, tokens }))
749+
},
750+
))
748751
}
749752

750753
/// Parses a `type` alias with the following grammar:
@@ -921,19 +924,21 @@ impl<'a> Parser<'a> {
921924

922925
/// Parses a foreign item (one in an `extern { ... }` block).
923926
pub fn parse_foreign_item(&mut self) -> PResult<'a, Option<Option<P<ForeignItem>>>> {
924-
Ok(self.parse_item_(|_| true)?.map(|Item { attrs, id, span, vis, ident, kind, tokens }| {
925-
let kind = match ForeignItemKind::try_from(kind) {
926-
Ok(kind) => kind,
927-
Err(kind) => match kind {
928-
ItemKind::Const(_, a, b) => {
929-
self.error_on_foreign_const(span, ident);
930-
ForeignItemKind::Static(a, Mutability::Not, b)
931-
}
932-
_ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"),
933-
},
934-
};
935-
Some(P(Item { attrs, id, span, vis, ident, kind, tokens }))
936-
}))
927+
Ok(self.parse_item_(|_| true, ForceCollect::No)?.map(
928+
|Item { attrs, id, span, vis, ident, kind, tokens }| {
929+
let kind = match ForeignItemKind::try_from(kind) {
930+
Ok(kind) => kind,
931+
Err(kind) => match kind {
932+
ItemKind::Const(_, a, b) => {
933+
self.error_on_foreign_const(span, ident);
934+
ForeignItemKind::Static(a, Mutability::Not, b)
935+
}
936+
_ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"),
937+
},
938+
};
939+
Some(P(Item { attrs, id, span, vis, ident, kind, tokens }))
940+
},
941+
))
937942
}
938943

939944
fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option<T> {
@@ -1515,7 +1520,7 @@ impl<'a> Parser<'a> {
15151520
{
15161521
let kw_token = self.token.clone();
15171522
let kw_str = pprust::token_to_string(&kw_token);
1518-
let item = self.parse_item()?;
1523+
let item = self.parse_item(ForceCollect::No)?;
15191524

15201525
self.struct_span_err(
15211526
kw_token.span,

compiler/rustc_parse/src/parser/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ enum BlockMode {
5454
Ignore,
5555
}
5656

57+
/// Whether or not we should force collection of tokens for an AST node,
58+
/// regardless of whether or not it has attributes
59+
pub enum ForceCollect {
60+
Yes,
61+
No,
62+
}
63+
5764
/// Like `maybe_whole_expr`, but for things other than expressions.
5865
#[macro_export]
5966
macro_rules! maybe_whole {
@@ -1413,3 +1420,16 @@ fn make_token_stream(
14131420
assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack);
14141421
TokenStream::new(final_buf.inner)
14151422
}
1423+
1424+
#[macro_export]
1425+
macro_rules! maybe_collect_tokens {
1426+
($self:ident, $force_collect:expr, $attrs:expr, $f:expr) => {
1427+
if matches!($force_collect, ForceCollect::Yes)
1428+
|| $crate::parser::attr::maybe_needs_tokens($attrs)
1429+
{
1430+
$self.collect_tokens($f)
1431+
} else {
1432+
$f($self)
1433+
}
1434+
};
1435+
}

compiler/rustc_parse/src/parser/nonterminal.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_errors::PResult;
55
use rustc_span::symbol::{kw, Ident};
66

77
use crate::parser::pat::{GateOr, RecoverComma};
8-
use crate::parser::{FollowedByType, Parser, PathStyle};
8+
use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle};
99

1010
impl<'a> Parser<'a> {
1111
/// Checks whether a non-terminal may begin with a particular token.
@@ -98,7 +98,7 @@ impl<'a> Parser<'a> {
9898
// in advance whether or not a proc-macro will be (transitively) invoked,
9999
// we always capture tokens for any `Nonterminal` which needs them.
100100
Ok(match kind {
101-
NonterminalKind::Item => match self.collect_tokens(|this| this.parse_item())? {
101+
NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? {
102102
Some(item) => token::NtItem(item),
103103
None => {
104104
return Err(self.struct_span_err(self.token.span, "expected an item keyword"));
@@ -107,7 +107,7 @@ impl<'a> Parser<'a> {
107107
NonterminalKind::Block => {
108108
token::NtBlock(self.collect_tokens(|this| this.parse_block())?)
109109
}
110-
NonterminalKind::Stmt => match self.collect_tokens(|this| this.parse_stmt())? {
110+
NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? {
111111
Some(s) => token::NtStmt(s),
112112
None => {
113113
return Err(self.struct_span_err(self.token.span, "expected a statement"));

compiler/rustc_parse/src/parser/stmt.rs

+15-18
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use super::diagnostics::{AttemptLocalParseRecovery, Error};
33
use super::expr::LhsExpr;
44
use super::pat::{GateOr, RecoverComma};
55
use super::path::PathStyle;
6-
use super::{BlockMode, Parser, Restrictions, SemiColonMode};
7-
use crate::maybe_whole;
6+
use super::{BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode};
7+
use crate::{maybe_collect_tokens, maybe_whole};
88

99
use rustc_ast as ast;
1010
use rustc_ast::attr::HasAttrs;
@@ -24,17 +24,21 @@ impl<'a> Parser<'a> {
2424
/// Parses a statement. This stops just before trailing semicolons on everything but items.
2525
/// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
2626
// Public for rustfmt usage.
27-
pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
28-
Ok(self.parse_stmt_without_recovery().unwrap_or_else(|mut e| {
27+
pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
28+
Ok(self.parse_stmt_without_recovery(force_collect).unwrap_or_else(|mut e| {
2929
e.emit();
3030
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
3131
None
3232
}))
3333
}
3434

35-
fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option<Stmt>> {
35+
/// If `force_capture` is true, forces collection of tokens regardless of whether
36+
/// or not we have attributes
37+
fn parse_stmt_without_recovery(
38+
&mut self,
39+
force_collect: ForceCollect,
40+
) -> PResult<'a, Option<Stmt>> {
3641
let mut attrs = self.parse_outer_attributes()?;
37-
let has_attrs = !attrs.is_empty();
3842
let lo = self.token.span;
3943

4044
maybe_whole!(self, NtStmt, |stmt| {
@@ -46,7 +50,7 @@ impl<'a> Parser<'a> {
4650
Some(stmt)
4751
});
4852

49-
let parse_stmt_inner = |this: &mut Self| {
53+
maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Self| {
5054
let stmt = if this.eat_keyword(kw::Let) {
5155
this.parse_local_mk(lo, attrs.into())?
5256
} else if this.is_kw_followed_by_ident(kw::Mut) {
@@ -69,7 +73,7 @@ impl<'a> Parser<'a> {
6973
// Also, we avoid stealing syntax from `parse_item_`.
7074
this.parse_stmt_path_start(lo, attrs)?
7175
} else if let Some(item) =
72-
this.parse_item_common(attrs.clone(), false, true, |_| true)?
76+
this.parse_item_common(attrs.clone(), false, true, |_| true, force_collect)?
7377
{
7478
// FIXME: Bad copy of attrs
7579
this.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
@@ -86,14 +90,7 @@ impl<'a> Parser<'a> {
8690
return Ok(None);
8791
};
8892
Ok(Some(stmt))
89-
};
90-
91-
let stmt = if has_attrs {
92-
self.collect_tokens(parse_stmt_inner)?
93-
} else {
94-
parse_stmt_inner(self)?
95-
};
96-
Ok(stmt)
93+
})
9794
}
9895

9996
fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, Stmt> {
@@ -292,7 +289,7 @@ impl<'a> Parser<'a> {
292289
// bar;
293290
//
294291
// which is valid in other languages, but not Rust.
295-
match self.parse_stmt_without_recovery() {
292+
match self.parse_stmt_without_recovery(ForceCollect::No) {
296293
// If the next token is an open brace (e.g., `if a b {`), the place-
297294
// inside-a-block suggestion would be more likely wrong than right.
298295
Ok(Some(_))
@@ -395,7 +392,7 @@ impl<'a> Parser<'a> {
395392
// Skip looking for a trailing semicolon when we have an interpolated statement.
396393
maybe_whole!(self, NtStmt, |x| Some(x));
397394

398-
let mut stmt = match self.parse_stmt_without_recovery()? {
395+
let mut stmt = match self.parse_stmt_without_recovery(ForceCollect::No)? {
399396
Some(stmt) => stmt,
400397
None => return Ok(None),
401398
};

0 commit comments

Comments
 (0)