Skip to content

Commit be0ea0c

Browse files
committedAug 16, 2024
Auto merge of #128725 - nnethercote:fix-assoc-expr-collect-problems, r=petrochenkov
Fix problems with assoc expr token collection There are several cases involving assoc exprs and attributes where the current code does the wrong thing. This PR adds some tests that demonstrate the problems and then fixes them. r? `@petrochenkov`
2 parents 4b7d074 + 9d31f86 commit be0ea0c

File tree

12 files changed

+483
-298
lines changed

12 files changed

+483
-298
lines changed
 

‎compiler/rustc_parse/src/parser/attr.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use rustc_span::{sym, BytePos, Span};
88
use thin_vec::ThinVec;
99
use tracing::debug;
1010

11-
use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle};
11+
use super::{
12+
AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle, Trailing,
13+
UsePreAttrPos,
14+
};
1215
use crate::{errors, fluent_generated as fluent, maybe_whole};
1316

1417
// Public for rustfmt usage
@@ -257,7 +260,8 @@ impl<'a> Parser<'a> {
257260
pub fn parse_attr_item(&mut self, force_collect: ForceCollect) -> PResult<'a, ast::AttrItem> {
258261
maybe_whole!(self, NtMeta, |attr| attr.into_inner());
259262

260-
let do_parse = |this: &mut Self, _empty_attrs| {
263+
// Attr items don't have attributes.
264+
self.collect_tokens(None, AttrWrapper::empty(), force_collect, |this, _empty_attrs| {
261265
let is_unsafe = this.eat_keyword(kw::Unsafe);
262266
let unsafety = if is_unsafe {
263267
let unsafe_span = this.prev_token.span;
@@ -273,10 +277,12 @@ impl<'a> Parser<'a> {
273277
if is_unsafe {
274278
this.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
275279
}
276-
Ok((ast::AttrItem { unsafety, path, args, tokens: None }, false))
277-
};
278-
// Attr items don't have attributes.
279-
self.collect_tokens_trailing_token(AttrWrapper::empty(), force_collect, do_parse)
280+
Ok((
281+
ast::AttrItem { unsafety, path, args, tokens: None },
282+
Trailing::No,
283+
UsePreAttrPos::No,
284+
))
285+
})
280286
}
281287

282288
/// Parses attributes that appear after the opening of an item. These should
@@ -309,8 +315,8 @@ impl<'a> Parser<'a> {
309315
};
310316
if let Some(attr) = attr {
311317
// If we are currently capturing tokens (i.e. we are within a call to
312-
// `Parser::collect_tokens_trailing_tokens`) record the token positions of this
313-
// inner attribute, for possible later processing in a `LazyAttrTokenStream`.
318+
// `Parser::collect_tokens`) record the token positions of this inner attribute,
319+
// for possible later processing in a `LazyAttrTokenStream`.
314320
if let Capturing::Yes = self.capture_state.capturing {
315321
let end_pos = self.num_bump_calls;
316322
let parser_range = ParserRange(start_pos..end_pos);

‎compiler/rustc_parse/src/parser/attr_wrapper.rs

Lines changed: 75 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,56 @@ use rustc_span::{sym, Span, DUMMY_SP};
1212

1313
use super::{
1414
Capturing, FlatToken, ForceCollect, NodeRange, NodeReplacement, Parser, ParserRange,
15-
TokenCursor,
15+
TokenCursor, Trailing,
1616
};
1717

18+
// When collecting tokens, this fully captures the start point. Usually its
19+
// just after outer attributes, but occasionally it's before.
20+
#[derive(Clone, Debug)]
21+
pub(super) struct CollectPos {
22+
start_token: (Token, Spacing),
23+
cursor_snapshot: TokenCursor,
24+
start_pos: u32,
25+
}
26+
27+
pub(super) enum UsePreAttrPos {
28+
No,
29+
Yes,
30+
}
31+
1832
/// A wrapper type to ensure that the parser handles outer attributes correctly.
1933
/// When we parse outer attributes, we need to ensure that we capture tokens
2034
/// for the attribute target. This allows us to perform cfg-expansion on
2135
/// a token stream before we invoke a derive proc-macro.
2236
///
2337
/// This wrapper prevents direct access to the underlying `ast::AttrVec`.
2438
/// Parsing code can only get access to the underlying attributes
25-
/// by passing an `AttrWrapper` to `collect_tokens_trailing_token`.
39+
/// by passing an `AttrWrapper` to `collect_tokens`.
2640
/// This makes it difficult to accidentally construct an AST node
2741
/// (which stores an `ast::AttrVec`) without first collecting tokens.
2842
///
2943
/// This struct has its own module, to ensure that the parser code
3044
/// cannot directly access the `attrs` field.
3145
#[derive(Debug, Clone)]
32-
pub struct AttrWrapper {
46+
pub(super) struct AttrWrapper {
3347
attrs: AttrVec,
3448
// The start of the outer attributes in the parser's token stream.
3549
// This lets us create a `NodeReplacement` for the entire attribute
36-
// target, including outer attributes.
37-
start_pos: u32,
50+
// target, including outer attributes. `None` if there are no outer
51+
// attributes.
52+
start_pos: Option<u32>,
3853
}
3954

4055
impl AttrWrapper {
4156
pub(super) fn new(attrs: AttrVec, start_pos: u32) -> AttrWrapper {
42-
AttrWrapper { attrs, start_pos }
57+
AttrWrapper { attrs, start_pos: Some(start_pos) }
4358
}
44-
pub fn empty() -> AttrWrapper {
45-
AttrWrapper { attrs: AttrVec::new(), start_pos: u32::MAX }
59+
60+
pub(super) fn empty() -> AttrWrapper {
61+
AttrWrapper { attrs: AttrVec::new(), start_pos: None }
4662
}
4763

48-
pub(crate) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
64+
pub(super) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
4965
psess.dcx().span_delayed_bug(
5066
self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP),
5167
"AttrVec is taken for recovery but no error is produced",
@@ -56,12 +72,12 @@ impl AttrWrapper {
5672

5773
/// Prepend `self.attrs` to `attrs`.
5874
// FIXME: require passing an NT to prevent misuse of this method
59-
pub(crate) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) {
75+
pub(super) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) {
6076
mem::swap(attrs, &mut self.attrs);
6177
attrs.extend(self.attrs);
6278
}
6379

64-
pub fn is_empty(&self) -> bool {
80+
pub(super) fn is_empty(&self) -> bool {
6581
self.attrs.is_empty()
6682
}
6783
}
@@ -77,7 +93,7 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
7793
}
7894

7995
// From a value of this type we can reconstruct the `TokenStream` seen by the
80-
// `f` callback passed to a call to `Parser::collect_tokens_trailing_token`, by
96+
// `f` callback passed to a call to `Parser::collect_tokens`, by
8197
// replaying the getting of the tokens. This saves us producing a `TokenStream`
8298
// if it is never needed, e.g. a captured `macro_rules!` argument that is never
8399
// passed to a proc macro. In practice, token stream creation happens rarely
@@ -166,16 +182,30 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
166182
}
167183

168184
impl<'a> Parser<'a> {
185+
pub(super) fn collect_pos(&self) -> CollectPos {
186+
CollectPos {
187+
start_token: (self.token.clone(), self.token_spacing),
188+
cursor_snapshot: self.token_cursor.clone(),
189+
start_pos: self.num_bump_calls,
190+
}
191+
}
192+
169193
/// Parses code with `f`. If appropriate, it records the tokens (in
170194
/// `LazyAttrTokenStream` form) that were parsed in the result, accessible
171-
/// via the `HasTokens` trait. The second (bool) part of the callback's
195+
/// via the `HasTokens` trait. The `Trailing` part of the callback's
172196
/// result indicates if an extra token should be captured, e.g. a comma or
173-
/// semicolon.
197+
/// semicolon. The `UsePreAttrPos` part of the callback's result indicates
198+
/// if we should use `pre_attr_pos` as the collection start position (only
199+
/// required in a few cases).
174200
///
175201
/// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The
176202
/// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for
177203
/// details.
178204
///
205+
/// `pre_attr_pos` is the position before the outer attributes (or the node
206+
/// itself, if no outer attributes are present). It is only needed if `f`
207+
/// can return `UsePreAttrPos::Yes`.
208+
///
179209
/// Note: If your callback consumes an opening delimiter (including the
180210
/// case where `self.token` is an opening delimiter on entry to this
181211
/// function), you must also consume the corresponding closing delimiter.
@@ -197,11 +227,12 @@ impl<'a> Parser<'a> {
197227
/// } // 32..33
198228
/// } // 33..34
199229
/// ```
200-
pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
230+
pub(super) fn collect_tokens<R: HasAttrs + HasTokens>(
201231
&mut self,
232+
pre_attr_pos: Option<CollectPos>,
202233
attrs: AttrWrapper,
203234
force_collect: ForceCollect,
204-
f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>,
235+
f: impl FnOnce(&mut Self, AttrVec) -> PResult<'a, (R, Trailing, UsePreAttrPos)>,
205236
) -> PResult<'a, R> {
206237
// We must collect if anything could observe the collected tokens, i.e.
207238
// if any of the following conditions hold.
@@ -220,23 +251,20 @@ impl<'a> Parser<'a> {
220251
return Ok(f(self, attrs.attrs)?.0);
221252
}
222253

223-
let start_token = (self.token.clone(), self.token_spacing);
224-
let cursor_snapshot = self.token_cursor.clone();
225-
let start_pos = self.num_bump_calls;
254+
let mut collect_pos = self.collect_pos();
226255
let has_outer_attrs = !attrs.attrs.is_empty();
227256
let parser_replacements_start = self.capture_state.parser_replacements.len();
228257

229258
// We set and restore `Capturing::Yes` on either side of the call to
230-
// `f`, so we can distinguish the outermost call to
231-
// `collect_tokens_trailing_token` (e.g. parsing `m` in the example
232-
// above) from any inner (indirectly recursive) calls (e.g. parsing `g`
233-
// in the example above). This distinction is used below and in
234-
// `Parser::parse_inner_attributes`.
235-
let (mut ret, capture_trailing) = {
259+
// `f`, so we can distinguish the outermost call to `collect_tokens`
260+
// (e.g. parsing `m` in the example above) from any inner (indirectly
261+
// recursive) calls (e.g. parsing `g` in the example above). This
262+
// distinction is used below and in `Parser::parse_inner_attributes`.
263+
let (mut ret, capture_trailing, use_pre_attr_pos) = {
236264
let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
237-
let ret_and_trailing = f(self, attrs.attrs);
265+
let res = f(self, attrs.attrs);
238266
self.capture_state.capturing = prev_capturing;
239-
ret_and_trailing?
267+
res?
240268
};
241269

242270
// When we're not in `capture_cfg` mode, then skip collecting and
@@ -279,10 +307,18 @@ impl<'a> Parser<'a> {
279307
return Ok(ret);
280308
}
281309

310+
// Replace the post-attribute collection start position with the
311+
// pre-attribute position supplied, if `f` indicated it is necessary.
312+
// (The caller is responsible for providing a non-`None` `pre_attr_pos`
313+
// if this is a possibility.)
314+
if matches!(use_pre_attr_pos, UsePreAttrPos::Yes) {
315+
collect_pos = pre_attr_pos.unwrap();
316+
}
317+
282318
let parser_replacements_end = self.capture_state.parser_replacements.len();
283319

284320
assert!(
285-
!(self.break_last_token && capture_trailing),
321+
!(self.break_last_token && matches!(capture_trailing, Trailing::Yes)),
286322
"Cannot set break_last_token and have trailing token"
287323
);
288324

@@ -294,7 +330,7 @@ impl<'a> Parser<'a> {
294330
// `AttrTokenStream`, we will create the proper token.
295331
+ self.break_last_token as u32;
296332

297-
let num_calls = end_pos - start_pos;
333+
let num_calls = end_pos - collect_pos.start_pos;
298334

299335
// Take the captured `ParserRange`s for any inner attributes that we parsed in
300336
// `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`,
@@ -328,7 +364,9 @@ impl<'a> Parser<'a> {
328364
.iter()
329365
.cloned()
330366
.chain(inner_attr_parser_replacements.iter().cloned())
331-
.map(|(parser_range, data)| (NodeRange::new(parser_range, start_pos), data))
367+
.map(|(parser_range, data)| {
368+
(NodeRange::new(parser_range, collect_pos.start_pos), data)
369+
})
332370
.collect()
333371
};
334372

@@ -355,9 +393,9 @@ impl<'a> Parser<'a> {
355393
// - `tokens`: lazy tokens for `g` (with its inner attr deleted).
356394

357395
let tokens = LazyAttrTokenStream::new(LazyAttrTokenStreamImpl {
358-
start_token,
396+
start_token: collect_pos.start_token,
397+
cursor_snapshot: collect_pos.cursor_snapshot,
359398
num_calls,
360-
cursor_snapshot,
361399
break_last_token: self.break_last_token,
362400
node_replacements,
363401
});
@@ -368,9 +406,9 @@ impl<'a> Parser<'a> {
368406
}
369407

370408
// If `capture_cfg` is set and we're inside a recursive call to
371-
// `collect_tokens_trailing_token`, then we need to register a replace range
372-
// if we have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager cfg-expansion
373-
// on the captured token stream.
409+
// `collect_tokens`, then we need to register a replace range if we
410+
// have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager
411+
// cfg-expansion on the captured token stream.
374412
if self.capture_cfg
375413
&& matches!(self.capture_state.capturing, Capturing::Yes)
376414
&& has_cfg_or_cfg_attr(ret.attrs())
@@ -389,7 +427,8 @@ impl<'a> Parser<'a> {
389427
// Set things up so that the entire AST node that we just parsed, including attributes,
390428
// will be replaced with `target` in the lazy token stream. This will allow us to
391429
// cfg-expand this AST node.
392-
let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
430+
let start_pos =
431+
if has_outer_attrs { attrs.start_pos.unwrap() } else { collect_pos.start_pos };
393432
let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens };
394433
self.capture_state
395434
.parser_replacements
@@ -490,7 +529,6 @@ mod size_asserts {
490529

491530
use super::*;
492531
// tidy-alphabetical-start
493-
static_assert_size!(AttrWrapper, 16);
494532
static_assert_size!(LazyAttrTokenStreamImpl, 96);
495533
// tidy-alphabetical-end
496534
}

‎compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2487,13 +2487,14 @@ impl<'a> Parser<'a> {
24872487
pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
24882488
let start = self.token.span;
24892489
let attrs = self.parse_outer_attributes()?;
2490-
let expr = self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
2491-
err.span_label(
2492-
start.shrink_to_lo(),
2493-
"while parsing a const generic argument starting here",
2494-
);
2495-
err
2496-
})?;
2490+
let (expr, _) =
2491+
self.parse_expr_res(Restrictions::CONST_EXPR, attrs).map_err(|mut err| {
2492+
err.span_label(
2493+
start.shrink_to_lo(),
2494+
"while parsing a const generic argument starting here",
2495+
);
2496+
err
2497+
})?;
24972498
if !self.expr_is_valid_const_arg(&expr) {
24982499
self.dcx().emit_err(ConstGenericWithoutBraces {
24992500
span: expr.span,
@@ -2613,7 +2614,7 @@ impl<'a> Parser<'a> {
26132614
let attrs = self.parse_outer_attributes()?;
26142615
self.parse_expr_res(Restrictions::CONST_EXPR, attrs)
26152616
})() {
2616-
Ok(expr) => {
2617+
Ok((expr, _)) => {
26172618
// Find a mistake like `MyTrait<Assoc == S::Assoc>`.
26182619
if snapshot.token == token::EqEq {
26192620
err.span_suggestion(
@@ -2671,7 +2672,7 @@ impl<'a> Parser<'a> {
26712672
})() {
26722673
// Since we don't know the exact reason why we failed to parse the type or the
26732674
// expression, employ a simple heuristic to weed out some pathological cases.
2674-
Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => {
2675+
Ok((expr, _)) if let token::Comma | token::Gt = snapshot.token.kind => {
26752676
self.restore_snapshot(snapshot);
26762677
Some(expr)
26772678
}

‎compiler/rustc_parse/src/parser/expr.rs

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma};
3636
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
3737
use super::{
3838
AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
39-
SemiColonMode, SeqSep, TokenType, Trailing,
39+
SemiColonMode, SeqSep, TokenType, Trailing, UsePreAttrPos,
4040
};
4141
use crate::{errors, maybe_recover_from_interpolated_ty_qpath};
4242

@@ -59,15 +59,30 @@ impl<'a> Parser<'a> {
5959
self.current_closure.take();
6060

6161
let attrs = self.parse_outer_attributes()?;
62-
self.parse_expr_res(Restrictions::empty(), attrs)
62+
self.parse_expr_res(Restrictions::empty(), attrs).map(|res| res.0)
6363
}
6464

6565
/// Parses an expression, forcing tokens to be collected.
6666
pub fn parse_expr_force_collect(&mut self) -> PResult<'a, P<Expr>> {
6767
self.current_closure.take();
6868

69+
// If the expression is associative (e.g. `1 + 2`), then any preceding
70+
// outer attribute actually belongs to the first inner sub-expression.
71+
// In which case we must use the pre-attr pos to include the attribute
72+
// in the collected tokens for the outer expression.
73+
let pre_attr_pos = self.collect_pos();
6974
let attrs = self.parse_outer_attributes()?;
70-
self.collect_tokens_no_attrs(|this| this.parse_expr_res(Restrictions::empty(), attrs))
75+
self.collect_tokens(
76+
Some(pre_attr_pos),
77+
AttrWrapper::empty(),
78+
ForceCollect::Yes,
79+
|this, _empty_attrs| {
80+
let (expr, is_assoc) = this.parse_expr_res(Restrictions::empty(), attrs)?;
81+
let use_pre_attr_pos =
82+
if is_assoc { UsePreAttrPos::Yes } else { UsePreAttrPos::No };
83+
Ok((expr, Trailing::No, use_pre_attr_pos))
84+
},
85+
)
7186
}
7287

7388
pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> {
@@ -77,7 +92,7 @@ impl<'a> Parser<'a> {
7792
fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
7893
let attrs = self.parse_outer_attributes()?;
7994
match self.parse_expr_res(restrictions, attrs) {
80-
Ok(expr) => Ok(expr),
95+
Ok((expr, _)) => Ok(expr),
8196
Err(err) => match self.token.ident() {
8297
Some((Ident { name: kw::Underscore, .. }, IdentIsRaw::No))
8398
if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
@@ -104,34 +119,38 @@ impl<'a> Parser<'a> {
104119
&mut self,
105120
r: Restrictions,
106121
attrs: AttrWrapper,
107-
) -> PResult<'a, P<Expr>> {
122+
) -> PResult<'a, (P<Expr>, bool)> {
108123
self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs))
109124
}
110125

111126
/// Parses an associative expression with operators of at least `min_prec` precedence.
127+
/// The `bool` in the return value indicates if it was an assoc expr, i.e. with an operator
128+
/// followed by a subexpression (e.g. `1 + 2`).
112129
pub(super) fn parse_expr_assoc_with(
113130
&mut self,
114131
min_prec: usize,
115132
attrs: AttrWrapper,
116-
) -> PResult<'a, P<Expr>> {
133+
) -> PResult<'a, (P<Expr>, bool)> {
117134
let lhs = if self.token.is_range_separator() {
118-
return self.parse_expr_prefix_range(attrs);
135+
return self.parse_expr_prefix_range(attrs).map(|res| (res, false));
119136
} else {
120137
self.parse_expr_prefix(attrs)?
121138
};
122139
self.parse_expr_assoc_rest_with(min_prec, false, lhs)
123140
}
124141

125142
/// Parses the rest of an associative expression (i.e. the part after the lhs) with operators
126-
/// of at least `min_prec` precedence.
143+
/// of at least `min_prec` precedence. The `bool` in the return value indicates if something
144+
/// was actually parsed.
127145
pub(super) fn parse_expr_assoc_rest_with(
128146
&mut self,
129147
min_prec: usize,
130148
starts_stmt: bool,
131149
mut lhs: P<Expr>,
132-
) -> PResult<'a, P<Expr>> {
150+
) -> PResult<'a, (P<Expr>, bool)> {
151+
let mut parsed_something = false;
133152
if !self.should_continue_as_assoc_expr(&lhs) {
134-
return Ok(lhs);
153+
return Ok((lhs, parsed_something));
135154
}
136155

137156
self.expected_tokens.push(TokenType::Operator);
@@ -156,10 +175,11 @@ impl<'a> Parser<'a> {
156175
self.err_larrow_operator(self.token.span);
157176
}
158177

178+
parsed_something = true;
159179
self.bump();
160180
if op.node.is_comparison() {
161181
if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? {
162-
return Ok(expr);
182+
return Ok((expr, parsed_something));
163183
}
164184
}
165185

@@ -263,7 +283,7 @@ impl<'a> Parser<'a> {
263283
// the special cases. The code is here only for future convenience.
264284
Fixity::None => 1,
265285
};
266-
let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
286+
let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
267287
let attrs = this.parse_outer_attributes()?;
268288
this.parse_expr_assoc_with(prec + prec_adjustment, attrs)
269289
})?;
@@ -319,7 +339,7 @@ impl<'a> Parser<'a> {
319339
}
320340
}
321341

322-
Ok(lhs)
342+
Ok((lhs, parsed_something))
323343
}
324344

325345
fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool {
@@ -441,7 +461,8 @@ impl<'a> Parser<'a> {
441461
let attrs = self.parse_outer_attributes()?;
442462
Some(
443463
self.parse_expr_assoc_with(prec + 1, attrs)
444-
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?,
464+
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?
465+
.0,
445466
)
446467
} else {
447468
None
@@ -498,7 +519,7 @@ impl<'a> Parser<'a> {
498519
// RHS must be parsed with more associativity than the dots.
499520
let attrs = this.parse_outer_attributes()?;
500521
this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs)
501-
.map(|x| (lo.to(x.span), Some(x)))
522+
.map(|(x, _)| (lo.to(x.span), Some(x)))
502523
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
503524
} else {
504525
(lo, None)
@@ -2335,7 +2356,7 @@ impl<'a> Parser<'a> {
23352356
let token = self.token.clone();
23362357
let attrs = self.parse_outer_attributes()?;
23372358
match self.parse_expr_res(restrictions, attrs) {
2338-
Ok(expr) => expr,
2359+
Ok((expr, _)) => expr,
23392360
Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
23402361
}
23412362
}
@@ -2445,7 +2466,7 @@ impl<'a> Parser<'a> {
24452466
fn parse_fn_block_param(&mut self) -> PResult<'a, Param> {
24462467
let lo = self.token.span;
24472468
let attrs = self.parse_outer_attributes()?;
2448-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
2469+
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
24492470
let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?;
24502471
let ty = if this.eat(&token::Colon) {
24512472
this.parse_ty()?
@@ -2462,7 +2483,8 @@ impl<'a> Parser<'a> {
24622483
id: DUMMY_NODE_ID,
24632484
is_placeholder: false,
24642485
},
2465-
this.token == token::Comma,
2486+
Trailing::from(this.token == token::Comma),
2487+
UsePreAttrPos::No,
24662488
))
24672489
})
24682490
}
@@ -2583,7 +2605,7 @@ impl<'a> Parser<'a> {
25832605
/// Parses the condition of a `if` or `while` expression.
25842606
fn parse_expr_cond(&mut self) -> PResult<'a, P<Expr>> {
25852607
let attrs = self.parse_outer_attributes()?;
2586-
let mut cond =
2608+
let (mut cond, _) =
25872609
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;
25882610

25892611
CondChecker::new(self).visit_expr(&mut cond);
@@ -2632,7 +2654,7 @@ impl<'a> Parser<'a> {
26322654
self.expect(&token::Eq)?;
26332655
}
26342656
let attrs = self.parse_outer_attributes()?;
2635-
let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?;
2657+
let (expr, _) = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?;
26362658
let span = lo.to(expr.span);
26372659
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
26382660
}
@@ -2766,7 +2788,7 @@ impl<'a> Parser<'a> {
27662788
// We know for sure we have seen `for ($SOMETHING in`. In the happy path this would
27672789
// happen right before the return of this method.
27682790
let attrs = self.parse_outer_attributes()?;
2769-
let expr = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) {
2791+
let (expr, _) = match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs) {
27702792
Ok(expr) => expr,
27712793
Err(expr_err) => {
27722794
// We don't know what followed the `in`, so cancel and bubble up the
@@ -2801,7 +2823,7 @@ impl<'a> Parser<'a> {
28012823
}
28022824
self.check_for_for_in_in_typo(self.prev_token.span);
28032825
let attrs = self.parse_outer_attributes()?;
2804-
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
2826+
let (expr, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
28052827
Ok((pat, expr))
28062828
}
28072829

@@ -2921,7 +2943,7 @@ impl<'a> Parser<'a> {
29212943
fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> {
29222944
let match_span = self.prev_token.span;
29232945
let attrs = self.parse_outer_attributes()?;
2924-
let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
2946+
let (scrutinee, _) = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, attrs)?;
29252947

29262948
self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix)
29272949
}
@@ -3069,7 +3091,7 @@ impl<'a> Parser<'a> {
30693091

30703092
pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
30713093
let attrs = self.parse_outer_attributes()?;
3072-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
3094+
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
30733095
let lo = this.token.span;
30743096
let (pat, guard) = this.parse_match_arm_pat_and_guard()?;
30753097

@@ -3126,7 +3148,7 @@ impl<'a> Parser<'a> {
31263148
let arm_start_span = this.token.span;
31273149

31283150
let attrs = this.parse_outer_attributes()?;
3129-
let expr =
3151+
let (expr, _) =
31303152
this.parse_expr_res(Restrictions::STMT_EXPR, attrs).map_err(|mut err| {
31313153
err.span_label(arrow_span, "while parsing the `match` arm starting here");
31323154
err
@@ -3243,7 +3265,8 @@ impl<'a> Parser<'a> {
32433265
id: DUMMY_NODE_ID,
32443266
is_placeholder: false,
32453267
},
3246-
false,
3268+
Trailing::No,
3269+
UsePreAttrPos::No,
32473270
))
32483271
})
32493272
}
@@ -3334,8 +3357,9 @@ impl<'a> Parser<'a> {
33343357

33353358
fn parse_match_guard_condition(&mut self) -> PResult<'a, P<Expr>> {
33363359
let attrs = self.parse_outer_attributes()?;
3337-
self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs).map_err(
3338-
|mut err| {
3360+
match self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs) {
3361+
Ok((expr, _)) => Ok(expr),
3362+
Err(mut err) => {
33393363
if self.prev_token == token::OpenDelim(Delimiter::Brace) {
33403364
let sugg_sp = self.prev_token.span.shrink_to_lo();
33413365
// Consume everything within the braces, let's avoid further parse
@@ -3355,9 +3379,9 @@ impl<'a> Parser<'a> {
33553379
err.span_suggestion_verbose(sugg_sp, msg, "=> ", applicability);
33563380
}
33573381
}
3358-
err
3359-
},
3360-
)
3382+
Err(err)
3383+
}
3384+
}
33613385
}
33623386

33633387
pub(crate) fn is_builtin(&self) -> bool {
@@ -3708,7 +3732,7 @@ impl<'a> Parser<'a> {
37083732
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
37093733
let attrs = self.parse_outer_attributes()?;
37103734
self.recover_vcs_conflict_marker();
3711-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
3735+
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
37123736
let lo = this.token.span;
37133737

37143738
// Check if a colon exists one ahead. This means we're parsing a fieldname.
@@ -3752,7 +3776,8 @@ impl<'a> Parser<'a> {
37523776
id: DUMMY_NODE_ID,
37533777
is_placeholder: false,
37543778
},
3755-
this.token == token::Comma,
3779+
Trailing::from(this.token == token::Comma),
3780+
UsePreAttrPos::No,
37563781
))
37573782
})
37583783
}
@@ -3846,15 +3871,17 @@ impl<'a> Parser<'a> {
38463871
attrs: AttrWrapper,
38473872
f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, P<Expr>>,
38483873
) -> PResult<'a, P<Expr>> {
3849-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
3874+
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
38503875
let res = f(this, attrs)?;
3851-
let trailing = (this.restrictions.contains(Restrictions::STMT_EXPR)
3852-
&& this.token == token::Semi)
3853-
// FIXME: pass an additional condition through from the place
3854-
// where we know we need a comma, rather than assuming that
3855-
// `#[attr] expr,` always captures a trailing comma.
3856-
|| this.token == token::Comma;
3857-
Ok((res, trailing))
3876+
let trailing = Trailing::from(
3877+
this.restrictions.contains(Restrictions::STMT_EXPR)
3878+
&& this.token == token::Semi
3879+
// FIXME: pass an additional condition through from the place
3880+
// where we know we need a comma, rather than assuming that
3881+
// `#[attr] expr,` always captures a trailing comma.
3882+
|| this.token == token::Comma,
3883+
);
3884+
Ok((res, trailing, UsePreAttrPos::No))
38583885
})
38593886
}
38603887
}

