use session::Session;
use syntax::ast;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrNestedMetaItemMethods, AttrMetaMethods};
use syntax::visit;
use syntax::visit::Visitor;
return;
}
};
+
for word in words {
- let word: &str = &word.name();
- let message = match word {
+ let name = match word.name() {
+ Some(word) => word,
+ None => continue,
+ };
+
+ let message = match &*name {
"C" => {
if target != Target::Struct && target != Target::Enum {
- "attribute should be applied to struct or enum"
+ "attribute should be applied to struct or enum"
} else {
continue
}
}
- "packed" |
- "simd" => {
+ "packed" | "simd" => {
if target != Target::Struct {
"attribute should be applied to struct"
} else {
"i32" | "u32" | "i64" | "u64" |
"isize" | "usize" => {
if target != Target::Enum {
- "attribute should be applied to enum"
+ "attribute should be applied to enum"
} else {
continue
}
}
_ => continue,
};
+
span_err!(self.sess, attr.span, E0517, "{}", message);
}
}
//! and returns a piece of the same type.
use hir::*;
-use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, Attribute, Attribute_, MetaItem};
-use syntax::ast::MetaItemKind;
+use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, Attribute, Attribute_};
+use syntax::ast::{NestedMetaItem, NestedMetaItemKind, MetaItem, MetaItemKind};
use hir;
use syntax_pos::Span;
use syntax::codemap::{respan, Spanned};
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)
}
})
}
+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),
},
use std::cmp;
use std::default::Default as StdDefault;
use std::mem;
-use syntax::attr::{self, AttrMetaMethods};
+use syntax::attr::{self, AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax::parse::token::InternedString;
use syntax::ast;
use syntax_pos::Span;
return out;
};
- for meta in metas {
- out.push(if meta.is_word() {
- Ok((meta.name().clone(), level, meta.span))
- } else {
- Err(meta.span)
- });
+ for li in metas {
+ out.push(li.word().map_or(Err(li.span), |word| {
+ Ok((word.name().clone(), level, word.span))
+ }));
}
out
use borrowck::BorrowckCtxt;
use syntax::ast::{self, MetaItem};
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};
if attr.check_name("rustc_mir") {
let items = attr.meta_item_list();
for item in items.iter().flat_map(|l| l.iter()) {
- if item.check_name(name) {
- return Some(item.clone())
+ match item.meta_item() {
+ Some(mi) if mi.check_name(name) => return Some(mi.clone()),
+ _ => continue
}
}
}
if !allow_unstable_cfg && GatedCfg::gate(&*cfg).is_some() {
continue;
}
+
if cfg.is_word() {
println!("{}", cfg.name());
- } else if cfg.is_value_str() {
- if let Some(s) = cfg.value_str() {
- println!("{}=\"{}\"", cfg.name(), s);
- }
+ } else if let Some(s) = cfg.value_str() {
+ println!("{}=\"{}\"", cfg.name(), s);
} else if cfg.is_meta_item_list() {
// Right now there are not and should not be any
// MetaItemKind::List items in the configuration returned by
// `build_configuration`.
- panic!("MetaItemKind::List encountered in default cfg")
+ panic!("Found an unexpected list in cfg attribute '{}'!", cfg.name())
+ } else {
+ // There also shouldn't be literals.
+ panic!("Found an unexpected literal in cfg attribute '{}'!", cfg.name())
}
}
}
use std::fs::File;
use std::io::Write;
use syntax::ast;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrNestedMetaItemMethods, AttrMetaMethods};
use syntax::parse::token::InternedString;
use syntax_pos::Span;
for attr in self.tcx.get_attrs(def_id).iter() {
if attr.check_name(IF_THIS_CHANGED) {
let mut id = None;
- for meta_item in attr.meta_item_list().unwrap_or_default() {
- if meta_item.is_word() && id.is_none() {
- id = Some(meta_item.name().clone());
- } else {
- // FIXME better-encapsulate meta_item (don't directly access `node`)
- span_bug!(meta_item.span(), "unexpected meta-item {:?}", meta_item.node)
+ for list_item in attr.meta_item_list().unwrap_or_default() {
+ match list_item.word() {
+ Some(word) if id.is_none() => {
+ id = Some(word.name().clone())
+ },
+ _ => {
+ // FIXME better-encapsulate meta_item (don't directly access `node`)
+ span_bug!(list_item.span(), "unexpected list-item {:?}", list_item.node)
+ }
}
}
+
let id = id.unwrap_or(InternedString::new(ID));
self.if_this_changed.entry(id)
.or_insert(FnvHashSet())
} else if attr.check_name(THEN_THIS_WOULD_NEED) {
let mut dep_node_interned = None;
let mut id = None;
- for meta_item in attr.meta_item_list().unwrap_or_default() {
- if meta_item.is_word() && dep_node_interned.is_none() {
- dep_node_interned = Some(meta_item.name().clone());
- } else if meta_item.is_word() && id.is_none() {
- id = Some(meta_item.name().clone());
- } else {
- // FIXME better-encapsulate meta_item (don't directly access `node`)
- span_bug!(meta_item.span(), "unexpected meta-item {:?}", meta_item.node)
+ for list_item in attr.meta_item_list().unwrap_or_default() {
+ match list_item.word() {
+ Some(word) if dep_node_interned.is_none() => {
+ dep_node_interned = Some(word.name().clone());
+ },
+ Some(word) if id.is_none() => {
+ id = Some(word.name().clone())
+ },
+ _ => {
+ // FIXME better-encapsulate meta_item (don't directly access `node`)
+ span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item.node)
+ }
}
}
+
let dep_node = match dep_node_interned {
Some(ref n) => {
match DepNode::from_label_string(&n[..], def_id) {
use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::Visitor;
use rustc_data_structures::fnv::FnvHashSet;
-use syntax::ast::{self, Attribute, MetaItem};
-use syntax::attr::AttrMetaMethods;
+use syntax::ast::{self, Attribute, NestedMetaItem};
+use syntax::attr::{AttrNestedMetaItemMethods, AttrMetaMethods};
use syntax::parse::token::InternedString;
use rustc::ty::TyCtxt;
}
impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
- fn expect_associated_value(&self, item: &MetaItem) -> InternedString {
+ fn expect_associated_value(&self, item: &NestedMetaItem) -> InternedString {
if let Some(value) = item.value_str() {
value
} else {
- self.tcx.sess.span_fatal(
- item.span,
- &format!("associated value expected for `{}`", item.name()));
+ let msg = if let Some(name) = item.name() {
+ format!("associated value expected for `{}`", name)
+ } else {
+ "expected an associated value".to_string()
+ };
+
+ self.tcx.sess.span_fatal(item.span, &msg);
}
}
use std::collections::HashSet;
use syntax::{ast};
-use syntax::attr::{self, AttrMetaMethods, AttributeMethods};
-use syntax_pos::Span;
+use syntax::attr::{self, AttrMetaMethods, AttrNestedMetaItemMethods, AttributeMethods};
+use syntax_pos::{Span};
use rustc::hir::{self, PatKind};
use rustc::hir::intravisit::FnKind;
let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
attr.check_name("doc") && match attr.meta_item_list() {
None => false,
- Some(l) => attr::contains_name(&l[..], "hidden"),
+ Some(l) => attr::list_contains_name(&l[..], "hidden"),
}
});
self.doc_hidden_stack.push(doc_hidden);
impl LateLintPass for UnusedAttributes {
fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
+ debug!("checking attribute: {:?}", attr);
+
// Note that check_name() marks the attribute as used if it matches.
for &(ref name, ty, _) in KNOWN_ATTRIBUTES {
match ty {
AttributeType::Whitelisted if attr.check_name(name) => {
+ debug!("{:?} is Whitelisted", name);
break;
},
_ => ()
let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
for &(ref name, ty) in plugin_attributes.iter() {
if ty == AttributeType::Whitelisted && attr.check_name(&name) {
+ debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
break;
}
}
if !attr::is_used(attr) {
+ debug!("Emitting warning for: {:?}", attr);
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
// Is it a builtin attribute that must be used at the crate level?
let known_crate = KNOWN_ATTRIBUTES.iter().find(|&&(name, ty, _)| {
};
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
}
+ } else {
+ debug!("Attr was used: {:?}", attr);
}
}
}
pub const tag_items_closure_ty: usize = 0x2b;
pub const tag_def_key: usize = 0x2c;
-// GAP 0x2d 0x2e
+// GAP 0x2d 0x34
pub const tag_index: usize = 0x110; // top-level only
pub const tag_xref_index: usize = 0x111; // top-level only
pub const tag_xref_data: usize = 0x112; // top-level only
-
-pub const tag_meta_item_name_value: usize = 0x2f;
-
-pub const tag_meta_item_name: usize = 0x30;
-
-pub const tag_meta_item_value: usize = 0x31;
-
pub const tag_attributes: usize = 0x101; // top-level only
-pub const tag_attribute: usize = 0x32;
-
-pub const tag_meta_item_word: usize = 0x33;
-
-pub const tag_meta_item_list: usize = 0x34;
-
// The list of crates that this crate depends on
pub const tag_crate_deps: usize = 0x102; // top-level only
use syntax::abi::Abi;
use syntax::codemap;
use syntax::parse;
-use syntax::attr;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{self, AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax::parse::token::InternedString;
use syntax::visit;
use syntax_pos::{self, Span, mk_sp, Pos};
use syntax::ast;
use syntax::codemap;
use syntax::print::pprust;
-use syntax::ptr::P;
use syntax_pos::{self, Span, BytePos, NO_EXPANSION};
pub type Cmd<'a> = &'a CrateMetadata;
})).collect()
}
-fn get_meta_items(md: rbml::Doc) -> Vec<P<ast::MetaItem>> {
- reader::tagged_docs(md, tag_meta_item_word).map(|meta_item_doc| {
- let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
- let n = token::intern_and_get_ident(nd.as_str());
- attr::mk_word_item(n)
- }).chain(reader::tagged_docs(md, tag_meta_item_name_value).map(|meta_item_doc| {
- let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
- let vd = reader::get_doc(meta_item_doc, tag_meta_item_value);
- let n = token::intern_and_get_ident(nd.as_str());
- let v = token::intern_and_get_ident(vd.as_str());
- // FIXME (#623): Should be able to decode MetaItemKind::NameValue variants,
- // but currently the encoder just drops them
- attr::mk_name_value_item_str(n, v)
- })).chain(reader::tagged_docs(md, tag_meta_item_list).map(|meta_item_doc| {
- let nd = reader::get_doc(meta_item_doc, tag_meta_item_name);
- let n = token::intern_and_get_ident(nd.as_str());
- let subitems = get_meta_items(meta_item_doc);
- attr::mk_list_item(n, subitems)
- })).collect()
-}
-
fn get_attributes(md: rbml::Doc) -> Vec<ast::Attribute> {
- match reader::maybe_get_doc(md, tag_attributes) {
- Some(attrs_d) => {
- reader::tagged_docs(attrs_d, tag_attribute).map(|attr_doc| {
- let is_sugared_doc = reader::doc_as_u8(
- reader::get_doc(attr_doc, tag_attribute_is_sugared_doc)
- ) == 1;
- let meta_items = get_meta_items(attr_doc);
- // Currently it's only possible to have a single meta item on
- // an attribute
- assert_eq!(meta_items.len(), 1);
- let meta_item = meta_items.into_iter().nth(0).unwrap();
- attr::mk_doc_attr_outer(attr::mk_attr_id(), meta_item, is_sugared_doc)
- }).collect()
- },
- None => vec![],
- }
+ reader::maybe_get_doc(md, tag_attributes).map_or(vec![], |attrs_doc| {
+ let mut decoder = reader::Decoder::new(attrs_doc);
+ let mut attrs: Vec<ast::Attribute> = decoder.read_opaque(|opaque_decoder, _| {
+ Decodable::decode(opaque_decoder)
+ }).unwrap();
+
+ // Need new unique IDs: old thread-local IDs won't map to new threads.
+ for attr in attrs.iter_mut() {
+ attr.node.id = attr::mk_attr_id();
+ }
+
+ attrs
+ })
}
fn list_crate_attributes(md: rbml::Doc, hash: &Svh,
use std::rc::Rc;
use std::u32;
use syntax::ast::{self, NodeId, Name, CRATE_NODE_ID, CrateNum};
-use syntax::attr::{self,AttrMetaMethods,AttributeMethods};
+use syntax::attr;
use errors::Handler;
use syntax;
use syntax_pos::BytePos;
rbml_w.end_tag();
}
-fn encode_meta_item(rbml_w: &mut Encoder, mi: &ast::MetaItem) {
- if mi.is_word() {
- let name = mi.name();
- rbml_w.start_tag(tag_meta_item_word);
- rbml_w.wr_tagged_str(tag_meta_item_name, &name);
- rbml_w.end_tag();
- } else if mi.is_value_str() {
- let name = mi.name();
- /* FIXME (#623): support other literal kinds */
- let value = mi.value_str().unwrap();
- rbml_w.start_tag(tag_meta_item_name_value);
- rbml_w.wr_tagged_str(tag_meta_item_name, &name);
- rbml_w.wr_tagged_str(tag_meta_item_value, &value);
- rbml_w.end_tag();
- } else { // it must be a list
- let name = mi.name();
- let items = mi.meta_item_list().unwrap();
- rbml_w.start_tag(tag_meta_item_list);
- rbml_w.wr_tagged_str(tag_meta_item_name, &name);
- for inner_item in items {
- encode_meta_item(rbml_w, &inner_item);
- }
- rbml_w.end_tag();
- }
-}
-
fn encode_attributes(rbml_w: &mut Encoder, attrs: &[ast::Attribute]) {
rbml_w.start_tag(tag_attributes);
- for attr in attrs {
- rbml_w.start_tag(tag_attribute);
- rbml_w.wr_tagged_u8(tag_attribute_is_sugared_doc, attr.node.is_sugared_doc as u8);
- encode_meta_item(rbml_w, attr.meta());
- rbml_w.end_tag();
- }
+ rbml_w.emit_opaque(|opaque_encoder| {
+ attrs.encode(opaque_encoder)
+ }).unwrap();
rbml_w.end_tag();
}
use std::collections::{HashSet, HashMap};
use syntax::parse::token;
use syntax::ast;
-use syntax::attr;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{self, AttrNestedMetaItemMethods, AttrMetaMethods};
use syntax::ext;
use syntax_pos::Span;
}
if let (Some(sel), Some(names)) = (import.as_mut(), names) {
for attr in names {
- if attr.is_word() {
- sel.insert(attr.name().clone(), attr.span());
+ if let Some(word) = attr.word() {
+ sel.insert(word.name().clone(), attr.span());
} else {
span_err!(self.sess, attr.span(), E0466, "bad macro import");
}
};
for attr in names {
- if attr.is_word() {
- reexport.insert(attr.name().clone(), attr.span());
+ if let Some(word) = attr.word() {
+ reexport.insert(word.name().clone(), attr.span());
} else {
call_bad_macro_reexport(self.sess, attr.span());
}
use std::mem;
use std::path::PathBuf;
use syntax::ast;
-use syntax::ptr::P;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax_pos::{Span, COMMAND_LINE_SP};
/// Pointer to a registrar function.
pub struct PluginRegistrar {
pub fun: PluginRegistrarFun,
- pub args: Vec<P<ast::MetaItem>>,
+ pub args: Vec<ast::NestedMetaItem>,
}
struct PluginLoader<'a> {
};
for plugin in plugins {
- if plugin.value_str().is_some() {
- call_malformed_plugin_attribute(sess, attr.span);
- continue;
+ // plugins must have a name and can't be key = value
+ match plugin.name() {
+ Some(ref name) if !plugin.is_value_str() => {
+ let args = plugin.meta_item_list().map(ToOwned::to_owned);
+ loader.load_plugin(plugin.span, name, args.unwrap_or_default());
+ },
+ _ => call_malformed_plugin_attribute(sess, attr.span),
}
-
- let args = plugin.meta_item_list().map(ToOwned::to_owned).unwrap_or_default();
- loader.load_plugin(plugin.span, &plugin.name(), args);
}
}
}
}
}
- fn load_plugin(&mut self, span: Span, name: &str, args: Vec<P<ast::MetaItem>>) {
+ fn load_plugin(&mut self, span: Span, name: &str, args: Vec<ast::NestedMetaItem>) {
let registrar = self.reader.find_plugin_registrar(span, name);
if let Some((lib, svh, index)) = registrar {
use syntax::ext::base::{IdentTT, MultiModifier, MultiDecorator};
use syntax::ext::base::{MacroExpanderFn, MacroRulesTT};
use syntax::parse::token;
-use syntax::ptr::P;
use syntax::ast;
use syntax::feature_gate::AttributeType;
use syntax_pos::Span;
pub sess: &'a Session,
#[doc(hidden)]
- pub args_hidden: Option<Vec<P<ast::MetaItem>>>,
+ pub args_hidden: Option<Vec<ast::NestedMetaItem>>,
#[doc(hidden)]
pub krate_span: Span,
///
/// Returns empty slice in case the plugin was loaded
/// with `--extra-plugins`
- pub fn args<'b>(&'b self) -> &'b [P<ast::MetaItem>] {
+ pub fn args<'b>(&'b self) -> &'b [ast::NestedMetaItem] {
self.args_hidden.as_ref().map(|v| &v[..]).unwrap_or(&[])
}
use rustc::ty::TyCtxt;
use syntax::ast;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax::parse::token::InternedString;
use {ModuleSource, ModuleTranslation};
use syntax::abi::Abi;
use syntax::ast;
use syntax::attr;
-use syntax::attr::{AttributeMethods, AttrMetaMethods};
+use syntax::attr::{AttributeMethods, AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax::codemap::Spanned;
use syntax::parse::token::{self, InternedString, keywords};
use syntax::ptr::P;
+use syntax::print::pprust as syntax_pprust;
use syntax_pos::{self, DUMMY_SP, Pos};
use rustc_trans::back::link;
}
}
+/// This is a flattened version of the AST's Attribute + MetaItem.
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
pub enum Attribute {
Word(String),
List(String, Vec<Attribute>),
- NameValue(String, String)
+ NameValue(String, String),
+ Literal(String),
+}
+
+impl Clean<Attribute> for ast::NestedMetaItem {
+ fn clean(&self, cx: &DocContext) -> Attribute {
+ if let Some(mi) = self.meta_item() {
+ mi.clean(cx)
+ } else { // must be a literal
+ let lit = self.literal().unwrap();
+ Literal(syntax_pprust::lit_to_string(lit))
+ }
+ }
}
impl Clean<Attribute> for ast::MetaItem {
}
// This is a rough approximation that gets us what we want.
-impl attr::AttrMetaMethods for Attribute {
- fn name(&self) -> InternedString {
+impl attr::AttrNestedMetaItemMethods for Attribute {
+ fn check_name(&self, name: &str) -> bool {
+ self.name().map_or(false, |mi_name| &*mi_name == name)
+ }
+
+ fn literal(&self) -> Option<&ast::Lit> { None }
+
+ fn is_literal(&self) -> bool {
+ match *self {
+ Literal(..) => true,
+ _ => false,
+ }
+ }
+
+ fn meta_item(&self) -> Option<&P<ast::MetaItem>> { None }
+
+ fn name(&self) -> Option<InternedString> {
match *self {
Word(ref n) | List(ref n, _) | NameValue(ref n, _) => {
- token::intern_and_get_ident(n)
- }
+ Some(token::intern_and_get_ident(n))
+ },
+ _ => None
}
}
_ => None,
}
}
- fn meta_item_list<'a>(&'a self) -> Option<&'a [P<ast::MetaItem>]> { None }
+
+ fn word(&self) -> Option<&P<ast::MetaItem>> { None }
fn is_word(&self) -> bool {
match *self {
}
}
- fn is_value_str(&self) -> bool {
- match *self {
- NameValue(..) => true,
- _ => false,
- }
- }
+ fn meta_item_list<'a>(&'a self) -> Option<&'a [ast::NestedMetaItem]> { None }
fn is_meta_item_list(&self) -> bool {
match *self {
// Don't inline doc(hidden) imports so they can be stripped at a later stage.
let denied = self.vis != hir::Public || self.attrs.iter().any(|a| {
&a.name()[..] == "doc" && match a.meta_item_list() {
- Some(l) => attr::contains_name(l, "no_inline") ||
- attr::contains_name(l, "hidden"),
+ Some(l) => attr::list_contains_name(l, "no_inline") ||
+ attr::list_contains_name(l, "hidden"),
None => false,
}
});
// Look for #![doc(test(no_crate_inject))], used by crates in the std facade
fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
use syntax::attr::AttrMetaMethods;
+ use syntax::attr::AttrNestedMetaItemMethods;
use syntax::print::pprust;
let mut opts = TestOptions {
if attr.check_name("attr") {
if let Some(l) = attr.meta_item_list() {
for item in l {
- opts.attrs.push(pprust::meta_item_to_string(item));
+ opts.attrs.push(pprust::meta_list_item_to_string(item));
}
}
}
use syntax::abi;
use syntax::ast;
use syntax::attr;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax_pos::Span;
use rustc::hir::map as hir_map;
let node = if item.vis == hir::Public {
let please_inline = item.attrs.iter().any(|item| {
match item.meta_item_list() {
- Some(list) if &item.name()[..] == "doc" => {
- list.iter().any(|i| &i.name()[..] == "inline")
+ Some(list) if item.check_name("doc") => {
+ list.iter().any(|i| i.check_name("inline"))
}
_ => false,
}
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"]`
/// 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"]`
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
- }
}
}
}
_ => 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
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};
MissingSince,
MissingFeature,
MultipleStabilityLevels,
+ UnsupportedLiteral
}
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;
})
}
+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()[..]
/// 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;
}
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()
}
}
}
- 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
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() }
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)
}
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)))
}
})
}
+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| {
/* 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")
}
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
}
}
/// 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)
}
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
}
}
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
}
}
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
}
}
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
}
}
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");
}
}
}
// 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};
return None;
}
};
+
let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
(2, Some(cfg), Some(mi)) => (cfg, mi),
_ => {
}
};
- 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
+ }
}
}
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)
})
}
```
"##,
+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! {
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,
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))
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, Spanned};
use syntax_pos::Span;
use errors::Handler;
(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! (
}
}
+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) {
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");
}
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;
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)
}
}
}
+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)
},
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()),
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))))
}
}
}
- /// 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())
}
}
// 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);
})
}
+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))
}
}
}
+ 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 {
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());
}
}
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;
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);
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)
}
}
let inline = cx.meta_word(span, InternedString::new("inline"));
- let hidden = cx.meta_word(span, InternedString::new("hidden"));
+ let hidden = cx.meta_list_item_word(span, InternedString::new("hidden"));
let doc = cx.meta_list(span, InternedString::new("doc"), vec![hidden]);
let attrs = vec![cx.attribute(span, inline), cx.attribute(span, doc)];
let trait_def = TraitDef {
let unused_qual = cx.attribute(self.span,
cx.meta_list(self.span,
InternedString::new("allow"),
- vec![cx.meta_word(self.span,
+ vec![cx.meta_list_item_word(self.span,
InternedString::new("unused_qualifications"))]));
let mut a = vec![attr, unused_qual];
a.extend(self.attributes.iter().cloned());
//! The compiler code necessary to implement the `#[derive]` extensions.
use syntax::ast::{self, MetaItem};
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrNestedMetaItemMethods, AttrMetaMethods};
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv};
use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
use syntax::ext::build::AstBuilder;
let mut eq_span = None;
for titem in traits.iter().rev() {
- let tname = if titem.is_word() {
- titem.name()
+ let tname = if let Some(word) = titem.word() {
+ word.name()
} else {
cx.span_err(titem.span, "malformed `derive` entry");
continue;
extern crate rustc;
extern crate rustc_plugin;
-use syntax::ast::{self, Item, MetaItem, ImplItem, TraitItem, ItemKind};
+use syntax::ast::{self, Item, MetaItem, ItemKind};
+use syntax::attr::{AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax::ext::base::*;
use syntax::parse::{self, token};
use syntax::ptr::P;
}
fn expand_into_foo_multi(cx: &mut ExtCtxt,
- sp: Span,
- attr: &MetaItem,
+ _sp: Span,
+ _attr: &MetaItem,
it: Annotatable) -> Annotatable {
match it {
Annotatable::Item(it) => {
..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
}))
}
- Annotatable::ImplItem(it) => {
+ Annotatable::ImplItem(_) => {
quote_item!(cx, impl X { fn foo(&self) -> i32 { 42 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Impl(_, _, _, _, _, mut items) => {
}
})
}
- Annotatable::TraitItem(it) => {
+ Annotatable::TraitItem(_) => {
quote_item!(cx, trait X { fn foo(&self) -> i32 { 0 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Trait(_, _, _, mut items) => {
// Create a duplicate of the annotatable, based on the MetaItem
fn expand_duplicate(cx: &mut ExtCtxt,
- sp: Span,
+ _sp: Span,
mi: &MetaItem,
it: &Annotatable,
push: &mut FnMut(Annotatable))
{
let copy_name = match mi.node {
ast::MetaItemKind::List(_, ref xs) => {
- if let ast::MetaItemKind::Word(ref w) = xs[0].node {
- token::str_to_ident(&w)
+ if let Some(word) = xs[0].word() {
+ token::str_to_ident(&word.name())
} else {
cx.span_err(mi.span, "Expected word");
return;
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(attr_literals)]
+
+// deprecated doesn't currently support literals
+#[deprecated("since")] //~ ERROR E0565
+fn f() { }
+
+fn main() { }
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(attr_literals)]
+
+// repr currently doesn't support literals
+#[repr("C")] //~ ERROR E0565
+struct A { }
+
+fn main() { }
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that literals in attributes parse just fine.
+
+#![feature(rustc_attrs, attr_literals)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+
+#[fake_attr] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(100)] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(1, 2, 3)] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr("hello")] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(name = "hello")] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(1, "hi", key = 12, true, false)] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(key = "hello", val = 10)] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(key("hello"), val(10))] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(enabled = true, disabled = false)] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(true)] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(pi = 3.14159)] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(b"hi")] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_doc(r"doc")] //~ ERROR attribute `fake_doc` is currently unknown
+struct Q { }
+
+#[rustc_error]
+fn main() { }
--- /dev/null
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that literals in attributes don't parse without the feature gate.
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+
+#[fake_attr] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(100)] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes
+#[fake_attr(1, 2, 3)] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes
+#[fake_attr("hello")] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR string literals in top-level positions, are experimental
+#[fake_attr(name = "hello")] //~ ERROR attribute `fake_attr` is currently unknown
+#[fake_attr(1, "hi", key = 12, true, false)] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes, or string literals in top-level positions
+#[fake_attr(key = "hello", val = 10)] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes
+#[fake_attr(key("hello"), val(10))] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes, or string literals in top-level positions
+#[fake_attr(enabled = true, disabled = false)] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes
+#[fake_attr(true)] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes
+#[fake_attr(pi = 3.14159)] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR non-string literals in attributes
+#[fake_attr(b"hi")] //~ ERROR attribute `fake_attr` is currently unknown
+ //~^ ERROR string literals in top-level positions, are experimental
+#[fake_doc(r"doc")] //~ ERROR attribute `fake_doc` is currently unknown
+ //~^ ERROR string literals in top-level positions, are experimental
+struct Q { }
+
+#[rustc_error]
+fn main() { }
+++ /dev/null
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// compile-flags: -Z parse-only
-
-// Issue #623 - non-string meta items are not serialized correctly;
-// for now just forbid them
-
-#[foo = 1] //~ ERROR: non-string literals are not allowed in meta-items
-fn main() { }
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+#[foo = 1usize] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1u8] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1u16] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1u32] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1u64] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1isize] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1i8] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1i16] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1i32] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1i64] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1.0f32] //~ ERROR: suffixed literals are not allowed in attributes
+#[foo = 1.0f64] //~ ERROR: suffixed literals are not allowed in attributes
+fn main() { }
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// pp-exact
+// Tests literals in attributes.
+
+#![feature(custom_attribute, attr_literals)]
+
+fn main() {
+ #![hello("hi", 1, 2, 1.012, pi = 3.14, bye, name("John"))]
+ #[align = 8]
+ fn f() { }
+
+ #[vec(1, 2, 3)]
+ fn g() { }
+}
use syntax::ext::base::{MultiDecorator, ExtCtxt, Annotatable};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
-use syntax::ptr::P;
use syntax_ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure};
use syntax_ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self};
use syntax_pos::Span;
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;
-use syntax_ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure};
+use syntax_ext::deriving::generic::{TraitDef, MethodDef, combine_substructure};
use syntax_ext::deriving::generic::{Substructure, Struct, EnumMatching};
use syntax_ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self};
use syntax_pos::Span;
extern crate rustc_plugin;
extern crate syntax_pos;
-use syntax::ast::{self, Item, MetaItem, ImplItem, TraitItem, ItemKind};
+use syntax::ast::{self, Item, MetaItem, ItemKind};
+use syntax::codemap::DUMMY_SP;
+use syntax::attr::{AttrMetaMethods, AttrNestedMetaItemMethods};
use syntax::ext::base::*;
+use syntax::ext::quote::rt::ToTokens;
use syntax::parse::{self, token};
use syntax::ptr::P;
use syntax::tokenstream::TokenTree;
token::intern("duplicate"),
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
MultiDecorator(Box::new(expand_duplicate)));
+ reg.register_syntax_extension(
+ token::intern("caller"),
+ // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
+ MultiDecorator(Box::new(expand_caller)));
}
-fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
- -> Box<MacResult+'static> {
+fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
if !tts.is_empty() {
cx.span_fatal(sp, "make_a_1 takes no arguments");
}
}
// See Issue #15750
-fn expand_identity(cx: &mut ExtCtxt, _span: Span, tts: &[TokenTree])
- -> Box<MacResult+'static> {
+fn expand_identity(cx: &mut ExtCtxt, _span: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
// Parse an expression and emit it unchanged.
- let mut parser = parse::new_parser_from_tts(cx.parse_sess(),
- cx.cfg(), tts.to_vec());
+ let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_vec());
let expr = parser.parse_expr().unwrap();
MacEager::expr(quote_expr!(&mut *cx, $expr))
}
fn expand_into_foo_multi(cx: &mut ExtCtxt,
- sp: Span,
- attr: &MetaItem,
- it: Annotatable) -> Vec<Annotatable> {
+ _sp: Span,
+ _attr: &MetaItem,
+ it: Annotatable)
+ -> Vec<Annotatable> {
match it {
Annotatable::Item(it) => vec![
Annotatable::Item(P(Item {
Annotatable::Item(quote_item!(cx, enum Foo3 { Bar }).unwrap()),
Annotatable::Item(quote_item!(cx, #[cfg(any())] fn foo2() {}).unwrap()),
],
- Annotatable::ImplItem(it) => vec![
+ Annotatable::ImplItem(_it) => vec![
quote_item!(cx, impl X { fn foo(&self) -> i32 { 42 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Impl(_, _, _, _, _, mut items) => {
}
})
],
- Annotatable::TraitItem(it) => vec![
+ Annotatable::TraitItem(_it) => vec![
quote_item!(cx, trait X { fn foo(&self) -> i32 { 0 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Trait(_, _, _, mut items) => {
// Create a duplicate of the annotatable, based on the MetaItem
fn expand_duplicate(cx: &mut ExtCtxt,
- sp: Span,
+ _sp: Span,
mi: &MetaItem,
it: &Annotatable,
- push: &mut FnMut(Annotatable))
-{
+ push: &mut FnMut(Annotatable)) {
let copy_name = match mi.node {
ast::MetaItemKind::List(_, ref xs) => {
- if let ast::MetaItemKind::Word(ref w) = xs[0].node {
- token::str_to_ident(&w)
+ if let Some(word) = xs[0].word() {
+ token::str_to_ident(&word.name())
} else {
cx.span_err(mi.span, "Expected word");
return;
}
}
+pub fn token_separate<T: ToTokens>(ecx: &ExtCtxt, things: &[T],
+ token: token::Token) -> Vec<TokenTree> {
+ let mut output: Vec<TokenTree> = vec![];
+ for (i, thing) in things.iter().enumerate() {
+ output.extend(thing.to_tokens(ecx));
+ if i < things.len() - 1 {
+ output.push(TokenTree::Token(DUMMY_SP, token.clone()));
+ }
+ }
+
+ output
+}
+
+fn expand_caller(cx: &mut ExtCtxt,
+ sp: Span,
+ mi: &MetaItem,
+ it: &Annotatable,
+ push: &mut FnMut(Annotatable)) {
+ let (orig_fn_name, ret_type) = match *it {
+ Annotatable::Item(ref item) => match item.node {
+ ItemKind::Fn(ref decl, _, _, _, _, _) => {
+ (item.ident, &decl.output)
+ }
+ _ => cx.span_fatal(item.span, "Only functions with return types can be annotated.")
+ },
+ _ => cx.span_fatal(sp, "Only functions can be annotated.")
+ };
+
+ let (caller_name, arguments) = if let Some(list) = mi.meta_item_list() {
+ if list.len() < 2 {
+ cx.span_fatal(mi.span(), "Need a function name and at least one parameter.");
+ }
+
+ let fn_name = match list[0].name() {
+ Some(name) => token::str_to_ident(&name),
+ None => cx.span_fatal(list[0].span(), "First parameter must be an ident.")
+ };
+
+ (fn_name, &list[1..])
+ } else {
+ cx.span_fatal(mi.span, "Expected list.");
+ };
+
+ let literals: Vec<ast::Lit> = arguments.iter().map(|arg| {
+ if let Some(lit) = arg.literal() {
+ lit.clone()
+ } else {
+ cx.span_fatal(arg.span(), "Expected literal.");
+ }
+ }).collect();
+
+ let arguments = token_separate(cx, literals.as_slice(), token::Comma);
+ if let ast::FunctionRetTy::Ty(ref rt) = *ret_type {
+ push(Annotatable::Item(quote_item!(cx,
+ fn $caller_name() -> $rt {
+ $orig_fn_name($arguments)
+ }).unwrap()))
+ } else {
+ push(Annotatable::Item(quote_item!(cx,
+ fn $caller_name() {
+ $orig_fn_name($arguments)
+ }).unwrap()))
+ }
+}
+
pub fn foo() {}
use rustc_plugin::Registry;
struct Expander {
- args: Vec<P<ast::MetaItem>>,
+ args: Vec<ast::NestedMetaItem>,
}
impl TTMacroExpander for Expander {
ecx: &'cx mut ExtCtxt,
sp: Span,
_: &[tokenstream::TokenTree]) -> Box<MacResult+'cx> {
- let args = self.args.iter().map(|i| pprust::meta_item_to_string(&*i))
+ let args = self.args.iter().map(|i| pprust::meta_list_item_to_string(i))
.collect::<Vec<_>>().join(", ");
let interned = token::intern_and_get_ident(&args[..]);
MacEager::expr(ecx.expr_str(sp, interned))
--- /dev/null
+// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:macro_crate_test.rs
+// ignore-stage1
+
+#![feature(plugin, custom_attribute, attr_literals)]
+#![plugin(macro_crate_test)]
+
+#[macro_use]
+#[no_link]
+extern crate macro_crate_test;
+
+// The `caller(name, args...)` attribute emits a new nullary function named
+// `name` that calls the annotated function with `args`. As an example, consider
+// the following:
+//
+// #[caller(simple, 1, "hello", 3.14)]
+// fn f(num: isize, string: &'static str, float: f32) -> (isize, &'static str, float) {
+// (num, string, float)
+// }
+//
+// This results in a function named `simple` that calls `f(1, "hello", 3.14)`.
+// As a result, the expression `simple()` evaluates to `(1, "helllo", 3.14)`.
+
+#[caller(simple, 1, "hello", 3.14)]
+#[caller(simple1, 2, "bye", 6.28)]
+#[caller(simple2, 3, "hi", 1.01)]
+fn f(num: isize, string: &'static str, float: f32) -> (isize, &'static str, f32) {
+ (num, string, float)
+}
+
+#[caller(complex, true, 10)]
+#[caller(complex1, false, 15)]
+#[caller(complex2, true, 20)]
+fn g(emit: bool, num: i32) -> Option<i32> {
+ match emit {
+ true => Some(num),
+ false => None
+ }
+}
+
+fn main() {
+ assert_eq!(simple(), (1, "hello", 3.14));
+ assert_eq!(simple1(), (2, "bye", 6.28));
+ assert_eq!(simple2(), (3, "hi", 1.01));
+
+ assert_eq!(complex(), Some(10));
+ assert_eq!(complex1(), None);
+ assert_eq!(complex2(), Some(20));
+}