Rollup merge of #62258 - petrochenkov:idclean, r=Centril

syntax: Unsupport `foo! bar { ... }` macros in the parser

Their support in expansion was removed in https://github.com/rust-lang/rust/pull/61606.

Also un-reserve `macro_rules` as a macro name, there's no ambiguity between `macro_rules` definitions and macro calls (it also wasn't reserved correctly).

cc https://github.com/rust-lang-nursery/wg-grammar/issues/51
This commit is contained in:
Mazdak Farrokhzad 2019-07-04 01:38:49 +02:00 committed by GitHub
commit 8867ba19de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 111 additions and 230 deletions

View File

@ -6,7 +6,7 @@ use rustc::util::nodemap::FxHashMap;
use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind, NamedSyntaxExtension}; use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind, NamedSyntaxExtension};
use syntax::ext::base::MacroExpanderFn; use syntax::ext::base::MacroExpanderFn;
use syntax::symbol::{Symbol, sym}; use syntax::symbol::Symbol;
use syntax::ast; use syntax::ast;
use syntax::feature_gate::AttributeType; use syntax::feature_gate::AttributeType;
use syntax_pos::Span; use syntax_pos::Span;
@ -85,9 +85,6 @@ impl<'a> Registry<'a> {
/// ///
/// This is the most general hook into `libsyntax`'s expansion behavior. /// This is the most general hook into `libsyntax`'s expansion behavior.
pub fn register_syntax_extension(&mut self, name: ast::Name, mut extension: SyntaxExtension) { pub fn register_syntax_extension(&mut self, name: ast::Name, mut extension: SyntaxExtension) {
if name == sym::macro_rules {
panic!("user-defined macros may not be named `macro_rules`");
}
if extension.def_info.is_none() { if extension.def_info.is_none() {
extension.def_info = Some((ast::CRATE_NODE_ID, self.krate_span)); extension.def_info = Some((ast::CRATE_NODE_ID, self.krate_span));
} }

View File

@ -1109,9 +1109,6 @@ impl<'a> Resolver<'a> {
current_legacy_scope: &mut LegacyScope<'a>) { current_legacy_scope: &mut LegacyScope<'a>) {
self.local_macro_def_scopes.insert(item.id, self.current_module); self.local_macro_def_scopes.insert(item.id, self.current_module);
let ident = item.ident; let ident = item.ident;
if ident.name == sym::macro_rules {
self.session.span_err(item.span, "user-defined macros may not be named `macro_rules`");
}
let def_id = self.definitions.local_def_id(item.id); let def_id = self.definitions.local_def_id(item.id);
let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess, let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,

View File

@ -13,8 +13,7 @@ use crate::parse::{DirectoryOwnership, PResult, ParseSess};
use crate::parse::token; use crate::parse::token;
use crate::parse::parser::Parser; use crate::parse::parser::Parser;
use crate::ptr::P; use crate::ptr::P;
use crate::symbol::Symbol; use crate::symbol::{sym, Symbol};
use crate::symbol::{kw, sym};
use crate::tokenstream::{TokenStream, TokenTree}; use crate::tokenstream::{TokenStream, TokenTree};
use crate::visit::{self, Visitor}; use crate::visit::{self, Visitor};
use crate::util::map_in_place::MapInPlace; use crate::util::map_in_place::MapInPlace;
@ -197,7 +196,6 @@ pub struct Invocation {
pub enum InvocationKind { pub enum InvocationKind {
Bang { Bang {
mac: ast::Mac, mac: ast::Mac,
ident: Option<Ident>,
span: Span, span: Span,
}, },
Attr { Attr {
@ -664,13 +662,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
ext: &SyntaxExtension) ext: &SyntaxExtension)
-> Option<AstFragment> { -> Option<AstFragment> {
let kind = invoc.fragment_kind; let kind = invoc.fragment_kind;
let (mac, ident, span) = match invoc.kind { let (mac, span) = match invoc.kind {
InvocationKind::Bang { mac, ident, span } => (mac, ident, span), InvocationKind::Bang { mac, span } => (mac, span),
_ => unreachable!(), _ => unreachable!(),
}; };
let path = &mac.node.path; let path = &mac.node.path;
let ident = ident.unwrap_or_else(|| Ident::invalid());
let validate = |this: &mut Self| { let validate = |this: &mut Self| {
// feature-gate the macro invocation // feature-gate the macro invocation
if let Some((feature, issue)) = ext.unstable_feature { if let Some((feature, issue)) = ext.unstable_feature {
@ -690,12 +687,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
} }
} }
if ident.name != kw::Invalid {
let msg = format!("macro {}! expects no ident argument, given '{}'", path, ident);
this.cx.span_err(path.span, &msg);
this.cx.trace_macros_diag();
return Err(kind.dummy(span));
}
Ok(()) Ok(())
}; };
@ -729,19 +720,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
} }
SyntaxExtensionKind::Bang(expander) => { SyntaxExtensionKind::Bang(expander) => {
if ident.name != kw::Invalid { self.gate_proc_macro_expansion_kind(span, kind);
let msg = let tok_result = expander.expand(self.cx, span, mac.node.stream());
format!("macro {}! expects no ident argument, given '{}'", path, ident); let result = self.parse_ast_fragment(tok_result, kind, path, span);
self.cx.span_err(path.span, &msg); self.gate_proc_macro_expansion(span, &result);
self.cx.trace_macros_diag(); result
kind.dummy(span)
} else {
self.gate_proc_macro_expansion_kind(span, kind);
let tok_result = expander.expand(self.cx, span, mac.node.stream());
let result = self.parse_ast_fragment(tok_result, kind, path, span);
self.gate_proc_macro_expansion(span, &result);
result
}
} }
}; };
@ -944,7 +927,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
} }
fn collect_bang(&mut self, mac: ast::Mac, span: Span, kind: AstFragmentKind) -> AstFragment { fn collect_bang(&mut self, mac: ast::Mac, span: Span, kind: AstFragmentKind) -> AstFragment {
self.collect(kind, InvocationKind::Bang { mac, ident: None, span }) self.collect(kind, InvocationKind::Bang { mac, span })
} }
fn collect_attr(&mut self, fn collect_attr(&mut self,
@ -1179,13 +1162,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
ast::ItemKind::Mac(..) => { ast::ItemKind::Mac(..) => {
self.check_attributes(&item.attrs); self.check_attributes(&item.attrs);
item.and_then(|item| match item.node { item.and_then(|item| match item.node {
ItemKind::Mac(mac) => { ItemKind::Mac(mac) => self.collect(
self.collect(AstFragmentKind::Items, InvocationKind::Bang { AstFragmentKind::Items, InvocationKind::Bang { mac, span: item.span }
mac, ).make_items(),
ident: Some(item.ident),
span: item.span,
}).make_items()
}
_ => unreachable!(), _ => unreachable!(),
}) })
} }

View File

@ -4324,51 +4324,49 @@ impl<'a> Parser<'a> {
fn eat_macro_def(&mut self, attrs: &[Attribute], vis: &Visibility, lo: Span) fn eat_macro_def(&mut self, attrs: &[Attribute], vis: &Visibility, lo: Span)
-> PResult<'a, Option<P<Item>>> { -> PResult<'a, Option<P<Item>>> {
let token_lo = self.token.span; let token_lo = self.token.span;
let (ident, def) = match self.token.kind { let (ident, def) = if self.eat_keyword(kw::Macro) {
token::Ident(name, false) if name == kw::Macro => { let ident = self.parse_ident()?;
self.bump(); let tokens = if self.check(&token::OpenDelim(token::Brace)) {
let ident = self.parse_ident()?; match self.parse_token_tree() {
let tokens = if self.check(&token::OpenDelim(token::Brace)) { TokenTree::Delimited(_, _, tts) => tts,
match self.parse_token_tree() { _ => unreachable!(),
TokenTree::Delimited(_, _, tts) => tts, }
_ => unreachable!(), } else if self.check(&token::OpenDelim(token::Paren)) {
} let args = self.parse_token_tree();
} else if self.check(&token::OpenDelim(token::Paren)) { let body = if self.check(&token::OpenDelim(token::Brace)) {
let args = self.parse_token_tree(); self.parse_token_tree()
let body = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_token_tree()
} else {
self.unexpected()?;
unreachable!()
};
TokenStream::new(vec![
args.into(),
TokenTree::token(token::FatArrow, token_lo.to(self.prev_span)).into(),
body.into(),
])
} else { } else {
self.unexpected()?; self.unexpected()?;
unreachable!() unreachable!()
}; };
TokenStream::new(vec![
args.into(),
TokenTree::token(token::FatArrow, token_lo.to(self.prev_span)).into(),
body.into(),
])
} else {
self.unexpected()?;
unreachable!()
};
(ident, ast::MacroDef { tokens: tokens.into(), legacy: false }) (ident, ast::MacroDef { tokens: tokens.into(), legacy: false })
} else if self.check_keyword(sym::macro_rules) &&
self.look_ahead(1, |t| *t == token::Not) &&
self.look_ahead(2, |t| t.is_ident()) {
let prev_span = self.prev_span;
self.complain_if_pub_macro(&vis.node, prev_span);
self.bump();
self.bump();
let ident = self.parse_ident()?;
let (delim, tokens) = self.expect_delimited_token_tree()?;
if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
} }
token::Ident(name, _) if name == sym::macro_rules &&
self.look_ahead(1, |t| *t == token::Not) => {
let prev_span = self.prev_span;
self.complain_if_pub_macro(&vis.node, prev_span);
self.bump();
self.bump();
let ident = self.parse_ident()?; (ident, ast::MacroDef { tokens, legacy: true })
let (delim, tokens) = self.expect_delimited_token_tree()?; } else {
if delim != MacDelimiter::Brace && !self.eat(&token::Semi) { return Ok(None);
self.report_invalid_macro_expansion_item();
}
(ident, ast::MacroDef { tokens, legacy: true })
}
_ => return Ok(None),
}; };
let span = lo.to(self.prev_span); let span = lo.to(self.prev_span);
@ -4412,14 +4410,14 @@ impl<'a> Parser<'a> {
!self.is_existential_type_decl() && !self.is_existential_type_decl() &&
!self.is_auto_trait_item() && !self.is_auto_trait_item() &&
!self.is_async_fn() { !self.is_async_fn() {
let pth = self.parse_path(PathStyle::Expr)?; let path = self.parse_path(PathStyle::Expr)?;
if !self.eat(&token::Not) { if !self.eat(&token::Not) {
let expr = if self.check(&token::OpenDelim(token::Brace)) { let expr = if self.check(&token::OpenDelim(token::Brace)) {
self.parse_struct_expr(lo, pth, ThinVec::new())? self.parse_struct_expr(lo, path, ThinVec::new())?
} else { } else {
let hi = self.prev_span; let hi = self.prev_span;
self.mk_expr(lo.to(hi), ExprKind::Path(None, pth), ThinVec::new()) self.mk_expr(lo.to(hi), ExprKind::Path(None, path), ThinVec::new())
}; };
let expr = self.with_res(Restrictions::STMT_EXPR, |this| { let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
@ -4434,34 +4432,6 @@ impl<'a> Parser<'a> {
})); }));
} }
// it's a macro invocation
let id = match self.token.kind {
token::OpenDelim(_) => Ident::invalid(), // no special identifier
_ => self.parse_ident()?,
};
// check that we're pointing at delimiters (need to check
// again after the `if`, because of `parse_ident`
// consuming more tokens).
match self.token.kind {
token::OpenDelim(_) => {}
_ => {
// we only expect an ident if we didn't parse one
// above.
let ident_str = if id.name == kw::Invalid {
"identifier, "
} else {
""
};
let tok_str = self.this_token_descr();
let mut err = self.fatal(&format!("expected {}`(` or `{{`, found {}",
ident_str,
tok_str));
err.span_label(self.token.span, format!("expected {}`(` or `{{`", ident_str));
return Err(err)
},
}
let (delim, tts) = self.expect_delimited_token_tree()?; let (delim, tts) = self.expect_delimited_token_tree()?;
let hi = self.prev_span; let hi = self.prev_span;
@ -4471,59 +4441,38 @@ impl<'a> Parser<'a> {
MacStmtStyle::NoBraces MacStmtStyle::NoBraces
}; };
if id.name == kw::Invalid { let mac = respan(lo.to(hi), Mac_ { path, tts, delim });
let mac = respan(lo.to(hi), Mac_ { path: pth, tts, delim }); let node = if delim == MacDelimiter::Brace ||
let node = if delim == MacDelimiter::Brace || self.token == token::Semi || self.token == token::Eof {
self.token == token::Semi || self.token == token::Eof { StmtKind::Mac(P((mac, style, attrs.into())))
StmtKind::Mac(P((mac, style, attrs.into()))) }
} // We used to incorrectly stop parsing macro-expanded statements here.
// We used to incorrectly stop parsing macro-expanded statements here. // If the next token will be an error anyway but could have parsed with the
// If the next token will be an error anyway but could have parsed with the // earlier behavior, stop parsing here and emit a warning to avoid breakage.
// earlier behavior, stop parsing here and emit a warning to avoid breakage. else if macro_legacy_warnings &&
else if macro_legacy_warnings && self.token.can_begin_expr() &&
self.token.can_begin_expr() && match self.token.kind {
match self.token.kind { // These can continue an expression, so we can't stop parsing and warn.
// These can continue an expression, so we can't stop parsing and warn. token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) | token::BinOp(token::Minus) | token::BinOp(token::Star) |
token::BinOp(token::Minus) | token::BinOp(token::Star) | token::BinOp(token::And) | token::BinOp(token::Or) |
token::BinOp(token::And) | token::BinOp(token::Or) | token::AndAnd | token::OrOr |
token::AndAnd | token::OrOr | token::DotDot | token::DotDotDot | token::DotDotEq => false,
token::DotDot | token::DotDotDot | token::DotDotEq => false, _ => true,
_ => true, } {
} { self.warn_missing_semicolon();
self.warn_missing_semicolon(); StmtKind::Mac(P((mac, style, attrs.into())))
StmtKind::Mac(P((mac, style, attrs.into())))
} else {
let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
let e = self.maybe_recover_from_bad_qpath(e, true)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
StmtKind::Expr(e)
};
Stmt {
id: ast::DUMMY_NODE_ID,
span: lo.to(hi),
node,
}
} else { } else {
// if it has a special ident, it's definitely an item let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
// let e = self.maybe_recover_from_bad_qpath(e, true)?;
// Require a semicolon or braces. let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
if style != MacStmtStyle::Braces && !self.eat(&token::Semi) { let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
self.report_invalid_macro_expansion_item(); StmtKind::Expr(e)
} };
let span = lo.to(hi); Stmt {
Stmt { id: ast::DUMMY_NODE_ID,
id: ast::DUMMY_NODE_ID, span: lo.to(hi),
span, node,
node: StmtKind::Item({
self.mk_item(
span, id /*id is good here*/,
ItemKind::Mac(respan(span, Mac_ { path: pth, tts, delim })),
respan(lo, VisibilityKind::Inherited),
attrs)
}),
}
} }
} else { } else {
// FIXME: Bad copy of attrs // FIXME: Bad copy of attrs
@ -7619,26 +7568,17 @@ impl<'a> Parser<'a> {
let mac_lo = self.token.span; let mac_lo = self.token.span;
// item macro. // item macro.
let pth = self.parse_path(PathStyle::Mod)?; let path = self.parse_path(PathStyle::Mod)?;
self.expect(&token::Not)?; self.expect(&token::Not)?;
// a 'special' identifier (like what `macro_rules!` uses)
// is optional. We should eventually unify invoc syntax
// and remove this.
let id = if self.token.is_ident() {
self.parse_ident()?
} else {
Ident::invalid() // no special identifier
};
// eat a matched-delimiter token tree:
let (delim, tts) = self.expect_delimited_token_tree()?; let (delim, tts) = self.expect_delimited_token_tree()?;
if delim != MacDelimiter::Brace && !self.eat(&token::Semi) { if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item(); self.report_invalid_macro_expansion_item();
} }
let hi = self.prev_span; let hi = self.prev_span;
let mac = respan(mac_lo.to(hi), Mac_ { path: pth, tts, delim }); let mac = respan(mac_lo.to(hi), Mac_ { path, tts, delim });
let item = self.mk_item(lo.to(hi), id, ItemKind::Mac(mac), visibility, attrs); let item =
self.mk_item(lo.to(hi), Ident::invalid(), ItemKind::Mac(mac), visibility, attrs);
return Ok(Some(item)); return Ok(Some(item));
} }
@ -7664,9 +7604,9 @@ impl<'a> Parser<'a> {
!(self.is_async_fn() && self.token.span.rust_2015()) { !(self.is_async_fn() && self.token.span.rust_2015()) {
let prev_span = self.prev_span; let prev_span = self.prev_span;
let lo = self.token.span; let lo = self.token.span;
let pth = self.parse_path(PathStyle::Mod)?; let path = self.parse_path(PathStyle::Mod)?;
if pth.segments.len() == 1 { if path.segments.len() == 1 {
if !self.eat(&token::Not) { if !self.eat(&token::Not) {
return Err(self.missing_assoc_item_kind_err(item_kind, prev_span)); return Err(self.missing_assoc_item_kind_err(item_kind, prev_span));
} }
@ -7686,7 +7626,7 @@ impl<'a> Parser<'a> {
self.expect(&token::Semi)?; self.expect(&token::Semi)?;
} }
Ok(Some(respan(lo.to(self.prev_span), Mac_ { path: pth, tts, delim }))) Ok(Some(respan(lo.to(self.prev_span), Mac_ { path, tts, delim })))
} else { } else {
Ok(None) Ok(None)
} }

View File

@ -1,7 +1,7 @@
// run-pass // run-pass
#![feature(decl_macro)] #![feature(decl_macro)]
r#macro_rules! r#struct { macro_rules! r#struct {
($r#struct:expr) => { $r#struct } ($r#struct:expr) => { $r#struct }
} }

View File

@ -11,13 +11,9 @@ macro_rules! foo{
pub fn main() { pub fn main() {
foo!(); foo!();
assert!({one! two()}); assert!({one! two()}); //~ ERROR expected open delimiter
//~^ ERROR macros that expand to items
//~| ERROR cannot find macro `one!` in this scope
//~| ERROR mismatched types
// regardless of whether nested macro_rules works, the following should at // regardless of whether nested macro_rules works, the following should at
// least throw a conventional error. // least throw a conventional error.
assert!({one! two}); assert!({one! two}); //~ ERROR expected open delimiter
//~^ ERROR expected `(` or `{`, found `}`
} }

View File

@ -1,38 +1,14 @@
error: macros that expand to items must be delimited with braces or followed by a semicolon error: expected open delimiter
--> $DIR/issue-10536.rs:14:22 --> $DIR/issue-10536.rs:14:19
| |
LL | assert!({one! two()}); LL | assert!({one! two()});
| ^^ | ^^^ expected open delimiter
help: change the delimiters to curly braces
|
LL | assert!({one! two {}});
| ^^
help: add a semicolon
|
LL | assert!({one! two();});
| ^
error: expected `(` or `{`, found `}` error: expected open delimiter
--> $DIR/issue-10536.rs:21:22 --> $DIR/issue-10536.rs:18:19
| |
LL | assert!({one! two}); LL | assert!({one! two});
| ^ expected `(` or `{` | ^^^ expected open delimiter
error: cannot find macro `one!` in this scope error: aborting due to 2 previous errors
--> $DIR/issue-10536.rs:14:14
|
LL | assert!({one! two()});
| ^^^
error[E0308]: mismatched types
--> $DIR/issue-10536.rs:14:13
|
LL | assert!({one! two()});
| ^^^^^^^^^^^^ expected bool, found ()
|
= note: expected type `bool`
found type `()`
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

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

View File

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

View File

@ -1,5 +1,9 @@
#![allow(unused_macros)] // check-pass
macro_rules! macro_rules { () => {} } //~ ERROR user-defined macros may not be named `macro_rules` macro_rules! macro_rules { () => { struct S; } } // OK
fn main() {} macro_rules! {} // OK, calls the macro defined above
fn main() {
let s = S;
}

View File

@ -1,8 +0,0 @@
error: user-defined macros may not be named `macro_rules`
--> $DIR/user-defined-macro-rules.rs:3:1
|
LL | macro_rules! macro_rules { () => {} }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error