Skip to content

Commit bbcda98

Browse files
committed
cfg_attr: avoid .outer_tokens
1 parent 9630dbb commit bbcda98

File tree

7 files changed

+177
-75
lines changed

7 files changed

+177
-75
lines changed

src/librustc_parse/config.rs

Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88
//!
99
//! [#64197]: https://github.com/rust-lang/rust/issues/64197
1010
11-
use crate::validate_attr;
11+
use crate::{parse_in, validate_attr};
1212
use rustc_feature::Features;
1313
use rustc_errors::Applicability;
1414
use syntax::attr::HasAttrs;
1515
use syntax::feature_gate::{feature_err, get_features};
1616
use syntax::attr;
17-
use syntax::ast;
17+
use syntax::ast::{self, Attribute, AttrItem, MetaItem};
1818
use syntax::edition::Edition;
1919
use syntax::mut_visit::*;
2020
use syntax::ptr::P;
21+
use syntax::tokenstream::DelimSpan;
2122
use syntax::sess::ParseSess;
2223
use syntax::util::map_in_place::MapInPlace;
24+
use syntax_pos::Span;
2325
use syntax_pos::symbol::sym;
2426

2527
use smallvec::SmallVec;
@@ -72,6 +74,11 @@ macro_rules! configure {
7274
}
7375
}
7476

77+
const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
78+
const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
79+
<https://doc.rust-lang.org/reference/conditional-compilation.html\
80+
#the-cfg_attr-attribute>";
81+
7582
impl<'a> StripUnconfigured<'a> {
7683
pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
7784
self.process_cfg_attrs(&mut node);
@@ -97,34 +104,14 @@ impl<'a> StripUnconfigured<'a> {
97104
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
98105
/// is in the original source file. Gives a compiler error if the syntax of
99106
/// the attribute is incorrect.
100-
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
107+
fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
101108
if !attr.has_name(sym::cfg_attr) {
102109
return vec![attr];
103110
}
104-
if let ast::MacArgs::Empty = attr.get_normal_item().args {
105-
self.sess.span_diagnostic
106-
.struct_span_err(
107-
attr.span,
108-
"malformed `cfg_attr` attribute input",
109-
).span_suggestion(
110-
attr.span,
111-
"missing condition and attribute",
112-
"#[cfg_attr(condition, attribute, other_attribute, ...)]".to_owned(),
113-
Applicability::HasPlaceholders,
114-
).note("for more information, visit \
115-
<https://doc.rust-lang.org/reference/conditional-compilation.html\
116-
#the-cfg_attr-attribute>")
117-
.emit();
118-
return vec![];
119-
}
120111

121-
let res = crate::parse_in_attr(self.sess, &attr, |p| p.parse_cfg_attr());
122-
let (cfg_predicate, expanded_attrs) = match res {
123-
Ok(result) => result,
124-
Err(mut e) => {
125-
e.emit();
126-
return vec![];
127-
}
112+
let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
113+
None => return vec![],
114+
Some(r) => r,
128115
};
129116

130117
// Lint on zero attributes in source.
@@ -135,24 +122,72 @@ impl<'a> StripUnconfigured<'a> {
135122
// At this point we know the attribute is considered used.
136123
attr::mark_used(&attr);
137124

138-
if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
139-
// We call `process_cfg_attr` recursively in case there's a
140-
// `cfg_attr` inside of another `cfg_attr`. E.g.
141-
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
142-
expanded_attrs.into_iter()
143-
.flat_map(|(item, span)| self.process_cfg_attr(attr::mk_attr_from_item(
144-
attr.style,
145-
item,
146-
span,
147-
)))
125+
if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
126+
return vec![];
127+
}
128+
129+
// We call `process_cfg_attr` recursively in case there's a
130+
// `cfg_attr` inside of another `cfg_attr`. E.g.
131+
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
132+
expanded_attrs
133+
.into_iter()
134+
.flat_map(|(item, span)| {
135+
let attr = attr::mk_attr_from_item(attr.style, item, span);
136+
self.process_cfg_attr(attr)
137+
})
148138
.collect()
149-
} else {
150-
vec![]
139+
}
140+
141+
fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
142+
match &attr.get_normal_item().args {
143+
ast::MacArgs::Delimited(dspan, delim, tts) if !tts.is_empty() => {
144+
if let ast::MacDelimiter::Brace | ast::MacDelimiter::Bracket = delim {
145+
self.error_malformed_cfg_attr_wrong_delim(*dspan);
146+
}
147+
match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
148+
Ok(r) => return Some(r),
149+
Err(mut e) => e
150+
.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
151+
.note(CFG_ATTR_NOTE_REF)
152+
.emit(),
153+
}
154+
}
155+
_ => self.error_malformed_cfg_attr_missing(attr.span),
151156
}
157+
None
158+
}
159+
160+
fn error_malformed_cfg_attr_wrong_delim(&self, dspan: DelimSpan) {
161+
self.sess
162+
.span_diagnostic
163+
.struct_span_err(dspan.entire(), "wrong `cfg_attr` delimiters")
164+
.multipart_suggestion(
165+
"the delimiters should be `(` and `)`",
166+
vec![
167+
(dspan.open, "(".to_string()),
168+
(dspan.close, ")".to_string()),
169+
],
170+
Applicability::MachineApplicable,
171+
)
172+
.emit();
173+
}
174+
175+
fn error_malformed_cfg_attr_missing(&self, span: Span) {
176+
self.sess
177+
.span_diagnostic
178+
.struct_span_err(span, "malformed `cfg_attr` attribute input")
179+
.span_suggestion(
180+
span,
181+
"missing condition and attribute",
182+
CFG_ATTR_GRAMMAR_HELP.to_string(),
183+
Applicability::HasPlaceholders,
184+
)
185+
.note(CFG_ATTR_NOTE_REF)
186+
.emit();
152187
}
153188

