From 966d96c9d2f16fd66eefda2eac25126a353bbb3a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 14 Sep 2019 19:36:41 +0300 Subject: [PATCH 1/3] feature_gate: Remove dead code from attribute checking Same checks are performed during name resolution, and all attributes go through name resolution now --- src/librustc_interface/passes.rs | 11 ++----- src/libsyntax/feature_gate/check.rs | 50 +++++------------------------ 2 files changed, 10 insertions(+), 51 deletions(-) diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 3cfae1686dfdf..e8e8da6733471 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -41,7 +41,6 @@ use syntax::mut_visit::MutVisitor; use syntax::parse::{self, PResult}; use syntax::util::node_count::NodeCounter; use syntax::symbol::Symbol; -use syntax::feature_gate::AttributeType; use syntax_pos::FileName; use syntax_ext; @@ -219,7 +218,6 @@ impl BoxedResolver { pub struct PluginInfo { syntax_exts: Vec, - attributes: Vec<(Symbol, AttributeType)>, } pub fn register_plugins<'a>( @@ -312,12 +310,9 @@ pub fn register_plugins<'a>( } *sess.plugin_llvm_passes.borrow_mut() = llvm_passes; - *sess.plugin_attributes.borrow_mut() = attributes.clone(); + *sess.plugin_attributes.borrow_mut() = attributes; - Ok((krate, PluginInfo { - syntax_exts, - attributes, - })) + Ok((krate, PluginInfo { syntax_exts })) } fn configure_and_expand_inner<'a>( @@ -329,7 +324,6 @@ fn configure_and_expand_inner<'a>( crate_loader: &'a mut CrateLoader<'a>, plugin_info: PluginInfo, ) -> Result<(ast::Crate, Resolver<'a>)> { - let attributes = plugin_info.attributes; time(sess, "pre ast expansion lint checks", || { lint::check_ast_crate( sess, @@ -522,7 +516,6 @@ fn configure_and_expand_inner<'a>( &krate, &sess.parse_sess, &sess.features_untracked(), - &attributes, sess.opts.unstable_features, ); }); diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs index 5711b269ff092..ddf847ca3a34c 100644 --- a/src/libsyntax/feature_gate/check.rs +++ b/src/libsyntax/feature_gate/check.rs @@ -1,7 +1,7 @@ use super::{active::{ACTIVE_FEATURES, Features}, Feature, State as FeatureState}; use super::accepted::ACCEPTED_FEATURES; use super::removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES}; -use super::builtin_attrs::{AttributeGate, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use super::builtin_attrs::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use crate::ast::{ self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind, @@ -33,9 +33,8 @@ pub enum Stability { } struct Context<'a> { - features: &'a Features, parse_sess: &'a ParseSess, - plugin_attributes: &'a [(Symbol, AttributeType)], + features: &'a Features, } macro_rules! gate_feature_fn { @@ -67,7 +66,6 @@ impl<'a> Context<'a> { &self, attr: &ast::Attribute, attr_info: Option<&BuiltinAttribute>, - is_macro: bool ) { debug!("check_attribute(attr = {:?})", attr); if let Some(&(name, ty, _template, ref gateage)) = attr_info { @@ -87,42 +85,15 @@ impl<'a> Context<'a> { } } debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage); - return; - } else { - for segment in &attr.path.segments { - if segment.ident.as_str().starts_with("rustc") { - let msg = "attributes starting with `rustc` are \ - reserved for use by the `rustc` compiler"; - gate_feature!(self, rustc_attrs, segment.ident.span, msg); - } - } - } - for &(n, ty) in self.plugin_attributes { - if attr.path == n { - // Plugins can't gate attributes, so we don't check for it - // unlike the code above; we only use this loop to - // short-circuit to avoid the checks below. - debug!("check_attribute: {:?} is registered by a plugin, {:?}", attr.path, ty); - return; - } - } - if !is_macro && !attr::is_known(attr) { - // Only run the custom attribute lint during regular feature gate - // checking. Macro gating runs before the plugin attributes are - // registered, so we skip this in that case. - let msg = format!("the attribute `{}` is currently unknown to the compiler and \ - may have meaning added to it in the future", attr.path); - gate_feature!(self, custom_attribute, attr.span, &msg); } } } pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) { - let cx = Context { features, parse_sess, plugin_attributes: &[] }; + let cx = Context { parse_sess, features }; cx.check_attribute( attr, attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name).map(|a| *a)), - true ); } @@ -321,7 +292,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { }); // Check for gated attributes. - self.context.check_attribute(attr, attr_info, false); + self.context.check_attribute(attr, attr_info); if attr.check_name(sym::doc) { if let Some(content) = attr.meta_item_list() { @@ -872,21 +843,16 @@ fn active_features_up_to(edition: Edition) -> impl Iterator { gate_all!($gate, $gate, $msg); }; ($spans:ident, $gate:ident, $msg:literal) => { - for span in &*sess.gated_spans.$spans.borrow() { + for span in &*parse_sess.gated_spans.$spans.borrow() { gate_feature!(&ctx, $gate, *span, $msg); } } From a01ba39b4b47e8dd397ccb5b6c0363ee168107b2 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 14 Sep 2019 21:29:59 +0300 Subject: [PATCH 2/3] feature_gate: Merge various attribute gating functions --- src/libsyntax/ext/expand.rs | 15 +-- src/libsyntax/feature_gate/builtin_attrs.rs | 1 + src/libsyntax/feature_gate/check.rs | 122 ++++++------------ src/libsyntax/feature_gate/mod.rs | 3 +- .../feature-gates/feature-gate-doc_alias.rs | 2 +- .../feature-gate-doc_alias.stderr | 2 +- .../ui/feature-gates/feature-gate-doc_cfg.rs | 2 +- .../feature-gates/feature-gate-doc_cfg.stderr | 2 +- .../feature-gates/feature-gate-doc_keyword.rs | 2 +- .../feature-gate-doc_keyword.stderr | 2 +- .../feature-gate-external_doc.rs | 2 +- .../feature-gate-external_doc.stderr | 2 +- 12 files changed, 54 insertions(+), 103 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 87e2d721f89a0..b80c530731dfc 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -26,7 +26,7 @@ use syntax_pos::{Span, DUMMY_SP, FileName}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use std::io::ErrorKind; -use std::{iter, mem}; +use std::{iter, mem, slice}; use std::ops::DerefMut; use std::rc::Rc; use std::path::PathBuf; @@ -1019,7 +1019,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn check_attributes(&mut self, attrs: &[ast::Attribute]) { let features = self.cx.ecfg.features.unwrap(); for attr in attrs.iter() { - self.check_attribute_inner(attr, features); + feature_gate::check_attribute(attr, self.cx.parse_sess, features); // macros are expanded before any lint passes so this warning has to be hardcoded if attr.path == sym::derive { @@ -1029,15 +1029,6 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } } - - fn check_attribute(&mut self, at: &ast::Attribute) { - let features = self.cx.ecfg.features.unwrap(); - self.check_attribute_inner(at, features); - } - - fn check_attribute_inner(&mut self, at: &ast::Attribute, features: &Features) { - feature_gate::check_attribute(at, self.cx.parse_sess, features); - } } impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { @@ -1445,7 +1436,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { if let Some(file) = it.value_str() { let err_count = self.cx.parse_sess.span_diagnostic.err_count(); - self.check_attribute(&at); + self.check_attributes(slice::from_ref(at)); if self.cx.parse_sess.span_diagnostic.err_count() > err_count { // avoid loading the file if they haven't enabled the feature return noop_visit_attribute(at, self); diff --git a/src/libsyntax/feature_gate/builtin_attrs.rs b/src/libsyntax/feature_gate/builtin_attrs.rs index 763c3ffd782df..b6e13200f32af 100644 --- a/src/libsyntax/feature_gate/builtin_attrs.rs +++ b/src/libsyntax/feature_gate/builtin_attrs.rs @@ -79,6 +79,7 @@ pub enum AttributeType { CrateLevel, } +#[derive(Clone, Copy)] pub enum AttributeGate { /// Is gated by a given feature gate, reason /// and function to check if enabled diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs index ddf847ca3a34c..3a9dae5a8b217 100644 --- a/src/libsyntax/feature_gate/check.rs +++ b/src/libsyntax/feature_gate/check.rs @@ -1,7 +1,7 @@ use super::{active::{ACTIVE_FEATURES, Features}, Feature, State as FeatureState}; use super::accepted::ACCEPTED_FEATURES; use super::removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES}; -use super::builtin_attrs::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; +use super::builtin_attrs::{AttributeGate, BUILTIN_ATTRIBUTE_MAP}; use crate::ast::{ self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind, @@ -61,40 +61,46 @@ macro_rules! gate_feature { }; } -impl<'a> Context<'a> { - fn check_attribute( - &self, - attr: &ast::Attribute, - attr_info: Option<&BuiltinAttribute>, - ) { - debug!("check_attribute(attr = {:?})", attr); - if let Some(&(name, ty, _template, ref gateage)) = attr_info { - if let AttributeGate::Gated(_, name, desc, ref has_feature) = *gateage { - if !attr.span.allows_unstable(name) { - gate_feature_fn!( - self, has_feature, attr.span, name, desc, GateStrength::Hard - ); - } - } else if name == sym::doc { - if let Some(content) = attr.meta_item_list() { - if content.iter().any(|c| c.check_name(sym::include)) { - gate_feature!(self, external_doc, attr.span, - "`#[doc(include = \"...\")]` is experimental" - ); - } - } +crate fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) { + let cx = &Context { parse_sess, features }; + let attr_info = + attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a); + // Check feature gates for built-in attributes. + if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info { + gate_feature_fn!(cx, has_feature, attr.span, name, descr, GateStrength::Hard); + } + // Check input tokens for built-in and key-value attributes. + match attr_info { + // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. + Some((name, _, template, _)) if name != sym::rustc_dummy => + check_builtin_attribute(parse_sess, attr, name, template), + _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() { + if token == token::Eq { + // All key-value attributes are restricted to meta-item syntax. + attr.parse_meta(parse_sess).map_err(|mut err| err.emit()).ok(); } - debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage); } } -} - -pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) { - let cx = Context { parse_sess, features }; - cx.check_attribute( - attr, - attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name).map(|a| *a)), - ); + // Check unstable flavors of the `#[doc]` attribute. + if attr.check_name(sym::doc) { + for nested_meta in attr.meta_item_list().unwrap_or_default() { + macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => { + $(if nested_meta.check_name(sym::$name) { + let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental"); + gate_feature!(cx, $feature, attr.span, msg); + })* + }} + + gate_doc!( + include => external_doc + cfg => doc_cfg + masked => doc_masked + spotlight => doc_spotlight + alias => doc_alias + keyword => doc_keyword + ); + } + } } fn find_lang_feature_issue(feature: Symbol) -> Option { @@ -210,7 +216,6 @@ pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &str = struct PostExpansionVisitor<'a> { context: &'a Context<'a>, - builtin_attributes: &'static FxHashMap, } macro_rules! gate_feature_post { @@ -287,50 +292,7 @@ impl<'a> PostExpansionVisitor<'a> { impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_attribute(&mut self, attr: &ast::Attribute) { - let attr_info = attr.ident().and_then(|ident| { - self.builtin_attributes.get(&ident.name).map(|a| *a) - }); - - // Check for gated attributes. - self.context.check_attribute(attr, attr_info); - - if attr.check_name(sym::doc) { - if let Some(content) = attr.meta_item_list() { - if content.len() == 1 && content[0].check_name(sym::cfg) { - gate_feature_post!(&self, doc_cfg, attr.span, - "`#[doc(cfg(...))]` is experimental" - ); - } else if content.iter().any(|c| c.check_name(sym::masked)) { - gate_feature_post!(&self, doc_masked, attr.span, - "`#[doc(masked)]` is experimental" - ); - } else if content.iter().any(|c| c.check_name(sym::spotlight)) { - gate_feature_post!(&self, doc_spotlight, attr.span, - "`#[doc(spotlight)]` is experimental" - ); - } else if content.iter().any(|c| c.check_name(sym::alias)) { - gate_feature_post!(&self, doc_alias, attr.span, - "`#[doc(alias = \"...\")]` is experimental" - ); - } else if content.iter().any(|c| c.check_name(sym::keyword)) { - gate_feature_post!(&self, doc_keyword, attr.span, - "`#[doc(keyword = \"...\")]` is experimental" - ); - } - } - } - - match attr_info { - // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. - Some(&(name, _, template, _)) if name != sym::rustc_dummy => - check_builtin_attribute(self.context.parse_sess, attr, name, template), - _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() { - if token == token::Eq { - // All key-value attributes are restricted to meta-item syntax. - attr.parse_meta(self.context.parse_sess).map_err(|mut err| err.emit()).ok(); - } - } - } + check_attribute(attr, self.context.parse_sess, self.context.features); } fn visit_name(&mut self, sp: Span, name: ast::Name) { @@ -864,11 +826,7 @@ pub fn check_crate(krate: &ast::Crate, gate_all!(yields, generators, "yield syntax is experimental"); gate_all!(or_patterns, "or-patterns syntax is experimental"); - let visitor = &mut PostExpansionVisitor { - context: &ctx, - builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP, - }; - visit::walk_crate(visitor, krate); + visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate); } #[derive(Clone, Copy, Hash)] diff --git a/src/libsyntax/feature_gate/mod.rs b/src/libsyntax/feature_gate/mod.rs index 1e41667ea411e..ca13ab3620508 100644 --- a/src/libsyntax/feature_gate/mod.rs +++ b/src/libsyntax/feature_gate/mod.rs @@ -58,7 +58,8 @@ pub use builtin_attrs::{ deprecated_attributes, is_builtin_attr, is_builtin_attr_name, }; pub use check::{ - check_attribute, check_crate, get_features, feature_err, emit_feature_err, + check_crate, get_features, feature_err, emit_feature_err, Stability, GateIssue, UnstableFeatures, EXPLAIN_STMT_ATTR_SYNTAX, EXPLAIN_UNSIZED_TUPLE_COERCION, }; +crate use check::check_attribute; diff --git a/src/test/ui/feature-gates/feature-gate-doc_alias.rs b/src/test/ui/feature-gates/feature-gate-doc_alias.rs index adb6fc217a329..c95722102d9b6 100644 --- a/src/test/ui/feature-gates/feature-gate-doc_alias.rs +++ b/src/test/ui/feature-gates/feature-gate-doc_alias.rs @@ -1,4 +1,4 @@ -#[doc(alias = "foo")] //~ ERROR: `#[doc(alias = "...")]` is experimental +#[doc(alias = "foo")] //~ ERROR: `#[doc(alias)]` is experimental pub struct Foo; fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-doc_alias.stderr b/src/test/ui/feature-gates/feature-gate-doc_alias.stderr index dddaa45de8ff8..540b1f5ccbe43 100644 --- a/src/test/ui/feature-gates/feature-gate-doc_alias.stderr +++ b/src/test/ui/feature-gates/feature-gate-doc_alias.stderr @@ -1,4 +1,4 @@ -error[E0658]: `#[doc(alias = "...")]` is experimental +error[E0658]: `#[doc(alias)]` is experimental --> $DIR/feature-gate-doc_alias.rs:1:1 | LL | #[doc(alias = "foo")] diff --git a/src/test/ui/feature-gates/feature-gate-doc_cfg.rs b/src/test/ui/feature-gates/feature-gate-doc_cfg.rs index bb3846e7f6b55..b12b8a1057182 100644 --- a/src/test/ui/feature-gates/feature-gate-doc_cfg.rs +++ b/src/test/ui/feature-gates/feature-gate-doc_cfg.rs @@ -1,2 +1,2 @@ -#[doc(cfg(unix))] //~ ERROR: `#[doc(cfg(...))]` is experimental +#[doc(cfg(unix))] //~ ERROR: `#[doc(cfg)]` is experimental fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-doc_cfg.stderr b/src/test/ui/feature-gates/feature-gate-doc_cfg.stderr index 7b0a231df4c3e..eaa908d0037ae 100644 --- a/src/test/ui/feature-gates/feature-gate-doc_cfg.stderr +++ b/src/test/ui/feature-gates/feature-gate-doc_cfg.stderr @@ -1,4 +1,4 @@ -error[E0658]: `#[doc(cfg(...))]` is experimental +error[E0658]: `#[doc(cfg)]` is experimental --> $DIR/feature-gate-doc_cfg.rs:1:1 | LL | #[doc(cfg(unix))] diff --git a/src/test/ui/feature-gates/feature-gate-doc_keyword.rs b/src/test/ui/feature-gates/feature-gate-doc_keyword.rs index 6cdcfa67c3a9b..4bb9a40deb0dd 100644 --- a/src/test/ui/feature-gates/feature-gate-doc_keyword.rs +++ b/src/test/ui/feature-gates/feature-gate-doc_keyword.rs @@ -1,4 +1,4 @@ -#[doc(keyword = "match")] //~ ERROR: `#[doc(keyword = "...")]` is experimental +#[doc(keyword = "match")] //~ ERROR: `#[doc(keyword)]` is experimental /// wonderful mod foo{} diff --git a/src/test/ui/feature-gates/feature-gate-doc_keyword.stderr b/src/test/ui/feature-gates/feature-gate-doc_keyword.stderr index abde0bea9b230..15a41d9ffa4ea 100644 --- a/src/test/ui/feature-gates/feature-gate-doc_keyword.stderr +++ b/src/test/ui/feature-gates/feature-gate-doc_keyword.stderr @@ -1,4 +1,4 @@ -error[E0658]: `#[doc(keyword = "...")]` is experimental +error[E0658]: `#[doc(keyword)]` is experimental --> $DIR/feature-gate-doc_keyword.rs:1:1 | LL | #[doc(keyword = "match")] diff --git a/src/test/ui/feature-gates/feature-gate-external_doc.rs b/src/test/ui/feature-gates/feature-gate-external_doc.rs index dec3fa185791c..9d68d3ec4f52a 100644 --- a/src/test/ui/feature-gates/feature-gate-external_doc.rs +++ b/src/test/ui/feature-gates/feature-gate-external_doc.rs @@ -1,2 +1,2 @@ -#[doc(include="asdf.md")] //~ ERROR: `#[doc(include = "...")]` is experimental +#[doc(include="asdf.md")] //~ ERROR: `#[doc(include)]` is experimental fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-external_doc.stderr b/src/test/ui/feature-gates/feature-gate-external_doc.stderr index a5a874374d1eb..683c0ad217426 100644 --- a/src/test/ui/feature-gates/feature-gate-external_doc.stderr +++ b/src/test/ui/feature-gates/feature-gate-external_doc.stderr @@ -1,4 +1,4 @@ -error[E0658]: `#[doc(include = "...")]` is experimental +error[E0658]: `#[doc(include)]` is experimental --> $DIR/feature-gate-external_doc.rs:1:1 | LL | #[doc(include="asdf.md")] From cb771fdd6c927a4308440cad1607570140f058d6 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 14 Sep 2019 21:54:16 +0300 Subject: [PATCH 3/3] feature_gate: Eliminate `check::Context` Use `PostExpansionVisitor` directly instead --- src/libsyntax/feature_gate/check.rs | 110 +++++++++++++--------------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs index 3a9dae5a8b217..b4491a87f0600 100644 --- a/src/libsyntax/feature_gate/check.rs +++ b/src/libsyntax/feature_gate/check.rs @@ -32,15 +32,10 @@ pub enum Stability { Deprecated(&'static str, Option<&'static str>), } -struct Context<'a> { - parse_sess: &'a ParseSess, - features: &'a Features, -} - macro_rules! gate_feature_fn { ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{ let (cx, has_feature, span, - name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level); + name, explain, level) = (&*$cx, $has_feature, $span, $name, $explain, $level); let has_feature: bool = has_feature(&$cx.features); debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature); if !has_feature && !span.allows_unstable($name) { @@ -62,45 +57,7 @@ macro_rules! gate_feature { } crate fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) { - let cx = &Context { parse_sess, features }; - let attr_info = - attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a); - // Check feature gates for built-in attributes. - if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info { - gate_feature_fn!(cx, has_feature, attr.span, name, descr, GateStrength::Hard); - } - // Check input tokens for built-in and key-value attributes. - match attr_info { - // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. - Some((name, _, template, _)) if name != sym::rustc_dummy => - check_builtin_attribute(parse_sess, attr, name, template), - _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() { - if token == token::Eq { - // All key-value attributes are restricted to meta-item syntax. - attr.parse_meta(parse_sess).map_err(|mut err| err.emit()).ok(); - } - } - } - // Check unstable flavors of the `#[doc]` attribute. - if attr.check_name(sym::doc) { - for nested_meta in attr.meta_item_list().unwrap_or_default() { - macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => { - $(if nested_meta.check_name(sym::$name) { - let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental"); - gate_feature!(cx, $feature, attr.span, msg); - })* - }} - - gate_doc!( - include => external_doc - cfg => doc_cfg - masked => doc_masked - spotlight => doc_spotlight - alias => doc_alias - keyword => doc_keyword - ); - } - } + PostExpansionVisitor { parse_sess, features }.visit_attribute(attr) } fn find_lang_feature_issue(feature: Symbol) -> Option { @@ -215,20 +172,21 @@ pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &str = "unsized tuple coercion is not stable enough for use and is subject to change"; struct PostExpansionVisitor<'a> { - context: &'a Context<'a>, + parse_sess: &'a ParseSess, + features: &'a Features, } macro_rules! gate_feature_post { ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{ let (cx, span) = ($cx, $span); if !span.allows_unstable(sym::$feature) { - gate_feature!(cx.context, $feature, span, $explain) + gate_feature!(cx, $feature, span, $explain) } }}; ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{ let (cx, span) = ($cx, $span); if !span.allows_unstable(sym::$feature) { - gate_feature!(cx.context, $feature, span, $explain, $level) + gate_feature!(cx, $feature, span, $explain, $level) } }} } @@ -292,7 +250,44 @@ impl<'a> PostExpansionVisitor<'a> { impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_attribute(&mut self, attr: &ast::Attribute) { - check_attribute(attr, self.context.parse_sess, self.context.features); + let attr_info = + attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a); + // Check feature gates for built-in attributes. + if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info { + gate_feature_fn!(self, has_feature, attr.span, name, descr, GateStrength::Hard); + } + // Check input tokens for built-in and key-value attributes. + match attr_info { + // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. + Some((name, _, template, _)) if name != sym::rustc_dummy => + check_builtin_attribute(self.parse_sess, attr, name, template), + _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() { + if token == token::Eq { + // All key-value attributes are restricted to meta-item syntax. + attr.parse_meta(self.parse_sess).map_err(|mut err| err.emit()).ok(); + } + } + } + // Check unstable flavors of the `#[doc]` attribute. + if attr.check_name(sym::doc) { + for nested_meta in attr.meta_item_list().unwrap_or_default() { + macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => { + $(if nested_meta.check_name(sym::$name) { + let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental"); + gate_feature!(self, $feature, attr.span, msg); + })* + }} + + gate_doc!( + include => external_doc + cfg => doc_cfg + masked => doc_masked + spotlight => doc_spotlight + alias => doc_alias + keyword => doc_keyword + ); + } + } } fn visit_name(&mut self, sp: Span, name: ast::Name) { @@ -300,7 +295,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!( &self, non_ascii_idents, - self.context.parse_sess.source_map().def_span(sp), + self.parse_sess.source_map().def_span(sp), "non-ascii idents are not fully supported" ); } @@ -356,12 +351,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - let has_feature = self.context.features.arbitrary_enum_discriminant; + let has_feature = self.features.arbitrary_enum_discriminant; if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) { - Parser::maybe_report_invalid_custom_discriminants( - self.context.parse_sess, - &variants, - ); + Parser::maybe_report_invalid_custom_discriminants(self.parse_sess, &variants); } } @@ -471,7 +463,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ExprKind::Type(..) => { // To avoid noise about type ascription in common syntax errors, only emit if it // is the *only* error. - if self.context.parse_sess.span_diagnostic.err_count() == 0 { + if self.parse_sess.span_diagnostic.err_count() == 0 { gate_feature_post!(&self, type_ascription, e.span, "type ascription is experimental"); } @@ -809,13 +801,13 @@ pub fn check_crate(krate: &ast::Crate, features: &Features, unstable: UnstableFeatures) { maybe_stage_features(&parse_sess.span_diagnostic, krate, unstable); - let ctx = Context { parse_sess, features }; + let mut visitor = PostExpansionVisitor { parse_sess, features }; macro_rules! gate_all { ($gate:ident, $msg:literal) => { gate_all!($gate, $gate, $msg); }; ($spans:ident, $gate:ident, $msg:literal) => { for span in &*parse_sess.gated_spans.$spans.borrow() { - gate_feature!(&ctx, $gate, *span, $msg); + gate_feature!(&visitor, $gate, *span, $msg); } } } @@ -826,7 +818,7 @@ pub fn check_crate(krate: &ast::Crate, gate_all!(yields, generators, "yield syntax is experimental"); gate_all!(or_patterns, "or-patterns syntax is experimental"); - visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate); + visit::walk_crate(&mut visitor, krate); } #[derive(Clone, Copy, Hash)]