1
1
//! Expansion of associated items
2
2
3
- use hir_expand:: {
4
- AstId , ExpandResult , InFile , Intern , Lookup , MacroCallKind , MacroDefKind , name:: Name ,
5
- } ;
6
- use smallvec:: SmallVec ;
7
- use span:: { HirFileId , MacroCallId } ;
8
- use syntax:: { Parse , ast} ;
3
+ use hir_expand:: { AstId , InFile , Intern , Lookup , MacroCallKind , MacroDefKind , name:: Name } ;
4
+ use span:: MacroCallId ;
5
+ use syntax:: ast;
9
6
use triomphe:: Arc ;
10
7
11
8
use crate :: {
12
9
AssocItemId , AstIdWithPath , ConstLoc , FunctionId , FunctionLoc , ImplId , ItemContainerId ,
13
10
ItemLoc , ModuleId , TraitId , TypeAliasId , TypeAliasLoc ,
14
11
db:: DefDatabase ,
15
- expander:: { Expander , Mark } ,
16
- item_tree:: { self , AssocItem , ItemTree , ItemTreeId , MacroCall , ModItem , TreeId } ,
12
+ item_tree:: { AssocItem , ItemTree , ItemTreeId , MacroCall , ModItem , TreeId } ,
17
13
macro_call_as_call_id,
18
14
nameres:: {
19
15
DefMap , LocalDefMap , MacroSubNs ,
@@ -26,6 +22,7 @@ use crate::{
26
22
pub struct TraitItems {
27
23
pub items : Box < [ ( Name , AssocItemId ) ] > ,
28
24
// box it as the vec is usually empty anyways
25
+ // FIXME: AstIds are rather unstable...
29
26
pub macro_calls : Option < Box < Vec < ( AstId < ast:: Item > , MacroCallId ) > > > ,
30
27
}
31
28
@@ -40,13 +37,11 @@ impl TraitItems {
40
37
tr : TraitId ,
41
38
) -> ( Arc < TraitItems > , DefDiagnostics ) {
42
39
let ItemLoc { container : module_id, id : tree_id } = tr. lookup ( db) ;
43
- let item_tree = tree_id. item_tree ( db) ;
44
- let tr_def = & item_tree[ tree_id. value ] ;
45
40
46
- let mut collector =
47
- AssocItemCollector :: new ( db , module_id , tree_id. file_id ( ) , ItemContainerId :: TraitId ( tr ) ) ;
48
- collector . collect ( & item_tree , tree_id . tree_id ( ) , & tr_def . items ) ;
49
- let ( items , macro_calls , diagnostics ) = collector. finish ( ) ;
41
+ let collector = AssocItemCollector :: new ( db , module_id , ItemContainerId :: TraitId ( tr ) ) ;
42
+ let item_tree = tree_id. item_tree ( db ) ;
43
+ let ( items , macro_calls , diagnostics ) =
44
+ collector. collect ( & item_tree , tree_id . tree_id ( ) , & item_tree [ tree_id . value ] . items ) ;
50
45
51
46
( Arc :: new ( TraitItems { macro_calls, items } ) , DefDiagnostics :: new ( diagnostics) )
52
47
}
@@ -81,6 +76,7 @@ impl TraitItems {
81
76
pub struct ImplItems {
82
77
pub items : Box < [ ( Name , AssocItemId ) ] > ,
83
78
// box it as the vec is usually empty anyways
79
+ // FIXME: AstIds are rather unstable...
84
80
pub macro_calls : Option < Box < Vec < ( AstId < ast:: Item > , MacroCallId ) > > > ,
85
81
}
86
82
@@ -97,13 +93,10 @@ impl ImplItems {
97
93
let _p = tracing:: info_span!( "impl_items_with_diagnostics_query" ) . entered ( ) ;
98
94
let ItemLoc { container : module_id, id : tree_id } = id. lookup ( db) ;
99
95
96
+ let collector = AssocItemCollector :: new ( db, module_id, ItemContainerId :: ImplId ( id) ) ;
100
97
let item_tree = tree_id. item_tree ( db) ;
101
- let impl_def = & item_tree[ tree_id. value ] ;
102
- let mut collector =
103
- AssocItemCollector :: new ( db, module_id, tree_id. file_id ( ) , ItemContainerId :: ImplId ( id) ) ;
104
- collector. collect ( & item_tree, tree_id. tree_id ( ) , & impl_def. items ) ;
105
-
106
- let ( items, macro_calls, diagnostics) = collector. finish ( ) ;
98
+ let ( items, macro_calls, diagnostics) =
99
+ collector. collect ( & item_tree, tree_id. tree_id ( ) , & item_tree[ tree_id. value ] . items ) ;
107
100
108
101
( Arc :: new ( ImplItems { items, macro_calls } ) , DefDiagnostics :: new ( diagnostics) )
109
102
}
@@ -120,157 +113,143 @@ struct AssocItemCollector<'a> {
120
113
local_def_map : Arc < LocalDefMap > ,
121
114
diagnostics : Vec < DefDiagnostic > ,
122
115
container : ItemContainerId ,
123
- expander : Expander ,
124
116
117
+ depth : usize ,
125
118
items : Vec < ( Name , AssocItemId ) > ,
126
119
macro_calls : Vec < ( AstId < ast:: Item > , MacroCallId ) > ,
127
120
}
128
121
129
122
impl < ' a > AssocItemCollector < ' a > {
130
- fn new (
131
- db : & ' a dyn DefDatabase ,
132
- module_id : ModuleId ,
133
- file_id : HirFileId ,
134
- container : ItemContainerId ,
135
- ) -> Self {
123
+ fn new ( db : & ' a dyn DefDatabase , module_id : ModuleId , container : ItemContainerId ) -> Self {
136
124
let ( def_map, local_def_map) = module_id. local_def_map ( db) ;
137
125
Self {
138
126
db,
139
127
module_id,
140
128
def_map,
141
129
local_def_map,
142
130
container,
143
- expander : Expander :: new ( db, file_id, module_id) ,
144
131
items : Vec :: new ( ) ,
132
+
133
+ depth : 0 ,
145
134
macro_calls : Vec :: new ( ) ,
146
135
diagnostics : Vec :: new ( ) ,
147
136
}
148
137
}
149
138
150
- fn finish (
151
- self ,
139
+ fn collect (
140
+ mut self ,
141
+ item_tree : & ItemTree ,
142
+ tree_id : TreeId ,
143
+ assoc_items : & [ AssocItem ] ,
152
144
) -> (
153
145
Box < [ ( Name , AssocItemId ) ] > ,
154
146
Option < Box < Vec < ( AstId < ast:: Item > , MacroCallId ) > > > ,
155
147
Vec < DefDiagnostic > ,
156
148
) {
149
+ self . items . reserve ( assoc_items. len ( ) ) ;
150
+ for & item in assoc_items {
151
+ self . collect_item ( item_tree, tree_id, item) ;
152
+ }
157
153
(
158
154
self . items . into_boxed_slice ( ) ,
159
155
if self . macro_calls . is_empty ( ) { None } else { Some ( Box :: new ( self . macro_calls ) ) } ,
160
156
self . diagnostics ,
161
157
)
162
158
}
163
159
164
- fn collect ( & mut self , item_tree : & ItemTree , tree_id : TreeId , assoc_items : & [ AssocItem ] ) {
165
- let container = self . container ;
166
- self . items . reserve ( assoc_items. len ( ) ) ;
167
-
168
- ' items: for & item in assoc_items {
169
- let attrs = item_tree. attrs ( self . db , self . module_id . krate , ModItem :: from ( item) . into ( ) ) ;
170
- if !attrs. is_cfg_enabled ( self . expander . cfg_options ( self . db ) ) {
171
- self . diagnostics . push ( DefDiagnostic :: unconfigured_code (
172
- self . module_id . local_id ,
173
- tree_id,
174
- ModItem :: from ( item) . into ( ) ,
175
- attrs. cfg ( ) . unwrap ( ) ,
176
- self . expander . cfg_options ( self . db ) . clone ( ) ,
177
- ) ) ;
178
- continue ;
179
- }
180
-
181
- ' attrs: for attr in & * attrs {
182
- let ast_id =
183
- AstId :: new ( self . expander . current_file_id ( ) , item. ast_id ( item_tree) . upcast ( ) ) ;
184
- let ast_id_with_path = AstIdWithPath { path : attr. path . clone ( ) , ast_id } ;
185
-
186
- match self . def_map . resolve_attr_macro (
187
- & self . local_def_map ,
188
- self . db ,
189
- self . module_id . local_id ,
190
- ast_id_with_path,
191
- attr,
192
- ) {
193
- Ok ( ResolvedAttr :: Macro ( call_id) ) => {
194
- let loc = self . db . lookup_intern_macro_call ( call_id) ;
195
- if let MacroDefKind :: ProcMacro ( _, exp, _) = loc. def . kind {
196
- // If there's no expander for the proc macro (e.g. the
197
- // proc macro is ignored, or building the proc macro
198
- // crate failed), skip expansion like we would if it was
199
- // disabled. This is analogous to the handling in
200
- // `DefCollector::collect_macros`.
201
- if let Some ( err) = exp. as_expand_error ( self . module_id . krate ) {
202
- self . diagnostics . push ( DefDiagnostic :: macro_error (
203
- self . module_id . local_id ,
204
- ast_id,
205
- ( * attr. path ) . clone ( ) ,
206
- err,
207
- ) ) ;
208
- continue ' attrs;
209
- }
210
- }
160
+ fn collect_item ( & mut self , item_tree : & ItemTree , tree_id : TreeId , item : AssocItem ) {
161
+ let attrs = item_tree. attrs ( self . db , self . module_id . krate , ModItem :: from ( item) . into ( ) ) ;
162
+ if !attrs. is_cfg_enabled ( self . module_id . krate . cfg_options ( self . db ) ) {
163
+ self . diagnostics . push ( DefDiagnostic :: unconfigured_code (
164
+ self . module_id . local_id ,
165
+ tree_id,
166
+ ModItem :: from ( item) . into ( ) ,
167
+ attrs. cfg ( ) . unwrap ( ) ,
168
+ self . module_id . krate . cfg_options ( self . db ) . clone ( ) ,
169
+ ) ) ;
170
+ return ;
171
+ }
211
172
212
- self . macro_calls . push ( ( ast_id, call_id) ) ;
213
- let res =
214
- self . expander . enter_expand_id :: < ast:: MacroItems > ( self . db , call_id) ;
215
- self . collect_macro_items ( res) ;
216
- continue ' items;
217
- }
218
- Ok ( _) => ( ) ,
219
- Err ( _) => {
220
- self . diagnostics . push ( DefDiagnostic :: unresolved_macro_call (
221
- self . module_id . local_id ,
222
- MacroCallKind :: Attr {
173
+ ' attrs: for attr in & * attrs {
174
+ let ast_id = AstId :: new ( tree_id. file_id ( ) , item. ast_id ( item_tree) . upcast ( ) ) ;
175
+ let ast_id_with_path = AstIdWithPath { path : attr. path . clone ( ) , ast_id } ;
176
+
177
+ match self . def_map . resolve_attr_macro (
178
+ & self . local_def_map ,
179
+ self . db ,
180
+ self . module_id . local_id ,
181
+ ast_id_with_path,
182
+ attr,
183
+ ) {
184
+ Ok ( ResolvedAttr :: Macro ( call_id) ) => {
185
+ let loc = self . db . lookup_intern_macro_call ( call_id) ;
186
+ if let MacroDefKind :: ProcMacro ( _, exp, _) = loc. def . kind {
187
+ // If there's no expander for the proc macro (e.g. the
188
+ // proc macro is ignored, or building the proc macro
189
+ // crate failed), skip expansion like we would if it was
190
+ // disabled. This is analogous to the handling in
191
+ // `DefCollector::collect_macros`.
192
+ if let Some ( err) = exp. as_expand_error ( self . module_id . krate ) {
193
+ self . diagnostics . push ( DefDiagnostic :: macro_error (
194
+ self . module_id . local_id ,
223
195
ast_id,
224
- attr_args : None ,
225
- invoc_attr_index : attr . id ,
226
- } ,
227
- attr . path ( ) . clone ( ) ,
228
- ) ) ;
196
+ ( * attr . path ) . clone ( ) ,
197
+ err ,
198
+ ) ) ;
199
+ continue ' attrs ;
200
+ }
229
201
}
202
+
203
+ self . macro_calls . push ( ( ast_id, call_id) ) ;
204
+ self . collect_macro_items ( call_id) ;
205
+ return ;
206
+ }
207
+ Ok ( _) => ( ) ,
208
+ Err ( _) => {
209
+ self . diagnostics . push ( DefDiagnostic :: unresolved_macro_call (
210
+ self . module_id . local_id ,
211
+ MacroCallKind :: Attr { ast_id, attr_args : None , invoc_attr_index : attr. id } ,
212
+ attr. path ( ) . clone ( ) ,
213
+ ) ) ;
230
214
}
231
215
}
232
-
233
- self . collect_item ( item_tree, tree_id, container, item) ;
234
216
}
217
+
218
+ self . record_item ( item_tree, tree_id, item) ;
235
219
}
236
220
237
- fn collect_item (
238
- & mut self ,
239
- item_tree : & ItemTree ,
240
- tree_id : TreeId ,
241
- container : ItemContainerId ,
242
- item : AssocItem ,
243
- ) {
221
+ fn record_item ( & mut self , item_tree : & ItemTree , tree_id : TreeId , item : AssocItem ) {
244
222
match item {
245
223
AssocItem :: Function ( id) => {
246
224
let item = & item_tree[ id] ;
247
225
let def =
248
- FunctionLoc { container, id : ItemTreeId :: new ( tree_id, id) } . intern ( self . db ) ;
226
+ FunctionLoc { container : self . container , id : ItemTreeId :: new ( tree_id, id) }
227
+ . intern ( self . db ) ;
249
228
self . items . push ( ( item. name . clone ( ) , def. into ( ) ) ) ;
250
229
}
251
230
AssocItem :: TypeAlias ( id) => {
252
231
let item = & item_tree[ id] ;
253
232
let def =
254
- TypeAliasLoc { container, id : ItemTreeId :: new ( tree_id, id) } . intern ( self . db ) ;
233
+ TypeAliasLoc { container : self . container , id : ItemTreeId :: new ( tree_id, id) }
234
+ . intern ( self . db ) ;
255
235
self . items . push ( ( item. name . clone ( ) , def. into ( ) ) ) ;
256
236
}
257
237
AssocItem :: Const ( id) => {
258
238
let item = & item_tree[ id] ;
259
239
let Some ( name) = item. name . clone ( ) else { return } ;
260
- let def = ConstLoc { container, id : ItemTreeId :: new ( tree_id, id) } . intern ( self . db ) ;
240
+ let def = ConstLoc { container : self . container , id : ItemTreeId :: new ( tree_id, id) }
241
+ . intern ( self . db ) ;
261
242
self . items . push ( ( name, def. into ( ) ) ) ;
262
243
}
263
244
AssocItem :: MacroCall ( call) => {
264
- let file_id = self . expander . current_file_id ( ) ;
265
245
let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[ call] ;
266
- let module = self . expander . module . local_id ;
267
246
268
247
let resolver = |path : & _ | {
269
248
self . def_map
270
249
. resolve_path (
271
250
& self . local_def_map ,
272
251
self . db ,
273
- module ,
252
+ self . module_id . local_id ,
274
253
path,
275
254
crate :: item_scope:: BuiltinShadowMode :: Other ,
276
255
Some ( MacroSubNs :: Bang ) ,
@@ -281,24 +260,23 @@ impl<'a> AssocItemCollector<'a> {
281
260
} ;
282
261
match macro_call_as_call_id (
283
262
self . db . upcast ( ) ,
284
- & AstIdWithPath :: new ( file_id, ast_id, Clone :: clone ( path) ) ,
263
+ & AstIdWithPath :: new ( tree_id . file_id ( ) , ast_id, Clone :: clone ( path) ) ,
285
264
ctxt,
286
265
expand_to,
287
- self . expander . krate ( ) ,
266
+ self . module_id . krate ( ) ,
288
267
resolver,
289
268
) {
290
269
Ok ( Some ( call_id) ) => {
291
- let res =
292
- self . expander . enter_expand_id :: < ast:: MacroItems > ( self . db , call_id) ;
293
- self . macro_calls . push ( ( InFile :: new ( file_id, ast_id. upcast ( ) ) , call_id) ) ;
294
- self . collect_macro_items ( res) ;
270
+ self . macro_calls
271
+ . push ( ( InFile :: new ( tree_id. file_id ( ) , ast_id. upcast ( ) ) , call_id) ) ;
272
+ self . collect_macro_items ( call_id) ;
295
273
}
296
274
Ok ( None ) => ( ) ,
297
275
Err ( _) => {
298
276
self . diagnostics . push ( DefDiagnostic :: unresolved_macro_call (
299
277
self . module_id . local_id ,
300
278
MacroCallKind :: FnLike {
301
- ast_id : InFile :: new ( file_id, ast_id) ,
279
+ ast_id : InFile :: new ( tree_id . file_id ( ) , ast_id) ,
302
280
expand_to,
303
281
eager : None ,
304
282
} ,
@@ -310,16 +288,19 @@ impl<'a> AssocItemCollector<'a> {
310
288
}
311
289
}
312
290
313
- fn collect_macro_items ( & mut self , res : ExpandResult < Option < ( Mark , Parse < ast:: MacroItems > ) > > ) {
314
- let Some ( ( mark, _parse) ) = res. value else { return } ;
315
-
316
- let tree_id = item_tree:: TreeId :: new ( self . expander . current_file_id ( ) , None ) ;
317
- let item_tree = tree_id. item_tree ( self . db ) ;
318
- let iter: SmallVec < [ _ ; 2 ] > =
319
- item_tree. top_level_items ( ) . iter ( ) . filter_map ( ModItem :: as_assoc_item) . collect ( ) ;
320
-
321
- self . collect ( & item_tree, tree_id, & iter) ;
291
+ fn collect_macro_items ( & mut self , macro_call_id : MacroCallId ) {
292
+ if self . depth > self . def_map . recursion_limit ( ) as usize {
293
+ tracing:: warn!( "macro expansion is too deep" ) ;
294
+ return ;
295
+ }
296
+ let file_id = macro_call_id. as_file ( ) ;
297
+ let tree_id = TreeId :: new ( file_id, None ) ;
298
+ let item_tree = self . db . file_item_tree ( file_id) ;
322
299
323
- self . expander . exit ( mark) ;
300
+ self . depth += 1 ;
301
+ for item in item_tree. top_level_items ( ) . iter ( ) . filter_map ( ModItem :: as_assoc_item) {
302
+ self . collect_item ( & item_tree, tree_id, item) ;
303
+ }
304
+ self . depth -= 1 ;
324
305
}
325
306
}
0 commit comments