about summary refs log tree commit diff
path: root/compiler/rustc_passes/src
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2021-07-29 12:00:41 -0500
committerAaron Hill <aa1ronham@gmail.com>2021-08-21 13:27:27 -0500
commitaf46699f8104ba5257d0da56d3d817bf8fc751cf (patch)
tree44efd77918662bdbd638ae9f3d26479e52318cfd /compiler/rustc_passes/src
parentb6e334d87349502766be70d649e6fe4a73573482 (diff)
downloadrust-af46699f8104ba5257d0da56d3d817bf8fc751cf.tar.gz
rust-af46699f8104ba5257d0da56d3d817bf8fc751cf.zip
Remove `Session.used_attrs` and move logic to `CheckAttrVisitor`
Instead of updating global state to mark attributes as used,
we now explicitly emit a warning when an attribute is used in
an unsupported position. As a side effect, we are to emit more
detailed warning messages (instead of just a generic "unused" message).

`Session.check_name` is removed, since its only purpose was to mark
the attribute as used. All of the callers are modified to use
`Attribute.has_name`

Additionally, `AttributeType::AssumedUsed` is removed - an 'assumed
used' attribute is implemented by simply not performing any checks
in `CheckAttrVisitor` for a particular attribute.

We no longer emit unused attribute warnings for the `#[rustc_dummy]`
attribute - it's an internal attribute used for tests, so it doesn't
mark sense to treat it as 'unused'.

With this commit, a large source of global untracked state is removed.
Diffstat (limited to 'compiler/rustc_passes/src')
-rw-r--r--compiler/rustc_passes/src/check_attr.rs161
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs9
-rw-r--r--compiler/rustc_passes/src/lang_items.rs3
-rw-r--r--compiler/rustc_passes/src/layout_test.rs2
-rw-r--r--compiler/rustc_passes/src/lib.rs1
-rw-r--r--compiler/rustc_passes/src/lib_features.rs4
-rw-r--r--compiler/rustc_passes/src/stability.rs3
-rw-r--r--compiler/rustc_passes/src/weak_lang_items.rs3
8 files changed, 169 insertions, 17 deletions
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 3fea75954b9..3009c2c3f2f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -8,8 +8,10 @@ use rustc_middle::hir::map::Map;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 
-use rustc_ast::{AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
+use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
+use rustc_data_structures::stable_set::FxHashSet;
 use rustc_errors::{pluralize, struct_span_err, Applicability};
+use rustc_feature::{AttributeType, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
@@ -66,9 +68,10 @@ impl CheckAttrVisitor<'tcx> {
     ) {
         let mut is_valid = true;
         let mut specified_inline = None;
+        let mut seen = FxHashSet::default();
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
-            is_valid &= match attr.name_or_empty() {
+            let attr_is_valid = match attr.name_or_empty() {
                 sym::inline => self.check_inline(hir_id, attr, span, target),
                 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
                 sym::marker => self.check_marker(hir_id, attr, span, target),
@@ -101,14 +104,66 @@ impl CheckAttrVisitor<'tcx> {
                 sym::default_method_body_is_const => {
                     self.check_default_method_body_is_const(attr, span, target)
                 }
+                sym::rustc_const_unstable
+                | sym::rustc_const_stable
+                | sym::unstable
+                | sym::stable
+                | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
                 _ => true,
             };
+            is_valid &= attr_is_valid;
+
             // lint-only checks
             match attr.name_or_empty() {
                 sym::cold => self.check_cold(hir_id, attr, span, target),
                 sym::link_name => self.check_link_name(hir_id, attr, span, target),
                 sym::link_section => self.check_link_section(hir_id, attr, span, target),
                 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
+                sym::deprecated | sym::rustc_deprecated => {
+                    self.check_deprecated(hir_id, attr, span, target)
+                }
+                sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
+                sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
+                sym::cfg_attr => self.check_cfg_attr(hir_id, attr),
+                sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
+                sym::macro_export => self.check_macro_export(hir_id, attr, target),
+                sym::ignore | sym::should_panic | sym::proc_macro_derive => {
+                    self.check_generic_attr(hir_id, attr, target, &[Target::Fn])
+                }
+                _ => {}
+            }
+
+            if hir_id != CRATE_HIR_ID {
+                if let Some((_, AttributeType::CrateLevel, ..)) =
+                    attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
+                {
+                    self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                        let msg = match attr.style {
+                            ast::AttrStyle::Outer => {
+                                "crate-level attribute should be an inner attribute: add an exclamation \
+                                 mark: `#![foo]`"
+                            }
+                            ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
+                        };
+                        lint.build(msg).emit()
+                    });
+                }
+            }
+
+            // Duplicate attributes
+            match attr.name_or_empty() {
+                name @ sym::macro_use => {
+                    let args = attr.meta_item_list().unwrap_or_else(Vec::new);
+                    let args: Vec<_> = args.iter().map(|arg| arg.name_or_empty()).collect();
+                    if !seen.insert((name, args)) {
+                        self.tcx.struct_span_lint_hir(
+                            UNUSED_ATTRIBUTES,
+                            hir_id,
+                            attr.span,
+                            |lint| lint.build("unused attribute").emit(),
+                        );
+                    }
+                }
                 _ => {}
             }
         }
@@ -211,6 +266,38 @@ impl CheckAttrVisitor<'tcx> {
         }
     }
 
+    fn check_generic_attr(
+        &self,
+        hir_id: HirId,
+        attr: &Attribute,
+        target: Target,
+        allowed_targets: &[Target],
+    ) {
+        if !allowed_targets.iter().any(|t| t == &target) {
+            let name = attr.name_or_empty();
+            let mut i = allowed_targets.iter();
+            // Pluralize
+            let b = i.next().map_or_else(String::new, |t| t.to_string() + "s");
+            let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| {
+                if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 {
+                    b.push_str(", and ");
+                } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 {
+                    b.push_str(" and ");
+                } else {
+                    b.push_str(", ");
+                }
+                // Pluralize
+                b.push_str(&(allowed_target.to_string() + "s"));
+                b
+            });
+            //let supported_names = allowed_targets.iter().fold(String::new(), |msg, t| msg + ", " + &t.to_string());
+            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                lint.build(&format!("`#[{name}]` only has an effect on {}", supported_names))
+                    .emit();
+            });
+        }
+    }
+
     /// Checks if `#[naked]` is applied to a function definition.
     fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
         match target {
@@ -1555,6 +1642,72 @@ impl CheckAttrVisitor<'tcx> {
             }
         }
     }
+
+    fn check_stability_promotable(&self, attr: &Attribute, _span: &Span, target: Target) -> bool {
+        match target {
+            Target::Expression => {
+                self.tcx
+                    .sess
+                    .struct_span_err(attr.span, "attribute cannot be applied to an expression")
+                    .emit();
+                false
+            }
+            _ => true,
+        }
+    }
+
+    fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: &Span, target: Target) {
+        match target {
+            Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute is ignored here").emit();
+                });
+            }
+            _ => {}
+        }
+    }
+
+    fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+        let name = attr.name_or_empty();
+        match target {
+            Target::ExternCrate | Target::Mod => {}
+            _ => {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build(&format!(
+                        "`#[{name}]` only has an effect on `extern crate` and modules"
+                    ))
+                    .emit();
+                });
+            }
+        }
+    }
+
+    fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+        if target != Target::MacroDef {
+            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                lint.build(&format!("`#[macro_export]` only has an effect on macro definitions"))
+                    .emit();
+            });
+        }
+    }
+
+    fn check_cfg_attr(&self, hir_id: HirId, attr: &Attribute) {
+        if let Some((_, attrs)) = rustc_parse::parse_cfg_attr(&attr, &self.tcx.sess.parse_sess) {
+            if attrs.is_empty() {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("`#[cfg_attr]` does not expand to any attributes").emit();
+                });
+            }
+        }
+    }
+
+    fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+        if target != Target::Fn {
+            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                lint.build("`#[plugin_registrar]` only has an effect on functions").emit();
+            });
+        }
+    }
 }
 
 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
