1
Fork 0

syntax: Introduce a struct MacArgs for macro arguments

This commit is contained in:
Vadim Petrochenkov 2019-12-01 02:25:32 +03:00
parent fdc0011561
commit a81804b4d5
19 changed files with 192 additions and 112 deletions

View file

@ -1453,7 +1453,7 @@ impl EarlyLintPass for KeywordIdents {
self.check_tokens(cx, mac_def.stream());
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::Mac) {
self.check_tokens(cx, mac.tts.clone().into());
self.check_tokens(cx, mac.args.inner_tokens());
}
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: ast::Ident) {
self.check_ident_token(cx, UnderMacro(false), ident);

View file

@ -244,7 +244,7 @@ impl<'a> Parser<'a> {
Ok(attrs)
}
fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
pub(super) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
let lit = self.parse_lit()?;
debug!("checking if {:?} is unusuffixed", lit);

View file

@ -922,12 +922,11 @@ impl<'a> Parser<'a> {
// `!`, as an operator, is prefix, so we know this isn't that.
if self.eat(&token::Not) {
// MACRO INVOCATION expression
let (delim, tts) = self.expect_delimited_token_tree()?;
let args = self.parse_mac_args()?;
hi = self.prev_span;
ex = ExprKind::Mac(Mac {
path,
tts,
delim,
args,
span: lo.to(hi),
prior_type_ascription: self.last_type_ascription,
});

View file

@ -8,7 +8,7 @@ use syntax::ast::{ItemKind, ImplItem, ImplItemKind, TraitItem, TraitItemKind, Us
use syntax::ast::{PathSegment, IsAuto, Constness, IsAsync, Unsafety, Defaultness, Extern, StrLit};
use syntax::ast::{Visibility, VisibilityKind, Mutability, FnHeader, ForeignItem, ForeignItemKind};
use syntax::ast::{Ty, TyKind, Generics, TraitRef, EnumDef, Variant, VariantData, StructField};
use syntax::ast::{Mac, MacDelimiter, Block, BindingMode, FnDecl, FnSig, SelfKind, Param};
use syntax::ast::{Mac, Block, BindingMode, FnDecl, FnSig, SelfKind, Param};
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::ThinVec;
@ -437,16 +437,15 @@ impl<'a> Parser<'a> {
// Item macro
let path = self.parse_path(PathStyle::Mod)?;
self.expect(&token::Not)?;
let (delim, tts) = self.expect_delimited_token_tree()?;
if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
let args = self.parse_mac_args()?;
if args.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
let hi = self.prev_span;
let mac = Mac {
path,
tts,
delim,
args,
span: mac_lo.to(hi),
prior_type_ascription: self.last_type_ascription,
};
@ -518,15 +517,14 @@ impl<'a> Parser<'a> {
*at_end = true;
// eat a matched-delimiter token tree:
let (delim, tts) = self.expect_delimited_token_tree()?;
if delim != MacDelimiter::Brace {
let args = self.parse_mac_args()?;
if args.need_semicolon() {
self.expect_semi()?;
}
Ok(Some(Mac {
path,
tts,
delim,
args,
span: lo.to(self.prev_span),
prior_type_ascription: self.last_type_ascription,
}))
@ -1660,12 +1658,12 @@ impl<'a> Parser<'a> {
self.bump();
let ident = self.parse_ident()?;
let (delim, tokens) = self.expect_delimited_token_tree()?;
if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
let args = self.parse_mac_args()?;
if args.need_semicolon() && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
(ident, ast::MacroDef { tokens, legacy: true })
(ident, ast::MacroDef { tokens: args.inner_tokens(), legacy: true })
} else {
return Ok(None);
};

View file

@ -16,7 +16,7 @@ use crate::lexer::UnmatchedBrace;
use syntax::ast::{
self, DUMMY_NODE_ID, AttrStyle, Attribute, CrateSugar, Extern, Ident, StrLit,
IsAsync, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety,
IsAsync, MacArgs, MacDelimiter, Mutability, Visibility, VisibilityKind, Unsafety,
};
use syntax::print::pprust;
@ -1010,27 +1010,49 @@ impl<'a> Parser<'a> {
}
}
fn expect_delimited_token_tree(&mut self) -> PResult<'a, (MacDelimiter, TokenStream)> {
let delim = match self.token.kind {
token::OpenDelim(delim) => delim,
_ => {
let msg = "expected open delimiter";
let mut err = self.fatal(msg);
err.span_label(self.token.span, msg);
return Err(err)
fn parse_mac_args(&mut self) -> PResult<'a, P<MacArgs>> {
self.parse_mac_args_common(true)
}
};
let tts = match self.parse_token_tree() {
TokenTree::Delimited(_, _, tts) => tts,
#[allow(dead_code)]
fn parse_attr_args(&mut self) -> PResult<'a, P<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)) ||
self.check(&token::OpenDelim(DelimToken::Bracket)) ||
self.check(&token::OpenDelim(DelimToken::Brace)) {
match self.parse_token_tree() {
TokenTree::Delimited(dspan, delim, tokens) =>
MacArgs::Delimited(dspan, MacDelimiter::from_token(delim), tokens),
_ => unreachable!(),
}
} else if !delimited_only {
if self.eat(&token::Eq) {
let eq_span = 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()
};
let delim = match delim {
token::Paren => MacDelimiter::Parenthesis,
token::Bracket => MacDelimiter::Bracket,
token::Brace => MacDelimiter::Brace,
token::NoDelim => self.bug("unexpected no delimiter"),
};
Ok((delim, tts.into()))
MacArgs::Eq(eq_span, token_tree.into())
} else {
MacArgs::Empty
}
} else {
return self.unexpected();
}))
}
fn parse_or_use_outer_attributes(

View file

@ -595,11 +595,10 @@ impl<'a> Parser<'a> {
/// Parse macro invocation
fn parse_pat_mac_invoc(&mut self, lo: Span, path: Path) -> PResult<'a, PatKind> {
self.bump();
let (delim, tts) = self.expect_delimited_token_tree()?;
let args = self.parse_mac_args()?;
let mac = Mac {
path,
tts,
delim,
args,
span: lo.to(self.prev_span),
prior_type_ascription: self.last_type_ascription,
};

View file

@ -10,7 +10,7 @@ use syntax::ThinVec;
use syntax::ptr::P;
use syntax::ast;
use syntax::ast::{DUMMY_NODE_ID, Stmt, StmtKind, Local, Block, BlockCheckMode, Expr, ExprKind};
use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac, MacDelimiter};
use syntax::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac};
use syntax::util::classify;
use syntax::token;
use syntax::source_map::{respan, Span};
@ -93,10 +93,11 @@ impl<'a> Parser<'a> {
}));
}
let (delim, tts) = self.expect_delimited_token_tree()?;
let args = self.parse_mac_args()?;
let delim = args.delim();
let hi = self.prev_span;
let style = if delim == MacDelimiter::Brace {
let style = if delim == token::Brace {
MacStmtStyle::Braces
} else {
MacStmtStyle::NoBraces
@ -104,12 +105,11 @@ impl<'a> Parser<'a> {
let mac = Mac {
path,
tts,
delim,
args,
span: lo.to(hi),
prior_type_ascription: self.last_type_ascription,
};
let kind = if delim == MacDelimiter::Brace ||
let kind = if delim == token::Brace ||
self.token == token::Semi || self.token == token::Eof {
StmtKind::Mac(P((mac, style, attrs.into())))
}

View file

@ -177,11 +177,10 @@ impl<'a> Parser<'a> {
let path = self.parse_path(PathStyle::Type)?;
if self.eat(&token::Not) {
// Macro invocation in type position
let (delim, tts) = self.expect_delimited_token_tree()?;
let args = self.parse_mac_args()?;
let mac = Mac {
path,
tts,
delim,
args,
span: lo.to(self.prev_span),
prior_type_ascription: self.last_type_ascription,
};

View file

@ -27,7 +27,7 @@ pub use syntax_pos::symbol::{Ident, Symbol as Name};
use crate::ptr::P;
use crate::source_map::{dummy_spanned, respan, Spanned};
use crate::token::{self, DelimToken};
use crate::tokenstream::TokenStream;
use crate::tokenstream::{TokenStream, TokenTree, DelimSpan};
use syntax_pos::symbol::{kw, sym, Symbol};
use syntax_pos::{Span, DUMMY_SP, ExpnId};
@ -40,6 +40,7 @@ use rustc_index::vec::Idx;
use rustc_serialize::{self, Decoder, Encoder};
use rustc_macros::HashStable_Generic;
use std::iter;
use std::fmt;
#[cfg(test)]
@ -1372,21 +1373,71 @@ pub enum Movability {
Movable,
}
/// Represents a macro invocation. The `Path` indicates which macro
/// is being invoked, and the vector of token-trees contains the source
/// of the macro invocation.
///
/// N.B., the additional ident for a `macro_rules`-style macro is actually
/// stored in the enclosing item.
/// Represents a macro invocation. The `path` indicates which macro
/// is being invoked, and the `args` are arguments passed to it.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Mac {
pub path: Path,
pub delim: MacDelimiter,
pub tts: TokenStream,
pub args: P<MacArgs>,
pub span: Span,
pub prior_type_ascription: Option<(Span, bool)>,
}
/// Arguments passed to an attribute or a function-like macro.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub enum MacArgs {
/// No arguments - `#[attr]`.
Empty,
/// Delimited arguments - `#[attr()/[]/{}]` or `mac!()/[]/{}`.
Delimited(DelimSpan, MacDelimiter, TokenStream),
/// Arguments of a key-value attribute - `#[attr = "value"]`.
/// Span belongs to the `=` token, token stream is the "value".
Eq(Span, TokenStream),
}
impl MacArgs {
pub fn delim(&self) -> DelimToken {
match self {
MacArgs::Delimited(_, delim, _) => delim.to_token(),
MacArgs::Empty | MacArgs::Eq(..) => token::NoDelim,
}
}
/// Tokens inside the delimiters or after `=`.
/// Proc macros see these tokens, for example.
pub fn inner_tokens(&self) -> TokenStream {
match self {
MacArgs::Empty => TokenStream::default(),
MacArgs::Delimited(.., tokens) => tokens.clone(),
MacArgs::Eq(.., tokens) => tokens.clone(),
}
}
/// Tokens together with the delimiters or `=`.
/// Use of this functions generally means that something suspicious or hacky is happening.
pub fn outer_tokens(&self) -> TokenStream {
match *self {
MacArgs::Empty => TokenStream::default(),
MacArgs::Delimited(dspan, delim, ref tokens) =>
TokenTree::Delimited(dspan, delim.to_token(), tokens.clone()).into(),
MacArgs::Eq(eq_span, ref tokens) => iter::once(TokenTree::token(token::Eq, eq_span))
.chain(tokens.trees()).collect(),
}
}
/// Whether a macro with these arguments needs a semicolon
/// when used as a standalone item or statement.
pub fn need_semicolon(&self) -> bool {
!matches!(self, MacArgs::Delimited(_, MacDelimiter::Brace ,_))
}
}
impl Mac {
pub fn stream(&self) -> TokenStream {
self.args.inner_tokens()
}
}
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
pub enum MacDelimiter {
Parenthesis,
@ -1394,12 +1445,6 @@ pub enum MacDelimiter {
Brace,
}
impl Mac {
pub fn stream(&self) -> TokenStream {
self.tts.clone()
}
}
impl MacDelimiter {
crate fn to_token(self) -> DelimToken {
match self {
@ -1408,6 +1453,15 @@ impl MacDelimiter {
MacDelimiter::Brace => DelimToken::Brace,
}
}
pub fn from_token(delim: DelimToken) -> MacDelimiter {
match delim {
token::Paren => MacDelimiter::Parenthesis,
token::Bracket => MacDelimiter::Bracket,
token::Brace => MacDelimiter::Brace,
token::NoDelim => panic!("expected a delimiter"),
}
}
}
/// Represents a macro definition.

View file

@ -12,6 +12,7 @@
#![feature(const_transmute)]
#![feature(crate_visibility_modifier)]
#![feature(label_break_value)]
#![feature(matches_macro)]
#![feature(nll)]
#![feature(try_trait)]
#![feature(slice_patterns)]

View file

@ -359,6 +359,26 @@ pub fn visit_fn_sig<T: MutVisitor>(FnSig { header, decl }: &mut FnSig, vis: &mut
vis.visit_fn_decl(decl);
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) {
match args {
MacArgs::Empty => {}
MacArgs::Delimited(dspan, _delim, tokens) => {
visit_delim_span(dspan, vis);
vis.visit_tts(tokens);
}
MacArgs::Eq(eq_span, tokens) => {
vis.visit_span(eq_span);
vis.visit_tts(tokens);
}
}
}
pub fn visit_delim_span<T: MutVisitor>(dspan: &mut DelimSpan, vis: &mut T) {
vis.visit_span(&mut dspan.open);
vis.visit_span(&mut dspan.close);
}
pub fn noop_flat_map_field_pattern<T: MutVisitor>(
mut fp: FieldPat,
vis: &mut T,
@ -560,9 +580,9 @@ pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
}
pub fn noop_visit_mac<T: MutVisitor>(mac: &mut Mac, vis: &mut T) {
let Mac { path, delim: _, tts, span, prior_type_ascription: _ } = mac;
let Mac { path, args, span, prior_type_ascription: _ } = mac;
vis.visit_path(path);
vis.visit_tts(tts);
visit_mac_args(args, vis);
vis.visit_span(span);
}

View file

@ -1,6 +1,6 @@
use crate::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
use crate::ast::{SelfKind, GenericBound, TraitBoundModifier};
use crate::ast::{Attribute, MacDelimiter, GenericArg};
use crate::ast::{Attribute, GenericArg};
use crate::util::parser::{self, AssocOp, Fixity};
use crate::util::comments;
use crate::attr;
@ -1097,9 +1097,8 @@ impl<'a> State<'a> {
}
ast::ForeignItemKind::Macro(ref m) => {
self.print_mac(m);
match m.delim {
MacDelimiter::Brace => {},
_ => self.s.word(";")
if m.args.need_semicolon() {
self.s.word(";");
}
}
}
@ -1361,9 +1360,8 @@ impl<'a> State<'a> {
}
ast::ItemKind::Mac(ref mac) => {
self.print_mac(mac);
match mac.delim {
MacDelimiter::Brace => {}
_ => self.s.word(";"),
if mac.args.need_semicolon() {
self.s.word(";");
}
}
ast::ItemKind::MacroDef(ref macro_def) => {
@ -1578,9 +1576,8 @@ impl<'a> State<'a> {
}
ast::TraitItemKind::Macro(ref mac) => {
self.print_mac(mac);
match mac.delim {
MacDelimiter::Brace => {}
_ => self.s.word(";"),
if mac.args.need_semicolon() {
self.s.word(";");
}
}
}
@ -1608,9 +1605,8 @@ impl<'a> State<'a> {
}
ast::ImplItemKind::Macro(ref mac) => {
self.print_mac(mac);
match mac.delim {
MacDelimiter::Brace => {}
_ => self.s.word(";"),
if mac.args.need_semicolon() {
self.s.word(";");
}
}
}
@ -1775,7 +1771,7 @@ impl<'a> State<'a> {
Some(MacHeader::Path(&m.path)),
true,
None,
m.delim.to_token(),
m.args.delim(),
m.stream(),
true,
m.span,

View file

@ -30,13 +30,6 @@ impl MutVisitor for Marker {
}
}
impl Marker {
fn visit_delim_span(&mut self, dspan: &mut DelimSpan) {
self.visit_span(&mut dspan.open);
self.visit_span(&mut dspan.close);
}
}
/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
Delimited { forest: Lrc<mbe::Delimited>, idx: usize, span: DelimSpan },
@ -271,7 +264,7 @@ pub(super) fn transcribe(
// jump back out of the Delimited, pop the result_stack and add the new results back to
// the previous results (from outside the Delimited).
mbe::TokenTree::Delimited(mut span, delimited) => {
marker.visit_delim_span(&mut span);
mut_visit::visit_delim_span(&mut span, &mut marker);
stack.push(Frame::Delimited { forest: delimited, idx: 0, span });
result_stack.push(mem::take(&mut result));
}

View file

@ -3,7 +3,6 @@ use crate::expand::{AstFragment, AstFragmentKind};
use syntax::ast;
use syntax::source_map::{DUMMY_SP, dummy_spanned};
use syntax::tokenstream::TokenStream;
use syntax::mut_visit::*;
use syntax::ptr::P;
use syntax::ThinVec;
@ -17,8 +16,7 @@ pub fn placeholder(kind: AstFragmentKind, id: ast::NodeId, vis: Option<ast::Visi
fn mac_placeholder() -> ast::Mac {
ast::Mac {
path: ast::Path { span: DUMMY_SP, segments: Vec::new() },
tts: TokenStream::default().into(),
delim: ast::MacDelimiter::Brace,
args: P(ast::MacArgs::Empty),
span: DUMMY_SP,
prior_type_ascription: None,
}

View file

@ -6,7 +6,7 @@ use syntax::token::{self, TokenKind};
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::symbol::{sym, Symbol};
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
use syntax_expand::base::*;
use syntax_pos::{Span, DUMMY_SP};
@ -26,9 +26,7 @@ pub fn expand_assert<'cx>(
// `core::panic` and `std::panic` are different macros, so we use call-site
// context to pick up whichever is currently in scope.
let sp = cx.with_call_site_ctxt(sp);
let panic_call = Mac {
path: Path::from_ident(Ident::new(sym::panic, sp)),
tts: custom_message.unwrap_or_else(|| {
let tokens = custom_message.unwrap_or_else(|| {
TokenStream::from(TokenTree::token(
TokenKind::lit(token::Str, Symbol::intern(&format!(
"assertion failed: {}",
@ -36,8 +34,11 @@ pub fn expand_assert<'cx>(
)), None),
DUMMY_SP,
))
}).into(),
delim: MacDelimiter::Parenthesis,
});
let args = P(MacArgs::Delimited(DelimSpan::from_single(sp), MacDelimiter::Parenthesis, tokens));
let panic_call = Mac {
path: Path::from_ident(Ident::new(sym::panic, sp)),
args,
span: sp,
prior_type_ascription: None,
};

View file

@ -11,9 +11,9 @@ macro_rules! foo{
pub fn main() {
foo!();
assert!({one! two()}); //~ ERROR expected open delimiter
assert!({one! two()}); //~ ERROR expected one of `(`, `[`, or `{`, found `two`
// regardless of whether nested macro_rules works, the following should at
// least throw a conventional error.
assert!({one! two}); //~ ERROR expected open delimiter
assert!({one! two}); //~ ERROR expected one of `(`, `[`, or `{`, found `two`
}

View file

@ -1,14 +1,14 @@
error: expected open delimiter
error: expected one of `(`, `[`, or `{`, found `two`
--> $DIR/issue-10536.rs:14:19
|
LL | assert!({one! two()});
| ^^^ expected open delimiter
| ^^^ expected one of `(`, `[`, or `{`
error: expected open delimiter
error: expected one of `(`, `[`, or `{`, found `two`
--> $DIR/issue-10536.rs:18:19
|
LL | assert!({one! two});
| ^^^ expected open delimiter
| ^^^ expected one of `(`, `[`, or `{`
error: aborting due to 2 previous errors

View file

@ -1,3 +1,3 @@
fn main() {
foo! bar < //~ ERROR expected open delimiter
foo! bar < //~ ERROR expected one of `(`, `[`, or `{`, found `bar`
}

View file

@ -1,8 +1,8 @@
error: expected open delimiter
error: expected one of `(`, `[`, or `{`, found `bar`
--> $DIR/macro-bad-delimiter-ident.rs:2:10
|
LL | foo! bar <
| ^^^ expected open delimiter
| ^^^ expected one of `(`, `[`, or `{`
error: aborting due to previous error