154189
/// Determines if a node with the given attributes should be included in this configuration.
155-
pub fn in_cfg(&self, attrs: &[ast::Attribute]) -> bool {
190+
pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
156191
attrs.iter().all(|attr| {
157192
if !is_cfg(attr) {
158193
return true;
@@ -199,15 +234,15 @@ impl<'a> StripUnconfigured<'a> {
199234
}
200235

201236
/// Visit attributes on expression and statements (but not attributes on items in blocks).
202-
fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
237+
fn visit_expr_attrs(&mut self, attrs: &[Attribute]) {
203238
// flag the offending attributes
204239
for attr in attrs.iter() {
205240
self.maybe_emit_expr_attr_err(attr);
206241
}
207242
}
208243

209244
/// If attributes are not allowed on expressions, emit an error for `attr`
210-
pub fn maybe_emit_expr_attr_err(&self, attr: &ast::Attribute) {
245+
pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
211246
if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
212247
let mut err = feature_err(self.sess,
213248
sym::stmt_expr_attributes,
@@ -350,7 +385,7 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
350385
}
351386
}
352387

353-
fn is_cfg(attr: &ast::Attribute) -> bool {
388+
fn is_cfg(attr: &Attribute) -> bool {
354389
attr.check_name(sym::cfg)
355390
}
356391

@@ -359,8 +394,8 @@ fn is_cfg(attr: &ast::Attribute) -> bool {
359394
pub fn process_configure_mod(
360395
sess: &ParseSess,
361396
cfg_mods: bool,
362-
attrs: &[ast::Attribute],
363-
) -> (bool, Vec<ast::Attribute>) {
397+
attrs: &[Attribute],
398+
) -> (bool, Vec<Attribute>) {
364399
// Don't perform gated feature checking.
365400
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
366401
let mut attrs = attrs.to_owned();

src/librustc_parse/lib.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -270,28 +270,31 @@ pub fn stream_to_parser_with_base_dir<'a>(
270270
}
271271

272272
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
273-
pub fn parse_in_attr<'a, T>(
273+
pub fn parse_in<'a, T>(
274274
sess: &'a ParseSess,
275-
attr: &ast::Attribute,
275+
tts: TokenStream,
276+
name: &'static str,
276277
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
277278
) -> PResult<'a, T> {
278-
let mut parser = Parser::new(
279-
sess,
280-
// FIXME(#66940, Centril | petrochenkov): refactor this function so it doesn't
281-
// require reconstructing and immediately re-parsing delimiters.
282-
attr.get_normal_item().args.outer_tokens(),
283-
None,
284-
false,
285-
false,
286-
Some("attribute"),
287-
);
279+
let mut parser = Parser::new(sess, tts, None, false, false, Some(name));
288280
let result = f(&mut parser)?;
289281
if parser.token != token::Eof {
290282
parser.unexpected()?;
291283
}
292284
Ok(result)
293285
}
294286

287+
/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
288+
pub fn parse_in_attr<'a, T>(
289+
sess: &'a ParseSess,
290+
attr: &ast::Attribute,
291+
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
292+
) -> PResult<'a, T> {
293+
// FIXME(#66940, Centril | petrochenkov): refactor this function so it doesn't
294+
// require reconstructing and immediately re-parsing delimiters.
295+
parse_in(sess, attr.get_normal_item().args.outer_tokens(), "attribute", f)
296+
}
297+
295298
// NOTE(Centril): The following probably shouldn't be here but it acknowledges the
296299
// fact that architecturally, we are using parsing (read on below to understand why).
297300

src/librustc_parse/parser/attr.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -238,22 +238,18 @@ impl<'a> Parser<'a> {
238238

239239
/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
240240
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
241-
self.expect(&token::OpenDelim(token::Paren))?;
242-
243241
let cfg_predicate = self.parse_meta_item()?;
244242
self.expect(&token::Comma)?;
245243

246244
// Presumably, the majority of the time there will only be one attr.
247245
let mut expanded_attrs = Vec::with_capacity(1);
248-
249-
while !self.check(&token::CloseDelim(token::Paren)) {
250-
let lo = self.token.span.lo();
246+
while self.token.kind != token::Eof {
247+
let lo = self.token.span;
251248
let item = self.parse_attr_item()?;
252-
expanded_attrs.push((item, self.prev_span.with_lo(lo)));
253-
self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
249+
expanded_attrs.push((item, lo.to(self.prev_span)));
250+
self.eat(&token::Comma);
254251
}
255252

256-
self.expect(&token::CloseDelim(token::Paren))?;
257253
Ok((cfg_predicate, expanded_attrs))
258254
}
259255

src/test/ui/conditional-compilation/cfg-attr-parse.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// Parse `cfg_attr` with varying numbers of attributes and trailing commas
22

33
// Completely empty `cfg_attr` input
4-
#[cfg_attr()] //~ error: expected identifier, found `)`
4+
#[cfg_attr()] //~ error: malformed `cfg_attr` attribute input
55
struct NoConfigurationPredicate;
66

77
// Zero attributes, zero trailing comma (comma manatory here)
8-
#[cfg_attr(all())] //~ error: expected `,`, found `)`
8+
#[cfg_attr(all())] //~ error: expected `,`, found end of `cfg_attr`
99
struct A0C0;
1010

1111
// Zero attributes, one trailing comma
@@ -40,4 +40,16 @@ struct A2C1;
4040
#[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
4141
struct A2C2;
4242

43+
// Wrong delimiter `[`
44+
#[cfg_attr[all(),,]]
45+
//~^ ERROR wrong `cfg_attr` delimiters
46+
//~| ERROR expected identifier, found `,`
47+
struct BracketZero;
48+
49+
// Wrong delimiter `{`
50+
#[cfg_attr{all(),,}]
51+
//~^ ERROR wrong `cfg_attr` delimiters
52+
//~| ERROR expected identifier, found `,`
53+
struct BraceZero;
54+
4355
fn main() {}
Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,86 @@
1-
error: expected identifier, found `)`
2-
--> $DIR/cfg-attr-parse.rs:4:12
1+
error: malformed `cfg_attr` attribute input
2+
--> $DIR/cfg-attr-parse.rs:4:1
33
|
44
LL | #[cfg_attr()]
5-
| ^ expected identifier
5+
| ^^^^^^^^^^^^^ help: missing condition and attribute: `#[cfg_attr(condition, attribute, other_attribute, ...)]`
6+
|
7+
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
68

7-
error: expected `,`, found `)`
9+
error: expected `,`, found end of `cfg_attr` input
810
--> $DIR/cfg-attr-parse.rs:8:17
911
|
1012
LL | #[cfg_attr(all())]
1113
| ^ expected `,`
14+
|
15+
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
16+
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
1217

1318
error: expected identifier, found `,`
1419
--> $DIR/cfg-attr-parse.rs:16:18
1520
|
1621
LL | #[cfg_attr(all(),,)]
1722
| ^ expected identifier
23+
|
24+
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
25+
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
1826

1927
error: expected identifier, found `,`
2028
--> $DIR/cfg-attr-parse.rs:28:28
2129
|
2230
LL | #[cfg_attr(all(), must_use,,)]
2331
| ^ expected identifier
32+
|
33+
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
34+
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
2435

2536
error: expected identifier, found `,`
2637
--> $DIR/cfg-attr-parse.rs:40:40
2738
|
2839
LL | #[cfg_attr(all(), must_use, deprecated,,)]
2940
| ^ expected identifier
41+
|
42+
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
43+
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
44+
45+
error: wrong `cfg_attr` delimiters
46+
--> $DIR/cfg-attr-parse.rs:44:11
47+
|
48+
LL | #[cfg_attr[all(),,]]
49+
| ^^^^^^^^^
50+
|
51+
help: the delimiters should be `(` and `)`
52+
|
53+
LL | #[cfg_attr(all(),,)]
54+
| ^ ^
55+
56+
error: expected identifier, found `,`
57+
--> $DIR/cfg-attr-parse.rs:44:18
58+
|
59+
LL | #[cfg_attr[all(),,]]
60+
| ^ expected identifier
61+
|
62+
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
63+
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
64+
65+
error: wrong `cfg_attr` delimiters
66+
--> $DIR/cfg-attr-parse.rs:50:11
67+
|
68+
LL | #[cfg_attr{all(),,}]
69+
| ^^^^^^^^^
70+
|
71+
help: the delimiters should be `(` and `)`
72+
|
73+
LL | #[cfg_attr(all(),,)]
74+
| ^ ^
75+
76+
error: expected identifier, found `,`
77+
--> $DIR/cfg-attr-parse.rs:50:18
78+
|
79+
LL | #[cfg_attr{all(),,}]
80+
| ^ expected identifier
81+
|
82+
= help: the valid syntax is `#[cfg_attr(condition, attribute, other_attribute, ...)]`
83+
= note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
3084

31-
error: aborting due to 5 previous errors
85+
error: aborting due to 9 previous errors
3286

0 commit comments

Comments
 (0)