Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntactically permit postfix macros to reject them later #78849

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,7 @@ pub struct MacCall {
pub path: Path,
pub args: P<MacArgs>,
pub prior_type_ascription: Option<(Span, bool)>,
pub postfix_self_arg: Option<P<Expr>>,
}

impl MacCall {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,10 @@ pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
}

pub fn noop_visit_mac<T: MutVisitor>(mac: &mut MacCall, vis: &mut T) {
let MacCall { path, args, prior_type_ascription: _ } = mac;
let MacCall { path, args, prior_type_ascription: _, postfix_self_arg } = mac;
if let Some(postfix_self_arg) = postfix_self_arg {
vis.visit_expr(postfix_self_arg);
}
vis.visit_path(path);
visit_mac_args(args, vis);
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1642,6 +1642,10 @@ impl<'a> State<'a> {
}

crate fn print_mac(&mut self, m: &ast::MacCall) {
if let Some(arg) = &m.postfix_self_arg {
self.print_expr(&*arg);
self.s.word(".");
}
self.print_mac_common(
Some(MacHeader::Path(&m.path)),
true,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub fn expand_assert<'cx>(
path: Path::from_ident(Ident::new(sym::panic, sp)),
args,
prior_type_ascription: None,
postfix_self_arg: None,
};
let if_expr = cx.expr_if(
sp,
Expand Down
41 changes: 39 additions & 2 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span()));
}

// Sanity check: ensure that no postfix macro slips through
// and accidentially gets expanded.
if let InvocationKind::Bang { mac, .. } = &invoc.kind {
mac.postfix_self_arg.as_ref().expect_none("postfix macro survived until expansion");
}

let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
ExpandResult::Ready(match invoc.kind {
InvocationKind::Bang { mac, .. } => match ext {
Expand Down Expand Up @@ -1146,6 +1152,26 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
}
}
}

// Postfix macro calls can be parsed to allow proc macros to support the syntax,
// but they may not be expanded.
fn check_postfix_mac_call(
&mut self,
call: &mut ast::MacCall,
span: Span,
) -> Option<P<ast::Expr>> {
let postfix_self_arg = call.postfix_self_arg.take();
if postfix_self_arg.is_some() {
let mut err = self.cx.struct_span_err(
span,
&format!("forbidden postfix macro call `{}`", pprust::path_to_string(&call.path)),
);
err.span_label(call.path.span, "macros can't be called in postfix position");

err.emit();
}
postfix_self_arg
}
}

impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
Expand Down Expand Up @@ -1175,8 +1201,13 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.into_inner();
}

if let ast::ExprKind::MacCall(mac) = expr.kind {
if let ast::ExprKind::MacCall(mut mac) = expr.kind {
self.check_attributes(&expr.attrs);
if let Some(postfix_self_arg) = self.check_postfix_mac_call(&mut mac, expr.span) {
let mut self_arg = postfix_self_arg.into_inner();
ensure_sufficient_stack(|| noop_visit_expr(&mut self_arg, self));
return self_arg;
}
self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr().into_inner()
} else {
ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self));
Expand Down Expand Up @@ -1322,8 +1353,14 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.map(|expr| expr.into_inner());
}

if let ast::ExprKind::MacCall(mac) = expr.kind {
if let ast::ExprKind::MacCall(mut mac) = expr.kind {
self.check_attributes(&expr.attrs);

if let Some(postfix_self_arg) = self.check_postfix_mac_call(&mut mac, expr.span) {
let mut self_arg = postfix_self_arg.into_inner();
noop_visit_expr(&mut self_arg, self);
return Some(self_arg);
}
self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr)
.make_opt_expr()
.map(|expr| expr.into_inner())
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_span)]
#![feature(option_expect_none)]
#![feature(try_blocks)]

#[macro_use]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_expand/src/placeholders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub fn placeholder(
path: ast::Path { span: DUMMY_SP, segments: Vec::new(), tokens: None },
args: P(ast::MacArgs::Empty),
prior_type_ascription: None,
postfix_self_arg: None,
}
}

Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,21 @@ impl<'a> Parser<'a> {
let fn_span = fn_span_lo.to(self.prev_token.span);
let span = lo.to(self.prev_token.span);
Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args, fn_span), AttrVec::new()))
} else if self.eat(&token::Not) {
// Postfix macro call
let path = ast::Path {
segments: vec![segment],
span: fn_span_lo.to(self.prev_token.span),
tokens: None,
};
let mac = MacCall {
path,
args: self.parse_mac_args()?,
prior_type_ascription: self.last_type_ascription,
postfix_self_arg: Some(self_arg),
};
let span = lo.to(self.prev_token.span);
Ok(self.mk_expr(span, ExprKind::MacCall(mac), AttrVec::new()))
} else {
// Field access `expr.f`
if let Some(args) = segment.args {
Expand Down Expand Up @@ -1215,6 +1230,7 @@ impl<'a> Parser<'a> {
path,
args: self.parse_mac_args()?,
prior_type_ascription: self.last_type_ascription,
postfix_self_arg: None,
};
(self.prev_token.span, ExprKind::MacCall(mac))
} else if self.check(&token::OpenDelim(token::Brace)) {
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,12 @@ impl<'a> Parser<'a> {
let args = self.parse_mac_args()?; // `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
self.eat_semi_for_macro_if_needed(&args);
self.complain_if_pub_macro(vis, false);
Ok(MacCall { path, args, prior_type_ascription: self.last_type_ascription })
Ok(MacCall {
path,
args,
prior_type_ascription: self.last_type_ascription,
postfix_self_arg: None,
})
}

/// Recover if we parsed attributes and expected an item but there was none.
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,12 @@ impl<'a> Parser<'a> {
fn parse_pat_mac_invoc(&mut self, path: Path) -> PResult<'a, PatKind> {
self.bump();
let args = self.parse_mac_args()?;
let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
let mac = MacCall {
path,
args,
prior_type_ascription: self.last_type_ascription,
postfix_self_arg: None,
};
Ok(PatKind::MacCall(mac))
}

Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ impl<'a> Parser<'a> {
let style =
if delim == token::Brace { MacStmtStyle::Braces } else { MacStmtStyle::NoBraces };

let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
let mac = MacCall {
path,
args,
prior_type_ascription: self.last_type_ascription,
postfix_self_arg: None,
};

let kind = if delim == token::Brace || self.token == token::Semi || self.token == token::Eof
{
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ impl<'a> Parser<'a> {
path,
args: self.parse_mac_args()?,
prior_type_ascription: self.last_type_ascription,
postfix_self_arg: None,
}))
} else if allow_plus == AllowPlus::Yes && self.check_plus() {
// `Trait1 + Trait2 + 'a`
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/parser/postfix-macros-pass.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// check-pass

// Basically a clone of postfix-macros.rs, but with the offending
// code behind a `#[cfg(FALSE)]`. Rust still parses this code,
// but doesn't do anything beyond with it.

fn main() {}

#[cfg(FALSE)]
fn foo() {
"Hello, world!".to_string().println!();

"Hello, world!".println!();

false.assert!();

Some(42).assert_eq!(None);

std::iter::once(42)
.map(|v| v + 3)
.dbg!()
.max()
.unwrap()
.dbg!();
}
17 changes: 17 additions & 0 deletions src/test/ui/parser/postfix-macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
fn main() {
"Hello, world!".to_string().println!(); //~ ERROR forbidden postfix macro

"Hello, world!".println!(); //~ ERROR forbidden postfix macro

false.assert!(); //~ ERROR forbidden postfix macro

Some(42).assert_eq!(None); //~ ERROR forbidden postfix macro

std::iter::once(42) //~ ERROR forbidden postfix macro
//~^ ERROR forbidden postfix macro
.map(|v| v + 3)
.dbg!()
.max()
.unwrap()
.dbg!();
}
59 changes: 59 additions & 0 deletions src/test/ui/parser/postfix-macros.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
error: forbidden postfix macro call `println`
--> $DIR/postfix-macros.rs:2:5
|
LL | "Hello, world!".to_string().println!();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------^^
| |
| macros can't be called in postfix position

error: forbidden postfix macro call `println`
--> $DIR/postfix-macros.rs:4:5
|
LL | "Hello, world!".println!();
| ^^^^^^^^^^^^^^^^--------^^
| |
| macros can't be called in postfix position

error: forbidden postfix macro call `assert`
--> $DIR/postfix-macros.rs:6:5
|
LL | false.assert!();
| ^^^^^^-------^^
| |
| macros can't be called in postfix position

error: forbidden postfix macro call `assert_eq`
--> $DIR/postfix-macros.rs:8:5
|
LL | Some(42).assert_eq!(None);
| ^^^^^^^^^----------^^^^^^
| |
| macros can't be called in postfix position

error: forbidden postfix macro call `dbg`
--> $DIR/postfix-macros.rs:10:5
|
LL | / std::iter::once(42)
LL | |
LL | | .map(|v| v + 3)
LL | | .dbg!()
LL | | .max()
LL | | .unwrap()
LL | | .dbg!();
| |__________----_^
| |
| macros can't be called in postfix position

error: forbidden postfix macro call `dbg`
--> $DIR/postfix-macros.rs:10:5
|
LL | / std::iter::once(42)
LL | |
LL | | .map(|v| v + 3)
LL | | .dbg!()
| |__________----_^
| |
| macros can't be called in postfix position

error: aborting due to 6 previous errors

74 changes: 74 additions & 0 deletions src/test/ui/proc-macro/auxiliary/demacroify.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// force-host
// no-prefer-dynamic

// An attr proc macro that removes all postfix macros,
// to test that parsing postfix macros is allowed.

#![crate_type = "proc-macro"]

extern crate proc_macro;

use proc_macro::{Delimiter, Group, Punct, Spacing, TokenStream, TokenTree as Tt};

#[proc_macro_attribute]
pub fn demacroify(_attrs: TokenStream, input: TokenStream) -> TokenStream {
let mut vis = Visitor;
let res = vis.visit_stream(input);
res
}

struct Visitor;

impl Visitor {
fn visit_stream(&mut self, stream: TokenStream) -> TokenStream {
let mut res = Vec::new();
let mut stream_iter = stream.into_iter();
while let Some(tt) = stream_iter.next() {
match tt {
Tt::Group(group) => {
let mut postfix_macro = false;
{
let last_three = res.rchunks(3).next();
if let Some(&[Tt::Punct(ref p1), Tt::Ident(_), Tt::Punct(ref p2)]) =
last_three
{
if (p1.as_char(), p1.spacing(), p2.as_char(), p2.spacing())
== ('.', Spacing::Alone, '!', Spacing::Alone)
{
postfix_macro = true;
}
}
}
if postfix_macro {
// Remove the ! and macro ident
let _mac_bang = res.pop().unwrap();
let _mac = res.pop().unwrap();
// Remove the . before the macro
let _dot = res.pop().unwrap();
} else {
let tt = Tt::Group(self.visit_group(group));
res.push(tt);
}
}
Tt::Ident(id) => {
res.push(Tt::Ident(id));
}
Tt::Punct(p) => {
res.push(Tt::Punct(p));
}
Tt::Literal(lit) => {
res.push(Tt::Literal(lit));
}
}
}
res.into_iter().collect()
}
fn visit_group(&mut self, group: Group) -> Group {
let delim = group.delimiter();
let span = group.span();
let stream = self.visit_stream(group.stream());
let mut gr = Group::new(delim, stream);
gr.set_span(span);
gr
}
}
Loading