diff options
| author | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-02-28 09:17:24 +0300 |
|---|---|---|
| committer | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-03-16 23:13:15 +0300 |
| commit | 8371caf5ee13e775d2b2dc64c9b08e37dab49eda (patch) | |
| tree | 630bb9b91f6f46469c6ec3eb51b21733c8e02e46 /src/libsyntax | |
| parent | e2009ea5ffdacd767ea85d463fbe40d0e8b06951 (diff) | |
| download | rust-8371caf5ee13e775d2b2dc64c9b08e37dab49eda.tar.gz rust-8371caf5ee13e775d2b2dc64c9b08e37dab49eda.zip | |
syntax: Do not accidentally treat multi-segment meta-items as single-segment
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/attr/builtin.rs | 94 | ||||
| -rw-r--r-- | src/libsyntax/attr/mod.rs | 77 | ||||
| -rw-r--r-- | src/libsyntax/ext/tt/macro_rules.rs | 11 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 33 | ||||
| -rw-r--r-- | src/libsyntax/test.rs | 5 |
5 files changed, 109 insertions, 111 deletions
diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs index f7a000935ca..f3caf16e1a7 100644 --- a/src/libsyntax/attr/builtin.rs +++ b/src/libsyntax/attr/builtin.rs @@ -1,6 +1,6 @@ //! Parsing and validation of builtin attributes -use crate::ast::{self, Attribute, MetaItem, Name, NestedMetaItemKind}; +use crate::ast::{self, Attribute, MetaItem, NestedMetaItemKind}; use crate::feature_gate::{Features, GatedCfg}; use crate::parse::ParseSess; @@ -10,8 +10,8 @@ use syntax_pos::{symbol::Symbol, Span}; use super::{mark_used, MetaItemKind}; enum AttrError { - MultipleItem(Name), - UnknownMetaItem(Name, &'static [&'static str]), + MultipleItem(String), + UnknownMetaItem(String, &'static [&'static str]), MissingSince, MissingFeature, MultipleStabilityLevels, @@ -155,10 +155,7 @@ pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool { attrs.iter().any(|item| { item.check_name("feature") && item.meta_item_list().map(|list| { - list.iter().any(|mi| { - mi.word().map(|w| w.name() == feature_name) - .unwrap_or(false) - }) + list.iter().any(|mi| mi.is_word() && mi.check_name(feature_name)) }).unwrap_or(false) }) } @@ -206,7 +203,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess, let meta = meta.as_ref().unwrap(); let get = |meta: &MetaItem, item: &mut Option<Symbol>| { if item.is_some() { - handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name())); + handle_errors(sess, meta.span, AttrError::MultipleItem(meta.ident.to_string())); return false } if let Some(v) = meta.value_str() { @@ -225,9 +222,9 @@ fn find_stability_generic<'a, I>(sess: &ParseSess, )+ for meta in metas { if let Some(mi) = meta.meta_item() { - match &*mi.name().as_str() { + match mi.ident_str() { $( - stringify!($name) + Some(stringify!($name)) => if !get(mi, &mut $name) { continue 'outer }, )+ _ => { @@ -235,7 +232,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess, handle_errors( sess, mi.span, - AttrError::UnknownMetaItem(mi.name(), expected), + AttrError::UnknownMetaItem(mi.ident.to_string(), expected), ); continue 'outer } @@ -255,7 +252,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess, } } - match &*meta.name().as_str() { + match meta.ident_str().expect("not a stability level") { "rustc_deprecated" => { if rustc_depr.is_some() { span_err!(diagnostic, item_sp, E0540, @@ -309,16 +306,16 @@ fn find_stability_generic<'a, I>(sess: &ParseSess, let mut issue = None; for meta in metas { if let Some(mi) = meta.meta_item() { - match &*mi.name().as_str() { - "feature" => if !get(mi, &mut feature) { continue 'outer }, - "reason" => if !get(mi, &mut reason) { continue 'outer }, - "issue" => if !get(mi, &mut issue) { continue 'outer }, + match mi.ident_str() { + Some("feature") => if !get(mi, &mut feature) { continue 'outer }, + Some("reason") => if !get(mi, &mut reason) { continue 'outer }, + Some("issue") => if !get(mi, &mut issue) { continue 'outer }, _ => { handle_errors( sess, meta.span, AttrError::UnknownMetaItem( - mi.name(), + mi.ident.to_string(), &["feature", "reason", "issue"] ), ); @@ -380,15 +377,17 @@ fn find_stability_generic<'a, I>(sess: &ParseSess, for meta in metas { match &meta.node { NestedMetaItemKind::MetaItem(mi) => { - match &*mi.name().as_str() { - "feature" => if !get(mi, &mut feature) { continue 'outer }, - "since" => if !get(mi, &mut since) { continue 'outer }, + match mi.ident_str() { + Some("feature") => + if !get(mi, &mut feature) { continue 'outer }, + Some("since") => + if !get(mi, &mut since) { continue 'outer }, _ => { handle_errors( sess, meta.span, AttrError::UnknownMetaItem( - mi.name(), &["since", "note"], + mi.ident.to_string(), &["since", "note"], ), ); continue 'outer @@ -502,7 +501,8 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat true } MetaItemKind::NameValue(..) | MetaItemKind::Word => { - sess.config.contains(&(cfg.name(), cfg.value_str())) + let ident = cfg.ident().expect("multi-segment cfg predicate"); + sess.config.contains(&(ident.name, cfg.value_str())) } } }) @@ -532,14 +532,14 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F) // The unwraps below may look dangerous, but we've already asserted // that they won't fail with the loop above. - match &*cfg.name().as_str() { - "any" => mis.iter().any(|mi| { + match cfg.ident_str() { + Some("any") => mis.iter().any(|mi| { eval_condition(mi.meta_item().unwrap(), sess, eval) }), - "all" => mis.iter().all(|mi| { + Some("all") => mis.iter().all(|mi| { eval_condition(mi.meta_item().unwrap(), sess, eval) }), - "not" => { + Some("not") => { if mis.len() != 1 { span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern"); return false; @@ -547,8 +547,9 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F) !eval_condition(mis[0].meta_item().unwrap(), sess, eval) }, - p => { - span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p); + _ => { + span_err!(sess.span_diagnostic, cfg.span, E0537, + "invalid predicate `{}`", cfg.ident); false } } @@ -602,7 +603,9 @@ fn find_deprecation_generic<'a, I>(sess: &ParseSess, MetaItemKind::List(list) => { let get = |meta: &MetaItem, item: &mut Option<Symbol>| { if item.is_some() { - handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name())); + handle_errors( + sess, meta.span, AttrError::MultipleItem(meta.ident.to_string()) + ); return false } if let Some(v) = meta.value_str() { @@ -632,14 +635,15 @@ fn find_deprecation_generic<'a, I>(sess: &ParseSess, for meta in list { match &meta.node { NestedMetaItemKind::MetaItem(mi) => { - match &*mi.name().as_str() { - "since" => if !get(mi, &mut since) { continue 'outer }, - "note" => if !get(mi, &mut note) { continue 'outer }, + match mi.ident_str() { + Some("since") => if !get(mi, &mut since) { continue 'outer }, + Some("note") => if !get(mi, &mut note) { continue 'outer }, _ => { handle_errors( sess, - meta.span, - AttrError::UnknownMetaItem(mi.name(), &["since", "note"]), + meta.span(), + AttrError::UnknownMetaItem(mi.ident.to_string(), + &["since", "note"]), ); continue 'outer } @@ -724,19 +728,13 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> { } let mut recognised = false; - if let Some(mi) = item.word() { - let word = &*mi.name().as_str(); - let hint = match word { - "C" => Some(ReprC), - "packed" => Some(ReprPacked(1)), - "simd" => Some(ReprSimd), - "transparent" => Some(ReprTransparent), - _ => match int_type_of_word(word) { - Some(ity) => Some(ReprInt(ity)), - None => { - None - } - } + if item.is_word() { + let hint = match item.ident_str() { + Some("C") => Some(ReprC), + Some("packed") => Some(ReprPacked(1)), + Some("simd") => Some(ReprSimd), + Some("transparent") => Some(ReprTransparent), + name => name.and_then(|name| int_type_of_word(name)).map(ReprInt), }; if let Some(h) = hint { @@ -782,7 +780,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> { } } else { if let Some(meta_item) = item.meta_item() { - if meta_item.name() == "align" { + if meta_item.check_name("align") { if let MetaItemKind::NameValue(ref value) = meta_item.node { recognised = true; let mut err = struct_span_err!(diagnostic, item.span, E0693, diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs index b5fc8507314..857325681c6 100644 --- a/src/libsyntax/attr/mod.rs +++ b/src/libsyntax/attr/mod.rs @@ -90,10 +90,12 @@ impl NestedMetaItem { self.meta_item().map_or(false, |meta_item| meta_item.check_name(name)) } - /// Returns the name of the meta item, e.g., `foo` in `#[foo]`, - /// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem - pub fn name(&self) -> Option<Name> { - self.meta_item().and_then(|meta_item| Some(meta_item.name())) + /// For a single-segment meta-item returns its name, otherwise returns `None`. + pub fn ident(&self) -> Option<Ident> { + self.meta_item().and_then(|meta_item| meta_item.ident()) + } + pub fn ident_str(&self) -> Option<&str> { + self.ident().map(|name| name.as_str().get()) } /// Gets the string value if self is a MetaItem and the MetaItem is a @@ -108,25 +110,14 @@ impl NestedMetaItem { |meta_item| meta_item.meta_item_list().and_then( |meta_item_list| { if meta_item_list.len() == 1 { - let nested_item = &meta_item_list[0]; - if nested_item.is_literal() { - Some((meta_item.name(), nested_item.literal().unwrap())) - } else { - None + if let Some(ident) = meta_item.ident() { + if let Some(lit) = meta_item_list[0].literal() { + return Some((ident.name, lit)); + } } } - else { - None - }})) - } - - /// Returns a MetaItem if self is a MetaItem with Kind Word. - pub fn word(&self) -> Option<&MetaItem> { - self.meta_item().and_then(|meta_item| if meta_item.is_word() { - Some(meta_item) - } else { - None - }) + None + })) } /// Gets a list of inner meta items from a list MetaItem type. @@ -146,7 +137,7 @@ impl NestedMetaItem { /// Returns `true` if self is a MetaItem and the meta item is a word. pub fn is_word(&self) -> bool { - self.word().is_some() + self.meta_item().map_or(false, |meta_item| meta_item.is_word()) } /// Returns `true` if self is a MetaItem and the meta item is a ValueString. @@ -160,10 +151,6 @@ impl NestedMetaItem { } } -fn name_from_path(path: &Path) -> Name { - path.segments.last().expect("empty path in attribute").ident.name -} - impl Attribute { /// Returns `true` if the attribute's path matches the argument. If it matches, then the /// attribute is marked as used. @@ -177,10 +164,16 @@ impl Attribute { matches } - /// Returns the **last** segment of the name of this attribute. - /// e.g., `foo` for `#[foo]`, `skip` for `#[rustfmt::skip]`. - pub fn name(&self) -> Name { - name_from_path(&self.path) + /// For a single-segment attribute returns its name, otherwise returns `None`. + pub fn ident(&self) -> Option<Ident> { + if self.path.segments.len() == 1 { + Some(self.path.segments[0].ident) + } else { + None + } + } + pub fn ident_str(&self) -> Option<&str> { + self.ident().map(|name| name.as_str().get()) } pub fn value_str(&self) -> Option<Symbol> { @@ -195,7 +188,7 @@ impl Attribute { } pub fn is_word(&self) -> bool { - self.path.segments.len() == 1 && self.tokens.is_empty() + self.tokens.is_empty() } pub fn span(&self) -> Span { @@ -213,8 +206,16 @@ impl Attribute { } impl MetaItem { - pub fn name(&self) -> Name { - name_from_path(&self.ident) + /// For a single-segment meta-item returns its name, otherwise returns `None`. + pub fn ident(&self) -> Option<Ident> { + if self.ident.segments.len() == 1 { + Some(self.ident.segments[0].ident) + } else { + None + } + } + pub fn ident_str(&self) -> Option<&str> { + self.ident().map(|name| name.as_str().get()) } // #[attribute(name = "value")] @@ -255,7 +256,7 @@ impl MetaItem { pub fn span(&self) -> Span { self.span } pub fn check_name(&self, name: &str) -> bool { - self.name() == name + self.ident == name } pub fn is_value_str(&self) -> bool { @@ -265,14 +266,6 @@ impl MetaItem { pub fn is_meta_item_list(&self) -> bool { self.meta_item_list().is_some() } - - pub fn is_scoped(&self) -> Option<Ident> { - if self.ident.segments.len() > 1 { - Some(self.ident.segments[0].ident) - } else { - None - } - } } impl Attribute { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index bd64bb01021..12912044e4e 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -380,9 +380,14 @@ pub fn compile( .map(|attr| attr .meta_item_list() .map(|list| list.iter() - .map(|it| it.name().unwrap_or_else(|| sess.span_diagnostic.span_bug( - it.span, "allow internal unstable expects feature names", - ))) + .filter_map(|it| { + let name = it.ident().map(|ident| ident.name); + if name.is_none() { + sess.span_diagnostic.span_err(it.span(), + "allow internal unstable expects feature names") + } + name + }) .collect::<Vec<Symbol>>().into() ) .unwrap_or_else(|| { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 280b17da0be..4acacf2b68e 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1289,9 +1289,8 @@ pub struct GatedCfg { impl GatedCfg { pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> { - let name = cfg.name().as_str(); GATED_CFGS.iter() - .position(|info| info.0 == name) + .position(|info| cfg.check_name(info.0)) .map(|idx| { GatedCfg { span: cfg.span, @@ -1342,16 +1341,16 @@ macro_rules! gate_feature { impl<'a> Context<'a> { fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) { debug!("check_attribute(attr = {:?})", attr); - let name = attr.name().as_str(); + let name = attr.ident_str(); for &(n, ty, _template, ref gateage) in BUILTIN_ATTRIBUTES { - if name == n { + if name == Some(n) { if let Gated(_, name, desc, ref has_feature) = *gateage { if !attr.span.allows_unstable(name) { gate_feature_fn!( self, has_feature, attr.span, name, desc, GateStrength::Hard ); } - } else if name == "doc" { + } else if n == "doc" { if let Some(content) = attr.meta_item_list() { if content.iter().any(|c| c.check_name("include")) { gate_feature!(self, external_doc, attr.span, @@ -1374,7 +1373,7 @@ impl<'a> Context<'a> { } } if !attr::is_known(attr) { - if name.starts_with("rustc_") { + if name.map_or(false, |name| name.starts_with("rustc_")) { let msg = "unless otherwise specified, attributes with the prefix `rustc_` \ are reserved for internal compiler diagnostics"; gate_feature!(self, rustc_attrs, attr.span, msg); @@ -2055,13 +2054,12 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute], }; for mi in list { - let name = if let Some(word) = mi.word() { - word.name() - } else { - continue + let name = match mi.ident_str() { + Some(name) if mi.is_word() => name, + _ => continue, }; - if incomplete_features.iter().any(|f| *f == name.as_str()) { + if incomplete_features.iter().any(|f| *f == name) { span_handler.struct_span_warn( mi.span, &format!( @@ -2101,12 +2099,13 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute], }; for mi in list { - let name = if let Some(word) = mi.word() { - word.name() - } else { - span_err!(span_handler, mi.span, E0556, - "malformed feature, expected just one word"); - continue + let name = match mi.ident() { + Some(ident) if mi.is_word() => ident.name, + _ => { + span_err!(span_handler, mi.span, E0556, + "malformed feature, expected just one word"); + continue + } }; if let Some(edition) = edition_enabled_features.get(&name) { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 56290fa771b..3afd55899c8 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -438,6 +438,9 @@ fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option<ast::Path sd.span_fatal(test_attr.span(), "#![test_runner(..)] accepts exactly 1 argument").raise() } - meta_list[0].word().as_ref().unwrap().ident.clone() + match meta_list[0].meta_item() { + Some(meta_item) if meta_item.is_word() => meta_item.ident.clone(), + _ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise() + } }) } |
