Skip to content

Commit

Permalink
syntax: Unsupport foo! bar { ... } macros in the parser
Browse files Browse the repository at this point in the history
Unreserve `macro_rules` as a macro name
  • Loading branch information
petrochenkov committed Jul 1, 2019
1 parent 5748825 commit 3f39dc1
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 186 deletions.
5 changes: 1 addition & 4 deletions src/librustc_plugin/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc::util::nodemap::FxHashMap;

use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind, NamedSyntaxExtension};
use syntax::ext::base::MacroExpanderFn;
use syntax::symbol::{Symbol, sym};
use syntax::symbol::Symbol;
use syntax::ast;
use syntax::feature_gate::AttributeType;
use syntax_pos::Span;
Expand Down Expand Up @@ -85,9 +85,6 @@ impl<'a> Registry<'a> {
///
/// This is the most general hook into `libsyntax`'s expansion behavior.
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() {
extension.def_info = Some((ast::CRATE_NODE_ID, self.krate_span));
}
Expand Down
3 changes: 0 additions & 3 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,9 +1109,6 @@ impl<'a> Resolver<'a> {
current_legacy_scope: &mut LegacyScope<'a>) {
self.local_macro_def_scopes.insert(item.id, self.current_module);
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 ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,
Expand Down
45 changes: 12 additions & 33 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ use crate::parse::{DirectoryOwnership, PResult, ParseSess};
use crate::parse::token;
use crate::parse::parser::Parser;
use crate::ptr::P;
use crate::symbol::Symbol;
use crate::symbol::{kw, sym};
use crate::symbol::{sym, Symbol};
use crate::tokenstream::{TokenStream, TokenTree};
use crate::visit::{self, Visitor};
use crate::util::map_in_place::MapInPlace;
Expand Down Expand Up @@ -197,7 +196,6 @@ pub struct Invocation {
pub enum InvocationKind {
Bang {
mac: ast::Mac,
ident: Option<Ident>,
span: Span,
},
Attr {
Expand Down Expand Up @@ -664,13 +662,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
ext: &SyntaxExtension)
-> Option<AstFragment> {
let kind = invoc.fragment_kind;
let (mac, ident, span) = match invoc.kind {
InvocationKind::Bang { mac, ident, span } => (mac, ident, span),
let (mac, span) = match invoc.kind {
InvocationKind::Bang { mac, span } => (mac, span),
_ => unreachable!(),
};
let path = &mac.node.path;

let ident = ident.unwrap_or_else(|| Ident::invalid());
let validate = |this: &mut Self| {
// feature-gate the macro invocation
if let Some((feature, issue)) = ext.unstable_feature {
Expand All @@ -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(())
};

Expand Down Expand Up @@ -729,19 +720,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}

SyntaxExtensionKind::Bang(expander) => {
if ident.name != kw::Invalid {
let msg =
format!("macro {}! expects no ident argument, given '{}'", path, ident);
self.cx.span_err(path.span, &msg);
self.cx.trace_macros_diag();
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
}
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
}
};

Expand Down Expand Up @@ -944,7 +927,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
}

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,
Expand Down Expand Up @@ -1179,13 +1162,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
ast::ItemKind::Mac(..) => {
self.check_attributes(&item.attrs);
item.and_then(|item| match item.node {
ItemKind::Mac(mac) => {
self.collect(AstFragmentKind::Items, InvocationKind::Bang {
mac,
ident: Some(item.ident),
span: item.span,
}).make_items()
}
ItemKind::Mac(mac) => self.collect(
AstFragmentKind::Items, InvocationKind::Bang { mac, span: item.span }
).make_items(),
_ => unreachable!(),
})
}
Expand Down
129 changes: 36 additions & 93 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4353,8 +4353,9 @@ impl<'a> Parser<'a> {

(ident, ast::MacroDef { tokens: tokens.into(), legacy: false })
}
token::Ident(name, _) if name == sym::macro_rules &&
self.look_ahead(1, |t| *t == token::Not) => {
token::Ident(name, false) if name == 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();
Expand Down Expand Up @@ -4434,34 +4435,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 hi = self.prev_span;

Expand All @@ -4471,59 +4444,38 @@ impl<'a> Parser<'a> {
MacStmtStyle::NoBraces
};

if id.name == kw::Invalid {
let mac = respan(lo.to(hi), Mac_ { path: pth, tts, delim });
let node = if delim == MacDelimiter::Brace ||
self.token == token::Semi || self.token == token::Eof {
StmtKind::Mac(P((mac, style, attrs.into())))
}
// 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
// earlier behavior, stop parsing here and emit a warning to avoid breakage.
else if macro_legacy_warnings &&
self.token.can_begin_expr() &&
match self.token.kind {
// These can continue an expression, so we can't stop parsing and warn.
token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
token::BinOp(token::Minus) | token::BinOp(token::Star) |
token::BinOp(token::And) | token::BinOp(token::Or) |
token::AndAnd | token::OrOr |
token::DotDot | token::DotDotDot | token::DotDotEq => false,
_ => true,
} {
self.warn_missing_semicolon();
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,
}
let mac = respan(lo.to(hi), Mac_ { path: pth, tts, delim });
let node = if delim == MacDelimiter::Brace ||
self.token == token::Semi || self.token == token::Eof {
StmtKind::Mac(P((mac, style, attrs.into())))
}
// 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
// earlier behavior, stop parsing here and emit a warning to avoid breakage.
else if macro_legacy_warnings &&
self.token.can_begin_expr() &&
match self.token.kind {
// These can continue an expression, so we can't stop parsing and warn.
token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
token::BinOp(token::Minus) | token::BinOp(token::Star) |
token::BinOp(token::And) | token::BinOp(token::Or) |
token::AndAnd | token::OrOr |
token::DotDot | token::DotDotDot | token::DotDotEq => false,
_ => true,
} {
self.warn_missing_semicolon();
StmtKind::Mac(P((mac, style, attrs.into())))
} else {
// if it has a special ident, it's definitely an item
//
// Require a semicolon or braces.
if style != MacStmtStyle::Braces && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}
let span = lo.to(hi);
Stmt {
id: ast::DUMMY_NODE_ID,
span,
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)
}),
}
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 {
// FIXME: Bad copy of attrs
Expand Down Expand Up @@ -7611,24 +7563,15 @@ impl<'a> Parser<'a> {
// item macro.
let pth = self.parse_path(PathStyle::Mod)?;
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()?;
if delim != MacDelimiter::Brace && !self.eat(&token::Semi) {
self.report_invalid_macro_expansion_item();
}

let hi = self.prev_span;
let mac = respan(mac_lo.to(hi), Mac_ { path: pth, 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));
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/rfcs/rfc-2151-raw-identifiers/macros.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// run-pass
#![feature(decl_macro)]

r#macro_rules! r#struct {
macro_rules! r#struct {
($r#struct:expr) => { $r#struct }
}

Expand Down
8 changes: 2 additions & 6 deletions src/test/ui/issues/issue-10536.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ macro_rules! foo{
pub fn main() {
foo!();

assert!({one! two()});
//~^ ERROR macros that expand to items
//~| ERROR cannot find macro `one!` in this scope
//~| ERROR mismatched types
assert!({one! two()}); //~ ERROR expected open delimiter

// regardless of whether nested macro_rules works, the following should at
// least throw a conventional error.
assert!({one! two});
//~^ ERROR expected `(` or `{`, found `}`
assert!({one! two}); //~ ERROR expected open delimiter
}
38 changes: 7 additions & 31 deletions src/test/ui/issues/issue-10536.stderr
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
error: macros that expand to items must be delimited with braces or followed by a semicolon
--> $DIR/issue-10536.rs:14:22
error: expected open delimiter
--> $DIR/issue-10536.rs:14:19
|
LL | assert!({one! two()});
| ^^
help: change the delimiters to curly braces
|
LL | assert!({one! two {}});
| ^^
help: add a semicolon
|
LL | assert!({one! two();});
| ^
| ^^^ expected open delimiter

error: expected `(` or `{`, found `}`
--> $DIR/issue-10536.rs:21:22
error: expected open delimiter
--> $DIR/issue-10536.rs:18:19
|
LL | assert!({one! two});
| ^ expected `(` or `{`

error: cannot find macro `one!` in this scope
--> $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 `()`
| ^^^ expected open delimiter

error: aborting due to 4 previous errors
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
2 changes: 1 addition & 1 deletion src/test/ui/parser/macro-bad-delimiter-ident.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
fn main() {
foo! bar < //~ ERROR expected `(` or `{`, found `<`
foo! bar < //~ ERROR expected open delimiter
}
6 changes: 3 additions & 3 deletions src/test/ui/parser/macro-bad-delimiter-ident.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error: expected `(` or `{`, found `<`
--> $DIR/macro-bad-delimiter-ident.rs:2:14
error: expected open delimiter
--> $DIR/macro-bad-delimiter-ident.rs:2:10
|
LL | foo! bar <
| ^ expected `(` or `{`
| ^^^ expected open delimiter

error: aborting due to previous error

10 changes: 7 additions & 3 deletions src/test/ui/user-defined-macro-rules.rs
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit 3f39dc1

Please sign in to comment.