diff --git a/src/doc/rust.md b/src/doc/rust.md index d686a02b2fe64..b8fa4075e7a75 100644 --- a/src/doc/rust.md +++ b/src/doc/rust.md @@ -661,6 +661,7 @@ Attributes on the anonymous crate module define important metadata that influenc the behavior of the compiler. ~~~~ {.rust} +# #![allow(unused_attribute)] // Crate ID #![crate_id = "projx#2.5"] diff --git a/src/doc/rustdoc.md b/src/doc/rustdoc.md index d6cb782bd83bb..af9cff7be67b0 100644 --- a/src/doc/rustdoc.md +++ b/src/doc/rustdoc.md @@ -11,6 +11,7 @@ Documenting Rust APIs is quite simple. To document a given item, we have "doc comments": ~~~ +# #![allow(unused_attribute)] // the "link" crate attribute is currently required for rustdoc, but normally // isn't needed. #![crate_id = "universe"] diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index da78869d9fa84..d85734508bc13 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -3166,6 +3166,7 @@ without conflict. Therefore, if you plan to compile your crate as a library, you should annotate it with that information: ~~~~ +# #![allow(unused_attribute)] // `lib.rs` # #![crate_type = "lib"] @@ -3189,6 +3190,7 @@ Other crate settings and metadata include things like enabling/disabling certain or setting the crate type (library or executable) explicitly: ~~~~ +# #![allow(unused_attribute)] // `lib.rs` // ... @@ -3208,6 +3210,7 @@ Now for something that you can actually compile yourself. We define two crates, and use one of them as a library in the other. ~~~~ +# #![allow(unused_attribute)] // `world.rs` #![crate_id = "world#0.42"] @@ -3282,11 +3285,13 @@ fn main() { Both auto-insertions can be disabled with an attribute if necessary: ~~~ +# #![allow(unused_attribute)] // In the crate root: #![no_std] ~~~ ~~~ +# #![allow(unused_attribute)] // In any module: #![no_implicit_prelude] ~~~ diff --git a/src/librustc/back/svh.rs b/src/librustc/back/svh.rs index 489722aa13fb4..9ad653498ba06 100644 --- a/src/librustc/back/svh.rs +++ b/src/librustc/back/svh.rs @@ -91,7 +91,12 @@ impl Svh { // types and then use hash_content. But, since all crate // attributes should appear near beginning of the file, it is // not such a big deal to be sensitive to their spans for now. - krate.attrs.hash(&mut state); + // + // We hash only the MetaItems instead of the entire Attribute + // to avoid hashing the AttrId + for attr in krate.attrs.iter() { + attr.node.value.hash(&mut state); + } let hash = state.result(); return Svh { diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 4e682e8e34bb2..ee9b10a805901 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -716,6 +716,45 @@ fn print_flowgraph(analysis: CrateAnalysis, pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { + // Unconditionally collect crate types from attributes to make them used + let attr_types: Vec = attrs.iter().filter_map(|a| { + if a.check_name("crate_type") { + match a.value_str() { + Some(ref n) if n.equiv(&("rlib")) => { + Some(config::CrateTypeRlib) + } + Some(ref n) if n.equiv(&("dylib")) => { + Some(config::CrateTypeDylib) + } + Some(ref n) if n.equiv(&("lib")) => { + Some(config::default_lib_output()) + } + Some(ref n) if n.equiv(&("staticlib")) => { + Some(config::CrateTypeStaticlib) + } + Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), + Some(_) => { + session.add_lint(lint::UnknownCrateType, + ast::CRATE_NODE_ID, + a.span, + "invalid `crate_type` \ + value".to_strbuf()); + None + } + _ => { + session.add_lint(lint::UnknownCrateType, + ast::CRATE_NODE_ID, + a.span, + "`crate_type` requires a \ + value".to_strbuf()); + None + } + } + } else { + None + } + }).collect(); + // If we're generating a test executable, then ignore all other output // styles at all other locations if session.opts.test { @@ -729,44 +768,7 @@ pub fn collect_crate_types(session: &Session, if base.len() > 0 { return base } else { - let iter = attrs.iter().filter_map(|a| { - if a.name().equiv(&("crate_type")) { - match a.value_str() { - Some(ref n) if n.equiv(&("rlib")) => { - Some(config::CrateTypeRlib) - } - Some(ref n) if n.equiv(&("dylib")) => { - Some(config::CrateTypeDylib) - } - Some(ref n) if n.equiv(&("lib")) => { - Some(config::default_lib_output()) - } - Some(ref n) if n.equiv(&("staticlib")) => { - Some(config::CrateTypeStaticlib) - } - Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), - Some(_) => { - session.add_lint(lint::UnknownCrateType, - ast::CRATE_NODE_ID, - a.span, - "invalid `crate_type` \ - value".to_strbuf()); - None - } - _ => { - session.add_lint(lint::UnknownCrateType, - ast::CRATE_NODE_ID, - a.span, - "`crate_type` requires a \ - value".to_strbuf()); - None - } - } - } else { - None - } - }); - base.extend(iter); + base.extend(attr_types.move_iter()); if base.len() == 0 { base.push(config::CrateTypeExecutable); } diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 21a7fc8d15044..4f9957ee98029 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -327,7 +327,7 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) { }; for attr in krate.attrs.iter() { - if !attr.name().equiv(&("feature")) { + if !attr.check_name("feature") { continue } diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index efaeeaa557510..fe636f7b686a0 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -78,7 +78,7 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> { with_version("std"), ast::DUMMY_NODE_ID), attrs: vec!( - attr::mk_attr_outer(attr::mk_list_item( + attr::mk_attr_outer(attr::mk_attr_id(), attr::mk_list_item( InternedString::new("phase"), vec!( attr::mk_word_item(InternedString::new("syntax")), @@ -110,10 +110,13 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> { // Add it during the prelude injection instead. // Add #![feature(phase)] here, because we use #[phase] on extern crate std. - let feat_phase_attr = attr::mk_attr_inner(attr::mk_list_item( + let feat_phase_attr = attr::mk_attr_inner(attr::mk_attr_id(), + attr::mk_list_item( InternedString::new("feature"), vec![attr::mk_word_item(InternedString::new("phase"))], )); + // std_inject runs after feature checking so manually mark this attr + attr::mark_used(&feat_phase_attr); krate.attrs.push(feat_phase_attr); krate @@ -138,7 +141,10 @@ impl<'a> fold::Folder for PreludeInjector<'a> { // This must happen here and not in StandardLibraryInjector because this // fold happens second. - let no_std_attr = attr::mk_attr_inner(attr::mk_word_item(InternedString::new("no_std"))); + let no_std_attr = attr::mk_attr_inner(attr::mk_attr_id(), + attr::mk_word_item(InternedString::new("no_std"))); + // std_inject runs after feature checking so manually mark this attr + attr::mark_used(&no_std_attr); krate.attrs.push(no_std_attr); if !no_prelude(krate.attrs.as_slice()) { @@ -146,11 +152,14 @@ impl<'a> fold::Folder for PreludeInjector<'a> { // `#![no_implicit_prelude]` at the crate level. // fold_mod() will insert glob path. - let globs_attr = attr::mk_attr_inner(attr::mk_list_item( + let globs_attr = attr::mk_attr_inner(attr::mk_attr_id(), + attr::mk_list_item( InternedString::new("feature"), vec!( attr::mk_word_item(InternedString::new("globs")), ))); + // std_inject runs after feature checking so manually mark this attr + attr::mark_used(&globs_attr); krate.attrs.push(globs_attr); krate.module = self.fold_mod(&krate.module); diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index e7a5aeb9ef5aa..6794442380855 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -253,7 +253,7 @@ fn is_bench_fn(cx: &TestCtxt, i: @ast::Item) -> bool { fn is_ignored(cx: &TestCtxt, i: @ast::Item) -> bool { i.attrs.iter().any(|attr| { // check ignore(cfg(foo, bar)) - attr.name().equiv(&("ignore")) && match attr.meta_item_list() { + attr.check_name("ignore") && match attr.meta_item_list() { Some(ref cfgs) => { attr::test_cfg(cx.config.as_slice(), cfgs.iter().map(|x| *x)) } @@ -341,7 +341,8 @@ fn mk_test_module(cx: &TestCtxt) -> @ast::Item { // This attribute tells resolve to let us call unexported functions let resolve_unexported_str = InternedString::new("!resolve_unexported"); let resolve_unexported_attr = - attr::mk_attr_inner(attr::mk_word_item(resolve_unexported_str)); + attr::mk_attr_inner(attr::mk_attr_id(), + attr::mk_word_item(resolve_unexported_str)); let item = ast::Item { ident: token::str_to_ident("__test"), diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index f30e24a3151d7..d2b567395f020 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -176,7 +176,7 @@ pub fn get_static_methods_if_impl(cstore: &cstore::CStore, pub fn get_item_attrs(cstore: &cstore::CStore, def_id: ast::DefId, - f: |Vec<@ast::MetaItem> |) { + f: |Vec |) { let cdata = cstore.get_crate_data(def_id.krate); decoder::get_item_attrs(&*cdata, def_id.node, f) } diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index b3ef888c0b466..54243ea6f1f4c 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -953,20 +953,14 @@ pub fn get_tuple_struct_definition_if_ctor(cdata: Cmd, pub fn get_item_attrs(cdata: Cmd, orig_node_id: ast::NodeId, - f: |Vec<@ast::MetaItem> |) { + f: |Vec|) { // The attributes for a tuple struct are attached to the definition, not the ctor; // we assume that someone passing in a tuple struct ctor is actually wanting to // look at the definition let node_id = get_tuple_struct_definition_if_ctor(cdata, orig_node_id); let node_id = node_id.map(|x| x.node).unwrap_or(orig_node_id); let item = lookup_item(node_id, cdata.data()); - reader::tagged_docs(item, tag_attributes, |attributes| { - reader::tagged_docs(attributes, tag_attribute, |attribute| { - f(get_meta_items(attribute)); - true - }); - true - }); + f(get_attributes(item)); } fn struct_field_family_to_visibility(family: Family) -> ast::Visibility { @@ -1056,6 +1050,7 @@ fn get_attributes(md: ebml::Doc) -> Vec { attrs.push( codemap::Spanned { node: ast::Attribute_ { + id: attr::mk_attr_id(), style: ast::AttrOuter, value: meta_item, is_sugared_doc: false, diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index a0742669cc07e..2e3dc360ac291 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1436,7 +1436,7 @@ fn synthesize_crate_attrs(ecx: &EncodeContext, fn synthesize_crateid_attr(ecx: &EncodeContext) -> Attribute { assert!(!ecx.link_meta.crateid.name.is_empty()); - attr::mk_attr_inner( + attr::mk_attr_inner(attr::mk_attr_id(), attr::mk_name_value_item_str( InternedString::new("crate_id"), token::intern_and_get_ident(ecx.link_meta @@ -1447,7 +1447,7 @@ fn synthesize_crate_attrs(ecx: &EncodeContext, let mut attrs = Vec::new(); for attr in krate.attrs.iter() { - if !attr.name().equiv(&("crate_id")) { + if !attr.check_name("crate_id") { attrs.push(*attr); } } diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 8feacba6e006d..2b1e28548f99f 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -64,7 +64,7 @@ use collections::SmallIntMap; use syntax::abi; use syntax::ast_map; use syntax::ast_util::IdVisitingOperation; -use syntax::attr::{AttrMetaMethods, AttributeMethods}; +use syntax::attr::AttrMetaMethods; use syntax::attr; use syntax::codemap::Span; use syntax::parse::token::InternedString; @@ -90,6 +90,7 @@ pub enum Lint { UnusedUnsafe, UnsafeBlock, AttributeUsage, + UnusedAttribute, UnknownFeatures, UnknownCrateType, UnsignedNegate, @@ -288,6 +289,13 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[ default: Warn }), + ("unused_attribute", + LintSpec { + lint: UnusedAttribute, + desc: "detects attributes that were not used by the compiler", + default: Warn + }), + ("unused_variable", LintSpec { lint: UnusedVariable, @@ -619,7 +627,7 @@ pub fn each_lint(sess: &session::Session, let xs = [Allow, Warn, Deny, Forbid]; for &level in xs.iter() { let level_name = level_to_str(level); - for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) { + for attr in attrs.iter().filter(|m| m.check_name(level_name)) { let meta = attr.node.value; let metas = match meta.node { ast::MetaList(_, ref metas) => metas, @@ -1137,6 +1145,54 @@ fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) { } } +fn check_unused_attribute(cx: &Context, attrs: &[ast::Attribute]) { + static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [ + // FIXME: #14408 whitelist docs since rustdoc looks at them + "doc", + + // FIXME: #14406 these are processed in trans, which happens after the + // lint pass + "address_insignificant", + "cold", + "inline", + "link", + "link_name", + "link_section", + "no_builtins", + "no_mangle", + "no_split_stack", + "packed", + "static_assert", + "thread_local", + + // not used anywhere (!?) but apparently we want to keep them around + "comment", + "desc", + "license", + + // FIXME: #14407 these are only looked at on-demand so we can't + // guarantee they'll have already been checked + "deprecated", + "experimental", + "frozen", + "locked", + "must_use", + "stable", + "unstable", + ]; + for attr in attrs.iter() { + for &name in ATTRIBUTE_WHITELIST.iter() { + if attr.check_name(name) { + break; + } + } + + if !attr::is_used(attr) { + cx.span_lint(UnusedAttribute, attr.span, "unused attribute"); + } + } +} + fn check_heap_expr(cx: &Context, e: &ast::Expr) { let ty = ty::expr_ty(cx.tcx, e); check_heap_type(cx, e.span, ty); @@ -1637,9 +1693,7 @@ fn check_stability(cx: &Context, e: &ast::Expr) { let stability = if ast_util::is_local(id) { // this crate let s = cx.tcx.map.with_attrs(id.node, |attrs| { - attrs.map(|a| { - attr::find_stability(a.iter().map(|a| a.meta())) - }) + attrs.map(|a| attr::find_stability(a.as_slice())) }); match s { Some(s) => s, @@ -1655,9 +1709,9 @@ fn check_stability(cx: &Context, e: &ast::Expr) { let mut s = None; // run through all the attributes and take the first // stability one. - csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |meta_items| { + csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| { if s.is_none() { - s = attr::find_stability(meta_items.move_iter()) + s = attr::find_stability(attrs.as_slice()) } }); s @@ -1694,6 +1748,7 @@ impl<'a> Visitor<()> for Context<'a> { check_heap_item(cx, it); check_missing_doc_item(cx, it); check_attrs_usage(cx, it.attrs.as_slice()); + check_unused_attribute(cx, it.attrs.as_slice()); check_raw_ptr_deriving(cx, it); cx.visit_ids(|v| v.visit_item(it, ())); @@ -1900,6 +1955,7 @@ pub fn check_crate(tcx: &ty::ctxt, check_crate_attrs_usage(cx, krate.attrs.as_slice()); // since the root module isn't visited as an item (because it isn't an item), warn for it // here. + check_unused_attribute(cx, krate.attrs.as_slice()); check_missing_doc_attrs(cx, None, krate.attrs.as_slice(), diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 43f3442ec472e..da28c3008ddd0 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -227,9 +227,8 @@ fn get_extern_rust_fn(ccx: &CrateContext, fn_ty: ty::t, name: &str, did: ast::De let f = decl_rust_fn(ccx, fn_ty, name); - csearch::get_item_attrs(&ccx.sess().cstore, did, |meta_items| { - set_llvm_fn_attrs(meta_items.iter().map(|&x| attr::mk_attr_outer(x)) - .collect::>().as_slice(), f) + csearch::get_item_attrs(&ccx.sess().cstore, did, |attrs| { + set_llvm_fn_attrs(attrs.as_slice(), f) }); ccx.externs.borrow_mut().insert(name.to_strbuf(), f); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 64619b636a33a..9176b33331f1a 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3889,20 +3889,22 @@ pub fn lookup_trait_def(cx: &ctxt, did: ast::DefId) -> Rc { } } -/// Iterate over meta_items of a definition. +/// Iterate over attributes of a definition. // (This should really be an iterator, but that would require csearch and // decoder to use iterators instead of higher-order functions.) -pub fn each_attr(tcx: &ctxt, did: DefId, f: |@ast::MetaItem| -> bool) -> bool { +pub fn each_attr(tcx: &ctxt, did: DefId, f: |&ast::Attribute| -> bool) -> bool { if is_local(did) { let item = tcx.map.expect_item(did.node); - item.attrs.iter().advance(|attr| f(attr.node.value)) + item.attrs.iter().advance(|attr| f(attr)) } else { + info!("getting foreign attrs"); let mut cont = true; - csearch::get_item_attrs(&tcx.sess.cstore, did, |meta_items| { + csearch::get_item_attrs(&tcx.sess.cstore, did, |attrs| { if cont { - cont = meta_items.iter().advance(|ptrptr| f(*ptrptr)); + cont = attrs.iter().advance(|attr| f(attr)); } }); + info!("done"); cont } } @@ -3911,7 +3913,7 @@ pub fn each_attr(tcx: &ctxt, did: DefId, f: |@ast::MetaItem| -> bool) -> bool { pub fn has_attr(tcx: &ctxt, did: DefId, attr: &str) -> bool { let mut found = false; each_attr(tcx, did, |item| { - if item.name().equiv(&attr) { + if item.check_name(attr) { found = true; false } else { diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs index fb2a80333e82d..6f3c6e4cd6f39 100644 --- a/src/librustdoc/clean.rs +++ b/src/librustdoc/clean.rs @@ -314,9 +314,9 @@ impl Clean for ast::Attribute { } // This is a rough approximation that gets us what we want. -impl<'a> attr::AttrMetaMethods for &'a Attribute { +impl attr::AttrMetaMethods for Attribute { fn name(&self) -> InternedString { - match **self { + match *self { Word(ref n) | List(ref n, _) | NameValue(ref n, _) => { token::intern_and_get_ident(n.as_slice()) } @@ -324,7 +324,7 @@ impl<'a> attr::AttrMetaMethods for &'a Attribute { } fn value_str(&self) -> Option { - match **self { + match *self { NameValue(_, ref v) => { Some(token::intern_and_get_ident(v.as_slice())) } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8744553955a63..b09ac8f94af98 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1091,8 +1091,8 @@ impl<'a> fmt::Show for Item<'a> { shortty(self.item), self.item.name.get_ref().as_slice())); // Write stability attributes - match attr::find_stability(self.item.attrs.iter()) { - Some(ref stability) => { + match attr::find_stability_generic(self.item.attrs.iter()) { + Some((ref stability, _)) => { try!(write!(fmt, "{lvl}", lvl = stability.level.to_str(), diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d4c01746098ea..e77d1faf05d89 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1024,9 +1024,13 @@ pub enum AttrStyle { AttrInner, } +#[deriving(Clone, Eq, TotalEq, Encodable, Decodable, Hash)] +pub struct AttrId(pub uint); + // doc-comments are promoted to attributes that have is_sugared_doc = true #[deriving(Clone, Eq, TotalEq, Encodable, Decodable, Hash)] pub struct Attribute_ { + pub id: AttrId, pub style: AttrStyle, pub value: @MetaItem, pub is_sugared_doc: bool, diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 77c335b893610..527e851ae35ac 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -11,7 +11,7 @@ // Functions dealing with attributes and meta items use ast; -use ast::{Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList}; +use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList}; use codemap::{Span, Spanned, spanned, dummy_spanned}; use codemap::BytePos; use diagnostic::SpanHandler; @@ -21,11 +21,26 @@ use parse::token; use crateid::CrateId; use collections::HashSet; +use collections::bitv::BitvSet; + +local_data_key!(used_attrs: BitvSet) + +pub fn mark_used(attr: &Attribute) { + let mut used = used_attrs.replace(None).unwrap_or_else(|| BitvSet::new()); + let AttrId(id) = attr.node.id; + used.insert(id); + used_attrs.replace(Some(used)); +} + +pub fn is_used(attr: &Attribute) -> bool { + let AttrId(id) = attr.node.id; + used_attrs.get().map_or(false, |used| used.contains(&id)) +} pub trait AttrMetaMethods { - // This could be changed to `fn check_name(&self, name: InternedString) -> - // bool` which would facilitate a side table recording which - // attributes/meta items are used/unused. + fn check_name(&self, name: &str) -> bool { + name == self.name().get() + } /// Retrieve the name of the meta item, e.g. foo in #[foo], /// #[foo="bar"] and #[foo(bar)] @@ -47,6 +62,13 @@ pub trait AttrMetaMethods { } impl AttrMetaMethods for Attribute { + fn check_name(&self, name: &str) -> bool { + let matches = name == self.name().get(); + if matches { + mark_used(self); + } + matches + } fn name(&self) -> InternedString { self.meta().name() } fn value_str(&self) -> Option { self.meta().value_str() @@ -127,9 +149,9 @@ impl AttributeMethods for Attribute { token::intern_and_get_ident(strip_doc_comment_decoration( comment.get()).as_slice())); if self.node.style == ast::AttrOuter { - mk_attr_outer(meta) + mk_attr_outer(self.node.id, meta) } else { - mk_attr_inner(meta) + mk_attr_inner(self.node.id, meta) } } else { *self @@ -158,9 +180,18 @@ pub fn mk_word_item(name: InternedString) -> @MetaItem { @dummy_spanned(MetaWord(name)) } +local_data_key!(next_attr_id: uint) + +pub fn mk_attr_id() -> AttrId { + let id = next_attr_id.replace(None).unwrap_or(0); + next_attr_id.replace(Some(id + 1)); + AttrId(id) +} + /// Returns an inner attribute with the given value. -pub fn mk_attr_inner(item: @MetaItem) -> Attribute { +pub fn mk_attr_inner(id: AttrId, item: @MetaItem) -> Attribute { dummy_spanned(Attribute_ { + id: id, style: ast::AttrInner, value: item, is_sugared_doc: false, @@ -168,19 +199,22 @@ pub fn mk_attr_inner(item: @MetaItem) -> Attribute { } /// Returns an outer attribute with the given value. -pub fn mk_attr_outer(item: @MetaItem) -> Attribute { +pub fn mk_attr_outer(id: AttrId, item: @MetaItem) -> Attribute { dummy_spanned(Attribute_ { + id: id, style: ast::AttrOuter, value: item, is_sugared_doc: false, }) } -pub fn mk_sugared_doc_attr(text: InternedString, lo: BytePos, hi: BytePos) +pub fn mk_sugared_doc_attr(id: AttrId, text: InternedString, lo: BytePos, + hi: BytePos) -> Attribute { let style = doc_comment_style(text.get()); let lit = spanned(lo, hi, ast::LitStr(text, ast::CookedStr)); let attr = Attribute_ { + id: id, style: style, value: @spanned(lo, hi, MetaNameValue(InternedString::new("doc"), lit)), @@ -206,14 +240,14 @@ pub fn contains_name(metas: &[AM], name: &str) -> bool { debug!("attr::contains_name (name={})", name); metas.iter().any(|item| { debug!(" testing: {}", item.name()); - item.name().equiv(&name) + item.check_name(name) }) } pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option { attrs.iter() - .find(|at| at.name().equiv(&name)) + .find(|at| at.check_name(name)) .and_then(|at| at.value_str()) } @@ -221,7 +255,7 @@ pub fn last_meta_item_value_str_by_name(items: &[@MetaItem], name: &str) -> Option { items.iter() .rev() - .find(|mi| mi.name().equiv(&name)) + .find(|mi| mi.check_name(name)) .and_then(|i| i.value_str()) } @@ -257,7 +291,7 @@ pub fn sort_meta_items(items: &[@MetaItem]) -> Vec<@MetaItem> { */ pub fn find_linkage_metas(attrs: &[Attribute]) -> Vec<@MetaItem> { let mut result = Vec::new(); - for attr in attrs.iter().filter(|at| at.name().equiv(&("link"))) { + for attr in attrs.iter().filter(|at| at.check_name("link")) { match attr.meta().node { MetaList(_, ref items) => result.push_all(items.as_slice()), _ => () @@ -286,17 +320,21 @@ pub fn find_inline_attr(attrs: &[Attribute]) -> InlineAttr { // FIXME (#2809)---validate the usage of #[inline] and #[inline] attrs.iter().fold(InlineNone, |ia,attr| { match attr.node.value.node { - MetaWord(ref n) if n.equiv(&("inline")) => InlineHint, - MetaList(ref n, ref items) if n.equiv(&("inline")) => { - if contains_name(items.as_slice(), "always") { - InlineAlways - } else if contains_name(items.as_slice(), "never") { - InlineNever - } else { + MetaWord(ref n) if n.equiv(&("inline")) => { + mark_used(attr); InlineHint } - } - _ => ia + MetaList(ref n, ref items) if n.equiv(&("inline")) => { + mark_used(attr); + if contains_name(items.as_slice(), "always") { + InlineAlways + } else if contains_name(items.as_slice(), "never") { + InlineNever + } else { + InlineHint + } + } + _ => ia } }) } @@ -314,9 +352,9 @@ pub fn test_cfg> // this would be much nicer as a chain of iterator adaptors, but // this doesn't work. - let some_cfg_matches = metas.any(|mi| { + let some_cfg_matches = metas.fold(false, |matches, mi| { debug!("testing name: {}", mi.name()); - if mi.name().equiv(&("cfg")) { // it is a #[cfg()] attribute + let this_matches = if mi.check_name("cfg") { // it is a #[cfg()] attribute debug!("is cfg"); no_cfgs = false; // only #[cfg(...)] ones are understood. @@ -344,7 +382,8 @@ pub fn test_cfg> } } else { false - } + }; + matches || this_matches }); debug!("test_cfg (no_cfgs={}, some_cfg_matches={})", no_cfgs, some_cfg_matches); no_cfgs || some_cfg_matches @@ -367,11 +406,13 @@ pub enum StabilityLevel { Locked } -/// Find the first stability attribute. `None` if none exists. -pub fn find_stability>(mut metas: It) - -> Option { - for m in metas { - let level = match m.name().get() { +pub fn find_stability_generic<'a, + AM: AttrMetaMethods, + I: Iterator<&'a AM>> + (mut attrs: I) + -> Option<(Stability, &'a AM)> { + for attr in attrs { + let level = match attr.name().get() { "deprecated" => Deprecated, "experimental" => Experimental, "unstable" => Unstable, @@ -381,14 +422,22 @@ pub fn find_stability>(mut metas: It) _ => continue // not a stability level }; - return Some(Stability { + return Some((Stability { level: level, - text: m.value_str() - }); + text: attr.value_str() + }, attr)); } None } +/// Find the first stability attribute. `None` if none exists. +pub fn find_stability(attrs: &[Attribute]) -> Option { + find_stability_generic(attrs.iter()).map(|(s, attr)| { + mark_used(attr); + s + }) +} + pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[@MetaItem]) { let mut set = HashSet::new(); for meta in metas.iter() { @@ -415,11 +464,12 @@ pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[@MetaItem]) { * present (before fields, if any) with that type; reprensentation * optimizations which would remove it will not be done. */ -pub fn find_repr_attr(diagnostic: &SpanHandler, attr: @ast::MetaItem, acc: ReprAttr) +pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr) -> ReprAttr { let mut acc = acc; - match attr.node { + match attr.node.value.node { ast::MetaList(ref s, ref items) if s.equiv(&("repr")) => { + mark_used(attr); for item in items.iter() { match item.node { ast::MetaWord(ref word) => { diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 3c7415ae0e9b9..449feb3afbf96 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -12,6 +12,7 @@ use abi; use ast::{P, Ident}; use ast; use ast_util; +use attr; use codemap::{Span, respan, Spanned, DUMMY_SP}; use ext::base::ExtCtxt; use ext::quote::rt::*; @@ -927,6 +928,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn attribute(&self, sp: Span, mi: @ast::MetaItem) -> ast::Attribute { respan(sp, ast::Attribute_ { + id: attr::mk_attr_id(), style: ast::AttrOuter, value: mi, is_sugared_doc: false, diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 0875daddc0f9f..5f18193437e9a 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -182,6 +182,7 @@ use std::cell::RefCell; use ast; use ast::{P, EnumDef, Expr, Ident, Generics, StructDef}; use ast_util; +use attr; use attr::AttrMetaMethods; use ext::base::ExtCtxt; use ext::build::AstBuilder; @@ -430,6 +431,8 @@ impl<'a> TraitDef<'a> { self.span, cx.meta_word(self.span, InternedString::new("automatically_derived"))); + // Just mark it now since we know that it'll end up used downstream + attr::mark_used(&attr); let opt_trait_ref = Some(trait_ref); let ident = ast_util::impl_pretty_name(&opt_trait_ref, self_type); cx.item( diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 989d0a463c387..658e4bafbe25a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -265,6 +265,8 @@ pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander) match fld.extsbox.find(&intern(mname.get())) { Some(&ItemDecorator(dec_fn)) => { + attr::mark_used(attr); + fld.cx.bt_push(ExpnInfo { call_site: attr.span, callee: NameAndSpan { @@ -336,6 +338,7 @@ fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander) match fld.extsbox.find(&intern(mname.get())) { Some(&ItemModifier(dec_fn)) => { + attr::mark_used(attr); fld.cx.bt_push(ExpnInfo { call_site: attr.span, callee: NameAndSpan { @@ -474,7 +477,7 @@ pub fn expand_view_item(vi: &ast::ViewItem, match vi.node { ast::ViewItemExternCrate(..) => { let should_load = vi.attrs.iter().any(|attr| { - attr.name().get() == "phase" && + attr.check_name("phase") && attr.meta_item_list().map_or(false, |phases| { attr::contains_name(phases, "syntax") }) @@ -972,6 +975,7 @@ mod test { use super::*; use ast; use ast::{Attribute_, AttrOuter, MetaWord}; + use attr; use codemap; use codemap::Spanned; use ext::base::{CrateLoader, MacroCrate}; @@ -1103,6 +1107,7 @@ mod test { Spanned { span:codemap::DUMMY_SP, node: Attribute_ { + id: attr::mk_attr_id(), style: AttrOuter, value: @Spanned { node: MetaWord(token::intern_and_get_ident(s)), diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9813e12de01ca..ae5cf550bb9bc 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -360,6 +360,7 @@ fn fold_attribute_(at: Attribute, fld: &mut T) -> Attribute { Spanned { span: fld.new_span(at.span), node: ast::Attribute_ { + id: at.node.id, style: at.node.style, value: fold_meta_item_(at.node.value, fld), is_sugared_doc: at.node.is_sugared_doc diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 89d1b8f9342dd..9dcc0877fa489 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use attr; use ast; use codemap::{spanned, Spanned, mk_sp, Span}; use parse::common::*; //resolve bug? @@ -39,6 +40,7 @@ impl<'a> ParserAttr for Parser<'a> { } token::DOC_COMMENT(s) => { let attr = ::attr::mk_sugared_doc_attr( + attr::mk_attr_id(), self.id_to_interned_str(s), self.span.lo, self.span.hi @@ -101,6 +103,7 @@ impl<'a> ParserAttr for Parser<'a> { return Spanned { span: span, node: ast::Attribute_ { + id: attr::mk_attr_id(), style: style, value: value, is_sugared_doc: false @@ -132,7 +135,10 @@ impl<'a> ParserAttr for Parser<'a> { // we need to get the position of this token before we bump. let Span { lo, hi, .. } = self.span; self.bump(); - ::attr::mk_sugared_doc_attr(self.id_to_interned_str(s), lo, hi) + attr::mk_sugared_doc_attr(attr::mk_attr_id(), + self.id_to_interned_str(s), + lo, + hi) } _ => { break; diff --git a/src/test/compile-fail/lint-misplaced-attr.rs b/src/test/compile-fail/lint-misplaced-attr.rs index d422dfc513d26..f7db5c97aab11 100644 --- a/src/test/compile-fail/lint-misplaced-attr.rs +++ b/src/test/compile-fail/lint-misplaced-attr.rs @@ -12,9 +12,12 @@ // injected intrinsics by the compiler. #![deny(attribute_usage)] +#![deny(unused_attribute)] mod a { #![crate_type = "bin"] //~ ERROR: crate-level attribute + //~^ ERROR: unused attribute } #[crate_type = "bin"] fn main() {} //~ ERROR: crate-level attribute + //~^ ERROR: unused attribute diff --git a/src/test/compile-fail/lint-obsolete-attr.rs b/src/test/compile-fail/lint-obsolete-attr.rs index 8b70953146da7..32058737ed302 100644 --- a/src/test/compile-fail/lint-obsolete-attr.rs +++ b/src/test/compile-fail/lint-obsolete-attr.rs @@ -12,10 +12,13 @@ // injected intrinsics by the compiler. #![deny(attribute_usage)] +#![deny(unused_attribute)] #![allow(dead_code)] #[abi="stdcall"] extern {} //~ ERROR: obsolete attribute + //~^ ERROR: unused attribute #[fixed_stack_segment] fn f() {} //~ ERROR: obsolete attribute + //~^ ERROR: unused attribute fn main() {} diff --git a/src/test/compile-fail/lint-unknown-attr.rs b/src/test/compile-fail/lint-unknown-attr.rs index dbbf91f725dc0..32c0722d1ac26 100644 --- a/src/test/compile-fail/lint-unknown-attr.rs +++ b/src/test/compile-fail/lint-unknown-attr.rs @@ -12,9 +12,13 @@ // injected intrinsics by the compiler. #![deny(attribute_usage)] +#![deny(unused_attribute)] #![mutable_doc] //~ ERROR: unknown crate attribute + //~^ ERROR: unused attribute #[dance] mod a {} //~ ERROR: unknown attribute + //~^ ERROR: unused attribute #[dance] fn main() {} //~ ERROR: unknown attribute + //~^ ERROR: unused attribute diff --git a/src/test/run-pass/attr-mix-new.rs b/src/test/run-pass/attr-mix-new.rs index af615912f2823..55ca75b4b7131 100644 --- a/src/test/run-pass/attr-mix-new.rs +++ b/src/test/run-pass/attr-mix-new.rs @@ -7,6 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(unused_attribute)] #[foo(bar)] mod foo { diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index 7f2c9e14af10d..cf6126a37fa85 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -9,8 +9,6 @@ // except according to those terms. // ignore-win32 FIXME #13259 -#![no_uv] - extern crate native; use std::os; diff --git a/src/test/run-pass/class-attributes-1.rs b/src/test/run-pass/class-attributes-1.rs index 55cf41d73d591..186fec45c4bb3 100644 --- a/src/test/run-pass/class-attributes-1.rs +++ b/src/test/run-pass/class-attributes-1.rs @@ -9,6 +9,7 @@ // except according to those terms. // pp-exact - Make sure we actually print the attributes +#![allow(unused_attribute)] struct cat { name: StrBuf, diff --git a/src/test/run-pass/class-attributes-2.rs b/src/test/run-pass/class-attributes-2.rs index b1ed70278ef32..6da8123c8c49c 100644 --- a/src/test/run-pass/class-attributes-2.rs +++ b/src/test/run-pass/class-attributes-2.rs @@ -7,6 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(unused_attribute)] struct cat { name: StrBuf, diff --git a/src/test/run-pass/issue-3250.rs b/src/test/run-pass/issue-3250.rs deleted file mode 100644 index 255f6b1635c1f..0000000000000 --- a/src/test/run-pass/issue-3250.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[auto_serialize] - -type t = (uint, uint); - -pub fn main() { } diff --git a/src/test/run-pass/item-attributes.rs b/src/test/run-pass/item-attributes.rs index ef825826ea495..bf94af601fea0 100644 --- a/src/test/run-pass/item-attributes.rs +++ b/src/test/run-pass/item-attributes.rs @@ -11,6 +11,7 @@ // These are attributes of the implicit crate. Really this just needs to parse // for completeness since .rs files linked from .rc files support this // notation to specify their module's attributes +#![allow(unused_attribute)] #![attr1 = "val"] #![attr2 = "val"] #![attr3] diff --git a/src/test/run-pass/method-attributes.rs b/src/test/run-pass/method-attributes.rs index 87c43da9ebcf4..c015244d520ce 100644 --- a/src/test/run-pass/method-attributes.rs +++ b/src/test/run-pass/method-attributes.rs @@ -9,6 +9,7 @@ // except according to those terms. // pp-exact - Make sure we print all the attributes +#![allow(unused_attribute)] #[frobable] trait frobable {