From 76b1ffaf6c70abd3fa4da2e694dc709116258098 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 19 Jul 2019 02:10:36 +0300 Subject: syntax_ext: Reuse built-in attribute template checking for macro attributes --- src/libsyntax/attr/builtin.rs | 96 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'src/libsyntax/attr') diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs index b41f1047fcb..71309441652 100644 --- a/src/libsyntax/attr/builtin.rs +++ b/src/libsyntax/attr/builtin.rs @@ -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(), + } +} -- cgit 1.4.1-3-g733a5 From 6e4f16173c331ca3ccae1ffecc5a69f1ad54f30c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 20 Jul 2019 03:14:11 +0300 Subject: Demote template check error to a lint for `#[test]` and `#[bench]` --- src/libsyntax/attr/builtin.rs | 3 ++- src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr | 10 +++++++--- src/test/ui/feature-gate/issue-43106-gating-of-test.stderr | 10 +++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'src/libsyntax/attr') diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs index 71309441652..dbf31ad0148 100644 --- a/src/libsyntax/attr/builtin.rs +++ b/src/libsyntax/attr/builtin.rs @@ -942,7 +942,8 @@ crate fn check_builtin_attribute( // 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; + name == sym::inline || name == sym::link || + name == sym::test || name == sym::bench; match attr.parse_meta(sess) { Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) { diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr index e82cb93c635..37b5e792dc2 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr @@ -1,13 +1,17 @@ -error: malformed `bench` attribute input +warning: attribute must be of the form `#[bench]` --> $DIR/issue-43106-gating-of-bench.rs:15:1 | LL | #![bench = "4100"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[bench]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(ill_formed_attribute_input)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 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 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr index 9866fa3730e..dbffabf3e02 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr @@ -1,13 +1,17 @@ -error: malformed `test` attribute input +warning: attribute must be of the form `#[test]` --> $DIR/issue-43106-gating-of-test.rs:10:1 | LL | #![test = "4200"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[test]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(ill_formed_attribute_input)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 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 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0601`. -- cgit 1.4.1-3-g733a5