From fe60f19f7e98af78526364563fa6b40825fa97a8 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 1 Mar 2021 16:02:09 -0500 Subject: [PATCH 1/2] Ban custom inner attributes in expressions and statements --- compiler/rustc_expand/src/expand.rs | 20 +- compiler/rustc_resolve/src/macros.rs | 21 +- src/test/ui/proc-macro/inner-attrs.rs | 21 +- src/test/ui/proc-macro/inner-attrs.stderr | 32 +++ src/test/ui/proc-macro/inner-attrs.stdout | 302 ++++++++++++---------- 5 files changed, 240 insertions(+), 156 deletions(-) create mode 100644 src/test/ui/proc-macro/inner-attrs.stderr diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 2674ccced6f6..470788a972aa 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -206,30 +206,36 @@ ast_fragments! { } } +pub enum SupportsMacroExpansion { + No, + Yes { supports_inner_attrs: bool }, +} + impl AstFragmentKind { crate fn dummy(self, span: Span) -> AstFragment { self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment") } - /// Fragment supports macro expansion and not just inert attributes, `cfg` and `cfg_attr`. - pub fn supports_macro_expansion(self) -> bool { + pub fn supports_macro_expansion(self) -> SupportsMacroExpansion { match self { AstFragmentKind::OptExpr | AstFragmentKind::Expr - | AstFragmentKind::Pat - | AstFragmentKind::Ty | AstFragmentKind::Stmts - | AstFragmentKind::Items + | AstFragmentKind::Ty + | AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false }, + AstFragmentKind::Items | AstFragmentKind::TraitItems | AstFragmentKind::ImplItems - | AstFragmentKind::ForeignItems => true, + | AstFragmentKind::ForeignItems => { + SupportsMacroExpansion::Yes { supports_inner_attrs: true } + } AstFragmentKind::Arms | AstFragmentKind::Fields | AstFragmentKind::FieldPats | AstFragmentKind::GenericParams | AstFragmentKind::Params | AstFragmentKind::StructFields - | AstFragmentKind::Variants => false, + | AstFragmentKind::Variants => SupportsMacroExpansion::No, } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 2e47d4cecee4..2efce1e1984f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -17,7 +17,7 @@ use rustc_errors::struct_span_err; use rustc_expand::base::Annotatable; use rustc_expand::base::{Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::compile_declarative_macro; -use rustc_expand::expand::{AstFragment, Invocation, InvocationKind}; +use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion}; use rustc_feature::is_builtin_attr_name; use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id; @@ -278,12 +278,12 @@ impl<'a> ResolverExpand for Resolver<'a> { // Derives are not included when `invocations` are collected, so we have to add them here. let parent_scope = &ParentScope { derives, ..parent_scope }; - let require_inert = !invoc.fragment_kind.supports_macro_expansion(); + let supports_macro_expansion = invoc.fragment_kind.supports_macro_expansion(); let node_id = self.lint_node_id(eager_expansion_root); let (ext, res) = self.smart_resolve_macro_path( path, kind, - require_inert, + supports_macro_expansion, inner_attr, parent_scope, node_id, @@ -457,7 +457,7 @@ impl<'a> Resolver<'a> { &mut self, path: &ast::Path, kind: MacroKind, - require_inert: bool, + supports_macro_expansion: SupportsMacroExpansion, inner_attr: bool, parent_scope: &ParentScope<'a>, node_id: NodeId, @@ -505,8 +505,17 @@ impl<'a> Resolver<'a> { let unexpected_res = if ext.macro_kind() != kind { Some((kind.article(), kind.descr_expected())) - } else if require_inert && matches!(res, Res::Def(..)) { - Some(("a", "non-macro attribute")) + } else if matches!(res, Res::Def(..)) { + match supports_macro_expansion { + SupportsMacroExpansion::No => Some(("a", "non-macro attribute")), + SupportsMacroExpansion::Yes { supports_inner_attrs } => { + if inner_attr && !supports_inner_attrs { + Some(("a", "non-macro inner attribute")) + } else { + None + } + } + } } else { None }; diff --git a/src/test/ui/proc-macro/inner-attrs.rs b/src/test/ui/proc-macro/inner-attrs.rs index 6a353ca3263d..5707621f80e6 100644 --- a/src/test/ui/proc-macro/inner-attrs.rs +++ b/src/test/ui/proc-macro/inner-attrs.rs @@ -1,10 +1,10 @@ -// check-pass // compile-flags: -Z span-debug --error-format human // aux-build:test-macros.rs #![feature(custom_inner_attributes)] #![feature(proc_macro_hygiene)] #![feature(stmt_expr_attributes)] +#![feature(rustc_attrs)] #![no_std] // Don't load unnecessary hygiene information from std extern crate std; @@ -25,17 +25,34 @@ struct MyStruct { fn bar() { (#![print_target_and_args(fifth)] 1, 2); + //~^ ERROR expected non-macro inner attribute, found attribute macro + + #[print_target_and_args(tuple_attrs)] ( + #![cfg_attr(FALSE, rustc_dummy)] + 3, 4, { + #![cfg_attr(not(FALSE), rustc_dummy(innermost))] + 5 + } + ); + + #[print_target_and_args(array_attrs)] [ + #![rustc_dummy(inner)] + true; 0 + ]; [#![print_target_and_args(sixth)] 1 , 2]; + //~^ ERROR expected non-macro inner attribute, found attribute macro [#![print_target_and_args(seventh)] true ; 5]; - + //~^ ERROR expected non-macro inner attribute, found attribute macro match 0 { #![print_target_and_args(eighth)] + //~^ ERROR expected non-macro inner attribute, found attribute macro _ => {} } MyStruct { #![print_target_and_args(ninth)] field: true }; + //~^ ERROR expected non-macro inner attribute, found attribute macro } extern { diff --git a/src/test/ui/proc-macro/inner-attrs.stderr b/src/test/ui/proc-macro/inner-attrs.stderr new file mode 100644 index 000000000000..db774cbfb8fc --- /dev/null +++ b/src/test/ui/proc-macro/inner-attrs.stderr @@ -0,0 +1,32 @@ +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:27:9 + | +LL | (#![print_target_and_args(fifth)] 1, 2); + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:43:9 + | +LL | [#![print_target_and_args(sixth)] 1 , 2]; + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:45:9 + | +LL | [#![print_target_and_args(seventh)] true ; 5]; + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:49:12 + | +LL | #![print_target_and_args(eighth)] + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_target_and_args` + --> $DIR/inner-attrs.rs:54:19 + | +LL | MyStruct { #![print_target_and_args(ninth)] field: true }; + | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/proc-macro/inner-attrs.stdout b/src/test/ui/proc-macro/inner-attrs.stdout index 2fd8d8a242ef..ae04544e5335 100644 --- a/src/test/ui/proc-macro/inner-attrs.stdout +++ b/src/test/ui/proc-macro/inner-attrs.stdout @@ -290,231 +290,251 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): fifth +PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "fifth", - span: $DIR/inner-attrs.rs:27:31: 27:36 (#0), + ident: "tuple_attrs", + span: $DIR/inner-attrs.rs:30:29: 30:40 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): (1, 2) ; +PRINT-ATTR INPUT (DISPLAY): (# ! [cfg_attr(FALSE, rustc_dummy)] 3, 4, + { # ! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Group { delimiter: Parenthesis, stream: TokenStream [ - Literal { - kind: Integer, - symbol: "1", - suffix: None, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:31:9: 31:10 (#0), }, Punct { - ch: ',', + ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), + span: $DIR/inner-attrs.rs:31:10: 31:11 (#0), }, - Literal { - kind: Integer, - symbol: "2", - suffix: None, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg_attr", + span: $DIR/inner-attrs.rs:31:12: 31:20 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "FALSE", + span: $DIR/inner-attrs.rs:31:21: 31:26 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:31:26: 31:27 (#0), + }, + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:31:28: 31:39 (#0), + }, + ], + span: $DIR/inner-attrs.rs:31:20: 31:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:31:11: 31:41 (#0), }, - ], - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), - }, - Punct { - ch: ';', - spacing: Alone, - span: $DIR/inner-attrs.rs:27:5: 27:45 (#0), - }, -] -PRINT-ATTR_ARGS INPUT (DISPLAY): sixth -PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ - Ident { - ident: "sixth", - span: $DIR/inner-attrs.rs:29:31: 29:36 (#0), - }, -] -PRINT-ATTR INPUT (DISPLAY): [1, 2] ; -PRINT-ATTR INPUT (DEBUG): TokenStream [ - Group { - delimiter: Bracket, - stream: TokenStream [ Literal { kind: Integer, - symbol: "1", + symbol: "3", suffix: None, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), + span: $DIR/inner-attrs.rs:32:9: 32:10 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), + span: $DIR/inner-attrs.rs:32:10: 32:11 (#0), }, Literal { kind: Integer, - symbol: "2", + symbol: "4", suffix: None, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), - }, - ], - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), - }, - Punct { - ch: ';', - spacing: Alone, - span: $DIR/inner-attrs.rs:29:5: 29:46 (#0), - }, -] -PRINT-ATTR_ARGS INPUT (DISPLAY): seventh -PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ - Ident { - ident: "seventh", - span: $DIR/inner-attrs.rs:30:31: 30:38 (#0), - }, -] -PRINT-ATTR INPUT (DISPLAY): [true ; 5] ; -PRINT-ATTR INPUT (DEBUG): TokenStream [ - Group { - delimiter: Bracket, - stream: TokenStream [ - Ident { - ident: "true", - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:32:12: 32:13 (#0), }, Punct { - ch: ';', + ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:32:13: 32:14 (#0), }, - Literal { - kind: Integer, - symbol: "5", - suffix: None, - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:33:13: 33:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:33:14: 33:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg_attr", + span: $DIR/inner-attrs.rs:33:16: 33:24 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "not", + span: $DIR/inner-attrs.rs:33:25: 33:28 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "FALSE", + span: $DIR/inner-attrs.rs:33:29: 33:34 (#0), + }, + ], + span: $DIR/inner-attrs.rs:33:28: 33:35 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:33:35: 33:36 (#0), + }, + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:33:37: 33:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "innermost", + span: $DIR/inner-attrs.rs:33:49: 33:58 (#0), + }, + ], + span: $DIR/inner-attrs.rs:33:48: 33:59 (#0), + }, + ], + span: $DIR/inner-attrs.rs:33:24: 33:60 (#0), + }, + ], + span: $DIR/inner-attrs.rs:33:15: 33:61 (#0), + }, + Literal { + kind: Integer, + symbol: "5", + suffix: None, + span: $DIR/inner-attrs.rs:34:13: 34:14 (#0), + }, + ], + span: $DIR/inner-attrs.rs:32:15: 35:10 (#0), }, ], - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:30:43: 36:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:30:5: 30:51 (#0), + span: $DIR/inner-attrs.rs:36:6: 36:7 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): eighth +PRINT-ATTR_ARGS INPUT (DISPLAY): array_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "eighth", - span: $DIR/inner-attrs.rs:34:34: 34:40 (#0), + ident: "array_attrs", + span: $DIR/inner-attrs.rs:38:29: 38:40 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): match 0 { _ => { } } +PRINT-ATTR INPUT (DISPLAY): [# ! [rustc_dummy(inner)] true ; 0] ; PRINT-ATTR INPUT (DEBUG): TokenStream [ - Ident { - ident: "match", - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), - }, - Literal { - kind: Integer, - symbol: "0", - suffix: None, - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), - }, Group { - delimiter: Brace, + delimiter: Bracket, stream: TokenStream [ - Ident { - ident: "_", - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), - }, Punct { - ch: '=', + ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), }, Punct { - ch: '>', + ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), }, Group { - delimiter: Brace, - stream: TokenStream [], - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), - }, - ], - span: $DIR/inner-attrs.rs:33:5: 36:6 (#0), - }, -] -PRINT-ATTR_ARGS INPUT (DISPLAY): ninth -PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ - Ident { - ident: "ninth", - span: $DIR/inner-attrs.rs:38:41: 38:46 (#0), - }, -] -PRINT-ATTR INPUT (DISPLAY): MyStruct { field : true, } ; -PRINT-ATTR INPUT (DEBUG): TokenStream [ - Ident { - ident: "MyStruct", - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), - }, - Group { - delimiter: Brace, - stream: TokenStream [ - Ident { - ident: "field", - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), - }, - Punct { - ch: ':', - spacing: Alone, - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "inner", + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + }, + ], + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + }, + ], + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), }, Ident { ident: "true", - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), }, Punct { - ch: ',', + ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), }, ], - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:38:5: 38:63 (#0), + span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): tenth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "tenth", - span: $DIR/inner-attrs.rs:43:42: 43:47 (#0), + span: $DIR/inner-attrs.rs:60:42: 60:47 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn weird_extern() { } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), }, Ident { ident: "weird_extern", - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:42:5: 44:6 (#0), + span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), }, ] From 7504b9bb96236a340c41c95f95cf3c4a09341afc Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 25 Mar 2021 18:05:49 -0400 Subject: [PATCH 2/2] Avoid double-collection for expression nonterminals --- compiler/rustc_parse/src/parser/expr.rs | 15 +++++++++++++++ compiler/rustc_parse/src/parser/mod.rs | 2 +- compiler/rustc_parse/src/parser/nonterminal.rs | 17 +---------------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d64e5173b926..fe190bfe9d98 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -92,6 +92,21 @@ impl<'a> Parser<'a> { self.parse_expr_res(Restrictions::empty(), None) } + /// Parses an expression, forcing tokens to be collected + pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P> { + // If we have outer attributes, then the call to `collect_tokens_trailing_token` + // will be made for us. + if matches!(self.token.kind, TokenKind::Pound | TokenKind::DocComment(..)) { + self.parse_expr() + } else { + // If we don't have outer attributes, then we need to ensure + // that collection happens by using `collect_tokens_no_attrs`. + // Expression don't support custom inner attributes, so `parse_expr` + // will never try to collect tokens if we don't have outer attributes. + self.collect_tokens_no_attrs(|this| this.parse_expr()) + } + } + pub(super) fn parse_anon_const_expr(&mut self) -> PResult<'a, AnonConst> { self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 71103840f139..f0ee76d328c4 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -987,7 +987,7 @@ impl<'a> Parser<'a> { } // Collect tokens because they are used during lowering to HIR. - let expr = self.collect_tokens_no_attrs(|this| this.parse_expr())?; + let expr = self.parse_expr_force_collect()?; let span = expr.span; match &expr.kind { diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index fc25e883666d..0c49d1035835 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -128,22 +128,7 @@ impl<'a> Parser<'a> { })?) } - // If there are attributes present, then `parse_expr` will end up collecting tokens, - // turning the outer `collect_tokens_no_attrs` into a no-op due to the already present - // tokens. If there are *not* attributes present, then the outer - // `collect_tokens_no_attrs` will ensure that we will end up collecting tokens for the - // expressions. - // - // This is less efficient than it could be, since the outer `collect_tokens_no_attrs` - // still needs to snapshot the `TokenCursor` before calling `parse_expr`, even when - // `parse_expr` will end up collecting tokens. Ideally, this would work more like - // `parse_item`, and take in a `ForceCollect` parameter. However, this would require - // adding a `ForceCollect` parameter in a bunch of places in expression parsing - // for little gain. If the perf impact from this turns out to be noticeable, we should - // revisit this apporach. - NonterminalKind::Expr => { - token::NtExpr(self.collect_tokens_no_attrs(|this| this.parse_expr())?) - } + NonterminalKind::Expr => token::NtExpr(self.parse_expr_force_collect()?), NonterminalKind::Literal => { // The `:literal` matcher does not support attributes token::NtLiteral(