|
1 |
| -//! This modules handles hygiene information. |
| 1 | +//! Machinery for hygienic macros. |
2 | 2 | //!
|
3 |
| -//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at |
4 |
| -//! this moment, this is horribly incomplete and handles only `$crate`. |
5 |
| -
|
6 |
| -// FIXME: Consider moving this into the span crate. |
| 3 | +//! Inspired by Matthew Flatt et al., “Macros That Work Together: Compile-Time Bindings, Partial |
| 4 | +//! Expansion, and Definition Contexts,” *Journal of Functional Programming* 22, no. 2 |
| 5 | +//! (March 1, 2012): 181–216, <https://doi.org/10.1017/S0956796812000093>. |
| 6 | +//! |
| 7 | +//! Also see https://rustc-dev-guide.rust-lang.org/macro-expansion.html#hygiene-and-hierarchies |
| 8 | +//! |
| 9 | +//! # The Expansion Order Hierarchy |
| 10 | +//! |
| 11 | +//! `ExpnData` in rustc, rust-analyzer's version is [`MacroCallLoc`]. Traversing the hierarchy |
| 12 | +//! upwards can be achieved by walking up [`MacroCallLoc::kind`]'s contained file id, as |
| 13 | +//! [`MacroFile`]s are interned [`MacroCallLoc`]s. |
| 14 | +//! |
| 15 | +//! # The Macro Definition Hierarchy |
| 16 | +//! |
| 17 | +//! `SyntaxContextData` in rustc and rust-analyzer. Basically the same in both. |
| 18 | +//! |
| 19 | +//! # The Call-site Hierarchy |
| 20 | +//! |
| 21 | +//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. |
| 22 | +// FIXME: Move this into the span crate? Not quite possible today as that depends on `MacroCallLoc` |
| 23 | +// which contains a bunch of unrelated things |
7 | 24 |
|
8 | 25 | use std::iter;
|
9 | 26 |
|
10 |
| -use base_db::salsa::{self, InternValue}; |
11 |
| -use span::{MacroCallId, Span, SyntaxContextId}; |
| 27 | +use span::{MacroCallId, Span, SyntaxContextData, SyntaxContextId}; |
12 | 28 |
|
13 | 29 | use crate::db::{ExpandDatabase, InternSyntaxContextQuery};
|
14 | 30 |
|
15 |
| -#[derive(Copy, Clone, Hash, PartialEq, Eq)] |
16 |
| -pub struct SyntaxContextData { |
17 |
| - pub outer_expn: Option<MacroCallId>, |
18 |
| - pub outer_transparency: Transparency, |
19 |
| - pub parent: SyntaxContextId, |
20 |
| - /// This context, but with all transparent and semi-transparent expansions filtered away. |
21 |
| - pub opaque: SyntaxContextId, |
22 |
| - /// This context, but with all transparent expansions filtered away. |
23 |
| - pub opaque_and_semitransparent: SyntaxContextId, |
24 |
| -} |
25 |
| - |
26 |
| -impl InternValue for SyntaxContextData { |
27 |
| - type Key = (SyntaxContextId, Option<MacroCallId>, Transparency); |
28 |
| - |
29 |
| - fn into_key(&self) -> Self::Key { |
30 |
| - (self.parent, self.outer_expn, self.outer_transparency) |
31 |
| - } |
32 |
| -} |
33 |
| - |
34 |
| -impl std::fmt::Debug for SyntaxContextData { |
35 |
| - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
36 |
| - f.debug_struct("SyntaxContextData") |
37 |
| - .field("outer_expn", &self.outer_expn) |
38 |
| - .field("outer_transparency", &self.outer_transparency) |
39 |
| - .field("parent", &self.parent) |
40 |
| - .field("opaque", &self.opaque) |
41 |
| - .field("opaque_and_semitransparent", &self.opaque_and_semitransparent) |
42 |
| - .finish() |
43 |
| - } |
44 |
| -} |
45 |
| - |
46 |
| -impl SyntaxContextData { |
47 |
| - pub fn root() -> Self { |
48 |
| - SyntaxContextData { |
49 |
| - outer_expn: None, |
50 |
| - outer_transparency: Transparency::Opaque, |
51 |
| - parent: SyntaxContextId::ROOT, |
52 |
| - opaque: SyntaxContextId::ROOT, |
53 |
| - opaque_and_semitransparent: SyntaxContextId::ROOT, |
54 |
| - } |
55 |
| - } |
56 |
| - |
57 |
| - pub fn fancy_debug( |
58 |
| - self, |
59 |
| - self_id: SyntaxContextId, |
60 |
| - db: &dyn ExpandDatabase, |
61 |
| - f: &mut std::fmt::Formatter<'_>, |
62 |
| - ) -> std::fmt::Result { |
63 |
| - write!(f, "#{self_id} parent: #{}, outer_mark: (", self.parent)?; |
64 |
| - match self.outer_expn { |
65 |
| - Some(id) => { |
66 |
| - write!(f, "{:?}::{{{{expn{:?}}}}}", db.lookup_intern_macro_call(id).krate, id)? |
67 |
| - } |
68 |
| - None => write!(f, "root")?, |
69 |
| - } |
70 |
| - write!(f, ", {:?})", self.outer_transparency) |
71 |
| - } |
72 |
| -} |
73 |
| - |
74 |
| -/// A property of a macro expansion that determines how identifiers |
75 |
| -/// produced by that expansion are resolved. |
76 |
| -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug)] |
77 |
| -pub enum Transparency { |
78 |
| - /// Identifier produced by a transparent expansion is always resolved at call-site. |
79 |
| - /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. |
80 |
| - Transparent, |
81 |
| - /// Identifier produced by a semi-transparent expansion may be resolved |
82 |
| - /// either at call-site or at definition-site. |
83 |
| - /// If it's a local variable, label or `$crate` then it's resolved at def-site. |
84 |
| - /// Otherwise it's resolved at call-site. |
85 |
| - /// `macro_rules` macros behave like this, built-in macros currently behave like this too, |
86 |
| - /// but that's an implementation detail. |
87 |
| - SemiTransparent, |
88 |
| - /// Identifier produced by an opaque expansion is always resolved at definition-site. |
89 |
| - /// Def-site spans in procedural macros, identifiers from `macro` by default use this. |
90 |
| - Opaque, |
91 |
| -} |
| 31 | +pub use span::Transparency; |
92 | 32 |
|
93 | 33 | pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
|
94 | 34 | span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque)
|
@@ -157,6 +97,8 @@ fn apply_mark_internal(
|
157 | 97 | call_id: Option<MacroCallId>,
|
158 | 98 | transparency: Transparency,
|
159 | 99 | ) -> SyntaxContextId {
|
| 100 | + use base_db::salsa; |
| 101 | + |
160 | 102 | let syntax_context_data = db.lookup_intern_syntax_context(ctxt);
|
161 | 103 | let mut opaque = syntax_context_data.opaque;
|
162 | 104 | let mut opaque_and_semitransparent = syntax_context_data.opaque_and_semitransparent;
|
@@ -199,6 +141,7 @@ fn apply_mark_internal(
|
199 | 141 | opaque_and_semitransparent,
|
200 | 142 | })
|
201 | 143 | }
|
| 144 | + |
202 | 145 | pub trait SyntaxContextExt {
|
203 | 146 | fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self;
|
204 | 147 | fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self;
|
@@ -277,9 +220,26 @@ pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
|
277 | 220 |
|
278 | 221 | impl<'a> std::fmt::Debug for SyntaxContextDebug<'a> {
|
279 | 222 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
280 |
| - self.2.fancy_debug(self.1, self.0, f) |
| 223 | + fancy_debug(self.2, self.1, self.0, f) |
281 | 224 | }
|
282 | 225 | }
|
| 226 | + |
| 227 | + pub fn fancy_debug( |
| 228 | + this: &SyntaxContextData, |
| 229 | + self_id: SyntaxContextId, |
| 230 | + db: &dyn ExpandDatabase, |
| 231 | + f: &mut std::fmt::Formatter<'_>, |
| 232 | + ) -> std::fmt::Result { |
| 233 | + write!(f, "#{self_id} parent: #{}, outer_mark: (", this.parent)?; |
| 234 | + match this.outer_expn { |
| 235 | + Some(id) => { |
| 236 | + write!(f, "{:?}::{{{{expn{:?}}}}}", db.lookup_intern_macro_call(id).krate, id)? |
| 237 | + } |
| 238 | + None => write!(f, "root")?, |
| 239 | + } |
| 240 | + write!(f, ", {:?})", this.outer_transparency) |
| 241 | + } |
| 242 | + |
283 | 243 | stdx::format_to!(s, "{:?}\n", SyntaxContextDebug(db, e.key, &e.value.unwrap()));
|
284 | 244 | }
|
285 | 245 | s
|
|
0 commit comments