]> git.lizzy.rs Git - rust.git/commitdiff
syntax_ext: Reuse built-in attribute template checking for macro attributes
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Thu, 18 Jul 2019 23:10:36 +0000 (02:10 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Wed, 24 Jul 2019 09:29:45 +0000 (12:29 +0300)
src/librustc_lint/builtin.rs
src/libsyntax/attr/builtin.rs
src/libsyntax/feature_gate.rs
src/libsyntax_ext/global_allocator.rs
src/libsyntax_ext/test.rs
src/libsyntax_ext/test_case.rs
src/test/ui/allocator/allocator-args.stderr
src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr
src/test/ui/feature-gate/issue-43106-gating-of-test.stderr

index 4105e030477f13ded267b7abec7b9851795a5775..b63d14ca949ee6b7b8189e5d71c068f0d75e480d 100644 (file)
 use syntax::ast;
 use syntax::ptr::P;
 use syntax::ast::Expr;
-use syntax::attr::{self, HasAttrs};
+use syntax::attr::{self, HasAttrs, AttributeTemplate};
 use syntax::source_map::Spanned;
 use syntax::edition::Edition;
-use syntax::feature_gate::{AttributeGate, AttributeTemplate, AttributeType};
+use syntax::feature_gate::{AttributeGate, AttributeType};
 use syntax::feature_gate::{Stability, deprecated_attributes};
 use syntax_pos::{BytePos, Span, SyntaxContext};
 use syntax::symbol::{Symbol, kw, sym};
index b41f1047fcba3765c952f4d3197b5e19a926a472..713094416524ba4f9997e1b79f20cfb0c80034ae 100644 (file)
@@ -1,6 +1,9 @@
 //! Parsing and validation of builtin attributes
 
 use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
+use crate::early_buffered_lints::BufferedEarlyLintId;
+use crate::ext::base::ExtCtxt;
+use crate::ext::build::AstBuilder;
 use crate::feature_gate::{Features, GatedCfg};
 use crate::parse::ParseSess;
 
@@ -19,6 +22,27 @@ enum AttrError {
     UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
 }
 
+/// A template that the attribute input must match.
+/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
+#[derive(Clone, Copy)]
+pub struct AttributeTemplate {
+    crate word: bool,
+    crate list: Option<&'static str>,
+    crate name_value_str: Option<&'static str>,
+}
+
+impl AttributeTemplate {
+    /// Checks that the given meta-item is compatible with this template.
+    fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
+        match meta_item_kind {
+            ast::MetaItemKind::Word => self.word,
+            ast::MetaItemKind::List(..) => self.list.is_some(),
+            ast::MetaItemKind::NameValue(lit) if lit.node.is_str() => self.name_value_str.is_some(),
+            ast::MetaItemKind::NameValue(..) => false,
+        }
+    }
+}
+
 fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
     let diag = &sess.span_diagnostic;
     match error {
@@ -901,3 +925,75 @@ pub fn find_transparency(
     let fallback = if is_legacy { Transparency::SemiTransparent } else { Transparency::Opaque };
     (transparency.map_or(fallback, |t| t.0), error)
 }
+
+pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) {
+    // All the built-in macro attributes are "words" at the moment.
+    let template = AttributeTemplate { word: true, list: None, name_value_str: None };
+    let attr = ecx.attribute(meta_item.span, meta_item.clone());
+    check_builtin_attribute(ecx.parse_sess, &attr, name, template);
+}
+
+crate fn check_builtin_attribute(
+    sess: &ParseSess, attr: &ast::Attribute, name: Symbol, template: AttributeTemplate
+) {
+    // Some special attributes like `cfg` must be checked
+    // before the generic check, so we skip them here.
+    let should_skip = |name| name == sym::cfg;
+    // Some of previously accepted forms were used in practice,
+    // report them as warnings for now.
+    let should_warn = |name| name == sym::doc || name == sym::ignore ||
+                             name == sym::inline || name == sym::link;
+
+    match attr.parse_meta(sess) {
+        Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) {
+            let error_msg = format!("malformed `{}` attribute input", name);
+            let mut msg = "attribute must be of the form ".to_owned();
+            let mut suggestions = vec![];
+            let mut first = true;
+            if template.word {
+                first = false;
+                let code = format!("#[{}]", name);
+                msg.push_str(&format!("`{}`", &code));
+                suggestions.push(code);
+            }
+            if let Some(descr) = template.list {
+                if !first {
+                    msg.push_str(" or ");
+                }
+                first = false;
+                let code = format!("#[{}({})]", name, descr);
+                msg.push_str(&format!("`{}`", &code));
+                suggestions.push(code);
+            }
+            if let Some(descr) = template.name_value_str {
+                if !first {
+                    msg.push_str(" or ");
+                }
+                let code = format!("#[{} = \"{}\"]", name, descr);
+                msg.push_str(&format!("`{}`", &code));
+                suggestions.push(code);
+            }
+            if should_warn(name) {
+                sess.buffer_lint(
+                    BufferedEarlyLintId::IllFormedAttributeInput,
+                    meta.span,
+                    ast::CRATE_NODE_ID,
+                    &msg,
+                );
+            } else {
+                sess.span_diagnostic.struct_span_err(meta.span, &error_msg)
+                    .span_suggestions(
+                        meta.span,
+                        if suggestions.len() == 1 {
+                            "must be of the form"
+                        } else {
+                            "the following are the possible correct uses"
+                        },
+                        suggestions.into_iter(),
+                        Applicability::HasPlaceholders,
+                    ).emit();
+            }
+        }
+        Err(mut err) => err.emit(),
+    }
+}
index 214160f10790d81c4436eddc54b3e65d6178148f..72184b0bd6400374f59ad582793a44c6707a9b25 100644 (file)
@@ -19,8 +19,7 @@
     self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
     PatKind, RangeEnd,
 };
