about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/rust.md1
-rw-r--r--src/doc/rustdoc.md1
-rw-r--r--src/doc/tutorial.md5
-rw-r--r--src/librustc/back/svh.rs7
-rw-r--r--src/librustc/driver/driver.rs78
-rw-r--r--src/librustc/front/feature_gate.rs2
-rw-r--r--src/librustc/front/std_inject.rs17
-rw-r--r--src/librustc/front/test.rs5
-rw-r--r--src/librustc/metadata/csearch.rs2
-rw-r--r--src/librustc/metadata/decoder.rs11
-rw-r--r--src/librustc/metadata/encoder.rs4
-rw-r--r--src/librustc/middle/lint.rs70
-rw-r--r--src/librustc/middle/trans/base.rs5
-rw-r--r--src/librustc/middle/ty.rs14
-rw-r--r--src/librustdoc/clean.rs6
-rw-r--r--src/librustdoc/html/render.rs4
-rw-r--r--src/libsyntax/ast.rs4
-rw-r--r--src/libsyntax/attr.rs120
-rw-r--r--src/libsyntax/ext/build.rs2
-rw-r--r--src/libsyntax/ext/deriving/generic/mod.rs3
-rw-r--r--src/libsyntax/ext/expand.rs7
-rw-r--r--src/libsyntax/fold.rs1
-rw-r--r--src/libsyntax/parse/attr.rs8
-rw-r--r--src/test/compile-fail/lint-misplaced-attr.rs3
-rw-r--r--src/test/compile-fail/lint-obsolete-attr.rs3
-rw-r--r--src/test/compile-fail/lint-unknown-attr.rs4
-rw-r--r--src/test/run-pass/attr-mix-new.rs1
-rw-r--r--src/test/run-pass/backtrace.rs2
-rw-r--r--src/test/run-pass/class-attributes-1.rs1
-rw-r--r--src/test/run-pass/class-attributes-2.rs1
-rw-r--r--src/test/run-pass/issue-3250.rs15
-rw-r--r--src/test/run-pass/item-attributes.rs1
-rw-r--r--src/test/run-pass/method-attributes.rs1
33 files changed, 277 insertions, 132 deletions
diff --git a/src/doc/rust.md b/src/doc/rust.md
index d686a02b2fe..b8fa4075e7a 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 d6cb782bd83..af9cff7be67 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 da78869d9fa..d85734508bc 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 489722aa13f..9ad653498ba 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 4e682e8e34b..ee9b10a8059 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -716,6 +716,45 @@ fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
 
 pub fn collect_crate_types(session: &Session,
                            attrs: &[ast::Attribute]) -> Vec<config::CrateType> {
+    // Unconditionally collect crate types from attributes to make them used
+    let attr_types: Vec<config::CrateType> = 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 21a7fc8d150..4f9957ee980 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 efaeeaa5575..fe636f7b686 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 e7a5aeb9ef5..67944423808 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 f30e24a3151..d2b567395f0 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<ast::Attribute> |) {
     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 b3ef888c0b4..54243ea6f1f 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<ast::Attribute>|) {
     // 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<ast::Attribute> {
             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 a0742669cc0..2e3dc360ac2 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 8feacba6e00..2b1e28548f9 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 43f3442ec47..da28c3008dd 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::<Vec<_>>().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 64619b636a3..9176b33331f 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<ty::TraitDef> {
     }
 }
 
-/// 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 fb2a80333e8..6f3c6e4cd6f 100644
--- a/src/librustdoc/clean.rs
+++ b/src/librustdoc/clean.rs
@@ -314,9 +314,9 @@ impl Clean<Attribute> 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<InternedString> {
-        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 8744553955a..b09ac8f94af 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,
                        "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
                        lvl = stability.level.to_str(),
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index d4c01746098..e77d1faf05d 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 77c335b8936..527e851ae35 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<InternedString> {
         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<AM: AttrMetaMethods>(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<InternedString> {
     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<InternedString> {
     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<AM: AttrMetaMethods, It: Iterator<AM>>
 
     // 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<AM: AttrMetaMethods, It: Iterator<AM>>
             }
         } 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<AM: AttrMetaMethods, It: Iterator<AM>>(mut metas: It)
-                      -> Option<Stability> {
-    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<AM: AttrMetaMethods, It: Iterator<AM>>(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<Stability> {
+    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 3c7415ae0e9..449feb3afbf 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 0875daddc0f..5f18193437e 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 989d0a463c3..658e4bafbe2 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 9813e12de01..ae5cf550bb9 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -360,6 +360,7 @@ fn fold_attribute_<T: Folder>(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 89d1b8f9342..9dcc0877fa4 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 d422dfc513d..f7db5c97aab 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 8b70953146d..32058737ed3 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 dbbf91f725d..32c0722d1ac 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 af615912f28..55ca75b4b71 100644
--- a/src/test/run-pass/attr-mix-new.rs
+++ b/src/test/run-pass/attr-mix-new.rs
@@ -7,6 +7,7 @@
 // <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.
+#![allow(unused_attribute)]
 
 #[foo(bar)]
 mod foo {
diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs
index 7f2c9e14af1..cf6126a37fa 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 55cf41d73d5..186fec45c4b 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 b1ed70278ef..6da8123c8c4 100644
--- a/src/test/run-pass/class-attributes-2.rs
+++ b/src/test/run-pass/class-attributes-2.rs
@@ -7,6 +7,7 @@
 // <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.
+#![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 255f6b1635c..00000000000
--- 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 <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.
-
-#[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 ef825826ea4..bf94af601fe 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 87c43da9ebc..c015244d520 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 {