|
1 | 1 | use super::{active::{ACTIVE_FEATURES, Features}, Feature, State as FeatureState};
|
2 | 2 | use super::accepted::ACCEPTED_FEATURES;
|
3 | 3 | use super::removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES};
|
4 |
| -use super::builtin_attrs::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; |
| 4 | +use super::builtin_attrs::{AttributeGate, BUILTIN_ATTRIBUTE_MAP}; |
5 | 5 |
|
6 | 6 | use crate::ast::{
|
7 | 7 | self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
|
@@ -61,40 +61,46 @@ macro_rules! gate_feature {
|
61 | 61 | };
|
62 | 62 | }
|
63 | 63 |
|
64 |
| -impl<'a> Context<'a> { |
65 |
| - fn check_attribute( |
66 |
| - &self, |
67 |
| - attr: &ast::Attribute, |
68 |
| - attr_info: Option<&BuiltinAttribute>, |
69 |
| - ) { |
70 |
| - debug!("check_attribute(attr = {:?})", attr); |
71 |
| - if let Some(&(name, ty, _template, ref gateage)) = attr_info { |
72 |
| - if let AttributeGate::Gated(_, name, desc, ref has_feature) = *gateage { |
73 |
| - if !attr.span.allows_unstable(name) { |
74 |
| - gate_feature_fn!( |
75 |
| - self, has_feature, attr.span, name, desc, GateStrength::Hard |
76 |
| - ); |
77 |
| - } |
78 |
| - } else if name == sym::doc { |
79 |
| - if let Some(content) = attr.meta_item_list() { |
80 |
| - if content.iter().any(|c| c.check_name(sym::include)) { |
81 |
| - gate_feature!(self, external_doc, attr.span, |
82 |
| - "`#[doc(include = \"...\")]` is experimental" |
83 |
| - ); |
84 |
| - } |
85 |
| - } |
| 64 | +crate fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) { |
| 65 | + let cx = &Context { parse_sess, features }; |
| 66 | + let attr_info = |
| 67 | + attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a); |
| 68 | + // Check feature gates for built-in attributes. |
| 69 | + if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info { |
| 70 | + gate_feature_fn!(cx, has_feature, attr.span, name, descr, GateStrength::Hard); |
| 71 | + } |
| 72 | + // Check input tokens for built-in and key-value attributes. |
| 73 | + match attr_info { |
| 74 | + // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. |
| 75 | + Some((name, _, template, _)) if name != sym::rustc_dummy => |
| 76 | + check_builtin_attribute(parse_sess, attr, name, template), |
| 77 | + _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() { |
| 78 | + if token == token::Eq { |
| 79 | + // All key-value attributes are restricted to meta-item syntax. |
| 80 | + attr.parse_meta(parse_sess).map_err(|mut err| err.emit()).ok(); |
86 | 81 | }
|
87 |
| - debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage); |
88 | 82 | }
|
89 | 83 | }
|
90 |
| -} |
91 |
| - |
92 |
| -pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) { |
93 |
| - let cx = Context { parse_sess, features }; |
94 |
| - cx.check_attribute( |
95 |
| - attr, |
96 |
| - attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name).map(|a| *a)), |
97 |
| - ); |
| 84 | + // Check unstable flavors of the `#[doc]` attribute. |
| 85 | + if attr.check_name(sym::doc) { |
| 86 | + for nested_meta in attr.meta_item_list().unwrap_or_default() { |
| 87 | + macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => { |
| 88 | + $(if nested_meta.check_name(sym::$name) { |
| 89 | + let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental"); |
| 90 | + gate_feature!(cx, $feature, attr.span, msg); |
| 91 | + })* |
| 92 | + }} |
| 93 | + |
| 94 | + gate_doc!( |
| 95 | + include => external_doc |
| 96 | + cfg => doc_cfg |
| 97 | + masked => doc_masked |
| 98 | + spotlight => doc_spotlight |
| 99 | + alias => doc_alias |
| 100 | + keyword => doc_keyword |
| 101 | + ); |
| 102 | + } |
| 103 | + } |
98 | 104 | }
|
99 | 105 |
|
100 | 106 | fn find_lang_feature_issue(feature: Symbol) -> Option<u32> {
|
@@ -210,7 +216,6 @@ pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &str =
|
210 | 216 |
|
211 | 217 | struct PostExpansionVisitor<'a> {
|
212 | 218 | context: &'a Context<'a>,
|
213 |
| - builtin_attributes: &'static FxHashMap<Symbol, &'static BuiltinAttribute>, |
214 | 219 | }
|
215 | 220 |
|
216 | 221 | macro_rules! gate_feature_post {
|
@@ -287,50 +292,7 @@ impl<'a> PostExpansionVisitor<'a> {
|
287 | 292 |
|
288 | 293 | impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
289 | 294 | fn visit_attribute(&mut self, attr: &ast::Attribute) {
|
290 |
| - let attr_info = attr.ident().and_then(|ident| { |
291 |
| - self.builtin_attributes.get(&ident.name).map(|a| *a) |
292 |
| - }); |
293 |
| - |
294 |
| - // Check for gated attributes. |
295 |
| - self.context.check_attribute(attr, attr_info); |
296 |
| - |
297 |
| - if attr.check_name(sym::doc) { |
298 |
| - if let Some(content) = attr.meta_item_list() { |
299 |
| - if content.len() == 1 && content[0].check_name(sym::cfg) { |
300 |
| - gate_feature_post!(&self, doc_cfg, attr.span, |
301 |
| - "`#[doc(cfg(...))]` is experimental" |
302 |
| - ); |
303 |
| - } else if content.iter().any(|c| c.check_name(sym::masked)) { |
304 |
| - gate_feature_post!(&self, doc_masked, attr.span, |
305 |
| - "`#[doc(masked)]` is experimental" |
306 |
| - ); |
307 |
| - } else if content.iter().any(|c| c.check_name(sym::spotlight)) { |
308 |
| - gate_feature_post!(&self, doc_spotlight, attr.span, |
309 |
| - "`#[doc(spotlight)]` is experimental" |
310 |
| - ); |
311 |
| - } else if content.iter().any(|c| c.check_name(sym::alias)) { |
312 |
| - gate_feature_post!(&self, doc_alias, attr.span, |
313 |
| - "`#[doc(alias = \"...\")]` is experimental" |
314 |
| - ); |
315 |
| - } else if content.iter().any(|c| c.check_name(sym::keyword)) { |
316 |
| - gate_feature_post!(&self, doc_keyword, attr.span, |
317 |
| - "`#[doc(keyword = \"...\")]` is experimental" |
318 |
| - ); |
319 |
| - } |
320 |
| - } |
321 |
| - } |
322 |
| - |
323 |
| - match attr_info { |
324 |
| - // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. |
325 |
| - Some(&(name, _, template, _)) if name != sym::rustc_dummy => |
326 |
| - check_builtin_attribute(self.context.parse_sess, attr, name, template), |
327 |
| - _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() { |
328 |
| - if token == token::Eq { |
329 |
| - // All key-value attributes are restricted to meta-item syntax. |
330 |
| - attr.parse_meta(self.context.parse_sess).map_err(|mut err| err.emit()).ok(); |
331 |
| - } |
332 |
| - } |
333 |
| - } |
| 295 | + check_attribute(attr, self.context.parse_sess, self.context.features); |
334 | 296 | }
|
335 | 297 |
|
336 | 298 | fn visit_name(&mut self, sp: Span, name: ast::Name) {
|
@@ -864,11 +826,7 @@ pub fn check_crate(krate: &ast::Crate,
|
864 | 826 | gate_all!(yields, generators, "yield syntax is experimental");
|
865 | 827 | gate_all!(or_patterns, "or-patterns syntax is experimental");
|
866 | 828 |
|
867 |
| - let visitor = &mut PostExpansionVisitor { |
868 |
| - context: &ctx, |
869 |
| - builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP, |
870 |
| - }; |
871 |
| - visit::walk_crate(visitor, krate); |
| 829 | + visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate); |
872 | 830 | }
|
873 | 831 |
|
874 | 832 | #[derive(Clone, Copy, Hash)]
|
|
0 commit comments