-use crate::attr;
-use crate::early_buffered_lints::BufferedEarlyLintId;
+use crate::attr::{self, check_builtin_attribute, AttributeTemplate};
 use crate::source_map::Spanned;
 use crate::edition::{ALL_EDITIONS, Edition};
 use crate::visit::{self, FnKind, Visitor};
@@ -906,27 +905,6 @@ pub enum AttributeGate {
     Ungated,
 }
 
-/// A template that the attribute input must match.
-/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
-#[derive(Clone, Copy)]
-pub struct AttributeTemplate {
-    word: bool,
-    list: Option<&'static str>,
-    name_value_str: Option<&'static str>,
-}
-
-impl AttributeTemplate {
-    /// Checks that the given meta-item is compatible with this template.
-    fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
-        match meta_item_kind {
-            ast::MetaItemKind::Word => self.word,
-            ast::MetaItemKind::List(..) => self.list.is_some(),
-            ast::MetaItemKind::NameValue(lit) if lit.node.is_str() => self.name_value_str.is_some(),
-            ast::MetaItemKind::NameValue(..) => false,
-        }
-    }
-}
-
 /// A convenience macro for constructing attribute templates.
 /// E.g., `template!(Word, List: "description")` means that the attribute
 /// supports forms `#[attr]` and `#[attr(description)]`.
@@ -1901,70 +1879,6 @@ fn check_abi(&self, abi: Abi, span: Span) {
             Abi::System => {}
         }
     }
