diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-01-02 17:42:45 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-01-02 17:42:45 +0000 |
commit | a88d4f8c7216105e8bffed55fe85d9de796dc172 (patch) | |
tree | da8fe6fd5f771181ee1753907af22d9c0ce60586 /crates/hir_expand/src | |
parent | 510abef5da1427c542e7b37dcea805c7b022984c (diff) | |
parent | 3545289603dae852fb2b99181e9be5e3117b0a2e (diff) |
Merge #7133
7133: Proper handling $crate and local_inner_macros r=jonas-schievink a=edwin0cheng
This PR introduces `HygineFrames` to store the macro definition/call site hierarchy in hyginee and when resolving `local_inner_macros` and `$crate`, we use the token to look up the corresponding frame and return the correct value.
See also: https://rustc-dev-guide.rust-lang.org/macro-expansion.html#hygiene-and-hierarchies
fixe #6890 and #6788
r? @jonas-schievink
Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r-- | crates/hir_expand/src/hygiene.rs | 151 | ||||
-rw-r--r-- | crates/hir_expand/src/lib.rs | 11 |
2 files changed, 130 insertions, 32 deletions
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs index 7ab0a5e52..6042e15b2 100644 --- a/crates/hir_expand/src/hygiene.rs +++ b/crates/hir_expand/src/hygiene.rs | |||
@@ -2,30 +2,94 @@ | |||
2 | //! | 2 | //! |
3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at | 3 | //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at |
4 | //! this moment, this is horribly incomplete and handles only `$crate`. | 4 | //! this moment, this is horribly incomplete and handles only `$crate`. |
5 | use std::sync::Arc; | ||
6 | |||
7 | use arena::{Arena, Idx}; | ||
5 | use base_db::CrateId; | 8 | use base_db::CrateId; |
6 | use either::Either; | 9 | use either::Either; |
7 | use syntax::ast; | 10 | use mbe::Origin; |
11 | use syntax::{ast, AstNode}; | ||
8 | 12 | ||
9 | use crate::{ | 13 | use crate::{ |
10 | db::AstDatabase, | 14 | db::AstDatabase, |
11 | name::{AsName, Name}, | 15 | name::{AsName, Name}, |
12 | HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, | 16 | ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, |
13 | }; | 17 | }; |
14 | 18 | ||
15 | #[derive(Clone, Debug)] | 19 | #[derive(Clone, Debug)] |
16 | pub struct Hygiene { | 20 | pub struct Hygiene { |
17 | // This is what `$crate` expands to | 21 | frames: Option<Arc<HygieneFrames>>, |
18 | def_crate: Option<CrateId>, | 22 | } |
23 | |||
24 | impl Hygiene { | ||
25 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { | ||
26 | Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) } | ||
27 | } | ||
28 | |||
29 | pub fn new_unhygienic() -> Hygiene { | ||
30 | Hygiene { frames: None } | ||
31 | } | ||
32 | |||
33 | // FIXME: this should just return name | ||
34 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | ||
35 | if let Some(frames) = &self.frames { | ||
36 | if name_ref.text() == "$crate" { | ||
37 | if let Some(krate) = frames.root_crate(&name_ref) { | ||
38 | return Either::Right(krate); | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | Either::Left(name_ref.as_name()) | ||
44 | } | ||
45 | |||
46 | pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> { | ||
47 | let frames = self.frames.as_ref()?; | ||
48 | |||
49 | let mut token = path.syntax().first_token()?; | ||
50 | let mut current = frames.first(); | ||
51 | |||
52 | while let Some((frame, data)) = | ||
53 | current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?))) | ||
54 | { | ||
55 | let (mapped, origin) = data; | ||
56 | if origin == Origin::Def { | ||
57 | return if frame.local_inner { frame.krate } else { None }; | ||
58 | } | ||
59 | current = Some(&frames.0[frame.call_site?]); | ||
60 | token = mapped.value; | ||
61 | } | ||
62 | None | ||
63 | } | ||
64 | } | ||
65 | |||
66 | #[derive(Default, Debug)] | ||
67 | struct HygieneFrames(Arena<HygieneFrame>); | ||
68 | |||
69 | #[derive(Clone, Debug)] | ||
70 | struct HygieneFrame { | ||
71 | expansion: Option<ExpansionInfo>, | ||
19 | 72 | ||
20 | // Indicate this is a local inner macro | 73 | // Indicate this is a local inner macro |
21 | local_inner: bool, | 74 | local_inner: bool, |
75 | krate: Option<CrateId>, | ||
76 | |||
77 | call_site: Option<Idx<HygieneFrame>>, | ||
78 | def_site: Option<Idx<HygieneFrame>>, | ||
22 | } | 79 | } |
23 | 80 | ||
24 | impl Hygiene { | 81 | impl HygieneFrames { |
25 | pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { | 82 | fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { |
26 | let (def_crate, local_inner) = match file_id.0 { | 83 | let mut frames = HygieneFrames::default(); |
84 | frames.add(db, file_id); | ||
85 | frames | ||
86 | } | ||
87 | |||
88 | fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<Idx<HygieneFrame>> { | ||
89 | let (krate, local_inner) = match file_id.0 { | ||
27 | HirFileIdRepr::FileId(_) => (None, false), | 90 | HirFileIdRepr::FileId(_) => (None, false), |
28 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { | 91 | HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { |
92 | MacroCallId::EagerMacro(_id) => (None, false), | ||
29 | MacroCallId::LazyMacro(id) => { | 93 | MacroCallId::LazyMacro(id) => { |
30 | let loc = db.lookup_intern_macro(id); | 94 | let loc = db.lookup_intern_macro(id); |
31 | match loc.def.kind { | 95 | match loc.def.kind { |
@@ -36,31 +100,68 @@ impl Hygiene { | |||
36 | MacroDefKind::ProcMacro(_) => (None, false), | 100 | MacroDefKind::ProcMacro(_) => (None, false), |
37 | } | 101 | } |
38 | } | 102 | } |
39 | MacroCallId::EagerMacro(_id) => (None, false), | ||
40 | }, | 103 | }, |
41 | }; | 104 | }; |
42 | Hygiene { def_crate, local_inner } | ||
43 | } | ||
44 | 105 | ||
45 | pub fn new_unhygienic() -> Hygiene { | 106 | let expansion = file_id.expansion_info(db); |
46 | Hygiene { def_crate: None, local_inner: false } | 107 | let expansion = match expansion { |
108 | None => { | ||
109 | return Some(self.0.alloc(HygieneFrame { | ||
110 | expansion: None, | ||
111 | local_inner, | ||
112 | krate, | ||
113 | call_site: None, | ||
114 | def_site: None, | ||
115 | })); | ||
116 | } | ||
117 | Some(it) => it, | ||
118 | }; | ||
119 | |||
120 | let def_site = expansion.def.clone(); | ||
121 | let call_site = expansion.arg.file_id; | ||
122 | let idx = self.0.alloc(HygieneFrame { | ||
123 | expansion: Some(expansion), | ||
124 | local_inner, | ||
125 | krate, | ||
126 | call_site: None, | ||
127 | def_site: None, | ||
128 | }); | ||
129 | |||
130 | self.0[idx].call_site = self.add(db, call_site); | ||
131 | self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id)); | ||
132 | |||
133 | Some(idx) | ||
47 | } | 134 | } |
48 | 135 | ||
49 | // FIXME: this should just return name | 136 | fn first(&self) -> Option<&HygieneFrame> { |
50 | pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { | 137 | self.0.iter().next().map(|it| it.1) |
51 | if let Some(def_crate) = self.def_crate { | ||
52 | if name_ref.text() == "$crate" { | ||
53 | return Either::Right(def_crate); | ||
54 | } | ||
55 | } | ||
56 | Either::Left(name_ref.as_name()) | ||
57 | } | 138 | } |
58 | 139 | ||
59 | pub fn local_inner_macros(&self) -> Option<CrateId> { | 140 | fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> { |
60 | if self.local_inner { | 141 | let mut token = name_ref.syntax().first_token()?; |
61 | self.def_crate | 142 | let first = self.first()?; |
62 | } else { | 143 | let mut result = first.krate; |
63 | None | 144 | let mut current = Some(first); |
145 | |||
146 | while let Some((frame, (mapped, origin))) = | ||
147 | current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?))) | ||
148 | { | ||
149 | result = frame.krate; | ||
150 | |||
151 | let site = match origin { | ||
152 | Origin::Def => frame.def_site, | ||
153 | Origin::Call => frame.call_site, | ||
154 | }; | ||
155 | |||
156 | let site = match site { | ||
157 | None => break, | ||
158 | Some(it) => it, | ||
159 | }; | ||
160 | |||
161 | current = Some(&self.0[site]); | ||
162 | token = mapped.value; | ||
64 | } | 163 | } |
164 | |||
165 | result | ||
65 | } | 166 | } |
66 | } | 167 | } |
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 3fa1b1d77..5b6734a5f 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs | |||
@@ -340,11 +340,8 @@ impl ExpansionInfo { | |||
340 | Some(self.expanded.with_value(token)) | 340 | Some(self.expanded.with_value(token)) |
341 | } | 341 | } |
342 | 342 | ||
343 | pub fn map_token_up( | 343 | pub fn map_token_up(&self, token: &SyntaxToken) -> Option<(InFile<SyntaxToken>, Origin)> { |
344 | &self, | 344 | let token_id = self.exp_map.token_by_range(token.text_range())?; |
345 | token: InFile<&SyntaxToken>, | ||
346 | ) -> Option<(InFile<SyntaxToken>, Origin)> { | ||
347 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; | ||
348 | 345 | ||
349 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 346 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); |
350 | let (token_map, tt) = match origin { | 347 | let (token_map, tt) = match origin { |
@@ -359,7 +356,7 @@ impl ExpansionInfo { | |||
359 | ), | 356 | ), |
360 | }; | 357 | }; |
361 | 358 | ||
362 | let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; | 359 | let range = token_map.range_by_token(token_id)?.by_kind(token.kind())?; |
363 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) | 360 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) |
364 | .into_token()?; | 361 | .into_token()?; |
365 | Some((tt.with_value(token), origin)) | 362 | Some((tt.with_value(token), origin)) |
@@ -495,7 +492,7 @@ fn ascend_call_token( | |||
495 | expansion: &ExpansionInfo, | 492 | expansion: &ExpansionInfo, |
496 | token: InFile<SyntaxToken>, | 493 | token: InFile<SyntaxToken>, |
497 | ) -> Option<InFile<SyntaxToken>> { | 494 | ) -> Option<InFile<SyntaxToken>> { |
498 | let (mapped, origin) = expansion.map_token_up(token.as_ref())?; | 495 | let (mapped, origin) = expansion.map_token_up(&token.value)?; |
499 | if origin != Origin::Call { | 496 | if origin != Origin::Call { |
500 | return None; | 497 | return None; |
501 | } | 498 | } |