Skip to content

Commit 74599cd

Browse files
committed
Always capture tokens for macro_rules! arguments
1 parent d4ecf31 commit 74599cd

File tree

9 files changed

+205
-5
lines changed

9 files changed

+205
-5
lines changed

src/librustc_expand/mbe/macro_parser.rs

+25-4
Original file line numberDiff line numberDiff line change
@@ -866,18 +866,39 @@ fn parse_nt(p: &mut Parser<'_>, sp: Span, name: Symbol) -> Result<Nonterminal, (
866866
}
867867

868868
fn parse_nt_inner<'a>(p: &mut Parser<'a>, sp: Span, name: Symbol) -> PResult<'a, Nonterminal> {
869+
// Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
870+
// needs to have them force-captured here.
871+
// A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
872+
// which requires having captured tokens available. Since we cannot determine
873+
// in advance whether or not a proc-macro will be (transitively) invoked,
874+
// we always capture tokens for any `Nonterminal` which needs them.
869875
Ok(match name {
870-
sym::item => match p.parse_item()? {
871-
Some(i) => token::NtItem(i),
872-
None => return Err(p.struct_span_err(p.token.span, "expected an item keyword")),
876+
sym::item => match p.collect_tokens(|this| this.parse_item())? {
877+
(Some(mut item), tokens) => {
878+
// If we captured tokens during parsing (due to outer attributes),
879+
// use those.
880+
if item.tokens.is_none() {
881+
item.tokens = Some(tokens);
882+
}
883+
token::NtItem(item)
884+
}
885+
(None, _) => return Err(p.struct_span_err(p.token.span, "expected an item keyword")),
873886
},
874887
sym::block => token::NtBlock(p.parse_block()?),
875888
sym::stmt => match p.parse_stmt()? {
876889
Some(s) => token::NtStmt(s),
877890
None => return Err(p.struct_span_err(p.token.span, "expected a statement")),
878891
},
879892
sym::pat => token::NtPat(p.parse_pat(None)?),
880-
sym::expr => token::NtExpr(p.parse_expr()?),
893+
sym::expr => {
894+
let (mut expr, tokens) = p.collect_tokens(|this| this.parse_expr())?;
895+
// If we captured tokens during parsing (due to outer attributes),
896+
// use those.
897+
if expr.tokens.is_none() {
898+
expr.tokens = Some(tokens);
899+
}
900+
token::NtExpr(expr)
901+
}
881902
sym::literal => token::NtLiteral(p.parse_literal_maybe_minus()?),
882903
sym::ty => token::NtTy(p.parse_ty()?),
883904
// this could be handled like a token, since it is one

src/librustc_parse/parser/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,7 @@ impl<'a> Parser<'a> {
11511151
/// This restriction shouldn't be an issue in practice,
11521152
/// since this function is used to record the tokens for
11531153
/// a parsed AST item, which always has matching delimiters.
1154-
fn collect_tokens<R>(
1154+
pub fn collect_tokens<R>(
11551155
&mut self,
11561156
f: impl FnOnce(&mut Self) -> PResult<'a, R>,
11571157
) -> PResult<'a, (R, TokenStream)> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// force-host
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
6+
extern crate proc_macro;
7+
8+
use proc_macro::{TokenStream, TokenTree, Group, Delimiter};
9+
10+
#[proc_macro_attribute]
11+
pub fn first(_attr: TokenStream, item: TokenStream) -> TokenStream {
12+
let tokens: TokenStream = "#[derive(Second)]".parse().unwrap();
13+
let wrapped = TokenTree::Group(Group::new(Delimiter::None, item.into_iter().collect()));
14+
tokens.into_iter().chain(std::iter::once(wrapped)).collect()
15+
}
16+
17+
#[proc_macro_derive(Second)]
18+
pub fn second(item: TokenStream) -> TokenStream {
19+
TokenStream::new()
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// force-host
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
6+
extern crate proc_macro;
7+
use proc_macro::TokenStream;
8+
9+
#[proc_macro]
10+
pub fn recollect(tokens: TokenStream) -> TokenStream {
11+
tokens.into_iter().collect()
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// force-host
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
6+
extern crate proc_macro;
7+
8+
use proc_macro::{TokenStream, TokenTree, Group};
9+
10+
fn find_my_ident(tokens: TokenStream) -> Option<TokenStream> {
11+
for token in tokens {
12+
if let TokenTree::Ident(ident) = &token {
13+
if ident.to_string() == "hidden_ident" {
14+
return Some(vec![token].into_iter().collect())
15+
}
16+
} else if let TokenTree::Group(g) = token {
17+
if let Some(stream) = find_my_ident(g.stream()) {
18+
return Some(stream)
19+
}
20+
}
21+
}
22+
return None;
23+
}
24+
25+
26+
#[proc_macro_derive(WeirdDerive)]
27+
pub fn weird_derive(item: TokenStream) -> TokenStream {
28+
let my_ident = find_my_ident(item).expect("Missing 'my_ident'!");
29+
let tokens: TokenStream = "call_it!();".parse().unwrap();
30+
let final_call = tokens.into_iter().map(|tree| {
31+
if let TokenTree::Group(g) = tree {
32+
return Group::new(g.delimiter(), my_ident.clone()).into()
33+
} else {
34+
return tree
35+
}
36+
}).collect();
37+
final_call
38+
}
39+
40+
#[proc_macro]
41+
pub fn recollect(item: TokenStream) -> TokenStream {
42+
item.into_iter().collect()
43+
}
44+
45+
#[proc_macro_attribute]
46+
pub fn recollect_attr(_attr: TokenStream, mut item: TokenStream) -> TokenStream {
47+
item.into_iter().collect()
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// aux-build:test-macros.rs
2+
// check-pass
3+
4+
extern crate test_macros;
5+
use test_macros::recollect;
6+
7+
macro_rules! use_expr {
8+
($expr:expr) => {
9+
recollect!($expr)
10+
}
11+
}
12+
13+
#[allow(dead_code)]
14+
struct Foo;
15+
impl Foo {
16+
#[allow(dead_code)]
17+
fn use_self(self) {
18+
drop(use_expr!(self));
19+
}
20+
}
21+
22+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// aux-build:first-second.rs
2+
// FIXME: The spans here are bad, see PR #73084
3+
4+
extern crate first_second;
5+
use first_second::*;
6+
7+
macro_rules! produce_it {
8+
($name:ident) => {
9+
#[first] //~ ERROR cannot find type
10+
struct $name {
11+
field: MissingType
12+
}
13+
}
14+
}
15+
16+
produce_it!(MyName);
17+
18+
fn main() {
19+
println!("Hello, world!");
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0412]: cannot find type `MissingType` in this scope
2+
--> $DIR/macro-rules-derive.rs:9:9
3+
|
4+
LL | #[first]
5+
| ^^^^^^^^ not found in this scope
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0412`.
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// aux-build:weird-hygiene.rs
2+
// check-pass
3+
// FIXME: This should actually error, see PR #73084
4+
5+
#![feature(stmt_expr_attributes)]
6+
#![feature(proc_macro_hygiene)]
7+
8+
extern crate weird_hygiene;
9+
use weird_hygiene::*;
10+
11+
macro_rules! other {
12+
($tokens:expr) => {
13+
macro_rules! call_it {
14+
($outer_ident:ident) => {
15+
macro_rules! inner {
16+
() => {
17+
$outer_ident;
18+
}
19+
}
20+
}
21+
}
22+
23+
#[derive(WeirdDerive)]
24+
enum MyEnum {
25+
Value = (stringify!($tokens + hidden_ident), 1).1
26+
}
27+
28+
inner!();
29+
}
30+
}
31+
32+
macro_rules! invoke_it {
33+
($token:expr) => {
34+
#[recollect_attr] {
35+
$token;
36+
hidden_ident
37+
}
38+
}
39+
}
40+
41+
fn main() {
42+
// `other` and `invoke_it` are both macro_rules! macros,
43+
// so it should be impossible for them to ever see `hidden_ident`,
44+
// even if they invoke a proc macro.
45+
let hidden_ident = "Hello1";
46+
other!(50);
47+
invoke_it!(25);
48+
}

0 commit comments

Comments
 (0)