Skip to content

Commit 941c4cd

Browse files
authored
Rollup merge of #67052 - Centril:config-1, r=petrochenkov
Ditch `parse_in_attr` Fixes #66940 r? @petrochenkov
2 parents 99fee78 + 99191c2 commit 941c4cd

18 files changed

+327
-164
lines changed

src/librustc_parse/config.rs

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@
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;
2121
use syntax::sess::ParseSess;
2222
use syntax::util::map_in_place::MapInPlace;
23+
use syntax_pos::Span;
2324
use syntax_pos::symbol::sym;
2425

2526
use smallvec::SmallVec;
@@ -72,6 +73,11 @@ macro_rules! configure {
7273
}
7374
}
7475

76+
const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
77+
const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
78+
<https://doc.rust-lang.org/reference/conditional-compilation.html\
79+
#the-cfg_attr-attribute>";
80+
7581
impl<'a> StripUnconfigured<'a> {
7682
pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
7783
self.process_cfg_attrs(&mut node);
@@ -97,34 +103,14 @@ impl<'a> StripUnconfigured<'a> {
97103
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
98104
/// is in the original source file. Gives a compiler error if the syntax of
99105
/// the attribute is incorrect.
100-
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
106+
fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
101107
if !attr.has_name(sym::cfg_attr) {
102108
return vec![attr];
103109
}
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-
}
120110

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-
}
111+
let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
112+
None => return vec![],
113+
Some(r) => r,
128114
};
129115

130116
// Lint on zero attributes in source.
@@ -135,24 +121,56 @@ impl<'a> StripUnconfigured<'a> {
135121
// At this point we know the attribute is considered used.
136122
attr::mark_used(&attr);
137123

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-
)))
124+
if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
125+
return vec![];
126+
}
127+
128+
// We call `process_cfg_attr` recursively in case there's a
129+
// `cfg_attr` inside of another `cfg_attr`. E.g.
130+
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
131+
expanded_attrs
132+
.into_iter()
133+
.flat_map(|(item, span)| {
134+
let attr = attr::mk_attr_from_item(attr.style, item, span);
135+
self.process_cfg_attr(attr)
136+
})
148137
.collect()
149-
} else {
150-
vec![]
138+
}
139+
140+
fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
141+
match attr.get_normal_item().args {
142+
ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
143+
let msg = "wrong `cfg_attr` delimiters";
144+
validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg);
145+
match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
146+
Ok(r) => return Some(r),
147+
Err(mut e) => e
148+
.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
149+
.note(CFG_ATTR_NOTE_REF)
150+
.emit(),
151+
}
152+
}
153+
_ => self.error_malformed_cfg_attr_missing(attr.span),
151154
}
155+
None
156+
}
157+
158+
fn error_malformed_cfg_attr_missing(&self, span: Span) {
159+
self.sess
160+
.span_diagnostic
161+
.struct_span_err(span, "malformed `cfg_attr` attribute input")
162+
.span_suggestion(
163+
span,
164+
"missing condition and attribute",
165+
CFG_ATTR_GRAMMAR_HELP.to_string(),
166+
Applicability::HasPlaceholders,
167+
)
168+
.note(CFG_ATTR_NOTE_REF)
169+
.emit();
152170
}
153171

154172
/// Determines if a node with the given attributes should be included in this configuration.
155-
pub fn in_cfg(&self, attrs: &[ast::Attribute]) -> bool {
173+
pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
156174
attrs.iter().all(|attr| {
157175
if !is_cfg(attr) {
158176
return true;
@@ -199,15 +217,15 @@ impl<'a> StripUnconfigured<'a> {
199217
}
200218

201219
/// Visit attributes on expression and statements (but not attributes on items in blocks).
202-
fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
220+
fn visit_expr_attrs(&mut self, attrs: &[Attribute]) {
203221
// flag the offending attributes
204222
for attr in attrs.iter() {
205223
self.maybe_emit_expr_attr_err(attr);
206224
}
207225
}
208226

209227
/// If attributes are not allowed on expressions, emit an error for `attr`
210-
pub fn maybe_emit_expr_attr_err(&self, attr: &ast::Attribute) {
228+
pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
211229
if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
212230
let mut err = feature_err(self.sess,
213231
sym::stmt_expr_attributes,
@@ -350,7 +368,7 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
350368
}
351369
}
352370

353-
fn is_cfg(attr: &ast::Attribute) -> bool {
371+
fn is_cfg(attr: &Attribute) -> bool {
354372
attr.check_name(sym::cfg)
355373
}
356374

@@ -359,8 +377,8 @@ fn is_cfg(attr: &ast::Attribute) -> bool {
359377
pub fn process_configure_mod(
360378
sess: &ParseSess,
361379
cfg_mods: bool,
362-
attrs: &[ast::Attribute],
363-
) -> (bool, Vec<ast::Attribute>) {
380+
attrs: &[Attribute],
381+
) -> (bool, Vec<Attribute>) {
364382
// Don't perform gated feature checking.
365383
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
366384
let mut attrs = attrs.to_owned();

src/librustc_parse/lib.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -271,21 +271,13 @@ pub fn stream_to_parser_with_base_dir<'a>(
271271
}
272272

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

src/librustc_parse/parser/attr.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ impl<'a> Parser<'a> {
220220
Ok(attrs)
221221
}
222222

223-
pub(super) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
223+
crate fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
224224
let lit = self.parse_lit()?;
225225
debug!("checking if {:?} is unusuffixed", lit);
226226

@@ -238,25 +238,36 @@ 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+
if !self.eat(&token::Comma) {
251+
break;
252+
}
254253
}
255254

256-
self.expect(&token::CloseDelim(token::Paren))?;
257255
Ok((cfg_predicate, expanded_attrs))
258256
}
259257

258+
/// Matches `COMMASEP(meta_item_inner)`.
259+
crate fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
260+
// Presumably, the majority of the time there will only be one attr.
261+
let mut nmis = Vec::with_capacity(1);
262+
while self.token.kind != token::Eof {
263+
nmis.push(self.parse_meta_item_inner()?);
264+
if !self.eat(&token::Comma) {
265+
break;
266+
}
267+
}
268+
Ok(nmis)
269+
}
270+
260271
/// Matches the following grammar (per RFC 1559).
261272
///
262273
/// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;

src/librustc_parse/parser/path.rs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::maybe_whole;
33
use rustc_errors::{PResult, Applicability, pluralize};
44
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
55
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
6-
use syntax::ast::MacArgs;
76
use syntax::ThinVec;
87
use syntax::token::{self, Token};
98
use syntax_pos::source_map::{Span, BytePos};
@@ -109,42 +108,6 @@ impl<'a> Parser<'a> {
109108
Ok(Path { segments, span: lo.to(self.prev_span) })
110109
}
111110

112-
/// Like `parse_path`, but also supports parsing `Word` meta items into paths for
113-
/// backwards-compatibility. This is used when parsing derive macro paths in `#[derive]`
114-
/// attributes.
115-
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
116-
let meta_ident = match self.token.kind {
117-
token::Interpolated(ref nt) => match **nt {
118-
token::NtMeta(ref item) => match item.args {
119-
MacArgs::Empty => Some(item.path.clone()),
120-
_ => None,
121-
},
122-
_ => None,
123-
},
124-
_ => None,
125-
};
126-
if let Some(path) = meta_ident {
127-
self.bump();
128-
return Ok(path);
129-
}
130-
self.parse_path(style)
131-
}
132-
133-
/// Parse a list of paths inside `#[derive(path_0, ..., path_n)]`.
134-
pub fn parse_derive_paths(&mut self) -> PResult<'a, Vec<Path>> {
135-
self.expect(&token::OpenDelim(token::Paren))?;
136-
let mut list = Vec::new();
137-
while !self.eat(&token::CloseDelim(token::Paren)) {
138-
let path = self.parse_path_allowing_meta(PathStyle::Mod)?;
139-
list.push(path);
140-
if !self.eat(&token::Comma) {
141-
self.expect(&token::CloseDelim(token::Paren))?;
142-
break
143-
}
144-
}
145-
Ok(list)
146-
}
147-
148111
pub(super) fn parse_path_segments(
149112
&mut self,
150113
segments: &mut Vec<PathSegment>,

src/librustc_parse/validate_attr.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
//! Meta-syntax validation logic of attributes for post-expansion.
22
3+
use crate::parse_in;
4+
35
use rustc_errors::{PResult, Applicability};
46
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
5-
use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MetaItem, MetaItemKind};
7+
use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
68
use syntax::attr::mk_name_value_item_str;
79
use syntax::early_buffered_lints::ILL_FORMED_ATTRIBUTE_INPUT;
10+
use syntax::tokenstream::DelimSpan;
811
use syntax::sess::ParseSess;
912
use syntax_pos::{Symbol, sym};
1013

@@ -27,16 +30,45 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
2730
pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
2831
Ok(match attr.kind {
2932
AttrKind::Normal(ref item) => MetaItem {
30-
path: item.path.clone(),
31-
kind: super::parse_in_attr(sess, attr, |p| p.parse_meta_item_kind())?,
3233
span: attr.span,
34+
path: item.path.clone(),
35+
kind: match &attr.get_normal_item().args {
36+
MacArgs::Empty => MetaItemKind::Word,
37+
MacArgs::Eq(_, t) => {
38+
let v = parse_in(sess, t.clone(), "name value", |p| p.parse_unsuffixed_lit())?;
39+
MetaItemKind::NameValue(v)
40+
}
41+
MacArgs::Delimited(dspan, delim, t) => {
42+
check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
43+
let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
44+
MetaItemKind::List(nmis)
45+
}
46+
}
3347
},
3448
AttrKind::DocComment(comment) => {
3549
mk_name_value_item_str(Ident::new(sym::doc, attr.span), comment, attr.span)
3650
}
3751
})
3852
}
3953

54+
crate fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
55+
if let ast::MacDelimiter::Parenthesis = delim {
56+
return;
57+
}
58+
59+
sess.span_diagnostic
60+
.struct_span_err(span.entire(), msg)
61+
.multipart_suggestion(
62+
"the delimiters should be `(` and `)`",
63+
vec![
64+
(span.open, "(".to_string()),
65+
(span.close, ")".to_string()),
66+
],
67+
Applicability::MachineApplicable,
68+
)
69+
.emit();
70+
}
71+
4072
/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
4173
fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
4274
match meta {

0 commit comments

Comments
 (0)