Skip to content

Commit addbc5b

Browse files
committed
unify/improve/simplify attribute parsing
1 parent be86b2d commit addbc5b

23 files changed

+140
-173
lines changed

src/librustc_parse/parser/attr.rs

Lines changed: 97 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{Parser, PathStyle, TokenType};
1+
use super::{Parser, PathStyle};
22
use rustc_ast::ast;
33
use rustc_ast::attr;
44
use rustc_ast::token::{self, Nonterminal};
@@ -10,63 +10,65 @@ use rustc_span::{Span, Symbol};
1010
use log::debug;
1111

1212
#[derive(Debug)]
13-
enum InnerAttributeParsePolicy<'a> {
13+
pub(super) enum InnerAttrPolicy<'a> {
1414
Permitted,
15-
NotPermitted { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
15+
Forbidden { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option<Span> },
1616
}
1717

1818
const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
1919
permitted in this context";
2020

21+
pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
22+
reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
23+
saw_doc_comment: false,
24+
prev_attr_sp: None,
25+
};
26+
2127
impl<'a> Parser<'a> {
2228
/// Parses attributes that appear before an item.
2329
pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
2430
let mut attrs: Vec<ast::Attribute> = Vec::new();
2531
let mut just_parsed_doc_comment = false;
2632
loop {
2733
debug!("parse_outer_attributes: self.token={:?}", self.token);
28-
match self.token.kind {
29-
token::Pound => {
30-
let inner_error_reason = if just_parsed_doc_comment {
31-
"an inner attribute is not permitted following an outer doc comment"
32-
} else if !attrs.is_empty() {
33-
"an inner attribute is not permitted following an outer attribute"
34-
} else {
35-
DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
36-
};
37-
let inner_parse_policy = InnerAttributeParsePolicy::NotPermitted {
38-
reason: inner_error_reason,
39-
saw_doc_comment: just_parsed_doc_comment,
40-
prev_attr_sp: attrs.last().map(|a| a.span),
41-
};
42-
let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
43-
attrs.push(attr);
44-
just_parsed_doc_comment = false;
45-
}
46-
token::DocComment(s) => {
47-
let attr = self.mk_doc_comment(s);
48-
if attr.style != ast::AttrStyle::Outer {
49-
let span = self.token.span;
50-
let mut err = self.struct_span_err(span, "expected outer doc comment");
51-
err.note(
34+
if self.check(&token::Pound) {
35+
let inner_error_reason = if just_parsed_doc_comment {
36+
"an inner attribute is not permitted following an outer doc comment"
37+
} else if !attrs.is_empty() {
38+
"an inner attribute is not permitted following an outer attribute"
39+
} else {
40+
DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
41+
};
42+
let inner_parse_policy = InnerAttrPolicy::Forbidden {
43+
reason: inner_error_reason,
44+
saw_doc_comment: just_parsed_doc_comment,
45+
prev_attr_sp: attrs.last().map(|a| a.span),
46+
};
47+
let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
48+
attrs.push(attr);
49+
just_parsed_doc_comment = false;
50+
} else if let token::DocComment(s) = self.token.kind {
51+
let attr = self.mk_doc_comment(s);
52+
if attr.style != ast::AttrStyle::Outer {
53+
self.struct_span_err(self.token.span, "expected outer doc comment")
54+
.note(
5255
"inner doc comments like this (starting with \
53-
`//!` or `/*!`) can only appear before items",
54-
);
55-
return Err(err);
56-
}
57-
attrs.push(attr);
58-
self.bump();
59-
just_parsed_doc_comment = true;
56+
`//!` or `/*!`) can only appear before items",
57+
)
58+
.emit();
6059
}
61-
_ => break,
60+
attrs.push(attr);
61+
self.bump();
62+
just_parsed_doc_comment = true;
63+
} else {
64+
break;
6265
}
6366
}
6467
Ok(attrs)
6568
}
6669

6770
fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute {
68-
let style = comments::doc_comment_style(&s.as_str());
69-
attr::mk_doc_comment(style, s, self.token.span)
71+
attr::mk_doc_comment(comments::doc_comment_style(&s.as_str()), s, self.token.span)
7072
}
7173

7274
/// Matches `attribute = # ! [ meta_item ]`.
@@ -75,96 +77,68 @@ impl<'a> Parser<'a> {
7577
/// attribute.
7678
pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
7779
debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token);
78-
let inner_parse_policy = if permit_inner {
79-
InnerAttributeParsePolicy::Permitted
80-
} else {
81-
InnerAttributeParsePolicy::NotPermitted {
82-
reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
83-
saw_doc_comment: false,
84-
prev_attr_sp: None,
85-
}
86-
};
80+
let inner_parse_policy =
81+
if permit_inner { InnerAttrPolicy::Permitted } else { DEFAULT_INNER_ATTR_FORBIDDEN };
8782
self.parse_attribute_with_inner_parse_policy(inner_parse_policy)
8883
}
8984

