1
1
//! Utilities to work with files, produced by macros.
2
2
use std:: iter:: successors;
3
3
4
- use hir:: { InFile , Origin } ;
4
+ use hir:: { db :: AstDatabase , InFile , Origin } ;
5
5
use ra_db:: FileId ;
6
6
use ra_ide_db:: RootDatabase ;
7
- use ra_syntax:: { ast, AstNode , SyntaxNode , SyntaxToken , TextRange } ;
7
+ use ra_syntax:: {
8
+ algo:: find_covering_element, ast, AstNode , NodeOrToken , SyntaxElement , SyntaxNode , SyntaxToken ,
9
+ TextRange ,
10
+ } ;
8
11
9
12
use crate :: FileRange ;
10
13
11
14
pub ( crate ) fn original_range ( db : & RootDatabase , node : InFile < & SyntaxNode > ) -> FileRange {
12
- if let Some ( ( range, Origin :: Call ) ) = original_range_and_origin ( db, node) {
13
- return range;
15
+ let mut elem: InFile < SyntaxElement > = node. map ( |n| n. clone ( ) . into ( ) ) ;
16
+
17
+ while let Some ( ( range, Origin :: Call ) ) = original_range_and_origin ( db, elem. as_ref ( ) ) {
18
+ let original_file = range. file_id . original_file ( db) ;
19
+
20
+ if range. file_id == original_file. into ( ) {
21
+ return FileRange { file_id : original_file, range : range. value } ;
22
+ }
23
+
24
+ // Fail to mapping up more, return the original file range instead
25
+ if elem. file_id == range. file_id {
26
+ log:: error!( "Fail to mapping up more for {:?}" , range) ;
27
+ return FileRange { file_id : range. file_id . original_file ( db) , range : range. value } ;
28
+ }
29
+
30
+ if let Some ( root) = db. parse_or_expand ( range. file_id ) {
31
+ let n = find_covering_element ( & root, range. value ) ;
32
+ elem = range. with_value ( n) ;
33
+ } else {
34
+ break ;
35
+ }
14
36
}
15
37
38
+ // Fall back to whole macro call
16
39
if let Some ( expansion) = node. file_id . expansion_info ( db) {
17
40
if let Some ( call_node) = expansion. call_node ( ) {
18
41
return FileRange {
@@ -27,14 +50,21 @@ pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> Fi
27
50
28
51
fn original_range_and_origin (
29
52
db : & RootDatabase ,
30
- node : InFile < & SyntaxNode > ,
31
- ) -> Option < ( FileRange , Origin ) > {
32
- let expansion = node. file_id . expansion_info ( db) ?;
53
+ elem : InFile < & SyntaxElement > ,
54
+ ) -> Option < ( InFile < TextRange > , Origin ) > {
55
+ let expansion = elem. file_id . expansion_info ( db) ?;
56
+
57
+ let node = match elem. as_ref ( ) . value {
58
+ NodeOrToken :: Node ( it) => elem. with_value ( it) ,
59
+ NodeOrToken :: Token ( tt) => {
60
+ let ( tt, origin) = expansion. map_token_up ( elem. with_value ( tt) ) ?;
61
+ return Some ( ( tt. with_value ( tt. value . text_range ( ) ) , origin) ) ;
62
+ }
63
+ } ;
33
64
34
65
// the input node has only one token ?
35
66
let single = node. value . first_token ( ) ? == node. value . last_token ( ) ?;
36
67
37
- // FIXME: We should handle recurside macro expansions
38
68
let ( range, origin) = node. value . descendants ( ) . find_map ( |it| {
39
69
let first = it. first_token ( ) ?;
40
70
let last = it. last_token ( ) ?;
@@ -58,10 +88,7 @@ fn original_range_and_origin(
58
88
) )
59
89
} ) ?;
60
90
61
- return Some ( (
62
- FileRange { file_id : range. file_id . original_file ( db) , range : range. value } ,
63
- origin,
64
- ) ) ;
91
+ return Some ( ( range, origin) ) ;
65
92
66
93
fn union_range ( a : TextRange , b : TextRange ) -> TextRange {
67
94
let start = a. start ( ) . min ( b. start ( ) ) ;
0 commit comments