‎compiler/rustc_parse/src/parser/generics.rs

Lines changed: 76 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_span::symbol::{kw, Ident};
77
use rustc_span::Span;
88
use thin_vec::ThinVec;
99

10-
use super::{ForceCollect, Parser};
10+
use super::{ForceCollect, Parser, Trailing, UsePreAttrPos};
1111
use crate::errors::{
1212
self, MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
1313
UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
@@ -169,94 +169,88 @@ impl<'a> Parser<'a> {
169169
let mut done = false;
170170
while !done {
171171
let attrs = self.parse_outer_attributes()?;
172-
let param =
173-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
174-
if this.eat_keyword_noexpect(kw::SelfUpper) {
175-
// `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing
176-
// as if `Self` never existed.
177-
this.dcx().emit_err(UnexpectedSelfInGenericParameters {
178-
span: this.prev_token.span,
179-
});
172+
let param = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
173+
if this.eat_keyword_noexpect(kw::SelfUpper) {
174+
// `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing
175+
// as if `Self` never existed.
176+
this.dcx()
177+
.emit_err(UnexpectedSelfInGenericParameters { span: this.prev_token.span });
178+
179+
// Eat a trailing comma, if it exists.
180+
let _ = this.eat(&token::Comma);
181+
}
182+
183+
let param = if this.check_lifetime() {
184+
let lifetime = this.expect_lifetime();
185+
// Parse lifetime parameter.
186+
let (colon_span, bounds) = if this.eat(&token::Colon) {
187+
(Some(this.prev_token.span), this.parse_lt_param_bounds())
188+
} else {
189+
(None, Vec::new())
190+
};
180191

181-
// Eat a trailing comma, if it exists.
182-
let _ = this.eat(&token::Comma);
192+
if this.check_noexpect(&token::Eq) && this.look_ahead(1, |t| t.is_lifetime()) {
193+
let lo = this.token.span;
194+
// Parse `= 'lifetime`.
195+
this.bump(); // `=`
196+
this.bump(); // `'lifetime`
197+
let span = lo.to(this.prev_token.span);
198+
this.dcx().emit_err(UnexpectedDefaultValueForLifetimeInGenericParameters {
199+
span,
200+
});
183201
}
184202

185-
let param = if this.check_lifetime() {
186-
let lifetime = this.expect_lifetime();
187-
// Parse lifetime parameter.
188-
let (colon_span, bounds) = if this.eat(&token::Colon) {
189-
(Some(this.prev_token.span), this.parse_lt_param_bounds())
190-
} else {
191-
(None, Vec::new())
192-
};
193-
194-
if this.check_noexpect(&token::Eq)
195-
&& this.look_ahead(1, |t| t.is_lifetime())
196-
{
197-
let lo = this.token.span;
198-
// Parse `= 'lifetime`.
199-
this.bump(); // `=`
200-
this.bump(); // `'lifetime`
201-
let span = lo.to(this.prev_token.span);
202-
this.dcx().emit_err(
203-
UnexpectedDefaultValueForLifetimeInGenericParameters { span },
204-
);
203+
Some(ast::GenericParam {
204+
ident: lifetime.ident,
205+
id: lifetime.id,
206+
attrs,
207+
bounds,
208+
kind: ast::GenericParamKind::Lifetime,
209+
is_placeholder: false,
210+
colon_span,
211+
})
212+
} else if this.check_keyword(kw::Const) {
213+
// Parse const parameter.
214+
Some(this.parse_const_param(attrs)?)
215+
} else if this.check_ident() {
216+
// Parse type parameter.
217+
Some(this.parse_ty_param(attrs)?)
218+
} else if this.token.can_begin_type() {
219+
// Trying to write an associated type bound? (#26271)
220+
let snapshot = this.create_snapshot_for_diagnostic();
221+
match this.parse_ty_where_predicate() {
222+
Ok(where_predicate) => {
223+
this.dcx().emit_err(errors::BadAssocTypeBounds {
224+
span: where_predicate.span(),
225+
});
226+
// FIXME - try to continue parsing other generics?
205227
}
206-
207-
Some(ast::GenericParam {
208-
ident: lifetime.ident,
209-
id: lifetime.id,
210-
attrs,
211-
bounds,
212-
kind: ast::GenericParamKind::Lifetime,
213-
is_placeholder: false,
214-
colon_span,
215-
})
216-
} else if this.check_keyword(kw::Const) {
217-
// Parse const parameter.
218-
Some(this.parse_const_param(attrs)?)
219-
} else if this.check_ident() {
220-
// Parse type parameter.
221-
Some(this.parse_ty_param(attrs)?)
222-
} else if this.token.can_begin_type() {
223-
// Trying to write an associated type bound? (#26271)
224-
let snapshot = this.create_snapshot_for_diagnostic();
225-
match this.parse_ty_where_predicate() {
226-
Ok(where_predicate) => {
227-
this.dcx().emit_err(errors::BadAssocTypeBounds {
228-
span: where_predicate.span(),
229-
});
230-
// FIXME - try to continue parsing other generics?
231-
return Ok((None, false));
232-
}
233-
Err(err) => {
234-
err.cancel();
235-
// FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
236-
this.restore_snapshot(snapshot);
237-
return Ok((None, false));
238-
}
228+
Err(err) => {
229+
err.cancel();
230+
// FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
231+
this.restore_snapshot(snapshot);
239232
}
240-
} else {
241-
// Check for trailing attributes and stop parsing.
242-
if !attrs.is_empty() {
243-
if !params.is_empty() {
244-
this.dcx()
245-
.emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
246-
} else {
247-
this.dcx()
248-
.emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
249-
}
233+
}
234+
return Ok((None, Trailing::No, UsePreAttrPos::No));
235+
} else {
236+
// Check for trailing attributes and stop parsing.
237+
if !attrs.is_empty() {
238+
if !params.is_empty() {
239+
this.dcx().emit_err(errors::AttrAfterGeneric { span: attrs[0].span });
240+
} else {
241+
this.dcx()
242+
.emit_err(errors::AttrWithoutGenerics { span: attrs[0].span });
250243
}
251-
return Ok((None, false));
252-
};
253-
254-
if !this.eat(&token::Comma) {
255-
done = true;
256244
}
257-
// We just ate the comma, so no need to capture the trailing token.
258-
Ok((param, false))
259-
})?;
245+
return Ok((None, Trailing::No, UsePreAttrPos::No));
246+
};
247+
248+
if !this.eat(&token::Comma) {
249+
done = true;
250+
}
251+
// We just ate the comma, so no need to capture the trailing token.
252+
Ok((param, Trailing::No, UsePreAttrPos::No))
253+
})?;
260254

261255
if let Some(param) = param {
262256
params.push(param);

‎compiler/rustc_parse/src/parser/item.rs

Lines changed: 76 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ use tracing::debug;
2020

2121
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
2222
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
23-
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing};
23+
use super::{
24+
AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing, UsePreAttrPos,
25+
};
2426
use crate::errors::{self, MacroExpandsToAdtField};
2527
use crate::{fluent_generated as fluent, maybe_whole};
2628

@@ -127,7 +129,7 @@ impl<'a> Parser<'a> {
127129
Some(item.into_inner())
128130
});
129131

130-
self.collect_tokens_trailing_token(attrs, force_collect, |this, mut attrs| {
132+
self.collect_tokens(None, attrs, force_collect, |this, mut attrs| {
131133
let lo = this.token.span;
132134
let vis = this.parse_visibility(FollowedByType::No)?;
133135
let mut def = this.parse_defaultness();
@@ -145,7 +147,7 @@ impl<'a> Parser<'a> {
145147
let span = lo.to(this.prev_token.span);
146148
let id = DUMMY_NODE_ID;
147149
let item = Item { ident, attrs, id, kind, vis, span, tokens: None };
148-
return Ok((Some(item), false));
150+
return Ok((Some(item), Trailing::No, UsePreAttrPos::No));
149151
}
150152

151153
// At this point, we have failed to parse an item.
@@ -160,7 +162,7 @@ impl<'a> Parser<'a> {
160162
if !attrs_allowed {
161163
this.recover_attrs_no_item(&attrs)?;
162164
}
163-
Ok((None, false))
165+
Ok((None, Trailing::No, UsePreAttrPos::No))
164166
})
165167
}
166168

@@ -1546,86 +1548,82 @@ impl<'a> Parser<'a> {
15461548
self.recover_vcs_conflict_marker();
15471549
let help = "enum variants can be `Variant`, `Variant = <integer>`, \
15481550
`Variant(Type, ..., TypeN)` or `Variant { fields: Types }`";
1549-
self.collect_tokens_trailing_token(
1550-
variant_attrs,
1551-
ForceCollect::No,
1552-
|this, variant_attrs| {
1553-
let vlo = this.token.span;
1554-
1555-
let vis = this.parse_visibility(FollowedByType::No)?;
1556-
if !this.recover_nested_adt_item(kw::Enum)? {
1557-
return Ok((None, false));
1558-
}
1559-
let ident = this.parse_field_ident("enum", vlo)?;
1560-
1561-
if this.token == token::Not {
1562-
if let Err(err) = this.unexpected() {
1563-
err.with_note(fluent::parse_macro_expands_to_enum_variant).emit();
1564-
}
1551+
self.collect_tokens(None, variant_attrs, ForceCollect::No, |this, variant_attrs| {
1552+
let vlo = this.token.span;
15651553

1566-
this.bump();
1567-
this.parse_delim_args()?;
1554+
let vis = this.parse_visibility(FollowedByType::No)?;
1555+
if !this.recover_nested_adt_item(kw::Enum)? {
1556+
return Ok((None, Trailing::No, UsePreAttrPos::No));
1557+
}
1558+
let ident = this.parse_field_ident("enum", vlo)?;
15681559

1569-
return Ok((None, this.token == token::Comma));
1560+
if this.token == token::Not {
1561+
if let Err(err) = this.unexpected() {
1562+
err.with_note(fluent::parse_macro_expands_to_enum_variant).emit();
15701563
}
15711564

1572-
let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
1573-
// Parse a struct variant.
1574-
let (fields, recovered) =
1575-
match this.parse_record_struct_body("struct", ident.span, false) {
1576-
Ok((fields, recovered)) => (fields, recovered),
1577-
Err(mut err) => {
1578-
if this.token == token::Colon {
1579-
// We handle `enum` to `struct` suggestion in the caller.
1580-
return Err(err);
1581-
}
1582-
this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
1583-
this.bump(); // }
1584-
err.span_label(span, "while parsing this enum");
1585-
err.help(help);
1586-
let guar = err.emit();
1587-
(thin_vec![], Recovered::Yes(guar))
1588-
}
1589-
};
1590-
VariantData::Struct { fields, recovered: recovered.into() }
1591-
} else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
1592-
let body = match this.parse_tuple_struct_body() {
1593-
Ok(body) => body,
1565+
this.bump();
1566+
this.parse_delim_args()?;
1567+
1568+
return Ok((None, Trailing::from(this.token == token::Comma), UsePreAttrPos::No));
1569+
}
1570+
1571+
let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
1572+
// Parse a struct variant.
1573+
let (fields, recovered) =
1574+
match this.parse_record_struct_body("struct", ident.span, false) {
1575+
Ok((fields, recovered)) => (fields, recovered),
15941576
Err(mut err) => {
15951577
if this.token == token::Colon {
15961578
// We handle `enum` to `struct` suggestion in the caller.
15971579
return Err(err);
15981580
}
1599-
this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]);
1600-
this.bump(); // )
1581+
this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
1582+
this.bump(); // }
16011583
err.span_label(span, "while parsing this enum");
16021584
err.help(help);
1603-
err.emit();
1604-
thin_vec![]
1585+
let guar = err.emit();
1586+
(thin_vec![], Recovered::Yes(guar))
16051587
}
16061588
};
1607-
VariantData::Tuple(body, DUMMY_NODE_ID)
1608-
} else {
1609-
VariantData::Unit(DUMMY_NODE_ID)
1589+
VariantData::Struct { fields, recovered: recovered.into() }
1590+
} else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
1591+
let body = match this.parse_tuple_struct_body() {
1592+
Ok(body) => body,
1593+
Err(mut err) => {
1594+
if this.token == token::Colon {
1595+
// We handle `enum` to `struct` suggestion in the caller.
1596+
return Err(err);
1597+
}
1598+
this.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]);
1599+
this.bump(); // )
1600+
err.span_label(span, "while parsing this enum");
1601+
err.help(help);
1602+
err.emit();
1603+
thin_vec![]
1604+
}
16101605
};
1606+
VariantData::Tuple(body, DUMMY_NODE_ID)
1607+
} else {
1608+
VariantData::Unit(DUMMY_NODE_ID)
1609+
};
16111610

1612-
let disr_expr =
1613-
if this.eat(&token::Eq) { Some(this.parse_expr_anon_const()?) } else { None };
1611+
let disr_expr =
1612+
if this.eat(&token::Eq) { Some(this.parse_expr_anon_const()?) } else { None };
16141613

1615-
let vr = ast::Variant {
1616-
ident,
1617-
vis,
1618-
id: DUMMY_NODE_ID,
1619-
attrs: variant_attrs,
1620-
data: struct_def,
1621-
disr_expr,
1622-
span: vlo.to(this.prev_token.span),
1623-
is_placeholder: false,
1624-
};
1614+
let vr = ast::Variant {
1615+
ident,
1616+
vis,
1617+
id: DUMMY_NODE_ID,
1618+
attrs: variant_attrs,
1619+
data: struct_def,
1620+
disr_expr,
1621+
span: vlo.to(this.prev_token.span),
1622+
is_placeholder: false,
1623+
};
16251624

1626-
Ok((Some(vr), this.token == token::Comma))
1627-
},
1628-
)
1625+
Ok((Some(vr), Trailing::from(this.token == token::Comma), UsePreAttrPos::No))
1626+
})
16291627
.map_err(|mut err| {
16301628
err.help(help);
16311629
err
@@ -1777,7 +1775,7 @@ impl<'a> Parser<'a> {
17771775
// Unit like structs are handled in parse_item_struct function
17781776
self.parse_paren_comma_seq(|p| {
17791777
let attrs = p.parse_outer_attributes()?;
1780-
p.collect_tokens_trailing_token(attrs, ForceCollect::No, |p, attrs| {
1778+
p.collect_tokens(None, attrs, ForceCollect::No, |p, attrs| {
17811779
let mut snapshot = None;
17821780
if p.is_vcs_conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
17831781
// Account for `<<<<<<<` diff markers. We can't proactively error here because
@@ -1815,7 +1813,8 @@ impl<'a> Parser<'a> {
18151813
attrs,
18161814
is_placeholder: false,
18171815
},
1818-
p.token == token::Comma,
1816+
Trailing::from(p.token == token::Comma),
1817+
UsePreAttrPos::No,
18191818
))
18201819
})
18211820
})
@@ -1827,10 +1826,11 @@ impl<'a> Parser<'a> {
18271826
self.recover_vcs_conflict_marker();
18281827
let attrs = self.parse_outer_attributes()?;
18291828
self.recover_vcs_conflict_marker();
1830-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
1829+
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
18311830
let lo = this.token.span;
18321831
let vis = this.parse_visibility(FollowedByType::No)?;
1833-
this.parse_single_struct_field(adt_ty, lo, vis, attrs).map(|field| (field, false))
1832+
this.parse_single_struct_field(adt_ty, lo, vis, attrs)
1833+
.map(|field| (field, Trailing::No, UsePreAttrPos::No))
18341834
})
18351835
}
18361836

@@ -2805,12 +2805,12 @@ impl<'a> Parser<'a> {
28052805
fn parse_param_general(&mut self, req_name: ReqName, first_param: bool) -> PResult<'a, Param> {
28062806
let lo = self.token.span;
28072807
let attrs = self.parse_outer_attributes()?;
2808-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
2808+
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
28092809
// Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
28102810
if let Some(mut param) = this.parse_self_param()? {
28112811
param.attrs = attrs;
28122812
let res = if first_param { Ok(param) } else { this.recover_bad_self_param(param) };
2813-
return Ok((res?, false));
2813+
return Ok((res?, Trailing::No, UsePreAttrPos::No));
28142814
}
28152815

28162816
let is_name_required = match this.token.kind {
@@ -2826,7 +2826,7 @@ impl<'a> Parser<'a> {
28262826
this.parameter_without_type(&mut err, pat, is_name_required, first_param)
28272827
{
28282828
let guar = err.emit();
2829-
Ok((dummy_arg(ident, guar), false))
2829+
Ok((dummy_arg(ident, guar), Trailing::No, UsePreAttrPos::No))
28302830
} else {
28312831
Err(err)
28322832
};
@@ -2869,7 +2869,8 @@ impl<'a> Parser<'a> {
28692869

28702870
Ok((
28712871
Param { attrs, id: ast::DUMMY_NODE_ID, is_placeholder: false, pat, span, ty },
2872-
false,
2872+
Trailing::No,
2873+
UsePreAttrPos::No,
28732874
))
28742875
})
28752876
}

‎compiler/rustc_parse/src/parser/mod.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::assert_matches::debug_assert_matches;
1414
use std::ops::Range;
1515
use std::{fmt, mem, slice};
1616

17-
use attr_wrapper::AttrWrapper;
17+
use attr_wrapper::{AttrWrapper, UsePreAttrPos};
1818
pub use diagnostics::AttemptLocalParseRecovery;
1919
pub(crate) use expr::ForbiddenLetReason;
2020
pub(crate) use item::FnParseMode;
@@ -238,6 +238,7 @@ impl NodeRange {
238238
// is the position of the function's start token. This gives
239239
// `NodeRange(10..15)`.
240240
fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange {
241+
assert!(parser_range.start >= start_pos && parser_range.end >= start_pos);
241242
NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos))
242243
}
243244
}
@@ -253,7 +254,7 @@ enum Capturing {
253254
Yes,
254255
}
255256

256-
// This state is used by `Parser::collect_tokens_trailing_token`.
257+
// This state is used by `Parser::collect_tokens`.
257258
#[derive(Clone, Debug)]
258259
struct CaptureState {
259260
capturing: Capturing,
@@ -388,6 +389,12 @@ enum Trailing {
388389
Yes,
389390
}
390391

392+
impl From<bool> for Trailing {
393+
fn from(b: bool) -> Trailing {
394+
if b { Trailing::Yes } else { Trailing::No }
395+
}
396+
}
397+
391398
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
392399
pub(super) enum TokenDescription {
393400
ReservedIdentifier,
@@ -459,8 +466,8 @@ impl<'a> Parser<'a> {
459466
parser.bump();
460467

461468
// Change this from 1 back to 0 after the bump. This eases debugging of
462-
// `Parser::collect_tokens_trailing_token` nicer because it makes the
463-
// token positions 0-indexed which is nicer than 1-indexed.
469+
// `Parser::collect_tokens` because 0-indexed token positions are nicer
470+
// than 1-indexed token positions.
464471
parser.num_bump_calls = 0;
465472

466473
parser
@@ -1546,11 +1553,9 @@ impl<'a> Parser<'a> {
15461553
) -> PResult<'a, R> {
15471554
// The only reason to call `collect_tokens_no_attrs` is if you want tokens, so use
15481555
// `ForceCollect::Yes`
1549-
self.collect_tokens_trailing_token(
1550-
AttrWrapper::empty(),
1551-
ForceCollect::Yes,
1552-
|this, _attrs| Ok((f(this)?, false)),
1553-
)
1556+
self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |this, _attrs| {
1557+
Ok((f(this)?, Trailing::No, UsePreAttrPos::No))
1558+
})
15541559
}
15551560

15561561
/// `::{` or `::*`

‎compiler/rustc_parse/src/parser/pat.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_span::symbol::{kw, sym, Ident};
1313
use rustc_span::{BytePos, ErrorGuaranteed, Span};
1414
use thin_vec::{thin_vec, ThinVec};
1515

16-
use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing};
16+
use super::{ForceCollect, Parser, PathStyle, Restrictions, Trailing, UsePreAttrPos};
1717
use crate::errors::{
1818
self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed,
1919
DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt,
@@ -403,7 +403,7 @@ impl<'a> Parser<'a> {
403403

404404
// Parse an associative expression such as `+ expr`, `% expr`, ...
405405
// Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
406-
if let Ok(expr) =
406+
if let Ok((expr, _)) =
407407
snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel())
408408
{
409409
// We got a valid expression.
@@ -1302,24 +1302,23 @@ impl<'a> Parser<'a> {
13021302
}
13031303
}
13041304

1305-
let field =
1306-
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
1307-
let field = match this.parse_pat_field(lo, attrs) {
1308-
Ok(field) => Ok(field),
1309-
Err(err) => {
1310-
if let Some(delayed_err) = delayed_err.take() {
1311-
delayed_err.emit();
1312-
}
1313-
return Err(err);
1305+
let field = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
1306+
let field = match this.parse_pat_field(lo, attrs) {
1307+
Ok(field) => Ok(field),
1308+
Err(err) => {
1309+
if let Some(delayed_err) = delayed_err.take() {
1310+
delayed_err.emit();
13141311
}
1315-
}?;
1316-
ate_comma = this.eat(&token::Comma);
1312+
return Err(err);
1313+
}
1314+
}?;
1315+
ate_comma = this.eat(&token::Comma);
13171316

1318-
last_non_comma_dotdot_span = Some(this.prev_token.span);
1317+
last_non_comma_dotdot_span = Some(this.prev_token.span);
13191318

1320-
// We just ate a comma, so there's no need to capture a trailing token.
1321-
Ok((field, false))
1322-
})?;
1319+
// We just ate a comma, so there's no need to capture a trailing token.
1320+
Ok((field, Trailing::No, UsePreAttrPos::No))
1321+
})?;
13231322