-
-    fn check_builtin_attribute(&mut self, attr: &ast::Attribute, name: Symbol,
-                               template: AttributeTemplate) {
-        // Some special attributes like `cfg` must be checked
-        // before the generic check, so we skip them here.
-        let should_skip = |name| name == sym::cfg;
-        // Some of previously accepted forms were used in practice,
-        // report them as warnings for now.
-        let should_warn = |name| name == sym::doc || name == sym::ignore ||
-                                 name == sym::inline || name == sym::link;
-
-        match attr.parse_meta(self.context.parse_sess) {
-            Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) {
-                let error_msg = format!("malformed `{}` attribute input", name);
-                let mut msg = "attribute must be of the form ".to_owned();
-                let mut suggestions = vec![];
-                let mut first = true;
-                if template.word {
-                    first = false;
-                    let code = format!("#[{}]", name);
-                    msg.push_str(&format!("`{}`", &code));
-                    suggestions.push(code);
-                }
-                if let Some(descr) = template.list {
-                    if !first {
-                        msg.push_str(" or ");
-                    }
-                    first = false;
-                    let code = format!("#[{}({})]", name, descr);
-                    msg.push_str(&format!("`{}`", &code));
-                    suggestions.push(code);
-                }
-                if let Some(descr) = template.name_value_str {
-                    if !first {
-                        msg.push_str(" or ");
-                    }
-                    let code = format!("#[{} = \"{}\"]", name, descr);
-                    msg.push_str(&format!("`{}`", &code));
-                    suggestions.push(code);
-                }
-                if should_warn(name) {
-                    self.context.parse_sess.buffer_lint(
-                        BufferedEarlyLintId::IllFormedAttributeInput,
-                        meta.span,
-                        ast::CRATE_NODE_ID,
-                        &msg,
-                    );
-                } else {
-                    self.context.parse_sess.span_diagnostic.struct_span_err(meta.span, &error_msg)
-                        .span_suggestions(
-                            meta.span,
-                            if suggestions.len() == 1 {
-                                "must be of the form"
-                            } else {
-                                "the following are the possible correct uses"
-                            },
-                            suggestions.into_iter(),
-                            Applicability::HasPlaceholders,
-                        ).emit();
-                }
-            }
-            Err(mut err) => err.emit(),
-        }
-    }
 }
 
 impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
@@ -2005,7 +1919,7 @@ fn visit_attribute(&mut self, attr: &ast::Attribute) {
         match attr_info {
             // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
             Some(&(name, _, template, _)) if name != sym::rustc_dummy =>
-                self.check_builtin_attribute(attr, name, template),
+                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.
index 785636abb121fcc4f6e771ce42dd636c02a7785a..196db3d7baa5b965bd29d8e6f5294915734213c3 100644 (file)
@@ -1,31 +1,22 @@
-use errors::Applicability;
-use syntax::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident, Item};
 use syntax::ast::{ItemKind, Mutability, Ty, TyKind, Unsafety, VisibilityKind};
-use syntax::source_map::respan;
+use syntax::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident, Item};
+use syntax::attr::check_builtin_macro_attribute;
 use syntax::ext::allocator::{AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};
 use syntax::ext::base::{Annotatable, ExtCtxt};
 use syntax::ext::build::AstBuilder;
 use syntax::ext::hygiene::SyntaxContext;
 use syntax::ptr::P;
