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.rs157
1 files changed, 132 insertions, 25 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}