Skip to content

Commit 3913df7

Browse files
authored
Merge pull request dtolnay#438 from buchgr/invalid-state
Add a function to reset thread-local span data.
2 parents 1edd1b9 + 2dffd33 commit 3913df7

File tree

4 files changed

+88
-7
lines changed

4 files changed

+88
-7
lines changed

src/fallback.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,17 +320,30 @@ impl Debug for SourceFile {
320320
}
321321
}
322322

323+
#[cfg(all(span_locations, not(fuzzing)))]
324+
fn dummy_file() -> FileInfo {
325+
FileInfo {
326+
source_text: String::new(),
327+
span: Span { lo: 0, hi: 0 },
328+
lines: vec![0],
329+
char_index_to_byte_offset: BTreeMap::new(),
330+
}
331+
}
332+
323333
#[cfg(all(span_locations, not(fuzzing)))]
324334
thread_local! {
325335
static SOURCE_MAP: RefCell<SourceMap> = RefCell::new(SourceMap {
326336
// Start with a single dummy file which all call_site() and def_site()
327337
// spans reference.
328-
files: vec![FileInfo {
329-
source_text: String::new(),
330-
span: Span { lo: 0, hi: 0 },
331-
lines: vec![0],
332-
char_index_to_byte_offset: BTreeMap::new(),
333-
}],
338+
files: vec![dummy_file()],
339+
});
340+
}
341+
342+
#[cfg(all(span_locations, not(fuzzing)))]
343+
pub fn invalidate_current_thread_spans() {
344+
SOURCE_MAP.with(|sm| {
345+
let mut sm = sm.borrow_mut();
346+
sm.files = vec![dummy_file()];
334347
});
335348
}
336349

src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ use std::error::Error;
170170
#[cfg(procmacro2_semver_exempt)]
171171
use std::path::PathBuf;
172172

173+
#[cfg(span_locations)]
174+
#[cfg_attr(doc_cfg, doc(cfg(feature = "span-locations")))]
175+
pub use crate::imp::invalidate_current_thread_spans;
176+
173177
#[cfg(span_locations)]
174178
#[cfg_attr(doc_cfg, doc(cfg(feature = "span-locations")))]
175179
pub use crate::location::LineColumn;
@@ -1325,4 +1329,4 @@ pub mod token_stream {
13251329
}
13261330
}
13271331
}
1328-
}
1332+
}

src/wrapper.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,3 +928,30 @@ impl Debug for Literal {
928928
}
929929
}
930930
}
931+
932+
/// Invalidates any `proc_macro2::Span` on the current thread
933+
///
934+
/// The implementation of the `proc_macro2::Span` type relies on thread-local
935+
/// memory and this function clears it. Calling any method on a
936+
/// `proc_macro2::Span` on the current thread and that was created before this
937+
/// function was called will either lead to incorrect results or abort your
938+
/// program.
939+
///
940+
/// This function is useful for programs that process more than 2^32 bytes of
941+
/// text on a single thread. The internal representation of `proc_macro2::Span`
942+
/// uses 32-bit integers to represent offsets and those will overflow when
943+
/// processing more than 2^32 bytes. This function resets all offsets
944+
/// and thereby also invalidates any previously created `proc_macro2::Span`.
945+
///
946+
/// This function requires the `span-locations` feature to be enabled. This
947+
/// function is not applicable to and will panic if called from a procedural macro.
948+
#[cfg(span_locations)]
949+
pub fn invalidate_current_thread_spans() {
950+
if inside_proc_macro() {
951+
panic!(
952+
"proc_macro2::invalidate_current_thread_spans is not available in procedural macros"
953+
);
954+
} else {
955+
crate::fallback::invalidate_current_thread_spans()
956+
}
957+
}

tests/test.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,3 +757,40 @@ fn byte_order_mark() {
757757
let string = "foo\u{feff}";
758758
string.parse::<TokenStream>().unwrap_err();
759759
}
760+
761+
// Creates a new Span from a TokenStream
762+
#[cfg(all(test, span_locations))]
763+
fn create_span() -> proc_macro2::Span {
764+
let tts: TokenStream = "1".parse().unwrap();
765+
match tts.into_iter().next().unwrap() {
766+
TokenTree::Literal(literal) => literal.span(),
767+
_ => unreachable!(),
768+
}
769+
}
770+
771+
#[cfg(span_locations)]
772+
#[test]
773+
fn test_invalidate_current_thread_spans() {
774+
let actual = format!("{:#?}", create_span());
775+
assert_eq!(actual, "bytes(1..2)");
776+
let actual = format!("{:#?}", create_span());
777+
assert_eq!(actual, "bytes(3..4)");
778+
779+
proc_macro2::invalidate_current_thread_spans();
780+
781+
let actual = format!("{:#?}", create_span());
782+
// Test that span offsets have been reset after the call
783+
// to invalidate_current_thread_spans()
784+
assert_eq!(actual, "bytes(1..2)");
785+
}
786+
787+
#[cfg(span_locations)]
788+
#[test]
789+
#[should_panic]
790+
fn test_use_span_after_invalidation() {
791+
let span = create_span();
792+
793+
proc_macro2::invalidate_current_thread_spans();
794+
795+
span.source_text();
796+
}

0 commit comments

Comments
 (0)