]>
Commit | Line | Data |
---|---|---|
83c7162d | 1 | //! Machinery for hygienic macros, inspired by the `MTWT[1]` paper. |
5bcae85e | 2 | //! |
83c7162d | 3 | //! `[1]` Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler. 2012. |
ff7c6d11 | 4 | //! *Macros that work together: Compile-time bindings, partial expansion, |
5bcae85e | 5 | //! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216. |
94b46f34 | 6 | //! DOI=10.1017/S0956796812000093 <https://doi.org/10.1017/S0956796812000093> |
5bcae85e | 7 | |
dc9dc135 XL |
8 | // Hygiene data is stored in a global variable and accessed via TLS, which |
9 | // means that accesses are somewhat expensive. (`HygieneData::with` | |
10 | // encapsulates a single access.) Therefore, on hot code paths it is worth | |
11 | // ensuring that multiple HygieneData accesses are combined into a single | |
12 | // `HygieneData::with`. | |
13 | // | |
416331ca | 14 | // This explains why `HygieneData`, `SyntaxContext` and `ExpnId` have interfaces |
dc9dc135 | 15 | // with a certain amount of redundancy in them. For example, |
e1599b0c XL |
16 | // `SyntaxContext::outer_expn_data` combines `SyntaxContext::outer` and |
17 | // `ExpnId::expn_data` so that two `HygieneData` accesses can be performed within | |
dc9dc135 XL |
18 | // a single `HygieneData::with` call. |
19 | // | |
20 | // It also explains why many functions appear in `HygieneData` and again in | |
416331ca | 21 | // `SyntaxContext` or `ExpnId`. For example, `HygieneData::outer` and |
dc9dc135 XL |
22 | // `SyntaxContext::outer` do the same thing, but the former is for use within a |
23 | // `HygieneData::with` call while the latter is for use outside such a call. | |
24 | // When modifying this file it is important to understand this distinction, | |
25 | // because getting it wrong can lead to nested `HygieneData::with` calls that | |
26 | // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) | |
27 | ||
dc9dc135 | 28 | use crate::edition::Edition; |
60c5eb7d | 29 | use crate::symbol::{kw, sym, Symbol}; |
dfeec247 XL |
30 | use crate::GLOBALS; |
31 | use crate::{Span, DUMMY_SP}; | |
cc61c64b | 32 | |
416331ca | 33 | use rustc_data_structures::fx::FxHashMap; |
9fa01778 | 34 | use rustc_data_structures::sync::Lrc; |
dfeec247 XL |
35 | use rustc_macros::HashStable_Generic; |
36 | use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; | |
416331ca | 37 | use std::fmt; |
5bcae85e | 38 | |
416331ca XL |
39 | /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". |
40 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
8faf50e0 XL |
41 | pub struct SyntaxContext(u32); |
42 | ||
416331ca | 43 | #[derive(Debug)] |
8faf50e0 | 44 | struct SyntaxContextData { |
416331ca XL |
45 | outer_expn: ExpnId, |
46 | outer_transparency: Transparency, | |
47 | parent: SyntaxContext, | |
48 | /// This context, but with all transparent and semi-transparent expansions filtered away. | |
8faf50e0 | 49 | opaque: SyntaxContext, |
416331ca | 50 | /// This context, but with all transparent expansions filtered away. |
8faf50e0 | 51 | opaque_and_semitransparent: SyntaxContext, |
48663c56 | 52 | /// Name of the crate to which `$crate` with this context would resolve. |
0731742a | 53 | dollar_crate_name: Symbol, |
5bcae85e SL |
54 | } |
55 | ||
416331ca XL |
56 | /// A unique ID associated with a macro invocation and expansion. |
57 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | |
58 | pub struct ExpnId(u32); | |
5bcae85e | 59 | |
8faf50e0 XL |
60 | /// A property of a macro expansion that determines how identifiers |
61 | /// produced by that expansion are resolved. | |
ba9703b0 XL |
62 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, RustcEncodable, RustcDecodable)] |
63 | #[derive(HashStable_Generic)] | |
8faf50e0 XL |
64 | pub enum Transparency { |
65 | /// Identifier produced by a transparent expansion is always resolved at call-site. | |
66 | /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. | |
67 | Transparent, | |
68 | /// Identifier produced by a semi-transparent expansion may be resolved | |
69 | /// either at call-site or at definition-site. | |
70 | /// If it's a local variable, label or `$crate` then it's resolved at def-site. | |
71 | /// Otherwise it's resolved at call-site. | |
72 | /// `macro_rules` macros behave like this, built-in macros currently behave like this too, | |
73 | /// but that's an implementation detail. | |
74 | SemiTransparent, | |
75 | /// Identifier produced by an opaque expansion is always resolved at definition-site. | |
76 | /// Def-site spans in procedural macros, identifiers from `macro` by default use this. | |
77 | Opaque, | |
ff7c6d11 XL |
78 | } |
79 | ||
416331ca | 80 | impl ExpnId { |
e1599b0c XL |
81 | pub fn fresh(expn_data: Option<ExpnData>) -> Self { |
82 | HygieneData::with(|data| data.fresh_expn(expn_data)) | |
5bcae85e | 83 | } |
9e0c209e | 84 | |
416331ca | 85 | /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. |
ff7c6d11 | 86 | #[inline] |
9e0c209e | 87 | pub fn root() -> Self { |
416331ca | 88 | ExpnId(0) |
9e0c209e SL |
89 | } |
90 | ||
ff7c6d11 | 91 | #[inline] |
cc61c64b XL |
92 | pub fn as_u32(self) -> u32 { |
93 | self.0 | |
c30ab7b3 SL |
94 | } |
95 | ||
ff7c6d11 | 96 | #[inline] |
416331ca XL |
97 | pub fn from_u32(raw: u32) -> ExpnId { |
98 | ExpnId(raw) | |
32a655c1 SL |
99 | } |
100 | ||
b7449926 | 101 | #[inline] |
e1599b0c XL |
102 | pub fn expn_data(self) -> ExpnData { |
103 | HygieneData::with(|data| data.expn_data(self).clone()) | |
b7449926 XL |
104 | } |
105 | ||
ff7c6d11 | 106 | #[inline] |
e1599b0c | 107 | pub fn set_expn_data(self, expn_data: ExpnData) { |
416331ca | 108 | HygieneData::with(|data| { |
e1599b0c XL |
109 | let old_expn_data = &mut data.expn_data[self.0 as usize]; |
110 | assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); | |
111 | *old_expn_data = Some(expn_data); | |
416331ca | 112 | }) |
7cac9316 XL |
113 | } |
114 | ||
416331ca | 115 | pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { |
dc9dc135 | 116 | HygieneData::with(|data| data.is_descendant_of(self, ancestor)) |
7cac9316 XL |
117 | } |
118 | ||
416331ca XL |
119 | /// `expn_id.outer_expn_is_descendant_of(ctxt)` is equivalent to but faster than |
120 | /// `expn_id.is_descendant_of(ctxt.outer_expn())`. | |
121 | pub fn outer_expn_is_descendant_of(self, ctxt: SyntaxContext) -> bool { | |
122 | HygieneData::with(|data| data.is_descendant_of(self, data.outer_expn(ctxt))) | |
83c7162d | 123 | } |
60c5eb7d XL |
124 | |
125 | /// Returns span for the macro which originally caused this expansion to happen. | |
126 | /// | |
127 | /// Stops backtracing at include! boundary. | |
128 | pub fn expansion_cause(mut self) -> Option<Span> { | |
129 | let mut last_macro = None; | |
130 | loop { | |
131 | let expn_data = self.expn_data(); | |
132 | // Stop going up the backtrace once include! is encountered | |
dfeec247 XL |
133 | if expn_data.is_root() |
134 | || expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) | |
135 | { | |
60c5eb7d XL |
136 | break; |
137 | } | |
138 | self = expn_data.call_site.ctxt().outer_expn(); | |
139 | last_macro = Some(expn_data.call_site); | |
140 | } | |
141 | last_macro | |
142 | } | |
5bcae85e SL |
143 | } |
144 | ||
8faf50e0 XL |
145 | #[derive(Debug)] |
146 | crate struct HygieneData { | |
e1599b0c XL |
147 | /// Each expansion should have an associated expansion data, but sometimes there's a delay |
148 | /// between creation of an expansion ID and obtaining its data (e.g. macros are collected | |
149 | /// first and then resolved later), so we use an `Option` here. | |
150 | expn_data: Vec<Option<ExpnData>>, | |
416331ca XL |
151 | syntax_context_data: Vec<SyntaxContextData>, |
152 | syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, | |
5bcae85e SL |
153 | } |
154 | ||
155 | impl HygieneData { | |
416331ca | 156 | crate fn new(edition: Edition) -> Self { |
5bcae85e | 157 | HygieneData { |
e1599b0c | 158 | expn_data: vec![Some(ExpnData::default(ExpnKind::Root, DUMMY_SP, edition))], |
416331ca XL |
159 | syntax_context_data: vec![SyntaxContextData { |
160 | outer_expn: ExpnId::root(), | |
161 | outer_transparency: Transparency::Opaque, | |
162 | parent: SyntaxContext(0), | |
8faf50e0 XL |
163 | opaque: SyntaxContext(0), |
164 | opaque_and_semitransparent: SyntaxContext(0), | |
dc9dc135 | 165 | dollar_crate_name: kw::DollarCrate, |
ff7c6d11 | 166 | }], |
416331ca | 167 | syntax_context_map: FxHashMap::default(), |
5bcae85e SL |
168 | } |
169 | } | |
170 | ||
171 | fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T { | |
0531ce1d | 172 | GLOBALS.with(|globals| f(&mut *globals.hygiene_data.borrow_mut())) |
5bcae85e | 173 | } |
5bcae85e | 174 | |
e1599b0c XL |
175 | fn fresh_expn(&mut self, expn_data: Option<ExpnData>) -> ExpnId { |
176 | self.expn_data.push(expn_data); | |
416331ca | 177 | ExpnId(self.expn_data.len() as u32 - 1) |
dc9dc135 XL |
178 | } |
179 | ||
e1599b0c | 180 | fn expn_data(&self, expn_id: ExpnId) -> &ExpnData { |
dfeec247 | 181 | self.expn_data[expn_id.0 as usize].as_ref().expect("no expansion data for an expansion ID") |
416331ca XL |
182 | } |
183 | ||
184 | fn is_descendant_of(&self, mut expn_id: ExpnId, ancestor: ExpnId) -> bool { | |
185 | while expn_id != ancestor { | |
186 | if expn_id == ExpnId::root() { | |
dc9dc135 XL |
187 | return false; |
188 | } | |
e1599b0c | 189 | expn_id = self.expn_data(expn_id).parent; |
dc9dc135 XL |
190 | } |
191 | true | |
192 | } | |
193 | ||
ba9703b0 | 194 | fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { |
416331ca | 195 | self.syntax_context_data[ctxt.0 as usize].opaque |
dc9dc135 XL |
196 | } |
197 | ||
ba9703b0 | 198 | fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { |
416331ca | 199 | self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent |
dc9dc135 XL |
200 | } |
201 | ||
416331ca XL |
202 | fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { |
203 | self.syntax_context_data[ctxt.0 as usize].outer_expn | |
dc9dc135 XL |
204 | } |
205 | ||
e1599b0c XL |
206 | fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { |
207 | let data = &self.syntax_context_data[ctxt.0 as usize]; | |
208 | (data.outer_expn, data.outer_transparency) | |
dc9dc135 XL |
209 | } |
210 | ||
416331ca XL |
211 | fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { |
212 | self.syntax_context_data[ctxt.0 as usize].parent | |
dc9dc135 XL |
213 | } |
214 | ||
e1599b0c XL |
215 | fn remove_mark(&self, ctxt: &mut SyntaxContext) -> (ExpnId, Transparency) { |
216 | let outer_mark = self.outer_mark(*ctxt); | |
416331ca | 217 | *ctxt = self.parent_ctxt(*ctxt); |
e1599b0c | 218 | outer_mark |
dc9dc135 XL |
219 | } |
220 | ||
416331ca | 221 | fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> { |
dc9dc135 | 222 | let mut marks = Vec::new(); |
e1599b0c XL |
223 | while ctxt != SyntaxContext::root() { |
224 | marks.push(self.outer_mark(ctxt)); | |
416331ca | 225 | ctxt = self.parent_ctxt(ctxt); |
dc9dc135 XL |
226 | } |
227 | marks.reverse(); | |
228 | marks | |
229 | } | |
230 | ||
231 | fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { | |
e1599b0c XL |
232 | while span.from_expansion() && span.ctxt() != to { |
233 | span = self.expn_data(self.outer_expn(span.ctxt())).call_site; | |
dc9dc135 XL |
234 | } |
235 | span | |
236 | } | |
237 | ||
416331ca | 238 | fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option<ExpnId> { |
dc9dc135 | 239 | let mut scope = None; |
416331ca | 240 | while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) { |
e1599b0c | 241 | scope = Some(self.remove_mark(ctxt).0); |
dc9dc135 XL |
242 | } |
243 | scope | |
244 | } | |
245 | ||
e1599b0c | 246 | fn apply_mark( |
dfeec247 XL |
247 | &mut self, |
248 | ctxt: SyntaxContext, | |
249 | expn_id: ExpnId, | |
250 | transparency: Transparency, | |
e1599b0c | 251 | ) -> SyntaxContext { |
416331ca | 252 | assert_ne!(expn_id, ExpnId::root()); |
dc9dc135 | 253 | if transparency == Transparency::Opaque { |
416331ca | 254 | return self.apply_mark_internal(ctxt, expn_id, transparency); |
dc9dc135 XL |
255 | } |
256 | ||
e1599b0c | 257 | let call_site_ctxt = self.expn_data(expn_id).call_site.ctxt(); |
dc9dc135 | 258 | let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { |
ba9703b0 | 259 | self.normalize_to_macros_2_0(call_site_ctxt) |
dc9dc135 | 260 | } else { |
ba9703b0 | 261 | self.normalize_to_macro_rules(call_site_ctxt) |
dc9dc135 XL |
262 | }; |
263 | ||
e1599b0c | 264 | if call_site_ctxt == SyntaxContext::root() { |
416331ca | 265 | return self.apply_mark_internal(ctxt, expn_id, transparency); |
dc9dc135 XL |
266 | } |
267 | ||
416331ca | 268 | // Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a |
dc9dc135 XL |
269 | // macros 2.0 expansion, i.e., a macros 1.0 invocation is in a macros 2.0 definition. |
270 | // | |
271 | // In this case, the tokens from the macros 1.0 definition inherit the hygiene | |
272 | // at their invocation. That is, we pretend that the macros 1.0 definition | |
273 | // was defined at its invocation (i.e., inside the macros 2.0 definition) | |
274 | // so that the macros 2.0 definition remains hygienic. | |
275 | // | |
416331ca XL |
276 | // See the example at `test/ui/hygiene/legacy_interaction.rs`. |
277 | for (expn_id, transparency) in self.marks(ctxt) { | |
278 | call_site_ctxt = self.apply_mark_internal(call_site_ctxt, expn_id, transparency); | |
dc9dc135 | 279 | } |
416331ca | 280 | self.apply_mark_internal(call_site_ctxt, expn_id, transparency) |
dc9dc135 XL |
281 | } |
282 | ||
416331ca | 283 | fn apply_mark_internal( |
dfeec247 XL |
284 | &mut self, |
285 | ctxt: SyntaxContext, | |
286 | expn_id: ExpnId, | |
287 | transparency: Transparency, | |
416331ca XL |
288 | ) -> SyntaxContext { |
289 | let syntax_context_data = &mut self.syntax_context_data; | |
290 | let mut opaque = syntax_context_data[ctxt.0 as usize].opaque; | |
dc9dc135 | 291 | let mut opaque_and_semitransparent = |
416331ca | 292 | syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent; |
dc9dc135 XL |
293 | |
294 | if transparency >= Transparency::Opaque { | |
416331ca | 295 | let parent = opaque; |
dfeec247 XL |
296 | opaque = *self |
297 | .syntax_context_map | |
298 | .entry((parent, expn_id, transparency)) | |
299 | .or_insert_with(|| { | |
300 | let new_opaque = SyntaxContext(syntax_context_data.len() as u32); | |
301 | syntax_context_data.push(SyntaxContextData { | |
302 | outer_expn: expn_id, | |
303 | outer_transparency: transparency, | |
304 | parent, | |
305 | opaque: new_opaque, | |
306 | opaque_and_semitransparent: new_opaque, | |
307 | dollar_crate_name: kw::DollarCrate, | |
308 | }); | |
309 | new_opaque | |
dc9dc135 | 310 | }); |
dc9dc135 XL |
311 | } |
312 | ||
313 | if transparency >= Transparency::SemiTransparent { | |
416331ca | 314 | let parent = opaque_and_semitransparent; |
dfeec247 XL |
315 | opaque_and_semitransparent = *self |
316 | .syntax_context_map | |
317 | .entry((parent, expn_id, transparency)) | |
318 | .or_insert_with(|| { | |
319 | let new_opaque_and_semitransparent = | |
320 | SyntaxContext(syntax_context_data.len() as u32); | |
321 | syntax_context_data.push(SyntaxContextData { | |
322 | outer_expn: expn_id, | |
323 | outer_transparency: transparency, | |
324 | parent, | |
325 | opaque, | |
326 | opaque_and_semitransparent: new_opaque_and_semitransparent, | |
327 | dollar_crate_name: kw::DollarCrate, | |
328 | }); | |
329 | new_opaque_and_semitransparent | |
dc9dc135 | 330 | }); |
dc9dc135 XL |
331 | } |
332 | ||
416331ca XL |
333 | let parent = ctxt; |
334 | *self.syntax_context_map.entry((parent, expn_id, transparency)).or_insert_with(|| { | |
dc9dc135 | 335 | let new_opaque_and_semitransparent_and_transparent = |
416331ca XL |
336 | SyntaxContext(syntax_context_data.len() as u32); |
337 | syntax_context_data.push(SyntaxContextData { | |
338 | outer_expn: expn_id, | |
339 | outer_transparency: transparency, | |
340 | parent, | |
dc9dc135 XL |
341 | opaque, |
342 | opaque_and_semitransparent, | |
343 | dollar_crate_name: kw::DollarCrate, | |
344 | }); | |
345 | new_opaque_and_semitransparent_and_transparent | |
346 | }) | |
347 | } | |
94b46f34 XL |
348 | } |
349 | ||
416331ca XL |
350 | pub fn clear_syntax_context_map() { |
351 | HygieneData::with(|data| data.syntax_context_map = FxHashMap::default()); | |
5bcae85e SL |
352 | } |
353 | ||
dc9dc135 XL |
354 | pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { |
355 | HygieneData::with(|data| data.walk_chain(span, to)) | |
356 | } | |
357 | ||
416331ca XL |
358 | pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { |
359 | // The new contexts that need updating are at the end of the list and have `$crate` as a name. | |
dfeec247 XL |
360 | let (len, to_update) = HygieneData::with(|data| { |
361 | ( | |
362 | data.syntax_context_data.len(), | |
363 | data.syntax_context_data | |
364 | .iter() | |
365 | .rev() | |
366 | .take_while(|scdata| scdata.dollar_crate_name == kw::DollarCrate) | |
367 | .count(), | |
368 | ) | |
369 | }); | |
416331ca XL |
370 | // The callback must be called from outside of the `HygieneData` lock, |
371 | // since it will try to acquire it too. | |
dfeec247 | 372 | let range_to_update = len - to_update..len; |
416331ca XL |
373 | let names: Vec<_> = |
374 | range_to_update.clone().map(|idx| get_name(SyntaxContext::from_u32(idx as u32))).collect(); | |
dfeec247 XL |
375 | HygieneData::with(|data| { |
376 | range_to_update.zip(names.into_iter()).for_each(|(idx, name)| { | |
377 | data.syntax_context_data[idx].dollar_crate_name = name; | |
378 | }) | |
379 | }) | |
416331ca XL |
380 | } |
381 | ||
e1599b0c XL |
382 | pub fn debug_hygiene_data(verbose: bool) -> String { |
383 | HygieneData::with(|data| { | |
384 | if verbose { | |
385 | format!("{:#?}", data) | |
386 | } else { | |
387 | let mut s = String::from(""); | |
388 | s.push_str("Expansions:"); | |
389 | data.expn_data.iter().enumerate().for_each(|(id, expn_info)| { | |
390 | let expn_info = expn_info.as_ref().expect("no expansion data for an expansion ID"); | |
391 | s.push_str(&format!( | |
392 | "\n{}: parent: {:?}, call_site_ctxt: {:?}, kind: {:?}", | |
393 | id, | |
394 | expn_info.parent, | |
395 | expn_info.call_site.ctxt(), | |
396 | expn_info.kind, | |
397 | )); | |
398 | }); | |
399 | s.push_str("\n\nSyntaxContexts:"); | |
400 | data.syntax_context_data.iter().enumerate().for_each(|(id, ctxt)| { | |
401 | s.push_str(&format!( | |
402 | "\n#{}: parent: {:?}, outer_mark: ({:?}, {:?})", | |
dfeec247 | 403 | id, ctxt.parent, ctxt.outer_expn, ctxt.outer_transparency, |
e1599b0c XL |
404 | )); |
405 | }); | |
406 | s | |
407 | } | |
408 | }) | |
409 | } | |
410 | ||
5bcae85e | 411 | impl SyntaxContext { |
48663c56 | 412 | #[inline] |
e1599b0c | 413 | pub const fn root() -> Self { |
5bcae85e SL |
414 | SyntaxContext(0) |
415 | } | |
416 | ||
48663c56 | 417 | #[inline] |
8faf50e0 XL |
418 | crate fn as_u32(self) -> u32 { |
419 | self.0 | |
420 | } | |
421 | ||
48663c56 | 422 | #[inline] |
8faf50e0 XL |
423 | crate fn from_u32(raw: u32) -> SyntaxContext { |
424 | SyntaxContext(raw) | |
425 | } | |
426 | ||
416331ca | 427 | /// Extend a syntax context with a given expansion and transparency. |
e1599b0c XL |
428 | crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext { |
429 | HygieneData::with(|data| data.apply_mark(self, expn_id, transparency)) | |
5bcae85e | 430 | } |
cc61c64b | 431 | |
83c7162d XL |
432 | /// Pulls a single mark off of the syntax context. This effectively moves the |
433 | /// context up one macro definition level. That is, if we have a nested macro | |
434 | /// definition as follows: | |
435 | /// | |
436 | /// ```rust | |
437 | /// macro_rules! f { | |
438 | /// macro_rules! g { | |
439 | /// ... | |
440 | /// } | |
441 | /// } | |
442 | /// ``` | |
443 | /// | |
444 | /// and we have a SyntaxContext that is referring to something declared by an invocation | |
445 | /// of g (call it g1), calling remove_mark will result in the SyntaxContext for the | |
446 | /// invocation of f that created g1. | |
447 | /// Returns the mark that was removed. | |
416331ca | 448 | pub fn remove_mark(&mut self) -> ExpnId { |
e1599b0c | 449 | HygieneData::with(|data| data.remove_mark(self).0) |
7cac9316 XL |
450 | } |
451 | ||
416331ca | 452 | pub fn marks(self) -> Vec<(ExpnId, Transparency)> { |
dc9dc135 | 453 | HygieneData::with(|data| data.marks(self)) |
2c00a5a8 XL |
454 | } |
455 | ||
7cac9316 XL |
456 | /// Adjust this context for resolution in a scope created by the given expansion. |
457 | /// For example, consider the following three resolutions of `f`: | |
ff7c6d11 | 458 | /// |
7cac9316 XL |
459 | /// ```rust |
460 | /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. | |
461 | /// m!(f); | |
462 | /// macro m($f:ident) { | |
463 | /// mod bar { | |
416331ca | 464 | /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. |
7cac9316 XL |
465 | /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty. |
466 | /// } | |
416331ca | 467 | /// foo::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` |
7cac9316 XL |
468 | /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`, |
469 | /// //| and it resolves to `::foo::f`. | |
416331ca | 470 | /// bar::f(); // `f`'s `SyntaxContext` has a single `ExpnId` from `m` |
7cac9316 XL |
471 | /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`, |
472 | /// //| and it resolves to `::bar::f`. | |
473 | /// bar::$f(); // `f`'s `SyntaxContext` is empty. | |
474 | /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`, | |
475 | /// //| and it resolves to `::bar::$f`. | |
476 | /// } | |
477 | /// ``` | |
478 | /// This returns the expansion whose definition scope we use to privacy check the resolution, | |
0731742a | 479 | /// or `None` if we privacy check as usual (i.e., not w.r.t. a macro definition scope). |
416331ca XL |
480 | pub fn adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { |
481 | HygieneData::with(|data| data.adjust(self, expn_id)) | |
dc9dc135 XL |
482 | } |
483 | ||
ba9703b0 XL |
484 | /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. |
485 | pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option<ExpnId> { | |
dc9dc135 | 486 | HygieneData::with(|data| { |
ba9703b0 | 487 | *self = data.normalize_to_macros_2_0(*self); |
416331ca | 488 | data.adjust(self, expn_id) |
dc9dc135 | 489 | }) |
7cac9316 XL |
490 | } |
491 | ||
492 | /// Adjust this context for resolution in a scope created by the given expansion | |
493 | /// via a glob import with the given `SyntaxContext`. | |
ff7c6d11 XL |
494 | /// For example: |
495 | /// | |
7cac9316 XL |
496 | /// ```rust |
497 | /// m!(f); | |
498 | /// macro m($i:ident) { | |
499 | /// mod foo { | |
416331ca | 500 | /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. |
7cac9316 XL |
501 | /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. |
502 | /// } | |
503 | /// n(f); | |
504 | /// macro n($j:ident) { | |
505 | /// use foo::*; | |
506 | /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` | |
507 | /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`. | |
508 | /// $i(); // `$i`'s `SyntaxContext` has a mark from `n` | |
509 | /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`. | |
510 | /// $j(); // `$j`'s `SyntaxContext` has a mark from `m` | |
511 | /// //^ This cannot be glob-adjusted, so this is a resolution error. | |
512 | /// } | |
513 | /// } | |
514 | /// ``` | |
515 | /// This returns `None` if the context cannot be glob-adjusted. | |
516 | /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). | |
416331ca | 517 | pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option<Option<ExpnId>> { |
dc9dc135 XL |
518 | HygieneData::with(|data| { |
519 | let mut scope = None; | |
ba9703b0 | 520 | let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); |
416331ca | 521 | while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { |
e1599b0c XL |
522 | scope = Some(data.remove_mark(&mut glob_ctxt).0); |
523 | if data.remove_mark(self).0 != scope.unwrap() { | |
dc9dc135 XL |
524 | return None; |
525 | } | |
526 | } | |
416331ca | 527 | if data.adjust(self, expn_id).is_some() { |
7cac9316 XL |
528 | return None; |
529 | } | |
dc9dc135 XL |
530 | Some(scope) |
531 | }) | |
7cac9316 XL |
532 | } |
533 | ||
534 | /// Undo `glob_adjust` if possible: | |
ff7c6d11 | 535 | /// |
7cac9316 XL |
536 | /// ```rust |
537 | /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { | |
538 | /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); | |
539 | /// } | |
540 | /// ``` | |
dfeec247 XL |
541 | pub fn reverse_glob_adjust( |
542 | &mut self, | |
543 | expn_id: ExpnId, | |
544 | glob_span: Span, | |
545 | ) -> Option<Option<ExpnId>> { | |
dc9dc135 | 546 | HygieneData::with(|data| { |
416331ca | 547 | if data.adjust(self, expn_id).is_some() { |
dc9dc135 XL |
548 | return None; |
549 | } | |
7cac9316 | 550 | |
ba9703b0 | 551 | let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); |
dc9dc135 | 552 | let mut marks = Vec::new(); |
416331ca | 553 | while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { |
dc9dc135 XL |
554 | marks.push(data.remove_mark(&mut glob_ctxt)); |
555 | } | |
7cac9316 | 556 | |
e1599b0c XL |
557 | let scope = marks.last().map(|mark| mark.0); |
558 | while let Some((expn_id, transparency)) = marks.pop() { | |
559 | *self = data.apply_mark(*self, expn_id, transparency); | |
dc9dc135 XL |
560 | } |
561 | Some(scope) | |
562 | }) | |
563 | } | |
564 | ||
416331ca | 565 | pub fn hygienic_eq(self, other: SyntaxContext, expn_id: ExpnId) -> bool { |
dc9dc135 | 566 | HygieneData::with(|data| { |
ba9703b0 XL |
567 | let mut self_normalized = data.normalize_to_macros_2_0(self); |
568 | data.adjust(&mut self_normalized, expn_id); | |
569 | self_normalized == data.normalize_to_macros_2_0(other) | |
dc9dc135 | 570 | }) |
7cac9316 XL |
571 | } |
572 | ||
ff7c6d11 | 573 | #[inline] |
ba9703b0 XL |
574 | pub fn normalize_to_macros_2_0(self) -> SyntaxContext { |
575 | HygieneData::with(|data| data.normalize_to_macros_2_0(self)) | |
8faf50e0 XL |
576 | } |
577 | ||
578 | #[inline] | |
ba9703b0 XL |
579 | pub fn normalize_to_macro_rules(self) -> SyntaxContext { |
580 | HygieneData::with(|data| data.normalize_to_macro_rules(self)) | |
7cac9316 XL |
581 | } |
582 | ||
ff7c6d11 | 583 | #[inline] |
416331ca XL |
584 | pub fn outer_expn(self) -> ExpnId { |
585 | HygieneData::with(|data| data.outer_expn(self)) | |
dc9dc135 XL |
586 | } |
587 | ||
e1599b0c XL |
588 | /// `ctxt.outer_expn_data()` is equivalent to but faster than |
589 | /// `ctxt.outer_expn().expn_data()`. | |
dc9dc135 | 590 | #[inline] |
e1599b0c XL |
591 | pub fn outer_expn_data(self) -> ExpnData { |
592 | HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone()) | |
dc9dc135 XL |
593 | } |
594 | ||
dc9dc135 | 595 | #[inline] |
e1599b0c | 596 | pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) { |
dc9dc135 | 597 | HygieneData::with(|data| { |
e1599b0c XL |
598 | let (expn_id, transparency) = data.outer_mark(self); |
599 | (expn_id, transparency, data.expn_data(expn_id).clone()) | |
dc9dc135 | 600 | }) |
cc61c64b | 601 | } |
0731742a XL |
602 | |
603 | pub fn dollar_crate_name(self) -> Symbol { | |
416331ca | 604 | HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) |
0731742a | 605 | } |
5bcae85e SL |
606 | } |
607 | ||
608 | impl fmt::Debug for SyntaxContext { | |
9fa01778 | 609 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
5bcae85e SL |
610 | write!(f, "#{}", self.0) |
611 | } | |
612 | } | |
cc61c64b | 613 | |
416331ca XL |
614 | impl Span { |
615 | /// Creates a fresh expansion with given properties. | |
616 | /// Expansions are normally created by macros, but in some cases expansions are created for | |
617 | /// other compiler-generated code to set per-span properties like allowed unstable features. | |
618 | /// The returned span belongs to the created expansion and has the new properties, | |
619 | /// but its location is inherited from the current span. | |
e1599b0c XL |
620 | pub fn fresh_expansion(self, expn_data: ExpnData) -> Span { |
621 | self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent) | |
622 | } | |
623 | ||
624 | pub fn fresh_expansion_with_transparency( | |
dfeec247 XL |
625 | self, |
626 | expn_data: ExpnData, | |
627 | transparency: Transparency, | |
e1599b0c | 628 | ) -> Span { |
416331ca | 629 | HygieneData::with(|data| { |
e1599b0c XL |
630 | let expn_id = data.fresh_expn(Some(expn_data)); |
631 | self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) | |
416331ca XL |
632 | }) |
633 | } | |
634 | } | |
635 | ||
636 | /// A subset of properties from both macro definition and macro call available through global data. | |
637 | /// Avoid using this if you have access to the original definition or call structures. | |
60c5eb7d | 638 | #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] |
e1599b0c | 639 | pub struct ExpnData { |
dc9dc135 | 640 | // --- The part unique to each expansion. |
e1599b0c XL |
641 | /// The kind of this expansion - macro or compiler desugaring. |
642 | pub kind: ExpnKind, | |
643 | /// The expansion that produced this expansion. | |
60c5eb7d | 644 | #[stable_hasher(ignore)] |
e1599b0c | 645 | pub parent: ExpnId, |
cc61c64b XL |
646 | /// The location of the actual macro invocation or syntax sugar , e.g. |
647 | /// `let x = foo!();` or `if let Some(y) = x {}` | |
648 | /// | |
0731742a | 649 | /// This may recursively refer to other macro invocations, e.g., if |
cc61c64b XL |
650 | /// `foo!()` invoked `bar!()` internally, and there was an |
651 | /// expression inside `bar!`; the call_site of the expression in | |
652 | /// the expansion would point to the `bar!` invocation; that | |
e1599b0c | 653 | /// call_site span would have its own ExpnData, with the call_site |
cc61c64b XL |
654 | /// pointing to the `foo!` invocation. |
655 | pub call_site: Span, | |
dc9dc135 XL |
656 | |
657 | // --- The part specific to the macro/desugaring definition. | |
e1599b0c XL |
658 | // --- It may be reasonable to share this part between expansions with the same definition, |
659 | // --- but such sharing is known to bring some minor inconveniences without also bringing | |
660 | // --- noticeable perf improvements (PR #62898). | |
416331ca | 661 | /// The span of the macro definition (possibly dummy). |
8faf50e0 | 662 | /// This span serves only informational purpose and is not used for resolution. |
416331ca | 663 | pub def_site: Span, |
9fa01778 XL |
664 | /// List of #[unstable]/feature-gated features that the macro is allowed to use |
665 | /// internally without forcing the whole crate to opt-in | |
cc61c64b | 666 | /// to them. |
9fa01778 | 667 | pub allow_internal_unstable: Option<Lrc<[Symbol]>>, |
3b2f2976 XL |
668 | /// Whether the macro is allowed to use `unsafe` internally |
669 | /// even if the user crate has `#![forbid(unsafe_code)]`. | |
670 | pub allow_internal_unsafe: bool, | |
8faf50e0 XL |
671 | /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) |
672 | /// for a given macro. | |
673 | pub local_inner_macros: bool, | |
94b46f34 XL |
674 | /// Edition of the crate in which the macro is defined. |
675 | pub edition: Edition, | |
cc61c64b XL |
676 | } |
677 | ||
e1599b0c XL |
678 | impl ExpnData { |
679 | /// Constructs expansion data with default properties. | |
680 | pub fn default(kind: ExpnKind, call_site: Span, edition: Edition) -> ExpnData { | |
681 | ExpnData { | |
416331ca | 682 | kind, |
e1599b0c XL |
683 | parent: ExpnId::root(), |
684 | call_site, | |
416331ca | 685 | def_site: DUMMY_SP, |
dc9dc135 XL |
686 | allow_internal_unstable: None, |
687 | allow_internal_unsafe: false, | |
688 | local_inner_macros: false, | |
689 | edition, | |
690 | } | |
691 | } | |
692 | ||
dfeec247 XL |
693 | pub fn allow_unstable( |
694 | kind: ExpnKind, | |
695 | call_site: Span, | |
696 | edition: Edition, | |
697 | allow_internal_unstable: Lrc<[Symbol]>, | |
698 | ) -> ExpnData { | |
e1599b0c | 699 | ExpnData { |
416331ca | 700 | allow_internal_unstable: Some(allow_internal_unstable), |
e1599b0c | 701 | ..ExpnData::default(kind, call_site, edition) |
dc9dc135 XL |
702 | } |
703 | } | |
e1599b0c XL |
704 | |
705 | #[inline] | |
706 | pub fn is_root(&self) -> bool { | |
707 | if let ExpnKind::Root = self.kind { true } else { false } | |
708 | } | |
dc9dc135 XL |
709 | } |
710 | ||
416331ca | 711 | /// Expansion kind. |
dfeec247 | 712 | #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable_Generic)] |
416331ca XL |
713 | pub enum ExpnKind { |
714 | /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind. | |
715 | Root, | |
716 | /// Expansion produced by a macro. | |
416331ca | 717 | Macro(MacroKind, Symbol), |
e1599b0c XL |
718 | /// Transform done by the compiler on the AST. |
719 | AstPass(AstPass), | |
cc61c64b | 720 | /// Desugaring done by the compiler during HIR lowering. |
dfeec247 | 721 | Desugaring(DesugaringKind), |
3b2f2976 XL |
722 | } |
723 | ||
416331ca | 724 | impl ExpnKind { |
dfeec247 | 725 | pub fn descr(&self) -> String { |
8faf50e0 | 726 | match *self { |
dfeec247 XL |
727 | ExpnKind::Root => kw::PathRoot.to_string(), |
728 | ExpnKind::Macro(macro_kind, name) => match macro_kind { | |
729 | MacroKind::Bang => format!("{}!", name), | |
730 | MacroKind::Attr => format!("#[{}]", name), | |
731 | MacroKind::Derive => format!("#[derive({})]", name), | |
732 | }, | |
733 | ExpnKind::AstPass(kind) => kind.descr().to_string(), | |
734 | ExpnKind::Desugaring(kind) => format!("desugaring of {}", kind.descr()), | |
416331ca XL |
735 | } |
736 | } | |
737 | } | |
738 | ||
739 | /// The kind of macro invocation or definition. | |
ba9703b0 XL |
740 | #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] |
741 | #[derive(HashStable_Generic)] | |
416331ca XL |
742 | pub enum MacroKind { |
743 | /// A bang macro `foo!()`. | |
744 | Bang, | |
745 | /// An attribute macro `#[foo]`. | |
746 | Attr, | |
747 | /// A derive macro `#[derive(Foo)]` | |
748 | Derive, | |
749 | } | |
750 | ||
751 | impl MacroKind { | |
752 | pub fn descr(self) -> &'static str { | |
753 | match self { | |
754 | MacroKind::Bang => "macro", | |
755 | MacroKind::Attr => "attribute macro", | |
756 | MacroKind::Derive => "derive macro", | |
757 | } | |
758 | } | |
759 | ||
e1599b0c XL |
760 | pub fn descr_expected(self) -> &'static str { |
761 | match self { | |
762 | MacroKind::Attr => "attribute", | |
763 | _ => self.descr(), | |
764 | } | |
765 | } | |
766 | ||
416331ca XL |
767 | pub fn article(self) -> &'static str { |
768 | match self { | |
769 | MacroKind::Attr => "an", | |
770 | _ => "a", | |
8faf50e0 XL |
771 | } |
772 | } | |
773 | } | |
774 | ||
e1599b0c | 775 | /// The kind of AST transform. |
60c5eb7d | 776 | #[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] |
e1599b0c XL |
777 | pub enum AstPass { |
778 | StdImports, | |
779 | TestHarness, | |
780 | ProcMacroHarness, | |
e1599b0c XL |
781 | } |
782 | ||
783 | impl AstPass { | |
784 | fn descr(self) -> &'static str { | |
785 | match self { | |
786 | AstPass::StdImports => "standard library imports", | |
787 | AstPass::TestHarness => "test harness", | |
788 | AstPass::ProcMacroHarness => "proc macro harness", | |
e1599b0c XL |
789 | } |
790 | } | |
791 | } | |
792 | ||
3b2f2976 | 793 | /// The kind of compiler desugaring. |
60c5eb7d | 794 | #[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)] |
416331ca | 795 | pub enum DesugaringKind { |
48663c56 XL |
796 | /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`. |
797 | /// However, we do not want to blame `c` for unreachability but rather say that `i` | |
798 | /// is unreachable. This desugaring kind allows us to avoid blaming `c`. | |
416331ca XL |
799 | /// This also applies to `while` loops. |
800 | CondTemporary, | |
3b2f2976 | 801 | QuestionMark, |
b7449926 | 802 | TryBlock, |
94b46f34 | 803 | /// Desugaring of an `impl Trait` in return type position |
416331ca | 804 | /// to an `type Foo = impl Trait;` and replacing the |
94b46f34 | 805 | /// `impl Trait` with `Foo`. |
416331ca | 806 | OpaqueTy, |
8faf50e0 | 807 | Async, |
48663c56 | 808 | Await, |
8faf50e0 | 809 | ForLoop, |
3b2f2976 XL |
810 | } |
811 | ||
416331ca XL |
812 | impl DesugaringKind { |
813 | /// The description wording should combine well with "desugaring of {}". | |
814 | fn descr(self) -> &'static str { | |
815 | match self { | |
816 | DesugaringKind::CondTemporary => "`if` or `while` condition", | |
817 | DesugaringKind::Async => "`async` block or function", | |
818 | DesugaringKind::Await => "`await` expression", | |
819 | DesugaringKind::QuestionMark => "operator `?`", | |
820 | DesugaringKind::TryBlock => "`try` block", | |
821 | DesugaringKind::OpaqueTy => "`impl Trait`", | |
822 | DesugaringKind::ForLoop => "`for` loop", | |
823 | } | |
3b2f2976 | 824 | } |
cc61c64b XL |
825 | } |
826 | ||
e1599b0c | 827 | impl Encodable for ExpnId { |
cc61c64b XL |
828 | fn encode<E: Encoder>(&self, _: &mut E) -> Result<(), E::Error> { |
829 | Ok(()) // FIXME(jseyfried) intercrate hygiene | |
830 | } | |
831 | } | |
832 | ||
e1599b0c XL |
833 | impl Decodable for ExpnId { |
834 | fn decode<D: Decoder>(_: &mut D) -> Result<Self, D::Error> { | |
835 | Ok(ExpnId::root()) // FIXME(jseyfried) intercrate hygiene | |
cc61c64b XL |
836 | } |
837 | } |