13241323
fields.push(field)
13251324
}

‎compiler/rustc_parse/src/parser/path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ impl<'a> Parser<'a> {
913913
let snapshot = self.create_snapshot_for_diagnostic();
914914
let attrs = self.parse_outer_attributes()?;
915915
match self.parse_expr_res(Restrictions::CONST_EXPR, attrs) {
916-
Ok(expr) => {
916+
Ok((expr, _)) => {
917917
return Ok(Some(self.dummy_const_arg_needs_braces(
918918
self.dcx().struct_span_err(expr.span, "invalid const generic expression"),
919919
expr.span,

‎compiler/rustc_parse/src/parser/stmt.rs

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use super::pat::{PatternLocation, RecoverComma};
2121
use super::path::PathStyle;
2222
use super::{
2323
AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
24+
Trailing, UsePreAttrPos,
2425
};
2526
use crate::errors::MalformedLoopLabel;
2627
use crate::{errors, maybe_whole};
@@ -45,6 +46,7 @@ impl<'a> Parser<'a> {
4546
capture_semi: bool,
4647
force_collect: ForceCollect,
4748
) -> PResult<'a, Option<Stmt>> {
49+
let pre_attr_pos = self.collect_pos();
4850
let attrs = self.parse_outer_attributes()?;
4951
let lo = self.token.span;
5052

@@ -65,11 +67,15 @@ impl<'a> Parser<'a> {
6567
}
6668

6769
Ok(Some(if self.token.is_keyword(kw::Let) {
68-
self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
70+
self.collect_tokens(None, attrs, force_collect, |this, attrs| {
6971
this.expect_keyword(kw::Let)?;
7072
let local = this.parse_local(attrs)?;
71-
let trailing = capture_semi && this.token == token::Semi;
72-
Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
73+
let trailing = Trailing::from(capture_semi && this.token == token::Semi);
74+
Ok((
75+
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
76+
trailing,
77+
UsePreAttrPos::No,
78+
))
7379
})?
7480
} else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
7581
self.recover_stmt_local_after_let(
@@ -103,10 +109,18 @@ impl<'a> Parser<'a> {
103109
// or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something
104110
// that starts like a path (1 token), but it fact not a path.
105111
// Also, we avoid stealing syntax from `parse_item_`.
106-
let stmt = self.collect_tokens_trailing_token(
112+
//
113+
// `UsePreAttrPos::Yes` here means the attribute belongs unconditionally to the
114+
// expression, not the statement. (But the statement attributes/tokens are obtained
115+
// from the expression anyway, because `Stmt` delegates `HasAttrs`/`HasTokens` to
116+
// the things within `StmtKind`.)
117+
let stmt = self.collect_tokens(
118+
Some(pre_attr_pos),
107119
AttrWrapper::empty(),
108120
force_collect,
109-
|this, _empty_attrs| Ok((this.parse_stmt_path_start(lo, attrs)?, false)),
121+
|this, _empty_attrs| {
122+
Ok((this.parse_stmt_path_start(lo, attrs)?, Trailing::No, UsePreAttrPos::Yes))
123+
},
110124
);
111125
match stmt {
112126
Ok(stmt) => stmt,
@@ -128,12 +142,15 @@ impl<'a> Parser<'a> {
128142
self.error_outer_attrs(attrs);
129143
self.mk_stmt(lo, StmtKind::Empty)
130144
} else if self.token != token::CloseDelim(Delimiter::Brace) {
131-
// Remainder are line-expr stmts.
132-
let e = self.collect_tokens_trailing_token(
145+
// Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case
146+
// above.
147+
let e = self.collect_tokens(
148+
Some(pre_attr_pos),
133149
AttrWrapper::empty(),
134150
force_collect,
135151
|this, _empty_attrs| {
136-
Ok((this.parse_expr_res(Restrictions::STMT_EXPR, attrs)?, false))
152+
let (expr, _) = this.parse_expr_res(Restrictions::STMT_EXPR, attrs)?;
153+
Ok((expr, Trailing::No, UsePreAttrPos::Yes))
137154
},
138155
)?;
139156
if matches!(e.kind, ExprKind::Assign(..)) && self.eat_keyword(kw::Else) {
@@ -150,12 +167,16 @@ impl<'a> Parser<'a> {
150167
}
151168

152169
fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> {
153-
let stmt = self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
170+
let stmt = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
154171
let path = this.parse_path(PathStyle::Expr)?;
155172

156173
if this.eat(&token::Not) {
157174
let stmt_mac = this.parse_stmt_mac(lo, attrs, path)?;
158-
return Ok((stmt_mac, this.token == token::Semi));
175+
return Ok((
176+
stmt_mac,
177+
Trailing::from(this.token == token::Semi),
178+
UsePreAttrPos::No,
179+
));
159180
}
160181

161182
let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) {
@@ -169,13 +190,17 @@ impl<'a> Parser<'a> {
169190
this.parse_expr_dot_or_call_with(attrs, expr, lo)
170191
})?;
171192
// `DUMMY_SP` will get overwritten later in this function
172-
Ok((this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)), false))
193+
Ok((
194+
this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)),
195+
Trailing::No,
196+
UsePreAttrPos::No,
197+
))
173198
})?;
174199

175200
if let StmtKind::Expr(expr) = stmt.kind {
176-
// Perform this outside of the `collect_tokens_trailing_token` closure,
177-
// since our outer attributes do not apply to this part of the expression
178-
let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
201+
// Perform this outside of the `collect_tokens` closure, since our
202+
// outer attributes do not apply to this part of the expression.
203+
let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| {
179204
this.parse_expr_assoc_rest_with(0, true, expr)
180205
})?;
181206
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
@@ -209,7 +234,7 @@ impl<'a> Parser<'a> {
209234
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
210235
let e = self.maybe_recover_from_bad_qpath(e)?;
211236
let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
212-
let e = self.parse_expr_assoc_rest_with(0, false, e)?;
237+
let (e, _) = self.parse_expr_assoc_rest_with(0, false, e)?;
213238
StmtKind::Expr(e)
214239
};
215240
Ok(self.mk_stmt(lo.to(hi), kind))
@@ -239,10 +264,14 @@ impl<'a> Parser<'a> {
239264
subdiagnostic: fn(Span) -> errors::InvalidVariableDeclarationSub,
240265
force_collect: ForceCollect,
241266
) -> PResult<'a, Stmt> {
242-
let stmt = self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
267+
let stmt = self.collect_tokens(None, attrs, force_collect, |this, attrs| {
243268
let local = this.parse_local(attrs)?;
244269
// FIXME - maybe capture semicolon in recovery?
245-
Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), false))
270+
Ok((
271+
this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)),
272+
Trailing::No,
273+
UsePreAttrPos::No,
274+
))
246275
})?;
247276
self.dcx()
248277
.emit_err(errors::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });

‎tests/ui/attributes/assoc-expr.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//@ check-pass
2+
// This test triggered an assertion failure in token collection due to
3+
// mishandling of attributes on associative expressions.
4+
5+
#![feature(cfg_eval)]
6+
#![feature(rustc_attrs)]
7+
#![feature(stmt_expr_attributes)]
8+
#![allow(internal_features)]
9+
10+
fn main() {}
11+
12+
#[cfg_eval]
13+
struct Foo1(
14+
[ bool; {
15+
let _x = 30;
16+
#[cfg_attr(unix, rustc_dummy(aa))] 1
17+
} ]
18+
);
19+
20+
#[cfg_eval]
21+
struct Foo12(
22+
[ bool; {
23+
let _x = 30;
24+
#[cfg_attr(unix, rustc_dummy(bb))] 1 + 2
25+
} ]
26+
);
27+
28+
#[cfg_eval]
29+
struct Foox(
30+
[ bool; {
31+
let _x = 30;
32+
#[cfg_attr(unix, rustc_dummy(cc))] _x
33+
} ]
34+
);
35+
36+
#[cfg_eval]
37+
struct Foox2(
38+
[ bool; {
39+
let _x = 30;
40+
#[cfg_attr(unix, rustc_dummy(dd))] _x + 2
41+
} ]
42+
);

‎tests/ui/macros/stringify.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ macro_rules! stmt { ($stmt:stmt) => { stringify!($stmt) }; }
3333
macro_rules! ty { ($ty:ty) => { stringify!($ty) }; }
3434
macro_rules! vis { ($vis:vis) => { stringify!($vis) }; }
3535

36-
// Use this when AST pretty-printing and TokenStream pretty-printing give
37-
// the same result (which is preferable.)
3836
macro_rules! c1 {
3937
($frag:ident, [$($tt:tt)*], $s:literal) => {
4038
// Prior to #125174:
@@ -66,6 +64,8 @@ fn test_block() {
6664
} ],
6765
"{ let _; true }"
6866
);
67+
68+
// Attributes are not allowed on vanilla blocks.
6969
}
7070

7171
#[test]
@@ -332,6 +332,20 @@ fn test_expr() {
332332
// ExprKind::FormatArgs: untestable because this test works pre-expansion.
333333

334334
// ExprKind::Err: untestable.
335+
336+
// Ones involving attributes.
337+
c1!(expr, [ #[aa] 1 ], "#[aa] 1");
338+
c1!(expr, [ #[aa] #[bb] x ], "#[aa] #[bb] x");
339+
c1!(expr, [ #[aa] 1 + 2 ], "#[aa] 1 + 2");
340+
c1!(expr, [ #[aa] x + 2 ], "#[aa] x + 2");
341+
c1!(expr, [ #[aa] 1 / #[bb] 2 ], "#[aa] 1 / #[bb] 2");
342+
c1!(expr, [ #[aa] x / #[bb] 2 ], "#[aa] x / #[bb] 2");
343+
c1!(expr, [ 1 << #[bb] 2 ], "1 << #[bb] 2");
344+
c1!(expr, [ x << #[bb] 2 ], "x << #[bb] 2");
345+
c1!(expr, [ #[aa] (1 + 2) ], "#[aa] (1 + 2)");
346+
c1!(expr, [ #[aa] #[bb] (x + 2) ], "#[aa] #[bb] (x + 2)");
347+
c1!(expr, [ #[aa] x[0].p ], "#[aa] x[0].p");
348+
c1!(expr, [ #[aa] { #![bb] 0 } ], "#[aa] { #![bb] 0 }");
335349
}
336350

337351
#[test]
@@ -484,6 +498,11 @@ fn test_item() {
484498
"macro_rules! stringify { () => {}; }"
485499
);
486500
c1!(item, [ pub macro stringify() {} ], "pub macro stringify() {}");
501+
502+
// Ones involving attributes.
503+
c1!(item, [ #[aa] mod m; ], "#[aa] mod m;");
504+
c1!(item, [ mod m { #![bb] } ], "mod m { #![bb] }");
505+
c1!(item, [ #[aa] mod m { #![bb] } ], "#[aa] mod m { #![bb] }");
487506
}
488507

489508
#[test]
@@ -492,6 +511,8 @@ fn test_meta() {
492511
c1!(meta, [ k = "v" ], "k = \"v\"");
493512
c1!(meta, [ list(k1, k2 = "v") ], "list(k1, k2 = \"v\")");
494513
c1!(meta, [ serde::k ], "serde::k");
514+
515+
// Attributes are not allowed on metas.
495516
}
496517

497518
#[test]
@@ -580,6 +601,8 @@ fn test_pat() {
580601
c1!(pat, [ mac!(...) ], "mac!(...)");
581602
c1!(pat, [ mac![...] ], "mac![...]");
582603
c1!(pat, [ mac! { ... } ], "mac! { ... }");
604+
605+
// Attributes are not allowed on patterns.
583606
}
584607

585608
#[test]
@@ -593,6 +616,8 @@ fn test_path() {
593616
c1!(path, [ Self::<'static> ], "Self::<'static>");
594617
c1!(path, [ Self() ], "Self()");
595618
c1!(path, [ Self() -> () ], "Self() -> ()");
619+
620+
// Attributes are not allowed on paths.
596621
}
597622

598623
#[test]
@@ -622,6 +647,20 @@ fn test_stmt() {
622647
c1!(stmt, [ mac!(...) ], "mac!(...)");
623648
c1!(stmt, [ mac![...] ], "mac![...]");
624649
c1!(stmt, [ mac! { ... } ], "mac! { ... }");
650+
651+
// Ones involving attributes.
652+
c1!(stmt, [ #[aa] 1 ], "#[aa] 1");
653+
c1!(stmt, [ #[aa] #[bb] x ], "#[aa] #[bb] x");
654+
c1!(stmt, [ #[aa] 1 as u32 ], "#[aa] 1 as u32");
655+
c1!(stmt, [ #[aa] x as u32 ], "#[aa] x as u32");
656+
c1!(stmt, [ #[aa] 1 .. #[bb] 2 ], "#[aa] 1 .. #[bb] 2");
657+
c1!(stmt, [ #[aa] x .. #[bb] 2 ], "#[aa] x .. #[bb] 2");
658+
c1!(stmt, [ 1 || #[bb] 2 ], "1 || #[bb] 2");
659+
c1!(stmt, [ x || #[bb] 2 ], "x || #[bb] 2");
660+
c1!(stmt, [ #[aa] (1 + 2) ], "#[aa] (1 + 2)");
661+
c1!(stmt, [ #[aa] #[bb] (x + 2) ], "#[aa] #[bb] (x + 2)");
662+
c1!(stmt, [ #[aa] x[0].p ], "#[aa] x[0].p");
663+
c1!(stmt, [ #[aa] { #![bb] 0 } ], "#[aa] { #![bb] 0 }");
625664
}
626665

627666
#[test]
@@ -708,6 +747,8 @@ fn test_ty() {
708747

709748
// TyKind::CVarArgs
710749
// FIXME: todo
750+
751+
// Attributes are not allowed on types.
711752
}
712753

713754
#[test]
@@ -732,6 +773,8 @@ fn test_vis() {
732773
macro_rules! inherited_vis { ($vis:vis struct) => { vis!($vis) }; }
733774
assert_eq!(inherited_vis!(struct), "");
734775
assert_eq!(stringify!(), "");
776+
777+
// Attributes are not allowed on visibilities.
735778
}
736779

737780
macro_rules! p {

0 commit comments

Comments
 (0)
Please sign in to comment.