diff options
| author | Aaron Hill <aa1ronham@gmail.com> | 2021-07-29 12:00:41 -0500 |
|---|---|---|
| committer | Aaron Hill <aa1ronham@gmail.com> | 2021-08-21 13:27:27 -0500 |
| commit | af46699f8104ba5257d0da56d3d817bf8fc751cf (patch) | |
| tree | 44efd77918662bdbd638ae9f3d26479e52318cfd /compiler/rustc_passes/src | |
| parent | b6e334d87349502766be70d649e6fe4a73573482 (diff) | |
| download | rust-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.rs | 161 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/diagnostic_items.rs | 9 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/lang_items.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/layout_test.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/lib_features.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/stability.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/weak_lang_items.rs | 3 |
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); |
