1
1
use std:: iter;
2
2
3
3
use base_db:: {
4
- span:: { HirFileId , HirFileIdRepr , MacroFile , SyntaxContextId } ,
5
- FileRange ,
4
+ span:: { HirFileId , HirFileIdRepr , MacroFileId , SyntaxContextId } ,
5
+ FileId , FileRange ,
6
6
} ;
7
7
use either:: Either ;
8
8
use syntax:: { AstNode , SyntaxNode , SyntaxToken , TextRange } ;
9
9
10
10
use crate :: { db, ExpansionInfo , HirFileIdExt as _} ;
11
11
12
- // FIXME: Make an InRealFile wrapper
13
12
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
14
13
///
15
14
/// Typical usages are:
@@ -18,55 +17,91 @@ use crate::{db, ExpansionInfo, HirFileIdExt as _};
18
17
/// * `InFile<ast::FnDef>` -- ast node in a file
19
18
/// * `InFile<TextSize>` -- offset in a file
20
19
#[ derive( Debug , PartialEq , Eq , Clone , Copy , Hash ) ]
21
- pub struct InFile < T > {
22
- pub file_id : HirFileId ,
20
+ pub struct InFileWrapper < FileKind , T > {
21
+ pub file_id : FileKind ,
23
22
pub value : T ,
24
23
}
24
+ pub type InFile < T > = InFileWrapper < HirFileId , T > ;
25
+ pub type InMacroFile < T > = InFileWrapper < MacroFileId , T > ;
26
+ pub type InRealFile < T > = InFileWrapper < FileId , T > ;
25
27
26
- impl < T > InFile < T > {
27
- pub fn new ( file_id : HirFileId , value : T ) -> InFile < T > {
28
- InFile { file_id, value }
28
+ impl < FileKind , T > InFileWrapper < FileKind , T > {
29
+ pub fn new ( file_id : FileKind , value : T ) -> Self {
30
+ Self { file_id, value }
29
31
}
30
32
31
- pub fn with_value < U > ( & self , value : U ) -> InFile < U > {
32
- InFile :: new ( self . file_id , value)
33
+ pub fn map < F : FnOnce ( T ) -> U , U > ( self , f : F ) -> InFileWrapper < FileKind , U > {
34
+ InFileWrapper :: new ( self . file_id , f ( self . value ) )
33
35
}
36
+ }
34
37
35
- pub fn map < F : FnOnce ( T ) -> U , U > ( self , f : F ) -> InFile < U > {
36
- InFile :: new ( self . file_id , f ( self . value ) )
38
+ impl < FileKind : Copy , T > InFileWrapper < FileKind , T > {
39
+ pub fn with_value < U > ( & self , value : U ) -> InFileWrapper < FileKind , U > {
40
+ InFileWrapper :: new ( self . file_id , value)
37
41
}
38
42
39
- pub fn as_ref ( & self ) -> InFile < & T > {
43
+ pub fn as_ref ( & self ) -> InFileWrapper < FileKind , & T > {
40
44
self . with_value ( & self . value )
41
45
}
46
+ }
42
47
43
- pub fn file_syntax ( & self , db : & dyn db:: ExpandDatabase ) -> SyntaxNode {
44
- db. parse_or_expand ( self . file_id )
48
+ impl < FileKind : Copy , T : Clone > InFileWrapper < FileKind , & T > {
49
+ pub fn cloned ( & self ) -> InFileWrapper < FileKind , T > {
50
+ self . with_value ( self . value . clone ( ) )
45
51
}
46
52
}
47
53
48
- impl < T : Clone > InFile < & T > {
49
- pub fn cloned ( & self ) -> InFile < T > {
50
- self . with_value ( self . value . clone ( ) )
54
+ impl < T > From < InMacroFile < T > > for InFile < T > {
55
+ fn from ( InMacroFile { file_id , value } : InMacroFile < T > ) -> Self {
56
+ InFile { file_id : file_id . into ( ) , value }
51
57
}
52
58
}
53
59
54
- impl < T > InFile < Option < T > > {
55
- pub fn transpose ( self ) -> Option < InFile < T > > {
56
- let value = self . value ?;
57
- Some ( InFile :: new ( self . file_id , value) )
60
+ impl < T > From < InRealFile < T > > for InFile < T > {
61
+ fn from ( InRealFile { file_id, value } : InRealFile < T > ) -> Self {
62
+ InFile { file_id : file_id. into ( ) , value }
58
63
}
59
64
}
60
65
61
- impl < L , R > InFile < Either < L , R > > {
62
- pub fn transpose ( self ) -> Either < InFile < L > , InFile < R > > {
66
+ // region:transpose impls
67
+
68
+ impl < FileKind , T > InFileWrapper < FileKind , Option < T > > {
69
+ pub fn transpose ( self ) -> Option < InFileWrapper < FileKind , T > > {
70
+ Some ( InFileWrapper :: new ( self . file_id , self . value ?) )
71
+ }
72
+ }
73
+
74
+ impl < FileKind , L , R > InFileWrapper < FileKind , Either < L , R > > {
75
+ pub fn transpose ( self ) -> Either < InFileWrapper < FileKind , L > , InFileWrapper < FileKind , R > > {
63
76
match self . value {
64
- Either :: Left ( l) => Either :: Left ( InFile :: new ( self . file_id , l) ) ,
65
- Either :: Right ( r) => Either :: Right ( InFile :: new ( self . file_id , r) ) ,
77
+ Either :: Left ( l) => Either :: Left ( InFileWrapper :: new ( self . file_id , l) ) ,
78
+ Either :: Right ( r) => Either :: Right ( InFileWrapper :: new ( self . file_id , r) ) ,
66
79
}
67
80
}
68
81
}
69
82
83
+ // endregion:transpose impls
84
+
85
+ // region:specific impls
86
+
87
+ impl < T > InFile < T > {
88
+ pub fn file_syntax ( & self , db : & dyn db:: ExpandDatabase ) -> SyntaxNode {
89
+ db. parse_or_expand ( self . file_id )
90
+ }
91
+ }
92
+
93
+ impl < T > InRealFile < T > {
94
+ pub fn file_syntax ( & self , db : & dyn db:: ExpandDatabase ) -> SyntaxNode {
95
+ db. parse ( self . file_id ) . syntax_node ( )
96
+ }
97
+ }
98
+
99
+ impl < T > InMacroFile < T > {
100
+ pub fn file_syntax ( & self , db : & dyn db:: ExpandDatabase ) -> SyntaxNode {
101
+ db. parse_macro_expansion ( self . file_id ) . value . 0 . syntax_node ( )
102
+ }
103
+ }
104
+
70
105
impl InFile < & SyntaxNode > {
71
106
pub fn ancestors_with_macros (
72
107
self ,
@@ -159,11 +194,17 @@ impl InFile<&SyntaxNode> {
159
194
}
160
195
}
161
196
162
- pub fn original_syntax_node ( self , db : & dyn db:: ExpandDatabase ) -> Option < InFile < SyntaxNode > > {
197
+ pub fn original_syntax_node (
198
+ self ,
199
+ db : & dyn db:: ExpandDatabase ,
200
+ ) -> Option < InRealFile < SyntaxNode > > {
163
201
// This kind of upmapping can only be achieved in attribute expanded files,
164
202
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
165
- let Some ( file_id) = self . file_id . macro_file ( ) else {
166
- return Some ( self . map ( Clone :: clone) ) ;
203
+ let file_id = match self . file_id . repr ( ) {
204
+ HirFileIdRepr :: FileId ( file_id) => {
205
+ return Some ( InRealFile { file_id, value : self . value . clone ( ) } )
206
+ }
207
+ HirFileIdRepr :: MacroFile ( m) => m,
167
208
} ;
168
209
if !self . file_id . is_attr_macro ( db) {
169
210
return None ;
@@ -182,7 +223,7 @@ impl InFile<&SyntaxNode> {
182
223
let kind = self . value . kind ( ) ;
183
224
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
184
225
let value = anc. ancestors ( ) . find ( |it| it. kind ( ) == kind) ?;
185
- Some ( InFile :: new ( file_id. into ( ) , value) )
226
+ Some ( InRealFile :: new ( file_id, value) )
186
227
}
187
228
}
188
229
@@ -230,29 +271,11 @@ impl InFile<SyntaxToken> {
230
271
}
231
272
}
232
273
233
- #[ derive( Debug , PartialEq , Eq , Clone , Copy , Hash ) ]
234
- pub struct InMacroFile < T > {
235
- pub file_id : MacroFile ,
236
- pub value : T ,
237
- }
238
-
239
- impl < T > From < InMacroFile < T > > for InFile < T > {
240
- fn from ( macro_file : InMacroFile < T > ) -> Self {
241
- InFile { file_id : macro_file. file_id . into ( ) , value : macro_file. value }
242
- }
243
- }
244
-
245
- pub fn ascend_range_up_macros (
246
- db : & dyn db:: ExpandDatabase ,
247
- range : InFile < TextRange > ,
248
- ) -> ( FileRange , SyntaxContextId ) {
249
- match range. file_id . repr ( ) {
250
- HirFileIdRepr :: FileId ( file_id) => {
251
- ( FileRange { file_id, range : range. value } , SyntaxContextId :: ROOT )
252
- }
253
- HirFileIdRepr :: MacroFile ( m) => {
254
- ExpansionInfo :: new ( db, m) . map_token_range_up ( db, range. value )
255
- }
274
+ impl InFile < TextRange > {
275
+ /// Attempts to map the syntax node back up its macro calls.
276
+ pub fn original_file_range ( self , db : & dyn db:: ExpandDatabase ) -> FileRange {
277
+ let ( range, _ctxt) = ascend_range_up_macros ( db, self ) ;
278
+ range
256
279
}
257
280
}
258
281
@@ -261,12 +284,14 @@ impl<N: AstNode> InFile<N> {
261
284
self . value . syntax ( ) . descendants ( ) . filter_map ( T :: cast) . map ( move |n| self . with_value ( n) )
262
285
}
263
286
264
- // FIXME: this should return `Option<InFileNotHirFile<N>>`
265
- pub fn original_ast_node ( self , db : & dyn db:: ExpandDatabase ) -> Option < InFile < N > > {
287
+ pub fn original_ast_node ( self , db : & dyn db:: ExpandDatabase ) -> Option < InRealFile < N > > {
266
288
// This kind of upmapping can only be achieved in attribute expanded files,
267
289
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
268
- let Some ( file_id) = self . file_id . macro_file ( ) else {
269
- return Some ( self ) ;
290
+ let file_id = match self . file_id . repr ( ) {
291
+ HirFileIdRepr :: FileId ( file_id) => {
292
+ return Some ( InRealFile { file_id, value : self . value } )
293
+ }
294
+ HirFileIdRepr :: MacroFile ( m) => m,
270
295
} ;
271
296
if !self . file_id . is_attr_macro ( db) {
272
297
return None ;
@@ -284,10 +309,24 @@ impl<N: AstNode> InFile<N> {
284
309
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
285
310
let anc = db. parse ( file_id) . syntax_node ( ) . covering_element ( range) ;
286
311
let value = anc. ancestors ( ) . find_map ( N :: cast) ?;
287
- return Some ( InFile :: new ( file_id. into ( ) , value) ) ;
312
+ Some ( InRealFile :: new ( file_id, value) )
288
313
}
289
314
290
315
pub fn syntax ( & self ) -> InFile < & SyntaxNode > {
291
316
self . with_value ( self . value . syntax ( ) )
292
317
}
293
318
}
319
+
320
+ fn ascend_range_up_macros (
321
+ db : & dyn db:: ExpandDatabase ,
322
+ range : InFile < TextRange > ,
323
+ ) -> ( FileRange , SyntaxContextId ) {
324
+ match range. file_id . repr ( ) {
325
+ HirFileIdRepr :: FileId ( file_id) => {
326
+ ( FileRange { file_id, range : range. value } , SyntaxContextId :: ROOT )
327
+ }
328
+ HirFileIdRepr :: MacroFile ( m) => {
329
+ ExpansionInfo :: new ( db, m) . map_token_range_up ( db, range. value )
330
+ }
331
+ }
332
+ }
0 commit comments