90-
/// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy`
85+
/// The same as `parse_attribute`, except it takes in an `InnerAttrPolicy`
9186
/// that prescribes how to handle inner attributes.
9287
fn parse_attribute_with_inner_parse_policy(
9388
&mut self,
94-
inner_parse_policy: InnerAttributeParsePolicy<'_>,
89+
inner_parse_policy: InnerAttrPolicy<'_>,
9590
) -> PResult<'a, ast::Attribute> {
9691
debug!(
9792
"parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
9893
inner_parse_policy, self.token
9994
);
100-
let (span, item, style) = match self.token.kind {
101-
token::Pound => {
102-
let lo = self.token.span;
103-
self.bump();
104-
105-
if let InnerAttributeParsePolicy::Permitted = inner_parse_policy {
106-
self.expected_tokens.push(TokenType::Token(token::Not));
107-
}
108-
109-
let style = if self.token == token::Not {
110-
self.bump();
111-
ast::AttrStyle::Inner
112-
} else {
113-
ast::AttrStyle::Outer
114-
};
95+
let lo = self.token.span;
96+
let (span, item, style) = if self.eat(&token::Pound) {
97+
let style =
98+
if self.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
11599

116-
self.expect(&token::OpenDelim(token::Bracket))?;
117-
let item = self.parse_attr_item()?;
118-
self.expect(&token::CloseDelim(token::Bracket))?;
119-
let hi = self.prev_token.span;
120-
121-
let attr_sp = lo.to(hi);
122-
123-
// Emit error if inner attribute is encountered and not permitted
124-
if style == ast::AttrStyle::Inner {
125-
if let InnerAttributeParsePolicy::NotPermitted {
126-
reason,
127-
saw_doc_comment,
128-
prev_attr_sp,
129-
} = inner_parse_policy
130-
{
131-
let prev_attr_note = if saw_doc_comment {
132-
"previous doc comment"
133-
} else {
134-
"previous outer attribute"
135-
};
136-
137-
let mut diagnostic = self.struct_span_err(attr_sp, reason);
138-
139-
if let Some(prev_attr_sp) = prev_attr_sp {
140-
diagnostic
141-
.span_label(attr_sp, "not permitted following an outer attribute")
142-
.span_label(prev_attr_sp, prev_attr_note);
143-
}
144-
145-
diagnostic
146-
.note(
147-
"inner attributes, like `#![no_std]`, annotate the item \
148-
enclosing them, and are usually found at the beginning of \
149-
source files. Outer attributes, like `#[test]`, annotate the \
150-
item following them.",
151-
)
152-
.emit();
153-
}
154-
}
100+
self.expect(&token::OpenDelim(token::Bracket))?;
101+
let item = self.parse_attr_item()?;
102+
self.expect(&token::CloseDelim(token::Bracket))?;
103+
let attr_sp = lo.to(self.prev_token.span);
155104

156-
(attr_sp, item, style)
157-
}
158-
_ => {
159-
let token_str = pprust::token_to_string(&self.token);
160-
let msg = &format!("expected `#`, found `{}`", token_str);
161-
return Err(self.struct_span_err(self.token.span, msg));
105+
// Emit error if inner attribute is encountered and forbidden.
106+
if style == ast::AttrStyle::Inner {
107+
self.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
162108
}
109+
110+
(attr_sp, item, style)
111+
} else {
112+
let token_str = pprust::token_to_string(&self.token);
113+
let msg = &format!("expected `#`, found `{}`", token_str);
114+
return Err(self.struct_span_err(self.token.span, msg));
163115
};
164116

165117
Ok(attr::mk_attr_from_item(style, item, span))
166118
}
167119

120+
pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) {
121+
if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy {
122+
let prev_attr_note =
123+
if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" };
124+
125+
let mut diag = self.struct_span_err(attr_sp, reason);
126+
127+
if let Some(prev_attr_sp) = prev_attr_sp {
128+
diag.span_label(attr_sp, "not permitted following an outer attribute")
129+
.span_label(prev_attr_sp, prev_attr_note);
130+
}
131+
132+
diag.note(
133+
"inner attributes, like `#![no_std]`, annotate the item \
134+
enclosing them, and are usually found at the beginning of \
135+
source files. Outer attributes, like `#[test]`, annotate the \
136+
item following them.",
137+
)
138+
.emit();
139+
}
140+
}
141+
168142
/// Parses an inner part of an attribute (the path and following tokens).
169143
/// The tokens must be either a delimited token stream, or empty token stream,
170144
/// or the "legacy" key-value form.
@@ -200,24 +174,22 @@ impl<'a> Parser<'a> {
200174
crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
201175
let mut attrs: Vec<ast::Attribute> = vec![];
202176
loop {
203-
match self.token.kind {
204-
// Only try to parse if it is an inner attribute (has `!`).
205-
token::Pound if self.look_ahead(1, |t| t == &token::Not) => {
206-
let attr = self.parse_attribute(true)?;
207-
assert_eq!(attr.style, ast::AttrStyle::Inner);
177+
// Only try to parse if it is an inner attribute (has `!`).
178+
if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
179+
let attr = self.parse_attribute(true)?;
180+
assert_eq!(attr.style, ast::AttrStyle::Inner);
181+
attrs.push(attr);
182+
} else if let token::DocComment(s) = self.token.kind {
183+
// We need to get the position of this token before we bump.
184+
let attr = self.mk_doc_comment(s);
185+
if attr.style == ast::AttrStyle::Inner {
208186
attrs.push(attr);
187+
self.bump();
188+
} else {
189+
break;
209190
}
210-
token::DocComment(s) => {
211-
// We need to get the position of this token before we bump.
212-
let attr = self.mk_doc_comment(s);
213-
if attr.style == ast::AttrStyle::Inner {
214-
attrs.push(attr);
215-
self.bump();
216-
} else {
217-
break;
218-
}
219-
}
220-
_ => break,
191+
} else {
192+
break;
221193
}
222194
}
223195
Ok(attrs)
@@ -228,8 +200,7 @@ impl<'a> Parser<'a> {
228200
debug!("checking if {:?} is unusuffixed", lit);
229201

230202
if !lit.kind.is_unsuffixed() {
231-
let msg = "suffixed literals are not allowed in attributes";
232-
self.struct_span_err(lit.span, msg)
203+
self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
233204
.help(
234205
"instead of using a suffixed literal \
235206
(`1u8`, `1.0f32`, etc.), use an unsuffixed version \

src/librustc_parse/parser/stmt.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
12
use super::diagnostics::Error;
23
use super::expr::LhsExpr;
34
use super::pat::GateOr;
@@ -238,13 +239,11 @@ impl<'a> Parser<'a> {
238239

239240
/// Parses a block. No inner attributes are allowed.
240241
pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
241-
maybe_whole!(self, NtBlock, |x| x);
242-
243-
if !self.eat(&token::OpenDelim(token::Brace)) {
244-
return self.error_block_no_opening_brace();
242+
let (attrs, block) = self.parse_inner_attrs_and_block()?;
243+
if let [.., last] = &*attrs {
244+
self.error_on_forbidden_inner_attr(last.span, DEFAULT_INNER_ATTR_FORBIDDEN);
245245
}
246-
247-
self.parse_block_tail(self.prev_token.span, BlockCheckMode::Default)
246+
Ok(block)
248247
}
249248

250249
fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {

src/test/ui/did_you_mean/issue-40006.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ LL | X() {}
3232
LL | }
3333
| - the item list ends here
3434

35-
error: expected `[`, found `#`
35+
error: expected one of `!` or `[`, found `#`
3636
--> $DIR/issue-40006.rs:19:17
3737
|
3838
LL | fn xxx() { ### }
39-
| ^ expected `[`
39+
| ^ expected one of `!` or `[`
4040

4141
error: expected one of `!` or `::`, found `=`
4242
--> $DIR/issue-40006.rs:22:7

src/test/ui/generic-associated-types/empty_generics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
trait Foo {
55
type Bar<,>;
6-
//~^ ERROR expected one of `>`, `const`, identifier, or lifetime, found `,`
6+
//~^ ERROR expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
77
}
88

99
fn main() {}

src/test/ui/generic-associated-types/empty_generics.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: expected one of `>`, `const`, identifier, or lifetime, found `,`
1+
error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
22
--> $DIR/empty_generics.rs:5:14
33
|
44
LL | trait Foo {
55
| - while parsing this item list starting here
66
LL | type Bar<,>;
7-
| ^ expected one of `>`, `const`, identifier, or lifetime
7+
| ^ expected one of `#`, `>`, `const`, identifier, or lifetime
88
LL |
99
LL | }
1010
| - the item list ends here

src/test/ui/issues/issue-20616-8.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type Type_5_<'a> = Type_1_<'a, ()>;
2929

3030

3131
type Type_8<'a,,> = &'a ();
32-
//~^ error: expected one of `>`, `const`, identifier, or lifetime, found `,`
32+
//~^ error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
3333

3434

3535
//type Type_9<T,,> = Box<T>; // error: expected identifier, found `,`
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `>`, `const`, identifier, or lifetime, found `,`
1+
error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
22
--> $DIR/issue-20616-8.rs:31:16
33
|
44
LL | type Type_8<'a,,> = &'a ();
5-
| ^ expected one of `>`, `const`, identifier, or lifetime
5+
| ^ expected one of `#`, `>`, `const`, identifier, or lifetime
66

77
error: aborting due to previous error
88

src/test/ui/issues/issue-20616-9.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ type Type_5_<'a> = Type_1_<'a, ()>;
3232

3333

3434
type Type_9<T,,> = Box<T>;
35-
//~^ error: expected one of `>`, `const`, identifier, or lifetime, found `,`
35+
//~^ error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `>`, `const`, identifier, or lifetime, found `,`
1+
error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `,`
22
--> $DIR/issue-20616-9.rs:34:15
33
|
44
LL | type Type_9<T,,> = Box<T>;
5-
| ^ expected one of `>`, `const`, identifier, or lifetime
5+
| ^ expected one of `#`, `>`, `const`, identifier, or lifetime
66

77
error: aborting due to previous error
88

src/test/ui/issues/issue-66473.rs

14 Bytes
Binary file not shown.

src/test/ui/issues/issue-66473.stderr

28 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)