about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-09-22 18:38:02 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2019-10-03 11:28:06 +0300
commitd1310dc6c989e191d85c73c943d4175fbf1dccb8 (patch)
treed0ca6fac9bdb651e328a8a6990c16208ad5dc43a
parentc6293e359848c8c7275330719b33ccd168df04c3 (diff)
downloadrust-d1310dc6c989e191d85c73c943d4175fbf1dccb8.tar.gz
rust-d1310dc6c989e191d85c73c943d4175fbf1dccb8.zip
proc_macro: Add `Span::mixed_site` exposing `macro_rules` hygiene
-rw-r--r--src/libproc_macro/bridge/mod.rs1
-rw-r--r--src/libproc_macro/lib.rs9
-rw-r--r--src/libsyntax/ext/base.rs6
-rw-r--r--src/libsyntax/ext/proc_macro_server.rs5
-rw-r--r--src/libsyntax_pos/lib.rs6
-rw-r--r--src/test/ui/proc-macro/auxiliary/mixed-site-span.rs42
-rw-r--r--src/test/ui/proc-macro/dollar-crate-issue-62325.stdout22
-rw-r--r--src/test/ui/proc-macro/dollar-crate.stdout48
-rw-r--r--src/test/ui/proc-macro/mixed-site-span.rs26
-rw-r--r--src/test/ui/proc-macro/mixed-site-span.stderr49
10 files changed, 179 insertions, 35 deletions
diff --git a/src/libproc_macro/bridge/mod.rs b/src/libproc_macro/bridge/mod.rs
index 3c48466fffa..c26b59f473c 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 d5cdc266acb..e199670b561 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 54cfb80573e..583fb3f7701 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 dfec9ee2880..021ec46d987 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 6e83e1d3f8d..30ee9b90515 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 00000000000..dea5ea04aa8
--- /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 7ee8078b2c5..619b2fd5321 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 4f7e000265e..5fdc6f8ee96 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 00000000000..69c32a96ca0
--- /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 00000000000..475e3e0ca35
--- /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`.