@@ -1675,7 +1828,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
 
     for attr in attrs {
         for attr_to_check in ATTRS_TO_CHECK {
-            if tcx.sess.check_name(attr, *attr_to_check) {
+            if attr.has_name(*attr_to_check) {
                 tcx.sess
                     .struct_span_err(
                         attr.span,
@@ -1692,7 +1845,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
 
 fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
     for attr in attrs {
-        if tcx.sess.check_name(attr, sym::inline) {
+        if attr.has_name(sym::inline) {
             struct_span_err!(
                 tcx.sess,
                 attr.span,
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index 3cf1d0cdd94..e43abda7133 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -15,7 +15,6 @@ use rustc_hir as hir;
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::Session;
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use rustc_span::symbol::{sym, Symbol};
 
@@ -51,7 +50,7 @@ impl<'tcx> DiagnosticItemCollector<'tcx> {
     fn observe_item(&mut self, def_id: LocalDefId) {
         let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
         let attrs = self.tcx.hir().attrs(hir_id);
-        if let Some(name) = extract(&self.tcx.sess, attrs) {
+        if let Some(name) = extract(attrs) {
             // insert into our table
             collect_item(self.tcx, &mut self.items, name, def_id.to_def_id());
         }
@@ -91,10 +90,10 @@ fn collect_item(
     }
 }
 
-/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
-fn extract(sess: &Session, attrs: &[ast::Attribute]) -> Option<Symbol> {
+/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.p
+fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
     attrs.iter().find_map(|attr| {
-        if sess.check_name(attr, sym::rustc_diagnostic_item) { attr.value_str() } else { None }
+        if attr.has_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None }
     })
 }
 
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 3a88d1932a8..62f9bcfdc47 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -13,6 +13,7 @@ use crate::weak_lang_items;
 use rustc_middle::middle::cstore::ExternCrate;
 use rustc_middle::ty::TyCtxt;
 
+use rustc_ast::Attribute;
 use rustc_errors::{pluralize, struct_span_err};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -57,7 +58,7 @@ impl LanguageItemCollector<'tcx> {
 
     fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) {
         let attrs = self.tcx.hir().attrs(hir_id);
-        let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+        let check_name = |attr: &Attribute, sym| attr.has_name(sym);
         if let Some((value, span)) = extract(check_name, &attrs) {
             match ITEM_REFS.get(&value).cloned() {
                 // Known lang item with attribute on correct target.
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 18c1d647060..43a3a3062e7 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -27,7 +27,7 @@ impl ItemLikeVisitor<'tcx> for LayoutTest<'tcx> {
             | ItemKind::Struct(..)
             | ItemKind::Union(..) => {
                 for attr in self.tcx.get_attrs(item.def_id.to_def_id()).iter() {
-                    if self.tcx.sess.check_name(attr, sym::rustc_layout) {
+                    if attr.has_name(sym::rustc_layout) {
                         self.dump_layout_of(item.def_id, item, attr);
                     }
                 }
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index cadb8d23580..f583a5d58d5 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -7,6 +7,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(crate_visibility_modifier)]
 #![feature(in_band_lifetimes)]
+#![feature(format_args_capture)]
 #![feature(iter_zip)]
 #![feature(nll)]
 #![feature(min_specialization)]
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 363a6417f99..605b52f1891 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -33,9 +33,7 @@ impl LibFeatureCollector<'tcx> {
 
         // Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
         // `#[rustc_const_unstable (..)]`).
-        if let Some(stab_attr) =
-            stab_attrs.iter().find(|stab_attr| self.tcx.sess.check_name(attr, **stab_attr))
-        {
+        if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) {
             let meta_item = attr.meta();
             if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta_item {
                 let mut feature = None;
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 484040de0fb..b64dcb0bbf0 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -148,7 +148,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         }
 
         if self.tcx.features().staged_api {
-            if let Some(a) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) {
+            if let Some(a) = attrs.iter().find(|a| a.has_name(sym::deprecated)) {
                 self.tcx
                     .sess
                     .struct_span_err(a.span, "`#[deprecated]` cannot be used in staged API")
@@ -350,7 +350,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         for attr in attrs {
             let name = attr.name_or_empty();
             if unstable_attrs.contains(&name) {
-                self.tcx.sess.mark_attr_used(attr);
                 struct_span_err!(
                     self.tcx.sess,
                     attr.span,
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index 629513c7cfe..3a662a9bab2 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -1,5 +1,6 @@
 //! Validity checking for weak lang items
 
+use rustc_ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
@@ -96,7 +97,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
     }
 
     fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) {
-        let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+        let check_name = |attr: &Attribute, sym| attr.has_name(sym);
         let attrs = self.tcx.hir().attrs(i.hir_id());
         if let Some((lang_item, _)) = lang_items::extract(check_name, attrs) {
             self.register(lang_item, i.span);