Skip to content

Commit

Permalink
Rollup merge of rust-lang#67052 - Centril:config-1, r=petrochenkov
Browse files Browse the repository at this point in the history
Ditch `parse_in_attr`

Fixes rust-lang#66940

r? @petrochenkov
  • Loading branch information
Centril committed Dec 6, 2019
2 parents 99fee78 + 99191c2 commit 941c4cd
Show file tree
Hide file tree
Showing 18 changed files with 327 additions and 164 deletions.
106 changes: 62 additions & 44 deletions src/librustc_parse/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@
//!
//! [#64197]: https://github.com/rust-lang/rust/issues/64197

use crate::validate_attr;
use crate::{parse_in, validate_attr};
use rustc_feature::Features;
use rustc_errors::Applicability;
use syntax::attr::HasAttrs;
use syntax::feature_gate::{feature_err, get_features};
use syntax::attr;
use syntax::ast;
use syntax::ast::{self, Attribute, AttrItem, MetaItem};
use syntax::edition::Edition;
use syntax::mut_visit::*;
use syntax::ptr::P;
use syntax::sess::ParseSess;
use syntax::util::map_in_place::MapInPlace;
use syntax_pos::Span;
use syntax_pos::symbol::sym;

use smallvec::SmallVec;
Expand Down Expand Up @@ -72,6 +73,11 @@ macro_rules! configure {
}
}

const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
<https://doc.rust-lang.org/reference/conditional-compilation.html\
#the-cfg_attr-attribute>";

impl<'a> StripUnconfigured<'a> {
pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
self.process_cfg_attrs(&mut node);
Expand All @@ -97,34 +103,14 @@ impl<'a> StripUnconfigured<'a> {
/// Gives a compiler warning when the `cfg_attr` contains no attributes and
/// is in the original source file. Gives a compiler error if the syntax of
/// the attribute is incorrect.
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
if !attr.has_name(sym::cfg_attr) {
return vec![attr];
}
if let ast::MacArgs::Empty = attr.get_normal_item().args {
self.sess.span_diagnostic
.struct_span_err(
attr.span,
"malformed `cfg_attr` attribute input",
).span_suggestion(
attr.span,
"missing condition and attribute",
"#[cfg_attr(condition, attribute, other_attribute, ...)]".to_owned(),
Applicability::HasPlaceholders,
).note("for more information, visit \
<https://doc.rust-lang.org/reference/conditional-compilation.html\
#the-cfg_attr-attribute>")
.emit();
return vec![];
}

let res = crate::parse_in_attr(self.sess, &attr, |p| p.parse_cfg_attr());
let (cfg_predicate, expanded_attrs) = match res {
Ok(result) => result,
Err(mut e) => {
e.emit();
return vec![];
}
let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
None => return vec![],
Some(r) => r,
};

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

if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
// We call `process_cfg_attr` recursively in case there's a
// `cfg_attr` inside of another `cfg_attr`. E.g.
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
expanded_attrs.into_iter()
.flat_map(|(item, span)| self.process_cfg_attr(attr::mk_attr_from_item(
attr.style,
item,
span,
)))
if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
return vec![];
}

// We call `process_cfg_attr` recursively in case there's a
// `cfg_attr` inside of another `cfg_attr`. E.g.
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
expanded_attrs
.into_iter()
.flat_map(|(item, span)| {
let attr = attr::mk_attr_from_item(attr.style, item, span);
self.process_cfg_attr(attr)
})
.collect()
} else {
vec![]
}

fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
match attr.get_normal_item().args {
ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
let msg = "wrong `cfg_attr` delimiters";
validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg);
match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
Ok(r) => return Some(r),
Err(mut e) => e
.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
.note(CFG_ATTR_NOTE_REF)
.emit(),
}
}
_ => self.error_malformed_cfg_attr_missing(attr.span),
}
None
}

fn error_malformed_cfg_attr_missing(&self, span: Span) {
self.sess
.span_diagnostic
.struct_span_err(span, "malformed `cfg_attr` attribute input")
.span_suggestion(
span,
"missing condition and attribute",
CFG_ATTR_GRAMMAR_HELP.to_string(),
Applicability::HasPlaceholders,
)
.note(CFG_ATTR_NOTE_REF)
.emit();
}

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

/// Visit attributes on expression and statements (but not attributes on items in blocks).
fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
fn visit_expr_attrs(&mut self, attrs: &[Attribute]) {
// flag the offending attributes
for attr in attrs.iter() {
self.maybe_emit_expr_attr_err(attr);
}
}

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

