aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand/src/hygiene.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_expand/src/hygiene.rs')
-rw-r--r--crates/hir_expand/src/hygiene.rs151
1 files changed, 25 insertions, 126 deletions
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 6042e15b2..7ab0a5e52 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -2,94 +2,30 @@
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
7use arena::{Arena, Idx};
8use base_db::CrateId; 5use base_db::CrateId;
9use either::Either; 6use either::Either;
10use mbe::Origin; 7use syntax::ast;
11use syntax::{ast, AstNode};
12 8
13use crate::{ 9use crate::{
14 db::AstDatabase, 10 db::AstDatabase,
15 name::{AsName, Name}, 11 name::{AsName, Name},
16 ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, 12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
17}; 13};
18 14
19#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
20pub struct Hygiene { 16pub struct Hygiene {
21 frames: Option<Arc<HygieneFrames>>, 17 // This is what `$crate` expands to
22} 18 def_crate: Option<CrateId>,
23
24impl 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)]
67struct HygieneFrames(Arena<HygieneFrame>);
68
69#[derive(Clone, Debug)]
70struct HygieneFrame {
71 expansion: Option<ExpansionInfo>,
72 19
73 // Indicate this is a local inner macro 20 // Indicate this is a local inner macro
74 local_inner: bool, 21 local_inner: bool,
75 krate: Option<CrateId>,
76
77 call_site: Option<Idx<HygieneFrame>>,
78 def_site: Option<Idx<HygieneFrame>>,
79} 22}
80 23
81impl HygieneFrames { 24impl Hygiene {
82 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self { 25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
83 let mut frames = HygieneFrames::default(); 26 let (def_crate, local_inner) = match file_id.0 {
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 {
90 HirFileIdRepr::FileId(_) => (None, false), 27 HirFileIdRepr::FileId(_) => (None, false),
91 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { 28 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
92 MacroCallId::EagerMacro(_id) => (None, false),
93 MacroCallId::LazyMacro(id) => { 29 MacroCallId::LazyMacro(id) => {
94 let loc = db.lookup_intern_macro(id); 30 let loc = db.lookup_intern_macro(id);
95 match loc.def.kind { 31 match loc.def.kind {
@@ -100,68 +36,31 @@ impl HygieneFrames {
100 MacroDefKind::ProcMacro(_) => (None, false), 36 MacroDefKind::ProcMacro(_) => (None, false),
101 } 37 }
102 } 38 }
39 MacroCallId::EagerMacro(_id) => (None, false),
103 }, 40 },
104 }; 41 };
105 42 Hygiene { def_crate, local_inner }
106 let expansion = file_id.expansion_info(db);
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)
134 } 43 }
135 44
136 fn first(&self) -> Option<&HygieneFrame> { 45 pub fn new_unhygienic() -> Hygiene {
137 self.0.iter().next().map(|it| it.1) 46 Hygiene { def_crate: None, local_inner: false }
138 } 47 }
139 48
140 fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> { 49 // FIXME: this should just return name
141 let mut token = name_ref.syntax().first_token()?; 50 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
142 let first = self.first()?; 51 if let Some(def_crate) = self.def_crate {
143 let mut result = first.krate; 52 if name_ref.text() == "$crate" {
144 let mut current = Some(first); 53 return Either::Right(def_crate);
145 54 }
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;
163 } 55 }
56 Either::Left(name_ref.as_name())
57 }
164 58
165 result 59 pub fn local_inner_macros(&self) -> Option<CrateId> {
60 if self.local_inner {
61 self.def_crate
62 } else {
63 None
64 }
166 } 65 }
167} 66}