+use syntax::source_map::respan;
 use syntax::symbol::{kw, sym};
 use syntax_pos::Span;
 
 pub fn expand(
     ecx: &mut ExtCtxt<'_>,
-    span: Span,
+    _span: Span,
     meta_item: &ast::MetaItem,
     item: Annotatable,
 ) -> Vec<Annotatable> {
-    if !meta_item.is_word() {
-        let msg = format!("malformed `{}` attribute input", meta_item.path);
-        ecx.parse_sess.span_diagnostic.struct_span_err(span, &msg)
-            .span_suggestion(
-                span,
-                "must be of the form",
-                format!("`#[{}]`", meta_item.path),
-                Applicability::MachineApplicable
-            ).emit();
-    }
+    check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
 
     let not_static = |item: Annotatable| {
         ecx.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
index f8755a1d1d791131dbf46354fa08db2875cfa4ca..d381c42f9ce84a1b543912144a35272f90c1e7d1 100644 (file)
@@ -1,31 +1,34 @@
 /// The expansion from a test function to the appropriate test struct for libtest
 /// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
 
+use syntax::ast;
+use syntax::attr::{self, check_builtin_macro_attribute};
 use syntax::ext::base::*;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::hygiene::SyntaxContext;
-use syntax::attr;
-use syntax::ast;
 use syntax::print::pprust;
 use syntax::symbol::{Symbol, sym};
 use syntax_pos::Span;
+
 use std::iter;
 
 pub fn expand_test(
     cx: &mut ExtCtxt<'_>,
     attr_sp: Span,
-    _meta_item: &ast::MetaItem,
+    meta_item: &ast::MetaItem,
     item: Annotatable,
 ) -> Vec<Annotatable> {
+    check_builtin_macro_attribute(cx, meta_item, sym::test);
     expand_test_or_bench(cx, attr_sp, item, false)
 }
 
 pub fn expand_bench(
     cx: &mut ExtCtxt<'_>,
     attr_sp: Span,
-    _meta_item: &ast::MetaItem,
+    meta_item: &ast::MetaItem,
     item: Annotatable,
 ) -> Vec<Annotatable> {
+    check_builtin_macro_attribute(cx, meta_item, sym::bench);
     expand_test_or_bench(cx, attr_sp, item, true)
 }
 
index 355f2428e0806d1d66e92e26ceab1680ee1019c7..ea4a8d541ab9928f8636bdde77b24a1adfc17532 100644 (file)
@@ -9,10 +9,11 @@
 // We mark item with an inert attribute "rustc_test_marker" which the test generation
 // logic will pick up on.
 
+use syntax::ast;
+use syntax::attr::check_builtin_macro_attribute;
 use syntax::ext::base::*;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::hygiene::SyntaxContext;
-use syntax::ast;
 use syntax::source_map::respan;
 use syntax::symbol::sym;
 use syntax_pos::Span;
 pub fn expand(
     ecx: &mut ExtCtxt<'_>,
     attr_sp: Span,
-    _meta_item: &ast::MetaItem,
+    meta_item: &ast::MetaItem,
     anno_item: Annotatable
 ) -> Vec<Annotatable> {
+    check_builtin_macro_attribute(ecx, meta_item, sym::test_case);
+
     if !ecx.ecfg.should_test { return vec![]; }
 
     let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id));
index d8ae7130e5d3da9578a5c0e72ab39fa92d5080a4..dfff2a7e7094d368a9a83a8854c3a429557fdcb9 100644 (file)
@@ -2,7 +2,7 @@ error: malformed `global_allocator` attribute input
   --> $DIR/allocator-args.rs:10:1
    |
 LL | #[global_allocator(malloc)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: ``#[global_allocator]``
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[global_allocator]`
 
 error: aborting due to previous error
 
index 503ef020d960419b15c930b05125d292a028c1e3..e82cb93c6359eb733f9300526fc4c9431c2a5a22 100644 (file)
@@ -1,7 +1,13 @@
+error: malformed `bench` attribute input
+  --> $DIR/issue-43106-gating-of-bench.rs:15:1
+   |
+LL | #![bench                   = "4100"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[bench]`
+
 error[E0601]: `main` function not found in crate `issue_43106_gating_of_bench`
    |
    = note: consider adding a `main` function to `$DIR/issue-43106-gating-of-bench.rs`
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0601`.
index 2ab35be43c576dfcfc576fa1f7b9bc6a2d7b8551..9866fa3730ef2dce697ac2c6a09e25bcee315e7f 100644 (file)
@@ -1,7 +1,13 @@
+error: malformed `test` attribute input
+  --> $DIR/issue-43106-gating-of-test.rs:10:1
+   |
+LL | #![test                    = "4200"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[test]`
+
 error[E0601]: `main` function not found in crate `issue_43106_gating_of_test`
    |
    = note: consider adding a `main` function to `$DIR/issue-43106-gating-of-test.rs`
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0601`.