Skip to content

Fix proc_macro::Ident's handling of $crate #141996

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions library/proc_macro/src/bridge/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl Symbol {
/// Validates and normalizes before converting it to a symbol.
pub(crate) fn new_ident(string: &str, is_raw: bool) -> Self {
// Fast-path: check if this is a valid ASCII identifier
if Self::is_valid_ascii_ident(string.as_bytes()) {
if Self::is_valid_ascii_ident(string.as_bytes()) || string == "$crate" {
if is_raw && !Self::can_be_raw(string) {
panic!("`{}` cannot be a raw identifier", string);
}
Expand Down Expand Up @@ -79,7 +79,7 @@ impl Symbol {
// Mimics the behavior of `Symbol::can_be_raw` from `rustc_span`
fn can_be_raw(string: &str) -> bool {
match string {
"_" | "super" | "self" | "Self" | "crate" => false,
"_" | "super" | "self" | "Self" | "crate" | "$crate" => false,
_ => true,
}
}
Expand Down
110 changes: 83 additions & 27 deletions tests/ui/proc-macro/auxiliary/mixed-site-span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,89 @@
extern crate proc_macro;
use proc_macro::*;


#[proc_macro]
pub fn proc_macro_item(input: TokenStream) -> TokenStream {
input
}

#[proc_macro]
pub fn proc_macro_rules(_input: TokenStream) -> TokenStream {
let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site()));
let item_def = id("ItemDef");
let local_def = id("local_def");
let item_use = id("ItemUse");
let local_use = id("local_use");
let mut single_quote = Punct::new('\'', Spacing::Joint);
single_quote.set_span(Span::mixed_site());
let label_use: TokenStream = [
TokenTree::from(single_quote),
id("label_use"),
].iter().cloned().collect();
let dollar_crate = id("$crate");
quote!(
use $dollar_crate::proc_macro_item as _; // OK
type A = $dollar_crate::ItemUse; // ERROR

struct $item_def;
let $local_def = 0;

$item_use; // OK
$local_use; // ERROR
break $label_use; // ERROR
)
}

#[proc_macro]
pub fn with_crate(input: TokenStream) -> TokenStream {
let mut input = input.into_iter();
let TokenTree::Ident(mut krate) = input.next().unwrap() else { panic!("missing $crate") };
let TokenTree::Ident(span) = input.next().unwrap() else { panic!("missing span") };
let TokenTree::Ident(ident) = input.next().unwrap() else { panic!("missing ident") };

match (krate.to_string().as_str(), span.to_string().as_str()) {
("$crate", "input") => {},
(_, "input") => krate = Ident::new("$crate", krate.span()),

("$crate", "mixed") => krate.set_span(Span::mixed_site()),
(_, "mixed") => krate = Ident::new("$crate", Span::mixed_site()),

("$crate", "call") => krate.set_span(Span::call_site()),
(_, "call") => krate = Ident::new("$crate", Span::call_site()),

(_, x) => panic!("bad span {}", x),
}

quote!(use $krate::$ident as _;)
}

#[proc_macro]
pub fn proc_macro_rules(input: TokenStream) -> TokenStream {
if input.is_empty() {
let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site()));
let item_def = id("ItemDef");
let local_def = id("local_def");
let item_use = id("ItemUse");
let local_use = id("local_use");
let mut single_quote = Punct::new('\'', Spacing::Joint);
single_quote.set_span(Span::mixed_site());
let label_use: TokenStream = [
TokenTree::from(single_quote),
id("label_use"),
].iter().cloned().collect();
quote!(
struct $item_def;
let $local_def = 0;

$item_use; // OK
$local_use; // ERROR
break $label_use; // ERROR
)
} else {
let mut dollar_crate = input.into_iter().next().unwrap();
dollar_crate.set_span(Span::mixed_site());
quote!(
type A = $dollar_crate::ItemUse;
)
pub fn declare_macro(input: TokenStream) -> TokenStream {
let mut input = input.into_iter();
let TokenTree::Ident(mut krate) = input.next().unwrap() else { panic!("missing $crate") };
let TokenTree::Ident(span) = input.next().unwrap() else { panic!("missing span") };
let TokenTree::Ident(ident) = input.next().unwrap() else { panic!("missing ident") };


match (krate.to_string().as_str(), span.to_string().as_str()) {
("$crate", "input") => {},
(_, "input") => krate = Ident::new("$crate", krate.span()),

("$crate", "mixed") => krate.set_span(Span::mixed_site()),
(_, "mixed") => krate = Ident::new("$crate", Span::mixed_site()),

("$crate", "call") => krate.set_span(Span::call_site()),
(_, "call") => krate = Ident::new("$crate", Span::call_site()),

(_, x) => panic!("bad span {}", x),
}

quote!(
#[macro_export]
macro_rules! $ident {
($$i:ident) => {
use $krate::$$i as _;
};
}
)
}
30 changes: 30 additions & 0 deletions tests/ui/proc-macro/auxiliary/token-site-span.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Testing token span hygiene.

//@ proc-macro: mixed-site-span.rs

extern crate mixed_site_span;

use mixed_site_span::declare_macro;

pub struct TokenItem;

#[macro_export]
macro_rules! invoke_with_crate {
($s:ident $i:ident) => { with_crate!{$crate $s $i} };
}

#[macro_export]
macro_rules! invoke_with_ident {
($s:ident $i:ident) => { with_crate!{krate $s $i} };
($m:ident $s:ident $i:ident) => { with_crate!{$m $s $i} };
}

macro_rules! local {() => {
declare_macro!{$crate input use_input_crate}
declare_macro!{$crate mixed use_mixed_crate}
declare_macro!{$crate call use_call_crate}
}}
local!{}
declare_macro!{krate input use_input_krate}
declare_macro!{krate mixed use_mixed_krate}
declare_macro!{krate call use_call_krate}
164 changes: 157 additions & 7 deletions tests/ui/proc-macro/mixed-site-span.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,174 @@
// Proc macros using `mixed_site` spans exhibit usual properties of `macro_rules` hygiene.

//@ aux-build: token-site-span.rs
//@ proc-macro: mixed-site-span.rs

#[macro_use]
extern crate mixed_site_span;
extern crate token_site_span;

struct ItemUse;
use mixed_site_span::{proc_macro_rules, with_crate};
use token_site_span::{
invoke_with_crate, invoke_with_ident,
use_input_crate, use_mixed_crate, use_call_crate,
use_input_krate, use_mixed_krate, use_call_krate,
};

pub struct ItemUse;

fn main() {
'label_use: loop {
let local_use = 1;
proc_macro_rules!();
//~^ ERROR use of undeclared label `'label_use`
//~^ ERROR cannot find type `ItemUse` in crate `$crate`
//~| ERROR use of undeclared label `'label_use`
//~| ERROR cannot find value `local_use` in this scope
ItemDef; // OK
local_def; //~ ERROR cannot find value `local_def` in this scope
}
}

macro_rules! pass_dollar_crate {
() => (proc_macro_rules!($crate);) //~ ERROR cannot find type `ItemUse` in crate `$crate`
}
pass_dollar_crate!();
// Successful resolutions of `mixed_site_span::proc_macro_item`
const _: () = {
invoke_with_crate!{mixed proc_macro_item}
invoke_with_ident!{mixed proc_macro_item}
invoke_with_ident!{krate mixed proc_macro_item}
with_crate!{krate mixed proc_macro_item}

macro_rules! test {() => {
invoke_with_ident!{$crate mixed proc_macro_item}
with_crate!{$crate mixed proc_macro_item}
}}
test!();
};

// Failed resolutions of `proc_macro_item`
const _: () = {
// token_site_span::proc_macro_item
invoke_with_crate!{input proc_macro_item} //~ ERROR unresolved import `$crate`
invoke_with_ident!{input proc_macro_item} //~ ERROR unresolved import `$crate`
invoke_with_crate!{call proc_macro_item} //~ ERROR unresolved import `$crate`
invoke_with_ident!{call proc_macro_item} //~ ERROR unresolved import `$crate`
invoke_with_ident!{hello call proc_macro_item} //~ ERROR unresolved import `$crate`

// crate::proc_macro_item
invoke_with_ident!{krate input proc_macro_item} //~ ERROR unresolved import `$crate::proc_macro_item`
with_crate!{krate input proc_macro_item} //~ ERROR unresolved import `$crate::proc_macro_item`
with_crate!{krate call proc_macro_item} //~ ERROR unresolved import `$crate`

macro_rules! test {() => {
// crate::proc_macro_item
invoke_with_ident!{$crate input proc_macro_item} //~ ERROR unresolved import `$crate`
with_crate!{$crate input proc_macro_item} //~ ERROR unresolved import `$crate`
with_crate!{$crate call proc_macro_item} //~ ERROR unresolved import `$crate`

// token_site_span::proc_macro_item
invoke_with_ident!{$crate call proc_macro_item} //~ ERROR unresolved import `$crate`
}}
test!();
};

// Successful resolutions of `token_site_span::TokenItem`
const _: () = {
invoke_with_crate!{input TokenItem}
invoke_with_ident!{input TokenItem}
invoke_with_crate!{call TokenItem}
invoke_with_ident!{call TokenItem}
invoke_with_ident!{hello call TokenItem}

macro_rules! test {() => {
invoke_with_ident!{$crate call TokenItem}
}}
test!();
};

// Failed resolutions of `TokenItem`
const _: () = {
// crate::TokenItem
invoke_with_ident!{krate input TokenItem} //~ ERROR unresolved import `$crate::TokenItem`
with_crate!{krate input TokenItem} //~ ERROR unresolved import `$crate::TokenItem`
with_crate!{krate call TokenItem} //~ ERROR unresolved import `$crate`

// mixed_site_span::TokenItem
invoke_with_crate!{mixed TokenItem} //~ ERROR unresolved import `$crate`
invoke_with_ident!{mixed TokenItem} //~ ERROR unresolved import `$crate`
invoke_with_ident!{krate mixed TokenItem} //~ ERROR unresolved import `$crate`
with_crate!{krate mixed TokenItem} //~ ERROR unresolved import `$crate`

macro_rules! test {() => {
// crate::TokenItem
invoke_with_ident!{$crate input TokenItem} //~ ERROR unresolved import `$crate`
with_crate!{$crate input TokenItem} //~ ERROR unresolved import `$crate`
with_crate!{$crate call TokenItem} //~ ERROR unresolved import `$crate`

// mixed_site_span::TokenItem
invoke_with_ident!{$crate mixed TokenItem} //~ ERROR unresolved import `$crate`
with_crate!{$crate mixed TokenItem} //~ ERROR unresolved import `$crate`

}}
test!();
};


// Successful resolutions of `crate::ItemUse`
const _: () = {
invoke_with_ident!{krate input ItemUse}
with_crate!{krate input ItemUse}
with_crate!{krate call ItemUse}

macro_rules! test {() => {
invoke_with_ident!{$crate input ItemUse}
with_crate!{$crate input ItemUse}
with_crate!{$crate call ItemUse}
}}
test!();
};

// Failed resolutions of `ItemUse`
const _: () = {
// token_site_span::ItemUse
invoke_with_crate!{input ItemUse} //~ ERROR unresolved import `$crate`
invoke_with_ident!{input ItemUse} //~ ERROR unresolved import `$crate`

// mixed_site_span::ItemUse
invoke_with_crate!{mixed ItemUse} //~ ERROR unresolved import `$crate`
invoke_with_ident!{mixed ItemUse} //~ ERROR unresolved import `$crate`
invoke_with_ident!{krate mixed ItemUse} //~ ERROR unresolved import `$crate`
with_crate!{krate mixed ItemUse} //~ ERROR unresolved import `$crate`

invoke_with_crate!{call ItemUse} //~ ERROR unresolved import `$crate`
invoke_with_ident!{call ItemUse} //~ ERROR unresolved import `$crate`
invoke_with_ident!{hello call ItemUse} //~ ERROR unresolved import `$crate`

macro_rules! test {() => {
invoke_with_ident!{$crate mixed ItemUse} //~ ERROR unresolved import `$crate`
with_crate!{$crate mixed ItemUse} //~ ERROR unresolved import `$crate`

invoke_with_ident!{$crate call ItemUse} //~ ERROR unresolved import `$crate`
}}
test!();
};


// Only mixed should see mixed_site_span::proc_macro_item
use_input_crate!{proc_macro_item} //~ ERROR unresolved import `$crate`
use_input_krate!{proc_macro_item} //~ ERROR unresolved import `$crate`
use_mixed_crate!{proc_macro_item}
use_mixed_krate!{proc_macro_item}
use_call_crate!{proc_macro_item} //~ ERROR unresolved import `$crate`
use_call_krate!{proc_macro_item} //~ ERROR unresolved import `$crate`

// Only mixed should fail to see token_site_span::TokenItem
use_input_crate!{TokenItem}
use_input_krate!{TokenItem}
use_mixed_crate!{TokenItem} //~ ERROR unresolved import `$crate`
use_mixed_krate!{TokenItem} //~ ERROR unresolved import `$crate`
use_call_crate!{TokenItem}
use_call_krate!{TokenItem}

// Everything should fail to see crate::ItemUse
use_input_crate!{ItemUse} //~ ERROR unresolved import `$crate`
use_input_krate!{ItemUse} //~ ERROR unresolved import `$crate`
use_mixed_crate!{ItemUse} //~ ERROR unresolved import `$crate`
use_mixed_krate!{ItemUse} //~ ERROR unresolved import `$crate`
use_call_crate!{ItemUse} //~ ERROR unresolved import `$crate`
use_call_krate!{ItemUse} //~ ERROR unresolved import `$crate`
Loading
Loading