proc_macro: Add Span::mixed_site exposing macro_rules hygiene

This commit is contained in:
Vadim Petrochenkov 2019-09-22 18:38:02 +03:00
parent c6293e3598
commit d1310dc6c9
10 changed files with 179 additions and 35 deletions

View File

@ -148,6 +148,7 @@ macro_rules! with_api {
fn debug($self: $S::Span) -> String;
fn def_site() -> $S::Span;
fn call_site() -> $S::Span;
fn mixed_site() -> $S::Span;
fn source_file($self: $S::Span) -> $S::SourceFile;
fn parent($self: $S::Span) -> Option<$S::Span>;
fn source($self: $S::Span) -> $S::Span;

View File

@ -271,6 +271,15 @@ impl Span {
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.
#[unstable(feature = "proc_macro_span", issue = "54725")]
pub fn source_file(&self) -> SourceFile {

View File

@ -953,6 +953,12 @@ impl<'a> ExtCtxt<'a> {
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.
///
/// Stops backtracing at include! boundary.

View File

@ -355,6 +355,7 @@ pub(crate) struct Rustc<'a> {
sess: &'a ParseSess,
def_site: Span,
call_site: Span,
mixed_site: Span,
}
impl<'a> Rustc<'a> {
@ -364,6 +365,7 @@ impl<'a> Rustc<'a> {
sess: cx.parse_sess,
def_site: cx.with_def_site_ctxt(expn_data.def_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 {
self.call_site
}
fn mixed_site(&mut self) -> Self::Span {
self.mixed_site
}
fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
self.sess.source_map().lookup_char_pos(span.lo()).file
}

View File

@ -526,6 +526,12 @@ impl Span {
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
/// 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).

View 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;
)
}
}

View File

@ -59,54 +59,54 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct B (identity ! ($crate :: S)) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
Ident {
ident: "B",
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "identity",
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
Punct {
ch: '!',
spacing: Alone,
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "$crate",
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Joint,
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Alone,
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
Ident {
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 {
ch: ';',
spacing: Alone,
span: #8 bytes(LO..HI),
span: #10 bytes(LO..HI),
},
]

View File

@ -124,40 +124,40 @@ PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
PRINT-BANG INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Ident {
ident: "M",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "$crate",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Joint,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Alone,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Ident {
ident: "S",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
],
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ';',
spacing: Alone,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
]
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 [
Ident {
ident: "struct",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Ident {
ident: "A",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "$crate",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Joint,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Alone,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Ident {
ident: "S",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
],
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ';',
spacing: Alone,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
]
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 [
Ident {
ident: "struct",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Ident {
ident: "D",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "$crate",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Joint,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ':',
spacing: Alone,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Ident {
ident: "S",
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
],
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
Punct {
ch: ';',
spacing: Alone,
span: #10 bytes(LO..HI),
span: #13 bytes(LO..HI),
},
]

View 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!();

View 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`.