From e88b0d9a94118168fd02a917e7c99ad47cf3418e Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 8 Nov 2018 10:07:02 +0100 Subject: [PATCH] Introduce proc_macro::Span::source_text --- src/libproc_macro/bridge/mod.rs | 1 + src/libproc_macro/lib.rs | 12 +++++++ src/libsyntax_ext/proc_macro_server.rs | 3 ++ .../proc-macro/auxiliary/span-api-tests.rs | 11 ++++++ .../run-pass/proc-macro/span-api-tests.rs | 34 +++++++++++++++++-- 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/libproc_macro/bridge/mod.rs b/src/libproc_macro/bridge/mod.rs index f03c63fc04c81..cf9229909d9ff 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; }, } }; diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 32c81302931f3..b39e139de5467 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 { + 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 56bd58b28a66e..a5a35662ec59e 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 { + 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 8e2c5c0a088d2..7afc341d41609 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 415cada265ec7..51cd8cfa20867 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!\""); +}