From d1310dc6c989e191d85c73c943d4175fbf1dccb8 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 22 Sep 2019 18:38:02 +0300 Subject: [PATCH] proc_macro: Add `Span::mixed_site` exposing `macro_rules` hygiene --- src/libproc_macro/bridge/mod.rs | 1 + src/libproc_macro/lib.rs | 9 ++++ src/libsyntax/ext/base.rs | 6 +++ src/libsyntax/ext/proc_macro_server.rs | 5 ++ src/libsyntax_pos/lib.rs | 6 +++ .../proc-macro/auxiliary/mixed-site-span.rs | 42 ++++++++++++++++ .../dollar-crate-issue-62325.stdout | 22 ++++----- src/test/ui/proc-macro/dollar-crate.stdout | 48 +++++++++--------- src/test/ui/proc-macro/mixed-site-span.rs | 26 ++++++++++ src/test/ui/proc-macro/mixed-site-span.stderr | 49 +++++++++++++++++++ 10 files changed, 179 insertions(+), 35 deletions(-) create mode 100644 src/test/ui/proc-macro/auxiliary/mixed-site-span.rs create mode 100644 src/test/ui/proc-macro/mixed-site-span.rs create mode 100644 src/test/ui/proc-macro/mixed-site-span.stderr diff --git a/src/libproc_macro/bridge/mod.rs b/src/libproc_macro/bridge/mod.rs index 3c48466fffa28..c26b59f473c36 100644 --- a/src/libproc_macro/bridge/mod.rs +++ b/src/libproc_macro/bridge/mod.rs @@ -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; diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index d5cdc266acb6a..e199670b561c2 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -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 { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 54cfb80573e5c..583fb3f770183 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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. diff --git a/src/libsyntax/ext/proc_macro_server.rs b/src/libsyntax/ext/proc_macro_server.rs index dfec9ee28809a..021ec46d987cf 100644 --- a/src/libsyntax/ext/proc_macro_server.rs +++ b/src/libsyntax/ext/proc_macro_server.rs @@ -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 } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 6e83e1d3f8d4f..30ee9b90515ee 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -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). diff --git a/src/test/ui/proc-macro/auxiliary/mixed-site-span.rs b/src/test/ui/proc-macro/auxiliary/mixed-site-span.rs new file mode 100644 index 0000000000000..dea5ea04aa850 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/mixed-site-span.rs @@ -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; + ) + } +} diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout index 7ee8078b2c5d2..619b2fd5321ff 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout @@ -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), }, ] diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout index 4f7e000265eb0..5fdc6f8ee96ca 100644 --- a/src/test/ui/proc-macro/dollar-crate.stdout +++ b/src/test/ui/proc-macro/dollar-crate.stdout @@ -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), }, ] diff --git a/src/test/ui/proc-macro/mixed-site-span.rs b/src/test/ui/proc-macro/mixed-site-span.rs new file mode 100644 index 0000000000000..69c32a96ca055 --- /dev/null +++ b/src/test/ui/proc-macro/mixed-site-span.rs @@ -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!(); diff --git a/src/test/ui/proc-macro/mixed-site-span.stderr b/src/test/ui/proc-macro/mixed-site-span.stderr new file mode 100644 index 0000000000000..475e3e0ca359b --- /dev/null +++ b/src/test/ui/proc-macro/mixed-site-span.stderr @@ -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`.