2
2
//!
3
3
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
4
4
//! this moment, this is horribly incomplete and handles only `$crate`.
5
+ use std:: sync:: Arc ;
6
+
5
7
use base_db:: CrateId ;
6
8
use either:: Either ;
7
- use syntax:: ast;
9
+ use mbe:: Origin ;
10
+ use syntax:: { ast, AstNode } ;
8
11
9
12
use crate :: {
10
13
db:: AstDatabase ,
11
14
name:: { AsName , Name } ,
12
- HirFileId , HirFileIdRepr , MacroCallId , MacroDefKind ,
15
+ ExpansionInfo , HirFileId , HirFileIdRepr , MacroCallId , MacroDefKind ,
13
16
} ;
14
17
15
18
#[ derive( Clone , Debug ) ]
16
19
pub struct Hygiene {
17
- // This is what `$crate` expands to
18
- def_crate : Option < CrateId > ,
20
+ frames : Option < Arc < HygieneFrames > > ,
21
+ }
22
+
23
+ impl 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 ) ]
66
+ struct HygieneFrameId ( usize ) ;
67
+
68
+ #[ derive( Clone , Debug , Default ) ]
69
+ struct HygieneFrames ( Vec < HygieneFrame > ) ;
70
+
71
+ #[ derive( Clone , Debug ) ]
72
+ struct HygieneFrame {
73
+ expansion : Option < ExpansionInfo > ,
19
74
20
75
// Indicate this is a local inner macro
21
76
local_inner : bool ,
77
+ krate : Option < CrateId > ,
78
+
79
+ call_site : Option < HygieneFrameId > ,
80
+ def_site : Option < HygieneFrameId > ,
22
81
}
23
82
24
- impl Hygiene {
25
- pub fn new ( db : & dyn AstDatabase , file_id : HirFileId ) -> Hygiene {
26
- let ( def_crate, local_inner) = match file_id. 0 {
83
+ impl HygieneFrames {
84
+ fn new ( db : & dyn AstDatabase , file_id : HirFileId ) -> Self {
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
92
HirFileIdRepr :: FileId ( _) => ( None , false ) ,
28
93
HirFileIdRepr :: MacroFile ( macro_file) => match macro_file. macro_call_id {
94
+ MacroCallId :: EagerMacro ( _id) => ( None , false ) ,
29
95
MacroCallId :: LazyMacro ( id) => {
30
96
let loc = db. lookup_intern_macro ( id) ;
31
97
match loc. def . kind {
@@ -36,31 +102,72 @@ impl Hygiene {
36
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 {
46
- Hygiene { def_crate : None , local_inner : false }
108
+ let expansion = file_id. expansion_info ( db) ;
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
50
- pub fn name_ref_to_name ( & self , name_ref : ast:: NameRef ) -> Either < Name , CrateId > {
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 ( ) )
142
+ fn get ( & self , id : HygieneFrameId ) -> Option < & HygieneFrame > {
143
+ self . 0 . get ( id. 0 )
57
144
}
58
145
59
- pub fn local_inner_macros ( & self ) -> Option < CrateId > {
60
- if self . local_inner {
61
- self . def_crate
62
- } else {
63
- None
146
+ fn root_crate ( & self , name_ref : & ast:: NameRef ) -> Option < CrateId > {
147
+ let mut token = name_ref. syntax ( ) . first_token ( ) ?;
148
+ let first = self . 0 . first ( ) ?;
149
+ let mut result = first. krate ;
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
}
0 commit comments