Split MacArgs
in two.
`MacArgs` is an enum with three variants: `Empty`, `Delimited`, and `Eq`. It's used in two ways: - For representing attribute macro arguments (e.g. in `AttrItem`), where all three variants are used. - For representing function-like macros (e.g. in `MacCall` and `MacroDef`), where only the `Delimited` variant is used. In other words, `MacArgs` is used in two quite different places due to them having partial overlap. I find this makes the code hard to read. It also leads to various unreachable code paths, and allows invalid values (such as accidentally using `MacArgs::Empty` in a `MacCall`). This commit splits `MacArgs` in two: - `DelimArgs` is a new struct just for the "delimited arguments" case. It is now used in `MacCall` and `MacroDef`. - `AttrArgs` is a renaming of the old `MacArgs` enum for the attribute macro case. Its `Delimited` variant now contains a `DelimArgs`. Various other related things are renamed as well. These changes make the code clearer, avoids several unreachable paths, and disallows the invalid values.
This commit is contained in:
parent
1cbc45942d
commit
3e3a4192d8
33 changed files with 252 additions and 248 deletions
|
@ -15,8 +15,7 @@ extern crate tracing;
|
|||
use rustc_ast as ast;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::Attribute;
|
||||
use rustc_ast::{AttrItem, MetaItem};
|
||||
use rustc_ast::{AttrItem, Attribute, MetaItem};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{Applicability, Diagnostic, FatalError, Level, PResult};
|
||||
|
@ -257,10 +256,12 @@ pub fn parse_cfg_attr(
|
|||
parse_sess: &ParseSess,
|
||||
) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
|
||||
match attr.get_normal_item().args {
|
||||
ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
|
||||
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
|
||||
if !tokens.is_empty() =>
|
||||
{
|
||||
let msg = "wrong `cfg_attr` delimiters";
|
||||
crate::validate_attr::check_meta_bad_delim(parse_sess, dspan, delim, msg);
|
||||
match parse_in(parse_sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
|
||||
match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
|
||||
Ok(r) => return Some(r),
|
||||
Err(mut e) => {
|
||||
e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
|
||||
|
|
|
@ -1501,7 +1501,7 @@ impl<'a> Parser<'a> {
|
|||
let lo = path.span;
|
||||
let mac = P(MacCall {
|
||||
path,
|
||||
args: self.parse_mac_args()?,
|
||||
args: self.parse_delim_args()?,
|
||||
prior_type_ascription: self.last_type_ascription,
|
||||
});
|
||||
(lo.to(self.prev_token.span), ExprKind::MacCall(mac))
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree,
|
|||
use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind};
|
||||
use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData};
|
||||
use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
|
||||
use rustc_ast::{MacArgs, MacCall, MacDelimiter};
|
||||
use rustc_ast::{MacCall, MacDelimiter};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{struct_span_err, Applicability, IntoDiagnostic, PResult, StashKey};
|
||||
use rustc_span::edition::Edition;
|
||||
|
@ -471,7 +471,7 @@ impl<'a> Parser<'a> {
|
|||
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> {
|
||||
let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`
|
||||
self.expect(&token::Not)?; // `!`
|
||||
match self.parse_mac_args() {
|
||||
match self.parse_delim_args() {
|
||||
// `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
|
||||
Ok(args) => {
|
||||
self.eat_semi_for_macro_if_needed(&args);
|
||||
|
@ -1867,7 +1867,7 @@ impl<'a> Parser<'a> {
|
|||
fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> {
|
||||
let ident = self.parse_ident()?;
|
||||
let body = if self.check(&token::OpenDelim(Delimiter::Brace)) {
|
||||
self.parse_mac_args()? // `MacBody`
|
||||
self.parse_delim_args()? // `MacBody`
|
||||
} else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
|
||||
let params = self.parse_token_tree(); // `MacParams`
|
||||
let pspan = params.span();
|
||||
|
@ -1880,7 +1880,7 @@ impl<'a> Parser<'a> {
|
|||
let arrow = TokenTree::token_alone(token::FatArrow, pspan.between(bspan)); // `=>`
|
||||
let tokens = TokenStream::new(vec![params, arrow, body]);
|
||||
let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
|
||||
P(MacArgs::Delimited(dspan, MacDelimiter::Brace, tokens))
|
||||
P(DelimArgs { dspan, delim: MacDelimiter::Brace, tokens })
|
||||
} else {
|
||||
return self.unexpected();
|
||||
};
|
||||
|
@ -1935,7 +1935,7 @@ impl<'a> Parser<'a> {
|
|||
.emit();
|
||||
}
|
||||
|
||||
let body = self.parse_mac_args()?;
|
||||
let body = self.parse_delim_args()?;
|
||||
self.eat_semi_for_macro_if_needed(&body);
|
||||
self.complain_if_pub_macro(vis, true);
|
||||
|
||||
|
@ -1974,14 +1974,14 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn eat_semi_for_macro_if_needed(&mut self, args: &MacArgs) {
|
||||
fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs) {
|
||||
if args.need_semicolon() && !self.eat(&token::Semi) {
|
||||
self.report_invalid_macro_expansion_item(args);
|
||||
}
|
||||
}
|
||||
|
||||
fn report_invalid_macro_expansion_item(&self, args: &MacArgs) {
|
||||
let span = args.span().expect("undelimited macro call");
|
||||
fn report_invalid_macro_expansion_item(&self, args: &DelimArgs) {
|
||||
let span = args.dspan.entire();
|
||||
let mut err = self.struct_span_err(
|
||||
span,
|
||||
"macros that expand to items must be delimited with braces or followed by a semicolon",
|
||||
|
@ -1990,10 +1990,7 @@ impl<'a> Parser<'a> {
|
|||
// macros within the same crate (that we can fix), which is sad.
|
||||
if !span.from_expansion() {
|
||||
if self.unclosed_delims.is_empty() {
|
||||
let DelimSpan { open, close } = match args {
|
||||
MacArgs::Empty | MacArgs::Eq(..) => unreachable!(),
|
||||
MacArgs::Delimited(dspan, ..) => *dspan,
|
||||
};
|
||||
let DelimSpan { open, close } = args.dspan;
|
||||
err.multipart_suggestion(
|
||||
"change the delimiters to curly braces",
|
||||
vec![(open, "{".to_string()), (close, '}'.to_string())],
|
||||
|
|
|
@ -25,8 +25,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
|||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::AttrId;
|
||||
use rustc_ast::DUMMY_NODE_ID;
|
||||
use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, Extern};
|
||||
use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacArgsEq, MacDelimiter, Mutability, StrLit};
|
||||
use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, DelimArgs, Extern};
|
||||
use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, MacDelimiter, Mutability, StrLit};
|
||||
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
@ -1249,39 +1249,40 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_mac_args(&mut self) -> PResult<'a, P<MacArgs>> {
|
||||
self.parse_mac_args_common(true).map(P)
|
||||
fn parse_delim_args(&mut self) -> PResult<'a, P<DelimArgs>> {
|
||||
if let Some(args) = self.parse_delim_args_inner() { Ok(P(args)) } else { self.unexpected() }
|
||||
}
|
||||
|
||||
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, MacArgs> {
|
||||
Ok(
|
||||
if self.check(&token::OpenDelim(Delimiter::Parenthesis))
|
||||
|| self.check(&token::OpenDelim(Delimiter::Bracket))
|
||||
|| self.check(&token::OpenDelim(Delimiter::Brace))
|
||||
{
|
||||
match self.parse_token_tree() {
|
||||
TokenTree::Delimited(dspan, delim, tokens) =>
|
||||
// We've confirmed above that there is a delimiter so unwrapping is OK.
|
||||
{
|
||||
MacArgs::Delimited(dspan, MacDelimiter::from_token(delim).unwrap(), tokens)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else if !delimited_only {
|
||||
if self.eat(&token::Eq) {
|
||||
let eq_span = self.prev_token.span;
|
||||
MacArgs::Eq(eq_span, MacArgsEq::Ast(self.parse_expr_force_collect()?))
|
||||
} else {
|
||||
MacArgs::Empty
|
||||
}
|
||||
fn parse_attr_args(&mut self) -> PResult<'a, AttrArgs> {
|
||||
Ok(if let Some(args) = self.parse_delim_args_inner() {
|
||||
AttrArgs::Delimited(args)
|
||||
} else {
|
||||
if self.eat(&token::Eq) {
|
||||
let eq_span = self.prev_token.span;
|
||||
AttrArgs::Eq(eq_span, AttrArgsEq::Ast(self.parse_expr_force_collect()?))
|
||||
} else {
|
||||
return self.unexpected();
|
||||
},
|
||||
)
|
||||
AttrArgs::Empty
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_delim_args_inner(&mut self) -> Option<DelimArgs> {
|
||||
if self.check(&token::OpenDelim(Delimiter::Parenthesis))
|
||||
|| self.check(&token::OpenDelim(Delimiter::Bracket))
|
||||
|| self.check(&token::OpenDelim(Delimiter::Brace))
|
||||
{
|
||||
match self.parse_token_tree() {
|
||||
// We've confirmed above that there is a delimiter so unwrapping is OK.
|
||||
TokenTree::Delimited(dspan, delim, tokens) => Some(DelimArgs {
|
||||
dspan,
|
||||
delim: MacDelimiter::from_token(delim).unwrap(),
|
||||
tokens,
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_or_use_outer_attributes(
|
||||
|
|
|
@ -693,7 +693,7 @@ impl<'a> Parser<'a> {
|
|||
/// Parse macro invocation
|
||||
fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
|
||||
self.bump();
|
||||
let args = self.parse_mac_args()?;
|
||||
let args = self.parse_delim_args()?;
|
||||
let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
|
||||
Ok(PatKind::MacCall(mac))
|
||||
}
|
||||
|
|
|
@ -167,14 +167,13 @@ impl<'a> Parser<'a> {
|
|||
/// Parses a statement macro `mac!(args)` provided a `path` representing `mac`.
|
||||
/// At this point, the `!` token after the path has already been eaten.
|
||||
fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> {
|
||||
let args = self.parse_mac_args()?;
|
||||
let delim = args.delim();
|
||||
let args = self.parse_delim_args()?;
|
||||
let delim = args.delim.to_token();
|
||||
let hi = self.prev_token.span;
|
||||
|
||||
let style = match delim {
|
||||
Some(Delimiter::Brace) => MacStmtStyle::Braces,
|
||||
Some(_) => MacStmtStyle::NoBraces,
|
||||
None => unreachable!(),
|
||||
Delimiter::Brace => MacStmtStyle::Braces,
|
||||
_ => MacStmtStyle::NoBraces,
|
||||
};
|
||||
|
||||
let mac = P(MacCall { path, args, prior_type_ascription: self.last_type_ascription });
|
||||
|
|
|
@ -665,7 +665,7 @@ impl<'a> Parser<'a> {
|
|||
// Macro invocation in type position
|
||||
Ok(TyKind::MacCall(P(MacCall {
|
||||
path,
|
||||
args: self.parse_mac_args()?,
|
||||
args: self.parse_delim_args()?,
|
||||
prior_type_ascription: self.last_type_ascription,
|
||||
})))
|
||||
} else if allow_plus == AllowPlus::Yes && self.check_plus() {
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
use crate::parse_in;
|
||||
|
||||
use rustc_ast::tokenstream::DelimSpan;
|
||||
use rustc_ast::{self as ast, Attribute, MacArgs, MacArgsEq, MacDelimiter, MetaItem, MetaItemKind};
|
||||
use rustc_ast::MetaItemKind;
|
||||
use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MacDelimiter, MetaItem};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, FatalError, PResult};
|
||||
use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
|
||||
|
@ -24,7 +25,7 @@ pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
|
|||
Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
|
||||
check_builtin_attribute(sess, attr, *name, *template)
|
||||
}
|
||||
_ if let MacArgs::Eq(..) = attr.get_normal_item().args => {
|
||||
_ if let AttrArgs::Eq(..) = attr.get_normal_item().args => {
|
||||
// All key-value attributes are restricted to meta-item syntax.
|
||||
parse_meta(sess, attr)
|
||||
.map_err(|mut err| {
|
||||
|
@ -42,13 +43,13 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
|
|||
span: attr.span,
|
||||
path: item.path.clone(),
|
||||
kind: match &item.args {
|
||||
MacArgs::Empty => MetaItemKind::Word,
|
||||
MacArgs::Delimited(dspan, delim, t) => {
|
||||
AttrArgs::Empty => MetaItemKind::Word,
|
||||
AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => {
|
||||
check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
|
||||
let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
|
||||
let nmis = parse_in(sess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?;
|
||||
MetaItemKind::List(nmis)
|
||||
}
|
||||
MacArgs::Eq(_, MacArgsEq::Ast(expr)) => {
|
||||
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => {
|
||||
if let ast::ExprKind::Lit(token_lit) = expr.kind
|
||||
&& let Ok(lit) = ast::Lit::from_token_lit(token_lit, expr.span)
|
||||
{
|
||||
|
@ -78,7 +79,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
|
|||
return Err(err);
|
||||
}
|
||||
}
|
||||
MacArgs::Eq(_, MacArgsEq::Hir(lit)) => MetaItemKind::NameValue(lit.clone()),
|
||||
AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => MetaItemKind::NameValue(lit.clone()),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue