aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand/src
diff options
context:
space:
mode:
authorEdwin Cheng <[email protected]>2021-01-02 12:25:05 +0000
committerEdwin Cheng <[email protected]>2021-01-02 12:39:57 +0000
commitfe5340d970b764175ebf8b8c32c971fa04c49efb (patch)
treea6409d67ffee8f5c013ca587db39c371cd0c8ffb /crates/hir_expand/src
parent51d29fe55456e6e7af69d23982aa57c7fcf91e81 (diff)
Introduce HygieneFrames for proper token hyginee
Diffstat (limited to 'crates/hir_expand/src')
-rw-r--r--crates/hir_expand/src/hygiene.rs157
-rw-r--r--crates/hir_expand/src/lib.rs11
2 files changed, 136 insertions, 32 deletions
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 7ab0a5e52..d49a67195 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -2,30 +2,96 @@
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`.
5use std::sync::Arc;
6
5use base_db::CrateId; 7use base_db::CrateId;
6use either::Either; 8use either::Either;
7use syntax::ast; 9use mbe::Origin;
10use syntax::{ast, AstNode};
8 11
9use crate::{ 12use crate::{
10 db::AstDatabase, 13 db::AstDatabase,
11 name::{AsName, Name}, 14 name::{AsName, Name},
12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, 15 ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
13}; 16};
14 17
15#[derive(Clone, Debug)] 18#[derive(Clone, Debug)]
16pub struct Hygiene { 19pub struct Hygiene {
17 // This is what `$crate` expands to 20 frames: Option<Arc<HygieneFrames>>,
18 def_crate: Option<CrateId>, 21}
22
23impl Hygiene {
24 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
25 Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) }
26 }
27
28 pub fn new_unhygienic() -> Hygiene {
29 Hygiene { frames: None }
30 }
31
32 // FIXME: this should just return name
33 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
34 if let Some(frames) = &self.frames {
35 if name_ref.text() == "$crate" {
36 if let Some(krate) = frames.root_crate(&name_ref) {
37 return Either::Right(krate);
38 }
39 }
40 }
41
42 Either::Left(name_ref.as_name())
43 }
44
45 pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
46 let frames = self.frames.as_ref()?;
47
48 let mut token = path.syntax().first_token()?;
49 let mut current = frames.0.first();
50
51 while let Some((frame, data)) =
52 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
53 {
54 let (mapped, origin) = data;
55 if origin == Origin::Def {
56 return if frame.local_inner { frame.krate } else { None };
57 }
58 current = frames.get(frame.call_site?);
59 token = mapped.value;
60 }
61 None
62 }
63}
64
65#[derive(Clone, Debug, Copy)]
66struct HygieneFrameId(usize);
67
68#[derive(Clone, Debug, Default)]
69struct HygieneFrames(Vec<HygieneFrame>);
70
71#[derive(Clone, Debug)]
72struct HygieneFrame {
73 expansion: Option<ExpansionInfo>,
19 74
20 // Indicate this is a local inner macro 75 // Indicate this is a local inner macro
21 local_inner: bool, 76 local_inner: bool,
77 krate: Option<CrateId>,
78
79 call_site: Option<HygieneFrameId>,
80 def_site: Option<HygieneFrameId>,
22} 81}
23 82
24impl Hygiene { 83impl HygieneFrames {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { 84 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self {
26 let (def_crate, local_inner) = match file_id.0 { 85 let mut frames = HygieneFrames::default();
86 frames.add(db, file_id);
87 frames
88 }
89
90 fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<HygieneFrameId> {
91 let (krate, local_inner) = match file_id.0 {
27 HirFileIdRepr::FileId(_) => (None, false), 92 HirFileIdRepr::FileId(_) => (None, false),
28 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { 93 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
94 MacroCallId::EagerMacro(_id) => (None, false),
29 MacroCallId::LazyMacro(id) => { 95 MacroCallId::LazyMacro(id) => {
30 let loc = db.lookup_intern_macro(id); 96 let loc = db.lookup_intern_macro(id);
31 match loc.def.kind { 97 match loc.def.kind {
@@ -36,31 +102,72 @@ impl Hygiene {
36 MacroDefKind::ProcMacro(_) => (None, false), 102 MacroDefKind::ProcMacro(_) => (None, false),
37 } 103 }
38 } 104 }
39 MacroCallId::EagerMacro(_id) => (None, false),
40 }, 105 },
41 }; 106 };
42 Hygiene { def_crate, local_inner }
43 }
44 107
45 pub fn new_unhygienic() -> Hygiene { 108 let expansion = file_id.expansion_info(db);
46 Hygiene { def_crate: None, local_inner: false } 109 let expansion = match expansion {
110 None => {
111 let idx = self.0.len();
112 self.0.push(HygieneFrame {
113 expansion: None,
114 local_inner,
115 krate,
116 call_site: None,
117 def_site: None,
118 });
119 return Some(HygieneFrameId(idx));
120 }
121 Some(it) => it,
122 };
123
124 let def_site = expansion.def.clone();
125 let call_site = expansion.arg.file_id;
126
127 let idx = self.0.len();
128 self.0.push(HygieneFrame {
129 expansion: Some(expansion),
130 local_inner,
131 krate,
132 call_site: None,
133 def_site: None,
134 });
135
136 self.0[idx].call_site = self.add(db, call_site);
137 self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id));
138
139 Some(HygieneFrameId(idx))
47 } 140 }
48 141
49 // FIXME: this should just return name 142 fn get(&self, id: HygieneFrameId) -> Option<&HygieneFrame> {
50 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { 143 self.0.get(id.0)
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 } 144 }
58 145
59 pub fn local_inner_macros(&self) -> Option<CrateId> { 146 fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> {
60 if self.local_inner { 147 let mut token = name_ref.syntax().first_token()?;
61 self.def_crate 148 let first = self.0.first()?;
62 } else { 149 let mut result = first.krate;
63 None 150 let mut current = Some(first);
151
152 while let Some((frame, (mapped, origin))) =
153 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
154 {
155 result = frame.krate;
156
157 let site = match origin {
158 Origin::Def => frame.def_site,
159 Origin::Call => frame.call_site,
160 };
161
162 let site = match site {
163 None => break,
164 Some(it) => it,
165 };
166
167 current = self.get(site);
168 token = mapped.value;
64 } 169 }
170
171 result
65 } 172 }
66} 173}
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 }