Skip to content

Commit

Permalink
move attr meta grammar to parse::validate_atr + ast_validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Centril committed Nov 9, 2019
1 parent 9e34664 commit 5011ec7
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 124 deletions.
5 changes: 5 additions & 0 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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, _, _) => {
Expand Down
69 changes: 1 addition & 68 deletions src/libsyntax/attr/builtin.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
}
}
19 changes: 0 additions & 19 deletions src/libsyntax/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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; }
};
Expand Down
23 changes: 3 additions & 20 deletions src/libsyntax/feature_gate/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod tests;
#[macro_use]
pub mod parser;
pub mod lexer;
pub mod validate_attr;

#[derive(Clone)]
pub struct Directory<'a> {
Expand Down
112 changes: 112 additions & 0 deletions src/libsyntax/parse/validate_attr.rs
Original file line number Diff line number Diff line change
@@ -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(),
}
}
4 changes: 3 additions & 1 deletion src/libsyntax_expand/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
5 changes: 3 additions & 2 deletions src/libsyntax_ext/util.rs
Original file line number Diff line number Diff line change
@@ -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);
}
Loading

0 comments on commit 5011ec7

Please sign in to comment.