fn is_cfg(attr: &ast::Attribute) -> bool {
fn is_cfg(attr: &Attribute) -> bool {
attr.check_name(sym::cfg)
}

Expand All @@ -359,8 +377,8 @@ fn is_cfg(attr: &ast::Attribute) -> bool {
pub fn process_configure_mod(
sess: &ParseSess,
cfg_mods: bool,
attrs: &[ast::Attribute],
) -> (bool, Vec<ast::Attribute>) {
attrs: &[Attribute],
) -> (bool, Vec<Attribute>) {
// Don't perform gated feature checking.
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
let mut attrs = attrs.to_owned();
Expand Down
16 changes: 4 additions & 12 deletions src/librustc_parse/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,21 +271,13 @@ pub fn stream_to_parser_with_base_dir<'a>(
}

/// Runs the given subparser `f` on the tokens of the given `attr`'s item.
pub fn parse_in_attr<'a, T>(
pub fn parse_in<'a, T>(
sess: &'a ParseSess,
attr: &ast::Attribute,
tts: TokenStream,
name: &'static str,
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, T> {
let mut parser = Parser::new(
sess,
// FIXME(#66940, Centril | petrochenkov): refactor this function so it doesn't
// require reconstructing and immediately re-parsing delimiters.
attr.get_normal_item().args.outer_tokens(),
None,
false,
false,
Some("attribute"),
);
let mut parser = Parser::new(sess, tts, None, false, false, Some(name));
let result = f(&mut parser)?;
if parser.token != token::Eof {
parser.unexpected()?;
Expand Down
29 changes: 20 additions & 9 deletions src/librustc_parse/parser/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ impl<'a> Parser<'a> {
Ok(attrs)
}

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

Expand All @@ -238,25 +238,36 @@ impl<'a> Parser<'a> {

/// Parses `cfg_attr(pred, attr_item_list)` where `attr_item_list` is comma-delimited.
pub fn parse_cfg_attr(&mut self) -> PResult<'a, (ast::MetaItem, Vec<(ast::AttrItem, Span)>)> {
self.expect(&token::OpenDelim(token::Paren))?;

let cfg_predicate = self.parse_meta_item()?;
self.expect(&token::Comma)?;

// Presumably, the majority of the time there will only be one attr.
let mut expanded_attrs = Vec::with_capacity(1);

while !self.check(&token::CloseDelim(token::Paren)) {
let lo = self.token.span.lo();
while self.token.kind != token::Eof {
let lo = self.token.span;
let item = self.parse_attr_item()?;
expanded_attrs.push((item, self.prev_span.with_lo(lo)));
self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
expanded_attrs.push((item, lo.to(self.prev_span)));
if !self.eat(&token::Comma) {
break;
}
}

self.expect(&token::CloseDelim(token::Paren))?;
Ok((cfg_predicate, expanded_attrs))
}

/// Matches `COMMASEP(meta_item_inner)`.
crate fn parse_meta_seq_top(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> {
// Presumably, the majority of the time there will only be one attr.
let mut nmis = Vec::with_capacity(1);
while self.token.kind != token::Eof {
nmis.push(self.parse_meta_item_inner()?);
if !self.eat(&token::Comma) {
break;
}
}
Ok(nmis)
}

/// Matches the following grammar (per RFC 1559).
///
/// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
Expand Down
37 changes: 0 additions & 37 deletions src/librustc_parse/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::maybe_whole;
use rustc_errors::{PResult, Applicability, pluralize};
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
use syntax::ast::MacArgs;
use syntax::ThinVec;
use syntax::token::{self, Token};
use syntax_pos::source_map::{Span, BytePos};
Expand Down Expand Up @@ -109,42 +108,6 @@ impl<'a> Parser<'a> {
Ok(Path { segments, span: lo.to(self.prev_span) })
}

/// Like `parse_path`, but also supports parsing `Word` meta items into paths for
/// backwards-compatibility. This is used when parsing derive macro paths in `#[derive]`
/// attributes.
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
let meta_ident = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
token::NtMeta(ref item) => match item.args {
MacArgs::Empty => Some(item.path.clone()),
_ => None,
},
_ => None,
},
_ => None,
};
if let Some(path) = meta_ident {
self.bump();
return Ok(path);
}
self.parse_path(style)
}

/// Parse a list of paths inside `#[derive(path_0, ..., path_n)]`.
pub fn parse_derive_paths(&mut self) -> PResult<'a, Vec<Path>> {
self.expect(&token::OpenDelim(token::Paren))?;
let mut list = Vec::new();
while !self.eat(&token::CloseDelim(token::Paren)) {
let path = self.parse_path_allowing_meta(PathStyle::Mod)?;
list.push(path);
if !self.eat(&token::Comma) {
self.expect(&token::CloseDelim(token::Paren))?;
break
}
}
Ok(list)
}

pub(super) fn parse_path_segments(
&mut self,
segments: &mut Vec<PathSegment>,
Expand Down
38 changes: 35 additions & 3 deletions src/librustc_parse/validate_attr.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! Meta-syntax validation logic of attributes for post-expansion.

use crate::parse_in;

use rustc_errors::{PResult, Applicability};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MetaItem, MetaItemKind};
use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
use syntax::attr::mk_name_value_item_str;
use syntax::early_buffered_lints::ILL_FORMED_ATTRIBUTE_INPUT;
use syntax::tokenstream::DelimSpan;
use syntax::sess::ParseSess;
use syntax_pos::{Symbol, sym};

Expand All @@ -27,16 +30,45 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
Ok(match attr.kind {
AttrKind::Normal(ref item) => MetaItem {
path: item.path.clone(),
kind: super::parse_in_attr(sess, attr, |p| p.parse_meta_item_kind())?,
span: attr.span,
path: item.path.clone(),
kind: match &attr.get_normal_item().args {
MacArgs::Empty => MetaItemKind::Word,
MacArgs::Eq(_, t) => {
let v = parse_in(sess, t.clone(), "name value", |p| p.parse_unsuffixed_lit())?;
MetaItemKind::NameValue(v)
}
MacArgs::Delimited(dspan, delim, t) => {
check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
MetaItemKind::List(nmis)
}
}
},
AttrKind::DocComment(comment) => {
mk_name_value_item_str(Ident::new(sym::doc, attr.span), comment, attr.span)
}
})
}

crate fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
if let ast::MacDelimiter::Parenthesis = delim {
return;
}

sess.span_diagnostic
.struct_span_err(span.entire(), msg)
.multipart_suggestion(
"the delimiters should be `(` and `)`",
vec![
(span.open, "(".to_string()),
(span.close, ")".to_string()),
],
Applicability::MachineApplicable,
)
.emit();
}

/// Checks that the given meta-item is compatible with this `AttributeTemplate`.
fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
match meta {
Expand Down
Loading

0 comments on commit 941c4cd

Please sign in to comment.