proc_macro: Add Span::mixed_site
exposing macro_rules
hygiene
This commit is contained in:
parent
c6293e3598
commit
d1310dc6c9
10 changed files with 179 additions and 35 deletions
|
@ -148,6 +148,7 @@ macro_rules! with_api {
|
||||||
fn debug($self: $S::Span) -> String;
|
fn debug($self: $S::Span) -> String;
|
||||||
fn def_site() -> $S::Span;
|
fn def_site() -> $S::Span;
|
||||||
fn call_site() -> $S::Span;
|
fn call_site() -> $S::Span;
|
||||||
|
fn mixed_site() -> $S::Span;
|
||||||
fn source_file($self: $S::Span) -> $S::SourceFile;
|
fn source_file($self: $S::Span) -> $S::SourceFile;
|
||||||
fn parent($self: $S::Span) -> Option<$S::Span>;
|
fn parent($self: $S::Span) -> Option<$S::Span>;
|
||||||
fn source($self: $S::Span) -> $S::Span;
|
fn source($self: $S::Span) -> $S::Span;
|
||||||
|
|
|
@ -271,6 +271,15 @@ impl Span {
|
||||||
Span(bridge::client::Span::call_site())
|
Span(bridge::client::Span::call_site())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro
|
||||||
|
/// definition site (local variables, labels, `$crate`) and sometimes at the macro
|
||||||
|
/// call site (everything else).
|
||||||
|
/// The span location is taken from the call-site.
|
||||||
|
#[unstable(feature = "proc_macro_mixed_site", issue = "65049")]
|
||||||
|
pub fn mixed_site() -> Span {
|
||||||
|
Span(bridge::client::Span::mixed_site())
|
||||||
|
}
|
||||||
|
|
||||||
/// The original source file into which this span points.
|
/// The original source file into which this span points.
|
||||||
#[unstable(feature = "proc_macro_span", issue = "54725")]
|
#[unstable(feature = "proc_macro_span", issue = "54725")]
|
||||||
pub fn source_file(&self) -> SourceFile {
|
pub fn source_file(&self) -> SourceFile {
|
||||||
|
|
|
@ -953,6 +953,12 @@ impl<'a> ExtCtxt<'a> {
|
||||||
span.with_call_site_ctxt(self.current_expansion.id)
|
span.with_call_site_ctxt(self.current_expansion.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Equivalent of `Span::mixed_site` from the proc macro API,
|
||||||
|
/// except that the location is taken from the span passed as an argument.
|
||||||
|
pub fn with_mixed_site_ctxt(&self, span: Span) -> Span {
|
||||||
|
span.with_mixed_site_ctxt(self.current_expansion.id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns span for the macro which originally caused the current expansion to happen.
|
/// Returns span for the macro which originally caused the current expansion to happen.
|
||||||
///
|
///
|
||||||
/// Stops backtracing at include! boundary.
|
/// Stops backtracing at include! boundary.
|
||||||
|
|
|
@ -355,6 +355,7 @@ pub(crate) struct Rustc<'a> {
|
||||||
sess: &'a ParseSess,
|
sess: &'a ParseSess,
|
||||||
def_site: Span,
|
def_site: Span,
|
||||||
call_site: Span,
|
call_site: Span,
|
||||||
|
mixed_site: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Rustc<'a> {
|
impl<'a> Rustc<'a> {
|
||||||
|
@ -364,6 +365,7 @@ impl<'a> Rustc<'a> {
|
||||||
sess: cx.parse_sess,
|
sess: cx.parse_sess,
|
||||||
def_site: cx.with_def_site_ctxt(expn_data.def_site),
|
def_site: cx.with_def_site_ctxt(expn_data.def_site),
|
||||||
call_site: cx.with_call_site_ctxt(expn_data.call_site),
|
call_site: cx.with_call_site_ctxt(expn_data.call_site),
|
||||||
|
mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,6 +666,9 @@ impl server::Span for Rustc<'_> {
|
||||||
fn call_site(&mut self) -> Self::Span {
|
fn call_site(&mut self) -> Self::Span {
|
||||||
self.call_site
|
self.call_site
|
||||||
}
|
}
|
||||||
|
fn mixed_site(&mut self) -> Self::Span {
|
||||||
|
self.mixed_site
|
||||||
|
}
|
||||||
fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
|
fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
|
||||||
self.sess.source_map().lookup_char_pos(span.lo()).file
|
self.sess.source_map().lookup_char_pos(span.lo()).file
|
||||||
}
|
}
|
||||||
|
|
|
@ -526,6 +526,12 @@ impl Span {
|
||||||
self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
|
self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Equivalent of `Span::mixed_site` from the proc macro API,
|
||||||
|
/// except that the location is taken from the `self` span.
|
||||||
|
pub fn with_mixed_site_ctxt(&self, expn_id: ExpnId) -> Span {
|
||||||
|
self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent)
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces a span with the same location as `self` and context produced by a macro with the
|
/// Produces a span with the same location as `self` and context produced by a macro with the
|
||||||
/// given ID and transparency, assuming that macro was defined directly and not produced by
|
/// given ID and transparency, assuming that macro was defined directly and not produced by
|
||||||
/// some other macro (which is the case for built-in and procedural macros).
|
/// some other macro (which is the case for built-in and procedural macros).
|
||||||
|
|
42
src/test/ui/proc-macro/auxiliary/mixed-site-span.rs
Normal file
42
src/test/ui/proc-macro/auxiliary/mixed-site-span.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// force-host
|
||||||
|
// no-prefer-dynamic
|
||||||
|
|
||||||
|
#![feature(proc_macro_hygiene)]
|
||||||
|
#![feature(proc_macro_mixed_site)]
|
||||||
|
#![feature(proc_macro_quote)]
|
||||||
|
|
||||||
|
#![crate_type = "proc-macro"]
|
||||||
|
|
||||||
|
extern crate proc_macro;
|
||||||
|
use proc_macro::*;
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,54 +59,54 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct B (identity ! ($crate :: S)) ;
|
||||||
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "struct",
|
ident: "struct",
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "B",
|
ident: "B",
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
delimiter: Parenthesis,
|
delimiter: Parenthesis,
|
||||||
stream: TokenStream [
|
stream: TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "identity",
|
ident: "identity",
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: '!',
|
ch: '!',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
delimiter: Parenthesis,
|
delimiter: Parenthesis,
|
||||||
stream: TokenStream [
|
stream: TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "$crate",
|
ident: "$crate",
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Joint,
|
spacing: Joint,
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "S",
|
ident: "S",
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ';',
|
ch: ';',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #8 bytes(LO..HI),
|
span: #10 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -124,40 +124,40 @@ PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
|
||||||
PRINT-BANG INPUT (DEBUG): TokenStream [
|
PRINT-BANG INPUT (DEBUG): TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "struct",
|
ident: "struct",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "M",
|
ident: "M",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
delimiter: Parenthesis,
|
delimiter: Parenthesis,
|
||||||
stream: TokenStream [
|
stream: TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "$crate",
|
ident: "$crate",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Joint,
|
spacing: Joint,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "S",
|
ident: "S",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ';',
|
ch: ';',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S);
|
PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S);
|
||||||
|
@ -165,40 +165,40 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ;
|
||||||
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
PRINT-ATTR INPUT (DEBUG): TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "struct",
|
ident: "struct",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "A",
|
ident: "A",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
delimiter: Parenthesis,
|
delimiter: Parenthesis,
|
||||||
stream: TokenStream [
|
stream: TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "$crate",
|
ident: "$crate",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Joint,
|
spacing: Joint,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "S",
|
ident: "S",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ';',
|
ch: ';',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S);
|
PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S);
|
||||||
|
@ -206,39 +206,39 @@ PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ;
|
||||||
PRINT-DERIVE INPUT (DEBUG): TokenStream [
|
PRINT-DERIVE INPUT (DEBUG): TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "struct",
|
ident: "struct",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "D",
|
ident: "D",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
delimiter: Parenthesis,
|
delimiter: Parenthesis,
|
||||||
stream: TokenStream [
|
stream: TokenStream [
|
||||||
Ident {
|
Ident {
|
||||||
ident: "$crate",
|
ident: "$crate",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Joint,
|
spacing: Joint,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ':',
|
ch: ':',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
ident: "S",
|
ident: "S",
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
Punct {
|
Punct {
|
||||||
ch: ';',
|
ch: ';',
|
||||||
spacing: Alone,
|
spacing: Alone,
|
||||||
span: #10 bytes(LO..HI),
|
span: #13 bytes(LO..HI),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
26
src/test/ui/proc-macro/mixed-site-span.rs
Normal file
26
src/test/ui/proc-macro/mixed-site-span.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Proc macros using `mixed_site` spans exhibit usual properties of `macro_rules` hygiene.
|
||||||
|
|
||||||
|
// aux-build:mixed-site-span.rs
|
||||||
|
|
||||||
|
#![feature(proc_macro_hygiene)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate mixed_site_span;
|
||||||
|
|
||||||
|
struct ItemUse;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
'label_use: loop {
|
||||||
|
let local_use = 1;
|
||||||
|
proc_macro_rules!();
|
||||||
|
//~^ 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!();
|
49
src/test/ui/proc-macro/mixed-site-span.stderr
Normal file
49
src/test/ui/proc-macro/mixed-site-span.stderr
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
error[E0426]: use of undeclared label `'label_use`
|
||||||
|
--> $DIR/mixed-site-span.rs:15:9
|
||||||
|
|
|
||||||
|
LL | proc_macro_rules!();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| undeclared label `'label_use`
|
||||||
|
| in this macro invocation
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `local_use` in this scope
|
||||||
|
--> $DIR/mixed-site-span.rs:15:9
|
||||||
|
|
|
||||||
|
LL | proc_macro_rules!();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| not found in this scope
|
||||||
|
| in this macro invocation
|
||||||
|
|
||||||
|
error[E0425]: cannot find value `local_def` in this scope
|
||||||
|
--> $DIR/mixed-site-span.rs:19:9
|
||||||
|
|
|
||||||
|
LL | local_def;
|
||||||
|
| ^^^^^^^^^ not found in this scope
|
||||||
|
|
||||||
|
error[E0412]: cannot find type `ItemUse` in crate `$crate`
|
||||||
|
--> $DIR/auxiliary/mixed-site-span.rs:14:1
|
||||||
|
|
|
||||||
|
LL | / pub fn proc_macro_rules(input: TokenStream) -> TokenStream {
|
||||||
|
LL | | if input.is_empty() {
|
||||||
|
LL | | let id = |s| TokenTree::from(Ident::new(s, Span::mixed_site()));
|
||||||
|
LL | | let item_def = id("ItemDef");
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | }
|
||||||
|
| |_^ not found in `$crate`
|
||||||
|
|
|
||||||
|
::: $DIR/mixed-site-span.rs:26:1
|
||||||
|
|
|
||||||
|
LL | pass_dollar_crate!();
|
||||||
|
| --------------------- in this macro invocation
|
||||||
|
help: possible candidate is found in another module, you can import it into scope
|
||||||
|
|
|
||||||
|
LL | use ItemUse;
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0412, E0425, E0426.
|
||||||
|
For more information about an error, try `rustc --explain E0412`.
|
Loading…
Add table
Add a link
Reference in a new issue