diff --git a/build.rs b/build.rs index b69d813f..59505a50 100644 --- a/build.rs +++ b/build.rs @@ -100,6 +100,10 @@ fn main() { println!("cargo:rustc-cfg=no_is_available"); } + if version.minor < 66 { + println!("cargo:rustc-cfg=no_source_text"); + } + let target = env::var("TARGET").unwrap(); if !enable_use_proc_macro(&target) { return; diff --git a/src/fallback.rs b/src/fallback.rs index fc3cd515..22857435 100644 --- a/src/fallback.rs +++ b/src/fallback.rs @@ -342,6 +342,8 @@ thread_local! { files: vec![FileInfo { #[cfg(procmacro2_semver_exempt)] name: "".to_owned(), + #[cfg(not(no_source_text))] + source_text: String::new(), span: Span { lo: 0, hi: 0 }, lines: vec![0], }], @@ -352,6 +354,8 @@ thread_local! { struct FileInfo { #[cfg(procmacro2_semver_exempt)] name: String, + #[cfg(not(no_source_text))] + source_text: String, span: Span, lines: Vec, } @@ -379,6 +383,13 @@ impl FileInfo { fn span_within(&self, span: Span) -> bool { span.lo >= self.span.lo && span.hi <= self.span.hi } + + #[cfg(not(no_source_text))] + fn source_text(&self, span: Span) -> String { + let lo = (span.lo - self.span.lo) as usize; + let hi = (span.hi - self.span.hi) as usize; + self.source_text[lo..hi].to_owned() + } } /// Computes the offsets of each line in the given source string @@ -425,6 +436,8 @@ impl SourceMap { self.files.push(FileInfo { #[cfg(procmacro2_semver_exempt)] name: name.to_owned(), + #[cfg(not(no_source_text))] + source_text: src.to_owned(), span, lines, }); @@ -554,6 +567,16 @@ impl Span { }) } + #[cfg(any(no_source_text, not(span_locations)))] + pub fn source_text(&self) -> Option { + None + } + + #[cfg(all(not(no_source_text), span_locations))] + pub fn source_text(&self) -> Option { + SOURCE_MAP.with(|cm| Some(cm.borrow().fileinfo(*self).source_text(*self))) + } + #[cfg(not(span_locations))] pub(crate) fn first_byte(self) -> Self { self diff --git a/src/lib.rs b/src/lib.rs index 4eb756bc..83e99ce4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -528,6 +528,17 @@ impl Span { pub fn eq(&self, other: &Span) -> bool { self.inner.eq(&other.inner) } + + /// 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. + pub fn source_text(&self) -> Option { + self.inner.source_text() + } } /// Prints a span in a form convenient for debugging. diff --git a/src/wrapper.rs b/src/wrapper.rs index 720dcdd3..00f67cd6 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -530,6 +530,16 @@ impl Span { } } + pub fn source_text(&self) -> Option { + match self { + #[cfg(not(no_source_text))] + Span::Compiler(s) => s.source_text(), + #[cfg(no_source_text)] + Span::Compiler(_) => None, + Span::Fallback(s) => s.source_text(), + } + } + fn unwrap_nightly(self) -> proc_macro::Span { match self { Span::Compiler(s) => s,