syntax_ext: Reuse built-in attribute template checking for macro attributes
This commit is contained in:
parent
433024147a
commit
76b1ffaf6c
@ -36,10 +36,10 @@ use syntax::tokenstream::{TokenTree, TokenStream};
|
||||
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};
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ use crate::ast::{
|
||||
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 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
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 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
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.
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
@ -20,9 +21,11 @@ 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));
|
||||
|
@ -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
|
||||
|
||||
|
@ -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`.
|
||||
|
@ -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`.
|
||||
|
Loading…
Reference in New Issue
Block a user