diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 21a7fc8d1504..4f9957ee9802 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 e380af7a4f01..fe636f7b686a 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -115,6 +115,8 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> { 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 @@ -141,6 +143,8 @@ impl<'a> fold::Folder for PreludeInjector<'a> { 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()) { @@ -154,6 +158,8 @@ impl<'a> fold::Folder for PreludeInjector<'a> { 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/middle/lint.rs b/src/librustc/middle/lint.rs index 8feacba6e006..6b5b98376b64 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -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: Allow + }), + ("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,15 @@ fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) { } } +fn check_unused_attribute(cx: &Context, attrs: &[ast::Attribute]) { + for attr in attrs.iter() { + if !attr::is_used(attr) { + cx.span_lint(UnusedAttribute, attr.span, + format!("unused attribute {}", attr.name()).as_slice()); + } + } +} + fn check_heap_expr(cx: &Context, e: &ast::Expr) { let ty = ty::expr_ty(cx.tcx, e); check_heap_type(cx, e.span, ty); @@ -1694,6 +1711,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 +1918,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/libsyntax/attr.rs b/src/libsyntax/attr.rs index 83ac2c08efb5..ac5792febbbb 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -35,9 +35,9 @@ pub fn is_used(attr: &Attribute) -> bool { } 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)] @@ -59,6 +59,14 @@ pub trait AttrMetaMethods { } impl AttrMetaMethods for Attribute { + fn check_name(&self, name: &str) -> bool { + if name == self.name().get() { + mark_used(self); + true + } else { + false + } + } fn name(&self) -> InternedString { self.meta().name() } fn value_str(&self) -> Option { self.meta().value_str() diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 83118df5e65c..64b7e1c28c17 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -474,7 +474,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") })