1 | //! Items which do not have a correspondence to any API in the proc_macro crate, |
2 | //! but are necessary to include in proc-macro2. |
3 | |
4 | use crate::fallback; |
5 | use crate::imp; |
6 | use crate::marker::{ProcMacroAutoTraits, MARKER}; |
7 | use crate::Span; |
8 | use core::fmt::{self, Debug}; |
9 | |
10 | /// Invalidate any `proc_macro2::Span` that exist on the current thread. |
11 | /// |
12 | /// The implementation of `Span` uses thread-local data structures and this |
13 | /// function clears them. Calling any method on a `Span` on the current thread |
14 | /// created prior to the invalidation will return incorrect values or crash. |
15 | /// |
16 | /// This function is useful for programs that process more than 2<sup>32</sup> |
17 | /// bytes of Rust source code on the same thread. Just like rustc, proc-macro2 |
18 | /// uses 32-bit source locations, and these wrap around when the total source |
19 | /// code processed by the same thread exceeds 2<sup>32</sup> bytes (4 |
20 | /// gigabytes). After a wraparound, `Span` methods such as `source_text()` can |
21 | /// return wrong data. |
22 | /// |
23 | /// # Example |
24 | /// |
25 | /// As of late 2023, there is 200 GB of Rust code published on crates.io. |
26 | /// Looking at just the newest version of every crate, it is 16 GB of code. So a |
27 | /// workload that involves parsing it all would overflow a 32-bit source |
28 | /// location unless spans are being invalidated. |
29 | /// |
30 | /// ``` |
31 | /// use flate2::read::GzDecoder; |
32 | /// use std::ffi::OsStr; |
33 | /// use std::io::{BufReader, Read}; |
34 | /// use std::str::FromStr; |
35 | /// use tar::Archive; |
36 | /// |
37 | /// rayon::scope(|s| { |
38 | /// for krate in every_version_of_every_crate() { |
39 | /// s.spawn(move |_| { |
40 | /// proc_macro2::extra::invalidate_current_thread_spans(); |
41 | /// |
42 | /// let reader = BufReader::new(krate); |
43 | /// let tar = GzDecoder::new(reader); |
44 | /// let mut archive = Archive::new(tar); |
45 | /// for entry in archive.entries().unwrap() { |
46 | /// let mut entry = entry.unwrap(); |
47 | /// let path = entry.path().unwrap(); |
48 | /// if path.extension() != Some(OsStr::new("rs")) { |
49 | /// continue; |
50 | /// } |
51 | /// let mut content = String::new(); |
52 | /// entry.read_to_string(&mut content).unwrap(); |
53 | /// match proc_macro2::TokenStream::from_str(&content) { |
54 | /// Ok(tokens) => {/* ... */}, |
55 | /// Err(_) => continue, |
56 | /// } |
57 | /// } |
58 | /// }); |
59 | /// } |
60 | /// }); |
61 | /// # |
62 | /// # fn every_version_of_every_crate() -> Vec<std::fs::File> { |
63 | /// # Vec::new() |
64 | /// # } |
65 | /// ``` |
66 | /// |
67 | /// # Panics |
68 | /// |
69 | /// This function is not applicable to and will panic if called from a |
70 | /// procedural macro. |
71 | #[cfg (span_locations)] |
72 | #[cfg_attr (doc_cfg, doc(cfg(feature = "span-locations" )))] |
73 | pub fn invalidate_current_thread_spans() { |
74 | crate::imp::invalidate_current_thread_spans(); |
75 | } |
76 | |
77 | /// An object that holds a [`Group`]'s `span_open()` and `span_close()` together |
78 | /// in a more compact representation than holding those 2 spans individually. |
79 | /// |
80 | /// [`Group`]: crate::Group |
81 | #[derive (Copy, Clone)] |
82 | pub struct DelimSpan { |
83 | inner: DelimSpanEnum, |
84 | _marker: ProcMacroAutoTraits, |
85 | } |
86 | |
87 | #[derive (Copy, Clone)] |
88 | enum DelimSpanEnum { |
89 | #[cfg (wrap_proc_macro)] |
90 | Compiler { |
91 | join: proc_macro::Span, |
92 | open: proc_macro::Span, |
93 | close: proc_macro::Span, |
94 | }, |
95 | Fallback(fallback::Span), |
96 | } |
97 | |
98 | impl DelimSpan { |
99 | pub(crate) fn new(group: &imp::Group) -> Self { |
100 | #[cfg (wrap_proc_macro)] |
101 | let inner = match group { |
102 | imp::Group::Compiler(group) => DelimSpanEnum::Compiler { |
103 | join: group.span(), |
104 | open: group.span_open(), |
105 | close: group.span_close(), |
106 | }, |
107 | imp::Group::Fallback(group) => DelimSpanEnum::Fallback(group.span()), |
108 | }; |
109 | |
110 | #[cfg (not(wrap_proc_macro))] |
111 | let inner = DelimSpanEnum::Fallback(group.span()); |
112 | |
113 | DelimSpan { |
114 | inner, |
115 | _marker: MARKER, |
116 | } |
117 | } |
118 | |
119 | /// Returns a span covering the entire delimited group. |
120 | pub fn join(&self) -> Span { |
121 | match &self.inner { |
122 | #[cfg (wrap_proc_macro)] |
123 | DelimSpanEnum::Compiler { join, .. } => Span::_new(imp::Span::Compiler(*join)), |
124 | DelimSpanEnum::Fallback(span) => Span::_new_fallback(*span), |
125 | } |
126 | } |
127 | |
128 | /// Returns a span for the opening punctuation of the group only. |
129 | pub fn open(&self) -> Span { |
130 | match &self.inner { |
131 | #[cfg (wrap_proc_macro)] |
132 | DelimSpanEnum::Compiler { open, .. } => Span::_new(imp::Span::Compiler(*open)), |
133 | DelimSpanEnum::Fallback(span) => Span::_new_fallback(span.first_byte()), |
134 | } |
135 | } |
136 | |
137 | /// Returns a span for the closing punctuation of the group only. |
138 | pub fn close(&self) -> Span { |
139 | match &self.inner { |
140 | #[cfg (wrap_proc_macro)] |
141 | DelimSpanEnum::Compiler { close, .. } => Span::_new(imp::Span::Compiler(*close)), |
142 | DelimSpanEnum::Fallback(span) => Span::_new_fallback(span.last_byte()), |
143 | } |
144 | } |
145 | } |
146 | |
147 | impl Debug for DelimSpan { |
148 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
149 | Debug::fmt(&self.join(), f) |
150 | } |
151 | } |
152 | |