AttrKind::Normal(ref item) => {
AttrKind::Normal(AttrItem {
path: item.path.clone(),
- tokens: self.lower_token_stream(item.tokens.clone()),
+ args: self.lower_mac_args(&item.args),
})
}
AttrKind::DocComment(comment) => AttrKind::DocComment(comment)
}
}
+ fn lower_mac_args(&mut self, args: &MacArgs) -> MacArgs {
+ match *args {
+ MacArgs::Empty => MacArgs::Empty,
+ MacArgs::Delimited(dspan, delim, ref tokens) =>
+ MacArgs::Delimited(dspan, delim, self.lower_token_stream(tokens.clone())),
+ MacArgs::Eq(eq_span, ref tokens) =>
+ MacArgs::Eq(eq_span, self.lower_token_stream(tokens.clone())),
+ }
+ }
+
fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream {
tokens
.into_trees()
if !attr.has_name(sym::cfg_attr) {
return vec![attr];
}
- if attr.get_normal_item().tokens.is_empty() {
+ if let ast::MacArgs::Empty = attr.get_normal_item().args {
self.sess.span_diagnostic
.struct_span_err(
attr.span,
) -> PResult<'a, T> {
let mut parser = Parser::new(
sess,
- attr.get_normal_item().tokens.clone(),
+ attr.get_normal_item().args.outer_tokens(),
None,
false,
false,
brackets.push(stream);
}
- brackets.push(item.tokens.clone());
+ brackets.push(item.args.outer_tokens());
// The span we list here for `#` and for `[ ... ]` are both wrong in
// that it encompasses more than each token, but it hopefully is "good
use syntax::attr;
use syntax::ast;
use syntax::util::comments;
-use syntax::token::{self, Nonterminal, DelimToken};
-use syntax::tokenstream::{TokenStream, TokenTree};
+use syntax::token::{self, Nonterminal};
use syntax_pos::{Span, Symbol};
use errors::PResult;
item
} else {
let path = self.parse_path(PathStyle::Mod)?;
- let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) ||
- self.check(&token::OpenDelim(DelimToken::Bracket)) ||
- self.check(&token::OpenDelim(DelimToken::Brace)) {
- self.parse_token_tree().into()
- } else if self.eat(&token::Eq) {
- let eq = TokenTree::token(token::Eq, self.prev_span);
- let mut is_interpolated_expr = false;
- if let token::Interpolated(nt) = &self.token.kind {
- if let token::NtExpr(..) = **nt {
- is_interpolated_expr = true;
- }
- }
- let token_tree = if is_interpolated_expr {
- // We need to accept arbitrary interpolated expressions to continue
- // supporting things like `doc = $expr` that work on stable.
- // Non-literal interpolated expressions are rejected after expansion.
- self.parse_token_tree()
- } else {
- self.parse_unsuffixed_lit()?.token_tree()
- };
- TokenStream::new(vec![eq.into(), token_tree.into()])
- } else {
- TokenStream::default()
- };
- ast::AttrItem { path, tokens }
+ let args = self.parse_attr_args()?;
+ ast::AttrItem { path, args }
})
}
}
fn parse_mac_args(&mut self) -> PResult<'a, P<MacArgs>> {
- self.parse_mac_args_common(true)
+ self.parse_mac_args_common(true).map(P)
}
- #[allow(dead_code)]
- fn parse_attr_args(&mut self) -> PResult<'a, P<MacArgs>> {
+ fn parse_attr_args(&mut self) -> PResult<'a, MacArgs> {
self.parse_mac_args_common(false)
}
- fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, P<MacArgs>> {
- Ok(P(if self.check(&token::OpenDelim(DelimToken::Paren)) ||
+ fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, MacArgs> {
+ Ok(if self.check(&token::OpenDelim(DelimToken::Paren)) ||
self.check(&token::OpenDelim(DelimToken::Bracket)) ||
self.check(&token::OpenDelim(DelimToken::Brace)) {
match self.parse_token_tree() {
}
} else {
return self.unexpected();
- }))
+ })
}
fn parse_or_use_outer_attributes(
use crate::maybe_whole;
use syntax::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs};
use syntax::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
+use syntax::ast::MacArgs;
use syntax::ThinVec;
use syntax::token::{self, Token};
use syntax::source_map::{Span, BytePos};
fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
let meta_ident = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
- token::NtMeta(ref item) => match item.tokens.is_empty() {
- true => Some(item.path.clone()),
- false => None,
+ token::NtMeta(ref item) => match item.args {
+ MacArgs::Empty => Some(item.path.clone()),
+ _ => None,
},
_ => None,
},
use errors::{PResult, Applicability};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
-use syntax::ast::{self, Attribute, AttrKind, Ident, MetaItem, MetaItemKind};
+use syntax::ast::{self, Attribute, AttrKind, Ident, MacArgs, MetaItem, MetaItemKind};
use syntax::attr::mk_name_value_item_str;
use syntax::early_buffered_lints::BufferedEarlyLintId;
-use syntax::token;
-use syntax::tokenstream::TokenTree;
use syntax::sess::ParseSess;
use syntax_pos::{Symbol, sym};
// `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();
- }
+ _ => if let MacArgs::Eq(..) = attr.get_normal_item().args {
+ // All key-value attributes are restricted to meta-item syntax.
+ parse_meta(sess, attr).map_err(|mut err| err.emit()).ok();
}
}
}
}
/// Arguments passed to an attribute or a function-like macro.
-#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub enum MacArgs {
/// No arguments - `#[attr]`.
Empty,
}
/// Tokens together with the delimiters or `=`.
- /// Use of this functions generally means that something suspicious or hacky is happening.
+ /// Use of this functions generally means that something suboptimal or hacky is happening.
pub fn outer_tokens(&self) -> TokenStream {
match *self {
MacArgs::Empty => TokenStream::default(),
}
}
-#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub enum MacDelimiter {
Parenthesis,
Bracket,
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub struct AttrItem {
pub path: Path,
- pub tokens: TokenStream,
+ pub args: MacArgs,
}
/// Metadata associated with an item.
use crate::ast;
use crate::ast::{AttrItem, AttrId, AttrKind, AttrStyle, Name, Ident, Path, PathSegment};
-use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem};
+use crate::ast::{MacArgs, MacDelimiter, 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};
pub fn is_word(&self) -> bool {
if let AttrKind::Normal(item) = &self.kind {
- item.tokens.is_empty()
+ matches!(item.args, MacArgs::Empty)
} else {
false
}
impl AttrItem {
pub fn meta(&self, span: Span) -> Option<MetaItem> {
- let mut tokens = self.tokens.trees().peekable();
+ let mut tokens = self.args.outer_tokens().trees().peekable();
Some(MetaItem {
path: self.path.clone(),
kind: if let Some(kind) = MetaItemKind::from_tokens(&mut tokens) {
AttrId(id)
}
-pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
- mk_attr_from_item(style, AttrItem { path, tokens }, span)
+pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
+ mk_attr_from_item(style, AttrItem { path, args }, span)
}
pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
/// Returns an inner attribute with the given value and span.
pub fn mk_attr_inner(item: MetaItem) -> Attribute {
- mk_attr(AttrStyle::Inner, item.path, item.kind.tokens(item.span), item.span)
+ mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span)
}
/// Returns an outer attribute with the given value and span.
pub fn mk_attr_outer(item: MetaItem) -> Attribute {
- mk_attr(AttrStyle::Outer, item.path, item.kind.tokens(item.span), item.span)
+ mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
}
pub fn mk_doc_comment(style: AttrStyle, comment: Symbol, span: Span) -> Attribute {
}
impl MetaItemKind {
- pub fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
+ pub fn mac_args(&self, span: Span) -> MacArgs {
+ match self {
+ MetaItemKind::Word => MacArgs::Empty,
+ MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.token_tree().into()),
+ MetaItemKind::List(list) => {
+ let mut tts = Vec::new();
+ for (i, item) in list.iter().enumerate() {
+ if i > 0 {
+ tts.push(TokenTree::token(token::Comma, span).into());
+ }
+ tts.extend(item.token_trees_and_joints())
+ }
+ MacArgs::Delimited(
+ DelimSpan::from_single(span), MacDelimiter::Parenthesis, TokenStream::new(tts)
+ )
+ }
+ }
+ }
+
+ fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
match *self {
MetaItemKind::Word => vec![],
MetaItemKind::NameValue(ref lit) => {
}
}
- // Premature conversions of `TokenTree`s to `TokenStream`s can hurt
- // performance. Do not use this function if `token_trees_and_joints()` can
- // be used instead.
- pub fn tokens(&self, span: Span) -> TokenStream {
- TokenStream::new(self.token_trees_and_joints(span))
- }
-
fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind>
where I: Iterator<Item = TokenTree>,
{
pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
let Attribute { kind, id: _, style: _, span } = attr;
match kind {
- AttrKind::Normal(AttrItem { path, tokens }) => {
+ AttrKind::Normal(AttrItem { path, args }) => {
vis.visit_path(path);
- vis.visit_tts(tokens);
+ visit_mac_args(args, vis);
}
AttrKind::DocComment(_) => {}
}
token::NtIdent(ident, _is_raw) => vis.visit_ident(ident),
token::NtLifetime(ident) => vis.visit_ident(ident),
token::NtLiteral(expr) => vis.visit_expr(expr),
- token::NtMeta(AttrItem { path, tokens }) => {
+ token::NtMeta(AttrItem { path, args }) => {
vis.visit_path(path);
- vis.visit_tts(tokens);
+ visit_mac_args(args, vis);
}
token::NtPath(path) => vis.visit_path(path),
token::NtTT(tt) => vis.visit_tt(tt),
use crate::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
use crate::ast::{SelfKind, GenericBound, TraitBoundModifier};
-use crate::ast::{Attribute, GenericArg};
+use crate::ast::{Attribute, GenericArg, MacArgs};
use crate::util::parser::{self, AssocOp, Fixity};
use crate::util::comments;
use crate::attr;
fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
self.ibox(0);
- match item.tokens.trees().next() {
- Some(TokenTree::Delimited(_, delim, tts)) => {
- self.print_mac_common(
- Some(MacHeader::Path(&item.path)), false, None, delim, tts, true, span
- );
- }
- tree => {
+ match &item.args {
+ MacArgs::Delimited(_, delim, tokens) => self.print_mac_common(
+ Some(MacHeader::Path(&item.path)),
+ false,
+ None,
+ delim.to_token(),
+ tokens.clone(),
+ true,
+ span,
+ ),
+ MacArgs::Empty | MacArgs::Eq(..) => {
self.print_path(&item.path, false, 0);
- if tree.is_some() {
+ if let MacArgs::Eq(_, tokens) = &item.args {
self.space();
- self.print_tts(item.tokens.clone(), true);
+ self.word_space("=");
+ self.print_tts(tokens.clone(), true);
}
}
}
pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
match attr.kind {
- AttrKind::Normal(ref item) => visitor.visit_tts(item.tokens.clone()),
+ AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
AttrKind::DocComment(_) => {}
}
}
+pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) {
+ match args {
+ MacArgs::Empty => {}
+ MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()),
+ MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()),
+ }
+}
+
pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) {
match tt {
TokenTree::Token(token) => visitor.visit_token(token),
use rustc_parse::parser::Parser;
use rustc_parse::validate_attr;
use syntax::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path};
-use syntax::ast::{MacStmtStyle, StmtKind, ItemKind};
+use syntax::ast::{MacArgs, MacStmtStyle, StmtKind, ItemKind};
use syntax::attr::{self, HasAttrs, is_builtin_attr};
use syntax::source_map::respan;
use syntax::feature_gate::{self, feature_err};
=> panic!("unexpected annotatable"),
})), DUMMY_SP).into();
let item = attr.unwrap_normal_item();
- let input = self.extract_proc_macro_attr_input(item.tokens, span);
- let tok_result = expander.expand(self.cx, span, input, item_tok);
+ if let MacArgs::Eq(..) = item.args {
+ self.cx.span_err(span, "key-value macro attributes are not supported");
+ }
+ let tok_result =
+ expander.expand(self.cx, span, item.args.inner_tokens(), item_tok);
self.parse_ast_fragment(tok_result, fragment_kind, &item.path, span)
}
SyntaxExtensionKind::LegacyAttr(expander) => {
}
}
- fn extract_proc_macro_attr_input(&self, tokens: TokenStream, span: Span) -> TokenStream {
- let mut trees = tokens.trees();
- match trees.next() {
- Some(TokenTree::Delimited(_, _, tts)) => {
- if trees.next().is_none() {
- return tts.into()
- }
- }
- Some(TokenTree::Token(..)) => {}
- None => return TokenStream::default(),
- }
- self.cx.span_err(span, "custom attribute invocations must be \
- of the form `#[foo]` or `#[foo(..)]`, the macro name must only be \
- followed by a delimiter token");
- TokenStream::default()
- }
-
fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
let kind = match item {
Annotatable::Item(item) => match &item.kind {
let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items);
*at = attr::Attribute {
kind: ast::AttrKind::Normal(
- AttrItem { path: meta.path, tokens: meta.kind.tokens(meta.span) },
+ AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span) },
),
span: at.span,
id: at.id,
use crate::base::{self, *};
use crate::proc_macro_server;
-use syntax::ast::{self, ItemKind};
+use syntax::ast::{self, ItemKind, MacArgs};
use syntax::errors::{Applicability, FatalError};
use syntax::symbol::sym;
use syntax::token;
}
let parse_derive_paths = |attr: &ast::Attribute| {
- if attr.get_normal_item().tokens.is_empty() {
+ if let MacArgs::Empty = attr.get_normal_item().args {
return Ok(Vec::new());
}
rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
);
let start_span = parser.token.span;
- let AttrItem { path, tokens } = panictry!(parser.parse_attr_item());
+ let AttrItem { path, args } = panictry!(parser.parse_attr_item());
let end_span = parser.token.span;
if parser.token != token::Eof {
parse_sess.span_diagnostic
continue;
}
- krate.attrs.push(mk_attr(AttrStyle::Inner, path, tokens, start_span.to(end_span)));
+ krate.attrs.push(mk_attr(AttrStyle::Inner, path, args, start_span.to(end_span)));
}
krate
//~| ERROR: non-builtin inner attributes are unstable
}
-#[empty_attr = "y"] //~ ERROR: must only be followed by a delimiter token
+#[empty_attr = "y"] //~ ERROR: key-value macro attributes are not supported
fn _test3() {}
fn attrs() {
= note: for more information, see https://github.com/rust-lang/rust/issues/54727
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
-error: custom attribute invocations must be of the form `#[foo]` or `#[foo(..)]`, the macro name must only be followed by a delimiter token
+error: key-value macro attributes are not supported
--> $DIR/proc-macro-gates.rs:21:1
|
LL | #[empty_attr = "y"]