Skip to content

Commit 6d1e93d

Browse files
Daniel BloomDaniel-Aaron-Bloom
authored andcommitted
Add support for $crate to Ident
1 parent fe5c95d commit 6d1e93d

File tree

5 files changed

+859
-49
lines changed

5 files changed

+859
-49
lines changed

library/proc_macro/src/bridge/symbol.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl Symbol {
3333
/// Validates and normalizes before converting it to a symbol.
3434
pub(crate) fn new_ident(string: &str, is_raw: bool) -> Self {
3535
// Fast-path: check if this is a valid ASCII identifier
36-
if Self::is_valid_ascii_ident(string.as_bytes()) {
36+
if Self::is_valid_ascii_ident(string.as_bytes()) || string == "$crate" {
3737
if is_raw && !Self::can_be_raw(string) {
3838
panic!("`{}` cannot be a raw identifier", string);
3939
}
@@ -79,7 +79,7 @@ impl Symbol {
7979
// Mimics the behavior of `Symbol::can_be_raw` from `rustc_span`
8080
fn can_be_raw(string: &str) -> bool {
8181
match string {
82-
"_" | "super" | "self" | "Self" | "crate" => false,
82+
"_" | "super" | "self" | "Self" | "crate" | "$crate" => false,
8383
_ => true,
8484
}
8585
}

tests/ui/proc-macro/auxiliary/mixed-site-span.rs

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,89 @@
33
extern crate proc_macro;
44
use proc_macro::*;
55

6+
7+
#[proc_macro]
8+
pub fn proc_macro_item(input: TokenStream) -> TokenStream {
9+
input
10+
}
11+
12+
#[proc_macro]
13+
pub fn proc_macro_rules(_input: TokenStream) -> TokenStream {
14+
let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site()));
15+
let item_def = id("ItemDef");
16+
let local_def = id("local_def");
17+
let item_use = id("ItemUse");
18+
let local_use = id("local_use");
19+
let mut single_quote = Punct::new('\'', Spacing::Joint);
20+
single_quote.set_span(Span::mixed_site());
21+
let label_use: TokenStream = [
22+
TokenTree::from(single_quote),
23+
id("label_use"),
24+
].iter().cloned().collect();
25+
let dollar_crate = id("$crate");
26+
quote!(
27+
use $dollar_crate::proc_macro_item as _; // OK
28+
type A = $dollar_crate::ItemUse; // ERROR
29+
30+
struct $item_def;
31+
let $local_def = 0;
32+
33+
$item_use; // OK
34+
$local_use; // ERROR
35+
break $label_use; // ERROR
36+
)
37+
}
38+
39+
#[proc_macro]
40+
pub fn with_crate(input: TokenStream) -> TokenStream {
41+
let mut input = input.into_iter();
42+
let TokenTree::Ident(mut krate) = input.next().unwrap() else { panic!("missing $crate") };
43+
let TokenTree::Ident(span) = input.next().unwrap() else { panic!("missing span") };
44+
let TokenTree::Ident(ident) = input.next().unwrap() else { panic!("missing ident") };
45+
46+
match (krate.to_string().as_str(), span.to_string().as_str()) {
47+
("$crate", "input") => {},
48+
(_, "input") => krate = Ident::new("$crate", krate.span()),
49+
50+
("$crate", "mixed") => krate.set_span(Span::mixed_site()),
51+
(_, "mixed") => krate = Ident::new("$crate", Span::mixed_site()),
52+
53+
("$crate", "call") => krate.set_span(Span::call_site()),
54+
(_, "call") => krate = Ident::new("$crate", Span::call_site()),
55+
56+
(_, x) => panic!("bad span {}", x),
57+
}
58+
59+
quote!(use $krate::$ident as _;)
60+
}
61+
662
#[proc_macro]
7-
pub fn proc_macro_rules(input: TokenStream) -> TokenStream {
8-
if input.is_empty() {
9-
let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site()));
10-
let item_def = id("ItemDef");
11-
let local_def = id("local_def");
12-
let item_use = id("ItemUse");
13-
let local_use = id("local_use");
14-
let mut single_quote = Punct::new('\'', Spacing::Joint);
15-
single_quote.set_span(Span::mixed_site());
16-
let label_use: TokenStream = [
17-
TokenTree::from(single_quote),
18-
id("label_use"),
19-
].iter().cloned().collect();
20-
quote!(
21-
struct $item_def;
22-
let $local_def = 0;
23-
24-
$item_use; // OK
25-
$local_use; // ERROR
26-
break $label_use; // ERROR
27-
)
28-
} else {
29-
let mut dollar_crate = input.into_iter().next().unwrap();
30-
dollar_crate.set_span(Span::mixed_site());
31-
quote!(
32-
type A = $dollar_crate::ItemUse;
33-
)
63+
pub fn declare_macro(input: TokenStream) -> TokenStream {
64+
let mut input = input.into_iter();
65+
let TokenTree::Ident(mut krate) = input.next().unwrap() else { panic!("missing $crate") };
66+
let TokenTree::Ident(span) = input.next().unwrap() else { panic!("missing span") };
67+
let TokenTree::Ident(ident) = input.next().unwrap() else { panic!("missing ident") };
68+
69+
70+
match (krate.to_string().as_str(), span.to_string().as_str()) {
71+
("$crate", "input") => {},
72+
(_, "input") => krate = Ident::new("$crate", krate.span()),
73+
74+
("$crate", "mixed") => krate.set_span(Span::mixed_site()),
75+
(_, "mixed") => krate = Ident::new("$crate", Span::mixed_site()),
76+
77+
("$crate", "call") => krate.set_span(Span::call_site()),
78+
(_, "call") => krate = Ident::new("$crate", Span::call_site()),
79+
80+
(_, x) => panic!("bad span {}", x),
3481
}
82+
83+
quote!(
84+
#[macro_export]
85+
macro_rules! $ident {
86+
($$i:ident) => {
87+
use $krate::$$i as _;
88+
};
89+
}
90+
)
3591
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Testing token span hygiene.
2+
3+
//@ proc-macro: mixed-site-span.rs
4+
5+
extern crate mixed_site_span;
6+
7+
use mixed_site_span::declare_macro;
8+
9+
pub struct TokenItem;
10+
11+
#[macro_export]
12+
macro_rules! invoke_with_crate {
13+
($s:ident $i:ident) => { with_crate!{$crate $s $i} };
14+
}
15+
16+
#[macro_export]
17+
macro_rules! invoke_with_ident {
18+
($s:ident $i:ident) => { with_crate!{krate $s $i} };
19+
($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} };
20+
}
21+
22+
macro_rules! local {() => {
23+
declare_macro!{$crate input use_input_crate}
24+
declare_macro!{$crate mixed use_mixed_crate}
25+
declare_macro!{$crate call use_call_crate}
26+
}}
27+
local!{}
28+
declare_macro!{krate input use_input_krate}
29+
declare_macro!{krate mixed use_mixed_krate}
30+
declare_macro!{krate call use_call_krate}
Lines changed: 157 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,174 @@
11
// Proc macros using `mixed_site` spans exhibit usual properties of `macro_rules` hygiene.
22

3+
//@ aux-build: token-site-span.rs
34
//@ proc-macro: mixed-site-span.rs
45

5-
#[macro_use]
66
extern crate mixed_site_span;
7+
extern crate token_site_span;
78

8-
struct ItemUse;
9+
use mixed_site_span::{proc_macro_rules, with_crate};
10+
use token_site_span::{
11+
invoke_with_crate, invoke_with_ident,
12+
use_input_crate, use_mixed_crate, use_call_crate,
13+
use_input_krate, use_mixed_krate, use_call_krate,
14+
};
15+
16+
pub struct ItemUse;
917

1018
fn main() {
1119
'label_use: loop {
1220
let local_use = 1;
1321
proc_macro_rules!();
14-
//~^ ERROR use of undeclared label `'label_use`
22+
//~^ ERROR cannot find type `ItemUse` in crate `$crate`
23+
//~| ERROR use of undeclared label `'label_use`
1524
//~| ERROR cannot find value `local_use` in this scope
1625
ItemDef; // OK
1726
local_def; //~ ERROR cannot find value `local_def` in this scope
1827
}
1928
}
2029

21-
macro_rules! pass_dollar_crate {
22-
() => (proc_macro_rules!($crate);) //~ ERROR cannot find type `ItemUse` in crate `$crate`
23-
}
24-
pass_dollar_crate!();
30+
// Successful resolutions of `mixed_site_span::proc_macro_item`
31+
const _: () = {
32+
invoke_with_crate!{mixed proc_macro_item}
33+
invoke_with_ident!{mixed proc_macro_item}
34+
invoke_with_ident!{krate mixed proc_macro_item}
35+
with_crate!{krate mixed proc_macro_item}
36+
37+
macro_rules! test {() => {
38+
invoke_with_ident!{$crate mixed proc_macro_item}
39+
with_crate!{$crate mixed proc_macro_item}
40+
}}
41+
test!();
42+
};
43+
44+
// Failed resolutions of `proc_macro_item`
45+
const _: () = {
46+
// token_site_span::proc_macro_item
47+
invoke_with_crate!{input proc_macro_item} //~ ERROR unresolved import `$crate`
48+
invoke_with_ident!{input proc_macro_item} //~ ERROR unresolved import `$crate`
49+
invoke_with_crate!{call proc_macro_item} //~ ERROR unresolved import `$crate`
50+
invoke_with_ident!{call proc_macro_item} //~ ERROR unresolved import `$crate`
51+
invoke_with_ident!{hello call proc_macro_item} //~ ERROR unresolved import `$crate`
52+
53+
// crate::proc_macro_item
54+
invoke_with_ident!{krate input proc_macro_item} //~ ERROR unresolved import `$crate::proc_macro_item`
55+
with_crate!{krate input proc_macro_item} //~ ERROR unresolved import `$crate::proc_macro_item`
56+
with_crate!{krate call proc_macro_item} //~ ERROR unresolved import `$crate`
57+
58+
macro_rules! test {() => {
59+
// crate::proc_macro_item
60+
invoke_with_ident!{$crate input proc_macro_item} //~ ERROR unresolved import `$crate`
61+
with_crate!{$crate input proc_macro_item} //~ ERROR unresolved import `$crate`
62+
with_crate!{$crate call proc_macro_item} //~ ERROR unresolved import `$crate`
63+
64+
// token_site_span::proc_macro_item
65+
invoke_with_ident!{$crate call proc_macro_item} //~ ERROR unresolved import `$crate`
66+
}}
67+
test!();
68+
};
69+
70+
// Successful resolutions of `token_site_span::TokenItem`
71+
const _: () = {
72+
invoke_with_crate!{input TokenItem}
73+
invoke_with_ident!{input TokenItem}
74+
invoke_with_crate!{call TokenItem}
75+
invoke_with_ident!{call TokenItem}
76+
invoke_with_ident!{hello call TokenItem}
77+
78+
macro_rules! test {() => {
79+
invoke_with_ident!{$crate call TokenItem}
80+
}}
81+
test!();
82+
};
83+
84+
// Failed resolutions of `TokenItem`
85+
const _: () = {
86+
// crate::TokenItem
87+
invoke_with_ident!{krate input TokenItem} //~ ERROR unresolved import `$crate::TokenItem`
88+
with_crate!{krate input TokenItem} //~ ERROR unresolved import `$crate::TokenItem`
89+
with_crate!{krate call TokenItem} //~ ERROR unresolved import `$crate`
90+
91+
// mixed_site_span::TokenItem
92+
invoke_with_crate!{mixed TokenItem} //~ ERROR unresolved import `$crate`
93+
invoke_with_ident!{mixed TokenItem} //~ ERROR unresolved import `$crate`
94+
invoke_with_ident!{krate mixed TokenItem} //~ ERROR unresolved import `$crate`
95+
with_crate!{krate mixed TokenItem} //~ ERROR unresolved import `$crate`
96+
97+
macro_rules! test {() => {
98+
// crate::TokenItem
99+
invoke_with_ident!{$crate input TokenItem} //~ ERROR unresolved import `$crate`
100+
with_crate!{$crate input TokenItem} //~ ERROR unresolved import `$crate`
101+
with_crate!{$crate call TokenItem} //~ ERROR unresolved import `$crate`
102+
103+
// mixed_site_span::TokenItem
104+
invoke_with_ident!{$crate mixed TokenItem} //~ ERROR unresolved import `$crate`
105+
with_crate!{$crate mixed TokenItem} //~ ERROR unresolved import `$crate`
106+
107+
}}
108+
test!();
109+
};
110+
111+
112+
// Successful resolutions of `crate::ItemUse`
113+
const _: () = {
114+
invoke_with_ident!{krate input ItemUse}
115+
with_crate!{krate input ItemUse}
116+
with_crate!{krate call ItemUse}
117+
118+
macro_rules! test {() => {
119+
invoke_with_ident!{$crate input ItemUse}
120+
with_crate!{$crate input ItemUse}
121+
with_crate!{$crate call ItemUse}
122+
}}
123+
test!();
124+
};
125+
126+
// Failed resolutions of `ItemUse`
127+
const _: () = {
128+
// token_site_span::ItemUse
129+
invoke_with_crate!{input ItemUse} //~ ERROR unresolved import `$crate`
130+
invoke_with_ident!{input ItemUse} //~ ERROR unresolved import `$crate`
131+
132+
// mixed_site_span::ItemUse
133+
invoke_with_crate!{mixed ItemUse} //~ ERROR unresolved import `$crate`
134+
invoke_with_ident!{mixed ItemUse} //~ ERROR unresolved import `$crate`
135+
invoke_with_ident!{krate mixed ItemUse} //~ ERROR unresolved import `$crate`
136+
with_crate!{krate mixed ItemUse} //~ ERROR unresolved import `$crate`
137+
138+
invoke_with_crate!{call ItemUse} //~ ERROR unresolved import `$crate`
139+
invoke_with_ident!{call ItemUse} //~ ERROR unresolved import `$crate`
140+
invoke_with_ident!{hello call ItemUse} //~ ERROR unresolved import `$crate`
141+
142+
macro_rules! test {() => {
143+
invoke_with_ident!{$crate mixed ItemUse} //~ ERROR unresolved import `$crate`
144+
with_crate!{$crate mixed ItemUse} //~ ERROR unresolved import `$crate`
145+
146+
invoke_with_ident!{$crate call ItemUse} //~ ERROR unresolved import `$crate`
147+
}}
148+
test!();
149+
};
150+
151+
152+
// Only mixed should see mixed_site_span::proc_macro_item
153+
use_input_crate!{proc_macro_item} //~ ERROR unresolved import `$crate`
154+
use_input_krate!{proc_macro_item} //~ ERROR unresolved import `$crate`
155+
use_mixed_crate!{proc_macro_item}
156+
use_mixed_krate!{proc_macro_item}
157+
use_call_crate!{proc_macro_item} //~ ERROR unresolved import `$crate`
158+
use_call_krate!{proc_macro_item} //~ ERROR unresolved import `$crate`
159+
160+
// Only mixed should fail to see token_site_span::TokenItem
161+
use_input_crate!{TokenItem}
162+
use_input_krate!{TokenItem}
163+
use_mixed_crate!{TokenItem} //~ ERROR unresolved import `$crate`
164+
use_mixed_krate!{TokenItem} //~ ERROR unresolved import `$crate`
165+
use_call_crate!{TokenItem}
166+
use_call_krate!{TokenItem}
167+
168+
// Everything should fail to see crate::ItemUse
169+
use_input_crate!{ItemUse} //~ ERROR unresolved import `$crate`
170+
use_input_krate!{ItemUse} //~ ERROR unresolved import `$crate`
171+
use_mixed_crate!{ItemUse} //~ ERROR unresolved import `$crate`
172+
use_mixed_krate!{ItemUse} //~ ERROR unresolved import `$crate`
173+
use_call_crate!{ItemUse} //~ ERROR unresolved import `$crate`
174+
use_call_krate!{ItemUse} //~ ERROR unresolved import `$crate`

0 commit comments

Comments
 (0)