about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libproc_macro/bridge/mod.rs1
-rw-r--r--src/libproc_macro/lib.rs12
-rw-r--r--src/libsyntax_ext/proc_macro_server.rs3
-rw-r--r--src/test/run-pass/proc-macro/auxiliary/span-api-tests.rs11
-rw-r--r--src/test/run-pass/proc-macro/span-api-tests.rs34
5 files changed, 59 insertions, 2 deletions
diff --git a/src/libproc_macro/bridge/mod.rs b/src/libproc_macro/bridge/mod.rs
index f03c63fc04c..cf9229909d9 100644
--- a/src/libproc_macro/bridge/mod.rs
+++ b/src/libproc_macro/bridge/mod.rs
@@ -165,6 +165,7 @@ macro_rules! with_api {
                 fn end($self: $S::Span) -> LineColumn;
                 fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
                 fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
+                fn source_text($self: $S::Span) -> Option<String>;
             },
         }
     };
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 32c81302931..b39e139de54 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -341,6 +341,18 @@ impl Span {
         self.0 == other.0
     }
 
+    /// Returns the source text behind a span. This preserves the original source
+    /// code, including spaces and comments. It only returns a result if the span
+    /// corresponds to real source code.
+    ///
+    /// Note: The observable result of a macro should only rely on the tokens and
+    /// not on this source text. The result of this function is a best effort to
+    /// be used for diagnostics only.
+    #[unstable(feature = "proc_macro_span", issue = "54725")]
+    pub fn source_text(&self) -> Option<String> {
+        self.0.source_text()
+    }
+
     diagnostic_method!(error, Level::Error);
     diagnostic_method!(warning, Level::Warning);
     diagnostic_method!(note, Level::Note);
diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs
index 56bd58b28a6..a5a35662ec5 100644
--- a/src/libsyntax_ext/proc_macro_server.rs
+++ b/src/libsyntax_ext/proc_macro_server.rs
@@ -748,4 +748,7 @@ impl server::Span for Rustc<'_> {
     fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span {
         span.with_ctxt(at.ctxt())
     }
+    fn source_text(&mut self,  span: Self::Span) -> Option<String> {
+        self.sess.source_map().span_to_snippet(span).ok()
+    }
 }
diff --git a/src/test/run-pass/proc-macro/auxiliary/span-api-tests.rs b/src/test/run-pass/proc-macro/auxiliary/span-api-tests.rs
index 8e2c5c0a088..7afc341d416 100644
--- a/src/test/run-pass/proc-macro/auxiliary/span-api-tests.rs
+++ b/src/test/run-pass/proc-macro/auxiliary/span-api-tests.rs
@@ -43,3 +43,14 @@ pub fn assert_source_file(input: TokenStream) -> TokenStream {
 
     "".parse().unwrap()
 }
+
+#[proc_macro]
+pub fn macro_stringify(input: TokenStream) -> TokenStream {
+    let mut tokens = input.into_iter();
+    let first_span = tokens.next().expect("first token").span();
+    let last_span = tokens.last().map(|x| x.span()).unwrap_or(first_span);
+    let span = first_span.join(last_span).expect("joined span");
+    let src = span.source_text().expect("source_text");
+    TokenTree::Literal(Literal::string(&src)).into()
+}
+
diff --git a/src/test/run-pass/proc-macro/span-api-tests.rs b/src/test/run-pass/proc-macro/span-api-tests.rs
index 415cada265e..51cd8cfa208 100644
--- a/src/test/run-pass/proc-macro/span-api-tests.rs
+++ b/src/test/run-pass/proc-macro/span-api-tests.rs
@@ -13,12 +13,14 @@
 
 // ignore-pretty
 
+#![feature(proc_macro_hygiene)]
+
 #[macro_use]
 extern crate span_test_macros;
 
 extern crate span_api_tests;
 
-use span_api_tests::{reemit, assert_fake_source_file, assert_source_file};
+use span_api_tests::{reemit, assert_fake_source_file, assert_source_file, macro_stringify};
 
 macro_rules! say_hello {
     ($macname:ident) => ( $macname! { "Hello, world!" })
@@ -38,4 +40,32 @@ reemit! {
     assert_source_file! { "Hello, world!" }
 }
 
-fn main() {}
+fn main() {
+    let s = macro_stringify!(Hello, world!);
+    assert_eq!(s, "Hello, world!");
+    assert_eq!(macro_stringify!(Hello, world!), "Hello, world!");
+    assert_eq!(reemit_legacy!(macro_stringify!(Hello, world!)), "Hello, world!");
+    reemit_legacy!(assert_eq!(macro_stringify!(Hello, world!), "Hello, world!"));
+    // reemit change the span to be that of the call site
+    assert_eq!(
+        reemit!(macro_stringify!(Hello, world!)),
+        "reemit!(macro_stringify!(Hello, world!))"
+    );
+    let r = "reemit!(assert_eq!(macro_stringify!(Hello, world!), r));";
+    reemit!(assert_eq!(macro_stringify!(Hello, world!), r));
+
+    assert_eq!(macro_stringify!(
+        Hello,
+        world!
+    ), "Hello,\n        world!");
+
+    assert_eq!(macro_stringify!(Hello, /*world */ !), "Hello, /*world */ !");
+        assert_eq!(macro_stringify!(
+        Hello,
+        // comment
+        world!
+    ), "Hello,\n        // comment\n        world!");
+
+    assert_eq!(say_hello! { macro_stringify }, "\"Hello, world!\"");
+    assert_eq!(say_hello_extern! { macro_stringify }, "\"Hello, world!\"");
+}