move attr meta grammar to parse::validate_atr + ast_validation
This commit is contained in:
parent
9e346646e9
commit
5011ec7fed
10 changed files with 142 additions and 124 deletions
|
|
@ -15,6 +15,7 @@ use syntax::ast::*;
|
|||
use syntax::attr;
|
||||
use syntax::expand::is_proc_macro_attr;
|
||||
use syntax::feature_gate::is_builtin_attr;
|
||||
use syntax::parse::validate_attr;
|
||||
use syntax::source_map::Spanned;
|
||||
use syntax::symbol::{kw, sym};
|
||||
use syntax::visit::{self, Visitor};
|
||||
|
|
@ -369,6 +370,10 @@ fn validate_generics_order<'a>(
|
|||
}
|
||||
|
||||
impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
fn visit_attribute(&mut self, attr: &Attribute) {
|
||||
validate_attr::check_meta(&self.session.parse_sess, attr);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||
match &expr.kind {
|
||||
ExprKind::Closure(_, _, _, fn_decl, _, _) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
|
||||
use crate::early_buffered_lints::BufferedEarlyLintId;
|
||||
use crate::feature_gate::{Features, GatedCfg};
|
||||
use crate::print::pprust;
|
||||
use crate::sess::ParseSess;
|
||||
|
|
@ -36,7 +35,7 @@ impl AttributeTemplate {
|
|||
}
|
||||
|
||||
/// Checks that the given meta-item is compatible with this template.
|
||||
fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
|
||||
pub 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(),
|
||||
|
|
@ -938,69 +937,3 @@ 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_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 ||
|
||||
name == sym::test || name == sym::bench;
|
||||
|
||||
match attr.parse_meta(sess) {
|
||||
Ok(meta) => if !should_skip(name) && !template.compatible(&meta.kind) {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,17 +14,13 @@ use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem};
|
|||
use crate::ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam};
|
||||
use crate::mut_visit::visit_clobber;
|
||||
use crate::source_map::{BytePos, Spanned};
|
||||
use crate::parse;
|
||||
use crate::token::{self, Token};
|
||||
use crate::ptr::P;
|
||||
use crate::sess::ParseSess;
|
||||
use crate::symbol::{sym, Symbol};
|
||||
use crate::ThinVec;
|
||||
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
|
||||
use crate::GLOBALS;
|
||||
|
||||
use errors::PResult;
|
||||
|
||||
use log::debug;
|
||||
use syntax_pos::Span;
|
||||
|
||||
|
|
@ -328,21 +324,6 @@ impl Attribute {
|
|||
Some(mk_name_value_item_str(Ident::new(sym::doc, self.span), comment, self.span)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
|
||||
match self.kind {
|
||||
AttrKind::Normal(ref item) => {
|
||||
Ok(MetaItem {
|
||||
path: item.path.clone(),
|
||||
kind: parse::parse_in_attr(sess, self, |parser| parser.parse_meta_item_kind())?,
|
||||
span: self.span,
|
||||
})
|
||||
}
|
||||
AttrKind::DocComment(comment) => {
|
||||
Ok(mk_name_value_item_str(Ident::new(sym::doc, self.span), comment, self.span))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Constructors */
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::attr;
|
|||
use crate::ast;
|
||||
use crate::edition::Edition;
|
||||
use crate::mut_visit::*;
|
||||
use crate::parse;
|
||||
use crate::parse::{self, validate_attr};
|
||||
use crate::ptr::P;
|
||||
use crate::sess::ParseSess;
|
||||
use crate::symbol::sym;
|
||||
|
|
@ -168,7 +168,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
true
|
||||
};
|
||||
|
||||
let meta_item = match attr.parse_meta(self.sess) {
|
||||
let meta_item = match validate_attr::parse_meta(self.sess, attr) {
|
||||
Ok(meta_item) => meta_item,
|
||||
Err(mut err) => { err.emit(); return true; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,18 +3,14 @@ use super::accepted::ACCEPTED_FEATURES;
|
|||
use super::removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES};
|
||||
use super::builtin_attrs::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
|
||||
|
||||
use crate::ast::{
|
||||
self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
|
||||
PatKind, RangeEnd, VariantData,
|
||||
};
|
||||
use crate::attr::{self, check_builtin_attribute};
|
||||
use crate::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId};
|
||||
use crate::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData};
|
||||
use crate::attr;
|
||||
use crate::source_map::Spanned;
|
||||
use crate::edition::{ALL_EDITIONS, Edition};
|
||||
use crate::visit::{self, FnKind, Visitor};
|
||||
use crate::token;
|
||||
use crate::sess::ParseSess;
|
||||
use crate::symbol::{Symbol, sym};
|
||||
use crate::tokenstream::TokenTree;
|
||||
|
||||
use errors::{Applicability, DiagnosticBuilder, Handler};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
|
@ -331,19 +327,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
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.get_normal_item().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() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ mod tests;
|
|||
#[macro_use]
|
||||
pub mod parser;
|
||||
pub mod lexer;
|
||||
pub mod validate_attr;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Directory<'a> {
|
||||
|
|
|
|||
112
src/libsyntax/parse/validate_attr.rs
Normal file
112
src/libsyntax/parse/validate_attr.rs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
//! Meta-syntax validation logic of attributes for post-expansion.
|
||||
|
||||
use crate::ast::{self, Attribute, AttrKind, Ident, MetaItem};
|
||||
use crate::attr::{AttributeTemplate, mk_name_value_item_str};
|
||||
use crate::sess::ParseSess;
|
||||
use crate::feature_gate::BUILTIN_ATTRIBUTE_MAP;
|
||||
use crate::early_buffered_lints::BufferedEarlyLintId;
|
||||
use crate::token;
|
||||
use crate::tokenstream::TokenTree;
|
||||
|
||||
use errors::{PResult, Applicability};
|
||||
use syntax_pos::{Symbol, sym};
|
||||
|
||||
pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
|
||||
let attr_info =
|
||||
attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
|
||||
|
||||
// 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(sess, attr, name, template),
|
||||
_ => if let Some(TokenTree::Token(token)) = attr.get_normal_item().tokens.trees().next() {
|
||||
if token == token::Eq {
|
||||
// All key-value attributes are restricted to meta-item syntax.
|
||||
parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
|
||||
Ok(match attr.kind {
|
||||
AttrKind::Normal(ref item) => MetaItem {
|
||||
path: item.path.clone(),
|
||||
kind: super::parse_in_attr(sess, attr, |p| p.parse_meta_item_kind())?,
|
||||
span: attr.span,
|
||||
},
|
||||
AttrKind::DocComment(comment) => {
|
||||
mk_name_value_item_str(Ident::new(sym::doc, attr.span), comment, attr.span)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_builtin_attribute(
|
||||
sess: &ParseSess,
|
||||
attr: &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 ||
|
||||
name == sym::test || name == sym::bench;
|
||||
|
||||
match parse_meta(sess, attr) {
|
||||
Ok(meta) => if !should_skip(name) && !template.compatible(&meta.kind) {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ use syntax::feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feat
|
|||
use syntax::mut_visit::*;
|
||||
use syntax::parse::DirectoryOwnership;
|
||||
use syntax::parse::parser::Parser;
|
||||
use syntax::parse::validate_attr;
|
||||
use syntax::print::pprust;
|
||||
use syntax::ptr::P;
|
||||
use syntax::sess::ParseSess;
|
||||
|
|
@ -640,7 +641,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
self.parse_ast_fragment(tok_result, fragment_kind, &item.path, span)
|
||||
}
|
||||
SyntaxExtensionKind::LegacyAttr(expander) => {
|
||||
match attr.parse_meta(self.cx.parse_sess) {
|
||||
match validate_attr::parse_meta(self.cx.parse_sess, &attr) {
|
||||
Ok(meta) => {
|
||||
let item = expander.expand(self.cx, span, &meta, item);
|
||||
fragment_kind.expect_from_annotatables(item)
|
||||
|
|
@ -1031,6 +1032,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
let features = self.cx.ecfg.features.unwrap();
|
||||
for attr in attrs.iter() {
|
||||
feature_gate::check_attribute(attr, self.cx.parse_sess, features);
|
||||
validate_attr::check_meta(self.cx.parse_sess, attr);
|
||||
|
||||
// macros are expanded before any lint passes so this warning has to be hardcoded
|
||||
if attr.has_name(sym::derive) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use syntax_pos::Symbol;
|
||||
use syntax::ast::MetaItem;
|
||||
use syntax::attr::{check_builtin_attribute, AttributeTemplate};
|
||||
use syntax::attr::AttributeTemplate;
|
||||
use syntax::parse::validate_attr;
|
||||
use syntax_expand::base::ExtCtxt;
|
||||
|
||||
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::only_word();
|
||||
let attr = ecx.attribute(meta_item.clone());
|
||||
check_builtin_attribute(ecx.parse_sess, &attr, name, template);
|
||||
validate_attr::check_builtin_attribute(ecx.parse_sess, &attr, name, template);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,15 @@
|
|||
error: malformed `proc_macro_derive` attribute input
|
||||
--> $DIR/attribute.rs:9:1
|
||||
|
|
||||
LL | #[proc_macro_derive]
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
|
||||
|
||||
error: malformed `proc_macro_derive` attribute input
|
||||
--> $DIR/attribute.rs:12:1
|
||||
|
|
||||
LL | #[proc_macro_derive = ""]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
|
||||
|
||||
error: attribute must have either one or two arguments
|
||||
--> $DIR/attribute.rs:15:1
|
||||
|
|
||||
|
|
@ -88,17 +100,5 @@ error: `self` cannot be a name of derive helper attribute
|
|||
LL | #[proc_macro_derive(d17, attributes(self))]
|
||||
| ^^^^
|
||||
|
||||
error: malformed `proc_macro_derive` attribute input
|
||||
--> $DIR/attribute.rs:9:1
|
||||
|
|
||||
LL | #[proc_macro_derive]
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
|
||||
|
||||
error: malformed `proc_macro_derive` attribute input
|
||||
--> $DIR/attribute.rs:12:1
|
||||
|
|
||||
LL | #[proc_macro_derive = ""]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue