diff options
| author | Sergio Benitez <sb@sergio.bz> | 2016-08-19 18:58:14 -0700 |
|---|---|---|
| committer | Sergio Benitez <sb@sergio.bz> | 2016-08-25 13:25:22 -0700 |
| commit | 8250a26b5bcea9190ac63e756c35d8a54bf9da0c (patch) | |
| tree | f95baa037c52c307de53d605ad752afd4880ffa4 /src/libsyntax | |
| parent | 528c6f3ed6a23a374dc5a40582d1bea2f2cfda65 (diff) | |
| download | rust-8250a26b5bcea9190ac63e756c35d8a54bf9da0c.tar.gz rust-8250a26b5bcea9190ac63e756c35d8a54bf9da0c.zip | |
Implement RFC#1559: allow all literals in attributes.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 58 | ||||
| -rw-r--r-- | src/libsyntax/attr.rs | 330 | ||||
| -rw-r--r-- | src/libsyntax/config.rs | 37 | ||||
| -rw-r--r-- | src/libsyntax/diagnostic_list.rs | 18 | ||||
| -rw-r--r-- | src/libsyntax/ext/build.rs | 13 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 51 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 19 | ||||
| -rw-r--r-- | src/libsyntax/parse/attr.rs | 64 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 19 | ||||
| -rw-r--r-- | src/libsyntax/test.rs | 9 |
10 files changed, 459 insertions, 159 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index f8a5cb0b04a..63fd2e7686f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -439,6 +439,22 @@ pub struct Crate { pub exported_macros: Vec<MacroDef>, } +/// A spanned compile-time attribute list item. +pub type NestedMetaItem = Spanned<NestedMetaItemKind>; + +/// Possible values inside of compile-time attribute lists. +/// +/// E.g. the '..' in `#[name(..)]`. +#[derive(Clone, Eq, RustcEncodable, RustcDecodable, Hash, Debug, PartialEq)] +pub enum NestedMetaItemKind { + /// A full MetaItem, for recursive meta items. + MetaItem(P<MetaItem>), + /// A literal. + /// + /// E.g. "foo", 64, true + Literal(Lit), +} + /// A spanned compile-time attribute item. /// /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]` @@ -456,7 +472,7 @@ pub enum MetaItemKind { /// List meta item. /// /// E.g. `derive(..)` as in `#[derive(..)]` - List(InternedString, Vec<P<MetaItem>>), + List(InternedString, Vec<NestedMetaItem>), /// Name value meta item. /// /// E.g. `feature = "foo"` as in `#[feature = "foo"]` @@ -472,19 +488,21 @@ impl PartialEq for MetaItemKind { Word(ref no) => (*ns) == (*no), _ => false }, + List(ref ns, ref miss) => match *other { + List(ref no, ref miso) => { + ns == no && + miss.iter().all(|mi| { + miso.iter().any(|x| x.node == mi.node) + }) + } + _ => false + }, NameValue(ref ns, ref vs) => match *other { NameValue(ref no, ref vo) => { (*ns) == (*no) && vs.node == vo.node } _ => false }, - List(ref ns, ref miss) => match *other { - List(ref no, ref miso) => { - ns == no && - miss.iter().all(|mi| miso.iter().any(|x| x.node == mi.node)) - } - _ => false - } } } } @@ -1105,6 +1123,30 @@ impl LitKind { _ => false, } } + + /// Returns true if this literal has no suffix. Note: this will return true + /// for literals with prefixes such as raw strings and byte strings. + pub fn is_unsuffixed(&self) -> bool { + match *self { + // unsuffixed variants + LitKind::Str(..) => true, + LitKind::ByteStr(..) => true, + LitKind::Byte(..) => true, + LitKind::Char(..) => true, + LitKind::Int(_, LitIntType::Unsuffixed) => true, + LitKind::FloatUnsuffixed(..) => true, + LitKind::Bool(..) => true, + // suffixed variants + LitKind::Int(_, LitIntType::Signed(..)) => false, + LitKind::Int(_, LitIntType::Unsigned(..)) => false, + LitKind::Float(..) => false, + } + } + + /// Returns true if this literal has a suffix. + pub fn is_suffixed(&self) -> bool { + !self.is_unsuffixed() + } } // NB: If you change this, you'll probably want to change the corresponding diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index b622f6861b3..4897425f2c0 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -15,9 +15,10 @@ pub use self::ReprAttr::*; pub use self::IntType::*; use ast; -use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaItemKind}; -use ast::{Expr, Item, Local, Stmt, StmtKind}; -use codemap::{respan, spanned, dummy_spanned, Spanned}; +use ast::{AttrId, Attribute, Attribute_}; +use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; +use ast::{Lit, Expr, Item, Local, Stmt, StmtKind}; +use codemap::{respan, spanned, dummy_spanned}; use syntax_pos::{Span, BytePos, DUMMY_SP}; use errors::Handler; use feature_gate::{Features, GatedCfg}; @@ -40,6 +41,7 @@ enum AttrError { MissingSince, MissingFeature, MultipleStabilityLevels, + UnsupportedLiteral } fn handle_errors(diag: &Handler, span: Span, error: AttrError) { @@ -52,10 +54,12 @@ fn handle_errors(diag: &Handler, span: Span, error: AttrError) { AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"), AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544, "multiple stability levels"), + AttrError::UnsupportedLiteral => span_err!(diag, span, E0565, "unsupported literal"), } } pub fn mark_used(attr: &Attribute) { + debug!("Marking {:?} as used.", attr); let AttrId(id) = attr.node.id; USED_ATTRS.with(|slot| { let idx = (id / 64) as usize; @@ -77,6 +81,93 @@ pub fn is_used(attr: &Attribute) -> bool { }) } +pub trait AttrNestedMetaItemMethods { + /// Returns true if this list item is a MetaItem with a name of `name`. + fn check_name(&self, name: &str) -> bool { + 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 + fn name(&self) -> Option<InternedString> { + self.meta_item().and_then(|meta_item| Some(meta_item.name())) + } + + /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem. + fn meta_item(&self) -> Option<&P<MetaItem>>; + + /// Returns the Lit if self is a NestedMetaItemKind::Literal. + fn literal(&self) -> Option<&Lit>; + + /// Gets the string value if self is a MetaItem and the MetaItem is a + /// MetaItemKind::NameValue variant containing a string, otherwise None. + fn value_str(&self) -> Option<InternedString> { + self.meta_item().and_then(|meta_item| meta_item.value_str()) + } + + /// Returns a MetaItem if self is a MetaItem with Kind Word. + fn word(&self) -> Option<&P<MetaItem>> { + self.meta_item().and_then(|meta_item| if meta_item.is_word() { + Some(meta_item) + } else { + None + }) + } + + /// Gets a list of inner meta items from a list MetaItem type. + fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { + self.meta_item().and_then(|meta_item| meta_item.meta_item_list()) + } + + /// Returns `true` if the variant is MetaItem. + fn is_meta_item(&self) -> bool { + self.meta_item().is_some() + } + + /// Returns `true` if the variant is Literal. + fn is_literal(&self) -> bool { + self.literal().is_some() + } + + /// Returns `true` if self is a MetaItem and the meta item is a word. + fn is_word(&self) -> bool { + self.word().is_some() + } + + /// Returns `true` if self is a MetaItem and the meta item is a ValueString. + fn is_value_str(&self) -> bool { + self.value_str().is_some() + } + + /// Returns `true` if self is a MetaItem and the meta item is a list. + fn is_meta_item_list(&self) -> bool { + self.meta_item_list().is_some() + } + + /// Returns the Span for `self`. + fn span(&self) -> Span; +} + +impl AttrNestedMetaItemMethods for NestedMetaItem { + fn meta_item(&self) -> Option<&P<MetaItem>> { + match self.node { + NestedMetaItemKind::MetaItem(ref item) => Some(&item), + _ => None + } + } + + fn literal(&self) -> Option<&Lit> { + match self.node { + NestedMetaItemKind::Literal(ref lit) => Some(&lit), + _ => None + } + } + + fn span(&self) -> Span { + self.span + } +} + pub trait AttrMetaMethods { fn check_name(&self, name: &str) -> bool { name == &self.name()[..] @@ -89,8 +180,9 @@ pub trait AttrMetaMethods { /// Gets the string value if self is a MetaItemKind::NameValue variant /// containing a string, otherwise None. fn value_str(&self) -> Option<InternedString>; + /// Gets a list of inner meta items from a list MetaItem type. - fn meta_item_list(&self) -> Option<&[P<MetaItem>]>; + fn meta_item_list(&self) -> Option<&[NestedMetaItem]>; /// Indicates if the attribute is a Word. fn is_word(&self) -> bool; @@ -116,11 +208,14 @@ impl AttrMetaMethods for Attribute { } matches } + fn name(&self) -> InternedString { self.meta().name() } + fn value_str(&self) -> Option<InternedString> { self.meta().value_str() } - fn meta_item_list(&self) -> Option<&[P<MetaItem>]> { + + fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { self.meta().meta_item_list() } @@ -150,7 +245,7 @@ impl AttrMetaMethods for MetaItem { } } - fn meta_item_list(&self) -> Option<&[P<MetaItem>]> { + fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { match self.node { MetaItemKind::List(_, ref l) => Some(&l[..]), _ => None @@ -171,7 +266,7 @@ impl AttrMetaMethods for MetaItem { impl AttrMetaMethods for P<MetaItem> { fn name(&self) -> InternedString { (**self).name() } fn value_str(&self) -> Option<InternedString> { (**self).value_str() } - fn meta_item_list(&self) -> Option<&[P<MetaItem>]> { + fn meta_item_list(&self) -> Option<&[NestedMetaItem]> { (**self).meta_item_list() } fn is_word(&self) -> bool { (**self).is_word() } @@ -229,10 +324,14 @@ pub fn mk_name_value_item(name: InternedString, value: ast::Lit) mk_spanned_name_value_item(DUMMY_SP, name, value) } -pub fn mk_list_item(name: InternedString, items: Vec<P<MetaItem>>) -> P<MetaItem> { +pub fn mk_list_item(name: InternedString, items: Vec<NestedMetaItem>) -> P<MetaItem> { mk_spanned_list_item(DUMMY_SP, name, items) } +pub fn mk_list_word_item(name: InternedString) -> ast::NestedMetaItem { + dummy_spanned(NestedMetaItemKind::MetaItem(mk_spanned_word_item(DUMMY_SP, name))) +} + pub fn mk_word_item(name: InternedString) -> P<MetaItem> { mk_spanned_word_item(DUMMY_SP, name) } @@ -242,7 +341,7 @@ pub fn mk_spanned_name_value_item(sp: Span, name: InternedString, value: ast::Li P(respan(sp, MetaItemKind::NameValue(name, value))) } -pub fn mk_spanned_list_item(sp: Span, name: InternedString, items: Vec<P<MetaItem>>) +pub fn mk_spanned_list_item(sp: Span, name: InternedString, items: Vec<NestedMetaItem>) -> P<MetaItem> { P(respan(sp, MetaItemKind::List(name, items))) } @@ -332,6 +431,14 @@ pub fn contains(haystack: &[P<MetaItem>], needle: &MetaItem) -> bool { }) } +pub fn list_contains_name<AM: AttrNestedMetaItemMethods>(items: &[AM], name: &str) -> bool { + debug!("attr::list_contains_name (name={})", name); + items.iter().any(|item| { + debug!(" testing: {:?}", item.name()); + item.check_name(name) + }) +} + pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool { debug!("attr::contains_name (name={})", name); metas.iter().any(|item| { @@ -357,27 +464,6 @@ pub fn last_meta_item_value_str_by_name(items: &[P<MetaItem>], name: &str) /* Higher-level applications */ -pub fn sort_meta_items(items: Vec<P<MetaItem>>) -> Vec<P<MetaItem>> { - // This is sort of stupid here, but we need to sort by - // human-readable strings. - let mut v = items.into_iter() - .map(|mi| (mi.name(), mi)) - .collect::<Vec<(InternedString, P<MetaItem>)>>(); - - v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b)); - - // There doesn't seem to be a more optimal way to do this - v.into_iter().map(|(_, m)| m.map(|Spanned {node, span}| { - Spanned { - node: match node { - MetaItemKind::List(n, mis) => MetaItemKind::List(n, sort_meta_items(mis)), - _ => node - }, - span: span - } - })).collect() -} - pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> { first_attr_value_str_by_name(attrs, "crate_name") } @@ -427,14 +513,15 @@ pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> In if items.len() != 1 { diagnostic.map(|d|{ span_err!(d, attr.span, E0534, "expected one argument"); }); InlineAttr::None - } else if contains_name(&items[..], "always") { + } else if list_contains_name(&items[..], "always") { InlineAttr::Always - } else if contains_name(&items[..], "never") { + } else if list_contains_name(&items[..], "never") { InlineAttr::Never } else { diagnostic.map(|d| { - span_err!(d, (*items[0]).span, E0535, "invalid argument"); + span_err!(d, items[0].span, E0535, "invalid argument"); }); + InlineAttr::None } } @@ -453,27 +540,44 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool { /// Tests if a cfg-pattern matches the cfg set pub fn cfg_matches(cfgs: &[P<MetaItem>], cfg: &ast::MetaItem, - sess: &ParseSess, features: Option<&Features>) + sess: &ParseSess, + features: Option<&Features>) -> bool { match cfg.node { - ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "any" => - mis.iter().any(|mi| cfg_matches(cfgs, &mi, sess, features)), - ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "all" => - mis.iter().all(|mi| cfg_matches(cfgs, &mi, sess, features)), - ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "not" => { - if mis.len() != 1 { - span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern"); - return false; + ast::MetaItemKind::List(ref pred, ref mis) => { + for mi in mis.iter() { + if !mi.is_meta_item() { + handle_errors(&sess.span_diagnostic, mi.span, AttrError::UnsupportedLiteral); + return false; + } + } + + // The unwraps below may look dangerous, but we've already asserted + // that they won't fail with the loop above. + match &pred[..] { + "any" => mis.iter().any(|mi| { + cfg_matches(cfgs, mi.meta_item().unwrap(), sess, features) + }), + "all" => mis.iter().all(|mi| { + cfg_matches(cfgs, mi.meta_item().unwrap(), sess, features) + }), + "not" => { + if mis.len() != 1 { + span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern"); + return false; + } + + !cfg_matches(cfgs, mis[0].meta_item().unwrap(), sess, features) + }, + p => { + span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p); + false + } } - !cfg_matches(cfgs, &mis[0], sess, features) - } - ast::MetaItemKind::List(ref pred, _) => { - span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", pred); - false }, ast::MetaItemKind::Word(_) | ast::MetaItemKind::NameValue(..) => { - if let (Some(features), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) { - gated_cfg.check_and_emit(sess, features); + if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) { + gated_cfg.check_and_emit(sess, feats); } contains(cfgs, cfg) } @@ -557,14 +661,19 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, let mut since = None; let mut reason = None; for meta in metas { - match &*meta.name() { - "since" => if !get(meta, &mut since) { continue 'outer }, - "reason" => if !get(meta, &mut reason) { continue 'outer }, - _ => { - handle_errors(diagnostic, meta.span, - AttrError::UnknownMetaItem(meta.name())); - continue 'outer + if let Some(mi) = meta.meta_item() { + match &*mi.name() { + "since" => if !get(mi, &mut since) { continue 'outer }, + "reason" => if !get(mi, &mut reason) { continue 'outer }, + _ => { + handle_errors(diagnostic, mi.span, + AttrError::UnknownMetaItem(mi.name())); + continue 'outer + } } + } else { + handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); + continue 'outer } } @@ -595,15 +704,20 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, let mut reason = None; let mut issue = None; for meta in metas { - match &*meta.name() { - "feature" => if !get(meta, &mut feature) { continue 'outer }, - "reason" => if !get(meta, &mut reason) { continue 'outer }, - "issue" => if !get(meta, &mut issue) { continue 'outer }, - _ => { - handle_errors(diagnostic, meta.span, - AttrError::UnknownMetaItem(meta.name())); - continue 'outer + if let Some(mi) = meta.meta_item() { + match &*mi.name() { + "feature" => if !get(mi, &mut feature) { continue 'outer }, + "reason" => if !get(mi, &mut reason) { continue 'outer }, + "issue" => if !get(mi, &mut issue) { continue 'outer }, + _ => { + handle_errors(diagnostic, meta.span, + AttrError::UnknownMetaItem(mi.name())); + continue 'outer + } } + } else { + handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); + continue 'outer } } @@ -645,14 +759,19 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, let mut feature = None; let mut since = None; for meta in metas { - match &*meta.name() { - "feature" => if !get(meta, &mut feature) { continue 'outer }, - "since" => if !get(meta, &mut since) { continue 'outer }, - _ => { - handle_errors(diagnostic, meta.span, - AttrError::UnknownMetaItem(meta.name())); - continue 'outer + if let NestedMetaItemKind::MetaItem(ref mi) = meta.node { + match &*mi.name() { + "feature" => if !get(mi, &mut feature) { continue 'outer }, + "since" => if !get(mi, &mut since) { continue 'outer }, + _ => { + handle_errors(diagnostic, meta.span, + AttrError::UnknownMetaItem(mi.name())); + continue 'outer + } } + } else { + handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); + continue 'outer } } @@ -739,14 +858,19 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler, let mut since = None; let mut note = None; for meta in metas { - match &*meta.name() { - "since" => if !get(meta, &mut since) { continue 'outer }, - "note" => if !get(meta, &mut note) { continue 'outer }, - _ => { - handle_errors(diagnostic, meta.span, - AttrError::UnknownMetaItem(meta.name())); - continue 'outer + if let NestedMetaItemKind::MetaItem(ref mi) = meta.node { + match &*mi.name() { + "since" => if !get(mi, &mut since) { continue 'outer }, + "note" => if !get(mi, &mut note) { continue 'outer }, + _ => { + handle_errors(diagnostic, meta.span, + AttrError::UnknownMetaItem(mi.name())); + continue 'outer + } } + } else { + handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral); + continue 'outer } } @@ -796,32 +920,36 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> ast::MetaItemKind::List(ref s, ref items) if s == "repr" => { mark_used(attr); for item in items { - match item.node { - ast::MetaItemKind::Word(ref word) => { - let hint = match &word[..] { - // Can't use "extern" because it's not a lexical identifier. - "C" => Some(ReprExtern), - "packed" => Some(ReprPacked), - "simd" => Some(ReprSimd), - _ => match int_type_of_word(&word) { - Some(ity) => Some(ReprInt(item.span, ity)), - None => { - // Not a word we recognize - span_err!(diagnostic, item.span, E0552, - "unrecognized representation hint"); - None - } - } - }; + if !item.is_meta_item() { + handle_errors(diagnostic, item.span, AttrError::UnsupportedLiteral); + continue + } - match hint { - Some(h) => acc.push(h), - None => { } + if let Some(mi) = item.word() { + let word = &*mi.name(); + let hint = match word { + // Can't use "extern" because it's not a lexical identifier. + "C" => Some(ReprExtern), + "packed" => Some(ReprPacked), + "simd" => Some(ReprSimd), + _ => match int_type_of_word(word) { + Some(ity) => Some(ReprInt(item.span, ity)), + None => { + // Not a word we recognize + span_err!(diagnostic, item.span, E0552, + "unrecognized representation hint"); + None + } } + }; + + match hint { + Some(h) => acc.push(h), + None => { } } - // Not a word: - _ => span_err!(diagnostic, item.span, E0553, - "unrecognized enum representation hint"), + } else { + span_err!(diagnostic, item.span, E0553, + "unrecognized enum representation hint"); } } } diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index a825cf866a8..4663143f4b1 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use attr::{AttrMetaMethods, HasAttrs}; +use attr::{AttrMetaMethods, AttrNestedMetaItemMethods, HasAttrs}; use feature_gate::{emit_feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue}; use fold::Folder; use {fold, attr}; @@ -52,6 +52,7 @@ impl<'a> StripUnconfigured<'a> { return None; } }; + let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) { (2, Some(cfg), Some(mi)) => (cfg, mi), _ => { @@ -61,15 +62,24 @@ impl<'a> StripUnconfigured<'a> { } }; - if attr::cfg_matches(self.config, &cfg, self.sess, self.features) { - self.process_cfg_attr(respan(mi.span, ast::Attribute_ { - id: attr::mk_attr_id(), - style: attr.node.style, - value: mi.clone(), - is_sugared_doc: false, - })) - } else { - None + use attr::cfg_matches; + match (cfg.meta_item(), mi.meta_item()) { + (Some(cfg), Some(mi)) => + if cfg_matches(self.config, &cfg, self.sess, self.features) { + self.process_cfg_attr(respan(mi.span, ast::Attribute_ { + id: attr::mk_attr_id(), + style: attr.node.style, + value: mi.clone(), + is_sugared_doc: false, + })) + } else { + None + }, + _ => { + let msg = "unexpected literal(s) in `#[cfg_attr(<cfg pattern>, <attr>)]`"; + self.sess.span_diagnostic.span_err(attr.span, msg); + None + } } } @@ -91,7 +101,12 @@ impl<'a> StripUnconfigured<'a> { return true; } - attr::cfg_matches(self.config, &mis[0], self.sess, self.features) + if !mis[0].is_meta_item() { + self.sess.span_diagnostic.span_err(mis[0].span, "unexpected literal"); + return true; + } + + attr::cfg_matches(self.config, mis[0].meta_item().unwrap(), self.sess, self.features) }) } diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index 010b1d638e6..9110e989a8a 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -161,6 +161,24 @@ fn main() {} ``` "##, +E0565: r##" +A literal was used in an attribute that doesn't support literals. + +Erroneous code example: + +```compile_fail,E0565 +#[inline("always")] // error: unsupported literal +pub fn something() {} +``` + +Literals in attributes are new and largely unsupported. Work to support literals +where appropriate is ongoing. Try using an unquoted name instead: + +``` +#[inline(always)] +pub fn something() {} +``` +"##, } register_diagnostics! { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 5d6429f7bdf..1d3939df6fb 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -277,10 +277,13 @@ pub trait AstBuilder { fn attribute(&self, sp: Span, mi: P<ast::MetaItem>) -> ast::Attribute; fn meta_word(&self, sp: Span, w: InternedString) -> P<ast::MetaItem>; + + fn meta_list_item_word(&self, sp: Span, w: InternedString) -> ast::NestedMetaItem; + fn meta_list(&self, sp: Span, name: InternedString, - mis: Vec<P<ast::MetaItem>> ) + mis: Vec<ast::NestedMetaItem> ) -> P<ast::MetaItem>; fn meta_name_value(&self, sp: Span, @@ -1141,10 +1144,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn meta_word(&self, sp: Span, w: InternedString) -> P<ast::MetaItem> { attr::mk_spanned_word_item(sp, w) } - fn meta_list(&self, sp: Span, name: InternedString, mis: Vec<P<ast::MetaItem>>) + + fn meta_list_item_word(&self, sp: Span, w: InternedString) -> ast::NestedMetaItem { + respan(sp, ast::NestedMetaItemKind::MetaItem(attr::mk_spanned_word_item(sp, w))) + } + + fn meta_list(&self, sp: Span, name: InternedString, mis: Vec<ast::NestedMetaItem>) -> P<ast::MetaItem> { attr::mk_spanned_list_item(sp, name, mis) } + fn meta_name_value(&self, sp: Span, name: InternedString, value: ast::LitKind) -> P<ast::MetaItem> { attr::mk_spanned_name_value_item(sp, name, respan(sp, value)) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index d746f8e2114..df1d5c4d9ca 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -26,10 +26,8 @@ use self::AttributeType::*; use self::AttributeGate::*; use abi::Abi; -use ast::{NodeId, PatKind}; -use ast; -use attr; -use attr::AttrMetaMethods; +use ast::{self, NodeId, PatKind}; +use attr::{self, AttrMetaMethods, AttrNestedMetaItemMethods}; use codemap::CodeMap; use syntax_pos::Span; use errors::Handler; @@ -283,7 +281,10 @@ declare_features! ( (active, relaxed_adts, "1.12.0", Some(35626)), // The `!` type - (active, never_type, "1.13.0", Some(35121)) + (active, never_type, "1.13.0", Some(35121)), + + // Allows all literals in attribute lists and values of key-value pairs. + (active, attr_literals, "1.13.0", Some(34981)) ); declare_features! ( @@ -831,11 +832,34 @@ impl<'a> PostExpansionVisitor<'a> { } } +fn contains_novel_literal(item: &ast::MetaItem) -> bool { + use ast::MetaItemKind::*; + use ast::NestedMetaItemKind::*; + + match item.node { + Word(..) => false, + NameValue(_, ref lit) => !lit.node.is_str(), + List(_, ref list) => list.iter().any(|li| { + match li.node { + MetaItem(ref mi) => contains_novel_literal(&**mi), + Literal(_) => true, + } + }), + } +} + impl<'a> Visitor for PostExpansionVisitor<'a> { fn visit_attribute(&mut self, attr: &ast::Attribute) { if !self.context.cm.span_allows_unstable(attr.span) { + // check for gated attributes self.context.check_attribute(attr, false); } + + if contains_novel_literal(&*(attr.node.value)) { + gate_feature_post!(&self, attr_literals, attr.span, + "non-string literals in attributes, or string \ + literals in top-level positions, are experimental"); + } } fn visit_name(&mut self, sp: Span, name: ast::Name) { @@ -895,7 +919,7 @@ impl<'a> Visitor for PostExpansionVisitor<'a> { for attr in &i.attrs { if attr.name() == "repr" { for item in attr.meta_item_list().unwrap_or(&[]) { - if item.name() == "simd" { + if item.check_name("simd") { gate_feature_post!(&self, repr_simd, i.span, "SIMD types are experimental \ and possibly buggy"); @@ -1155,13 +1179,14 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F } Some(list) => { for mi in list { - let name = if mi.is_word() { - mi.name() - } else { - span_err!(span_handler, mi.span, E0556, - "malformed feature, expected just one word"); - continue - }; + 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 + }; + if let Some(&(_, _, _, setter)) = ACTIVE_FEATURES.iter() .find(|& &(n, _, _, _)| name == n) { *(setter(&mut features)) = true; diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index b257ab98987..b361a856dbe 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -47,6 +47,10 @@ pub trait Folder : Sized { noop_fold_meta_items(meta_items, self) } + fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem { + noop_fold_meta_list_item(list_item, self) + } + fn fold_meta_item(&mut self, meta_item: P<MetaItem>) -> P<MetaItem> { noop_fold_meta_item(meta_item, self) } @@ -513,12 +517,25 @@ pub fn noop_fold_mac<T: Folder>(Spanned {node, span}: Mac, fld: &mut T) -> Mac { } } +pub fn noop_fold_meta_list_item<T: Folder>(li: NestedMetaItem, fld: &mut T) + -> NestedMetaItem { + Spanned { + node: match li.node { + NestedMetaItemKind::MetaItem(mi) => { + NestedMetaItemKind::MetaItem(fld.fold_meta_item(mi)) + }, + NestedMetaItemKind::Literal(lit) => NestedMetaItemKind::Literal(lit) + }, + span: fld.new_span(li.span) + } +} + pub fn noop_fold_meta_item<T: Folder>(mi: P<MetaItem>, fld: &mut T) -> P<MetaItem> { mi.map(|Spanned {node, span}| Spanned { node: match node { MetaItemKind::Word(id) => MetaItemKind::Word(id), MetaItemKind::List(id, mis) => { - MetaItemKind::List(id, mis.move_map(|e| fld.fold_meta_item(e))) + MetaItemKind::List(id, mis.move_map(|e| fld.fold_meta_list_item(e))) } MetaItemKind::NameValue(id, s) => MetaItemKind::NameValue(id, s) }, diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 2ae3236cd5a..27dd055cd3a 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -193,9 +193,26 @@ impl<'a> Parser<'a> { Ok(attrs) } - /// matches meta_item = IDENT - /// | IDENT = lit - /// | IDENT meta_seq + fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> { + let lit = self.parse_lit()?; + debug!("Checking if {:?} is unusuffixed.", lit); + + if !lit.node.is_unsuffixed() { + let msg = "suffixed literals are not allowed in attributes"; + self.diagnostic().struct_span_err(lit.span, msg) + .help("instead of using a suffixed literal \ + (1u8, 1.0f32, etc.), use an unsuffixed version \ + (1, 1.0, etc.).") + .emit() + } + + Ok(lit) + } + + /// Per RFC#1559, matches the following grammar: + /// + /// meta_item : IDENT ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ; + /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; pub fn parse_meta_item(&mut self) -> PResult<'a, P<ast::MetaItem>> { let nt_meta = match self.token { token::Interpolated(token::NtMeta(ref e)) => Some(e.clone()), @@ -213,16 +230,7 @@ impl<'a> Parser<'a> { match self.token { token::Eq => { self.bump(); - let lit = self.parse_lit()?; - // FIXME #623 Non-string meta items are not serialized correctly; - // just forbid them for now - match lit.node { - ast::LitKind::Str(..) => {} - _ => { - self.span_err(lit.span, - "non-string literals are not allowed in meta-items"); - } - } + let lit = self.parse_unsuffixed_lit()?; let hi = self.span.hi; Ok(P(spanned(lo, hi, ast::MetaItemKind::NameValue(name, lit)))) } @@ -238,11 +246,35 @@ impl<'a> Parser<'a> { } } - /// matches meta_seq = ( COMMASEP(meta_item) ) - fn parse_meta_seq(&mut self) -> PResult<'a, Vec<P<ast::MetaItem>>> { + /// matches meta_item_inner : (meta_item | UNSUFFIXED_LIT) ; + fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> { + let sp = self.span; + let lo = self.span.lo; + + match self.parse_unsuffixed_lit() { + Ok(lit) => { + return Ok(spanned(lo, self.span.hi, ast::NestedMetaItemKind::Literal(lit))) + } + Err(ref mut err) => self.diagnostic().cancel(err) + } + + match self.parse_meta_item() { + Ok(mi) => { + return Ok(spanned(lo, self.span.hi, ast::NestedMetaItemKind::MetaItem(mi))) + } + Err(ref mut err) => self.diagnostic().cancel(err) + } + + let found = self.this_token_to_string(); + let msg = format!("expected unsuffixed literal or identifier, found {}", found); + Err(self.diagnostic().struct_span_err(sp, &msg)) + } + + /// matches meta_seq = ( COMMASEP(meta_item_inner) ) + fn parse_meta_seq(&mut self) -> PResult<'a, Vec<ast::NestedMetaItem>> { self.parse_unspanned_seq(&token::OpenDelim(token::Paren), &token::CloseDelim(token::Paren), SeqSep::trailing_allowed(token::Comma), - |p: &mut Parser<'a>| p.parse_meta_item()) + |p: &mut Parser<'a>| p.parse_meta_item_inner()) } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index a77c678248b..562cc896aef 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -120,7 +120,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap, // of the feature gate, so we fake them up here. // #![feature(prelude_import)] - let prelude_import_meta = attr::mk_word_item(InternedString::new("prelude_import")); + let prelude_import_meta = attr::mk_list_word_item(InternedString::new("prelude_import")); let list = attr::mk_list_item(InternedString::new("feature"), vec![prelude_import_meta]); let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), list); @@ -406,6 +406,10 @@ pub fn block_to_string(blk: &ast::Block) -> String { }) } +pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String { + to_string(|s| s.print_meta_list_item(li)) +} + pub fn meta_item_to_string(mi: &ast::MetaItem) -> String { to_string(|s| s.print_meta_item(mi)) } @@ -764,6 +768,17 @@ pub trait PrintState<'a> { } } + fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) -> io::Result<()> { + match item.node { + ast::NestedMetaItemKind::MetaItem(ref mi) => { + self.print_meta_item(mi) + }, + ast::NestedMetaItemKind::Literal(ref lit) => { + self.print_literal(lit) + } + } + } + fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> { try!(self.ibox(INDENT_UNIT)); match item.node { @@ -780,7 +795,7 @@ pub trait PrintState<'a> { try!(self.popen()); try!(self.commasep(Consistent, &items[..], - |s, i| s.print_meta_item(&i))); + |s, i| s.print_meta_list_item(&i))); try!(self.pclose()); } } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index faf6a17a150..ce917f248e1 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -19,8 +19,7 @@ use std::iter; use std::slice; use std::mem; use std::vec; -use attr::AttrMetaMethods; -use attr; +use attr::{self, AttrMetaMethods, AttrNestedMetaItemMethods}; use syntax_pos::{self, DUMMY_SP, NO_EXPANSION, Span, FileMap, BytePos}; use std::rc::Rc; @@ -210,9 +209,8 @@ impl fold::Folder for EntryPointCleaner { folded.map(|ast::Item {id, ident, attrs, node, vis, span}| { let allow_str = InternedString::new("allow"); let dead_code_str = InternedString::new("dead_code"); - let allow_dead_code_item = - attr::mk_list_item(allow_str, - vec![attr::mk_word_item(dead_code_str)]); + let word_vec = vec![attr::mk_list_word_item(dead_code_str)]; + let allow_dead_code_item = attr::mk_list_item(allow_str, word_vec); let allow_dead_code = attr::mk_attr_outer(attr::mk_attr_id(), allow_dead_code_item); @@ -413,6 +411,7 @@ fn should_panic(i: &ast::Item) -> ShouldPanic { Some(attr) => { let msg = attr.meta_item_list() .and_then(|list| list.iter().find(|mi| mi.check_name("expected"))) + .and_then(|li| li.meta_item()) .and_then(|mi| mi.value_str()); ShouldPanic::Yes(msg) } |
