diff options
| author | Mazdak Farrokhzad <twingoow@gmail.com> | 2019-12-06 23:26:59 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-12-06 23:26:59 +0100 |
| commit | 941c4cd56bee9bc9f16eba7a7ab6c2b332da0f9f (patch) | |
| tree | 88407d3a1aeac319d4ca6025438cbb78bb14d6c5 /src/librustc_parse | |
| parent | 99fee7896b9a5e8e21af1ba6787b21566e945d78 (diff) | |
| parent | 99191c2e717883bfec51b49df0e412a34849fc4a (diff) | |
Rollup merge of #67052 - Centril:config-1, r=petrochenkov
Ditch `parse_in_attr` Fixes #66940 r? @petrochenkov
Diffstat (limited to 'src/librustc_parse')
| -rw-r--r-- | src/librustc_parse/config.rs | 106 | ||||
| -rw-r--r-- | src/librustc_parse/lib.rs | 16 | ||||
| -rw-r--r-- | src/librustc_parse/parser/attr.rs | 29 | ||||
| -rw-r--r-- | src/librustc_parse/parser/path.rs | 37 | ||||
| -rw-r--r-- | src/librustc_parse/validate_attr.rs | 38 |
5 files changed, 121 insertions, 105 deletions
diff --git a/src/librustc_parse/config.rs b/src/librustc_parse/config.rs index 7dbfd98de75..1e9203f377f 100644 --- a/src/librustc_parse/config.rs +++ b/src/librustc_parse/config.rs @@ -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; @@ -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); @@ -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. @@ -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; @@ -199,7 +217,7 @@ 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); @@ -207,7 +225,7 @@ impl<'a> StripUnconfigured<'a> { } /// 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, @@ -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) } @@ -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(); diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 69050534ee6..faff386e923 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -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()?; diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs index b2ae934ce64..00fd6b8a25b 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/src/librustc_parse/parser/attr.rs @@ -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); @@ -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? ')' )? ; diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 70c3458e7c0..5334fc485e7 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -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}; @@ -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>, diff --git a/src/librustc_parse/validate_attr.rs b/src/librustc_parse/validate_attr.rs index 97e9cb8dcdf..94d3fe7b551 100644 --- a/src/librustc_parse/validate_attr.rs +++ b/src/librustc_parse/validate_attr.rs @@ -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}; @@ -27,9 +30,20 @@ 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) @@ -37,6 +51,24 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta }) } +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 { |
