@@ -38,32 +38,41 @@ pub(crate) fn goto_type_definition(
38
38
} ;
39
39
let range = token. text_range ( ) ;
40
40
sema. descend_into_macros ( token)
41
- . iter ( )
41
+ . into_iter ( )
42
42
. filter_map ( |token| {
43
- let ty = sema. token_ancestors_with_macros ( token. clone ( ) ) . find_map ( |node| {
44
- let ty = match_ast ! {
45
- match node {
46
- ast:: Expr ( it) => sema. type_of_expr( & it) ?. original,
47
- ast:: Pat ( it) => sema. type_of_pat( & it) ?. original,
48
- ast:: SelfParam ( it) => sema. type_of_self( & it) ?,
49
- ast:: Type ( it) => sema. resolve_type( & it) ?,
50
- ast:: RecordField ( it) => sema. to_def( & it) . map( |d| d. ty( db. upcast( ) ) ) ?,
51
- // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
52
- ast:: NameRef ( it) => {
53
- if let Some ( record_field) = ast:: RecordExprField :: for_name_ref( & it) {
54
- let ( _, _, ty) = sema. resolve_record_field( & record_field) ?;
55
- ty
56
- } else {
57
- let record_field = ast:: RecordPatField :: for_field_name_ref( & it) ?;
58
- sema. resolve_record_pat_field( & record_field) ?. 1
59
- }
60
- } ,
61
- _ => return None ,
62
- }
63
- } ;
43
+ let ty = sema
44
+ . token_ancestors_with_macros ( token)
45
+ // When `token` is within a macro call, we can't determine its type. Don't continue
46
+ // this traversal because otherwise we'll end up returning the type of *that* macro
47
+ // call, which is not what we want in general.
48
+ //
49
+ // Macro calls always wrap `TokenTree`s, so it's sufficient and efficient to test
50
+ // if the current node is a `TokenTree`.
51
+ . take_while ( |node| !ast:: TokenTree :: can_cast ( node. kind ( ) ) )
52
+ . find_map ( |node| {
53
+ let ty = match_ast ! {
54
+ match node {
55
+ ast:: Expr ( it) => sema. type_of_expr( & it) ?. original,
56
+ ast:: Pat ( it) => sema. type_of_pat( & it) ?. original,
57
+ ast:: SelfParam ( it) => sema. type_of_self( & it) ?,
58
+ ast:: Type ( it) => sema. resolve_type( & it) ?,
59
+ ast:: RecordField ( it) => sema. to_def( & it) ?. ty( db. upcast( ) ) ,
60
+ // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
61
+ ast:: NameRef ( it) => {
62
+ if let Some ( record_field) = ast:: RecordExprField :: for_name_ref( & it) {
63
+ let ( _, _, ty) = sema. resolve_record_field( & record_field) ?;
64
+ ty
65
+ } else {
66
+ let record_field = ast:: RecordPatField :: for_field_name_ref( & it) ?;
67
+ sema. resolve_record_pat_field( & record_field) ?. 1
68
+ }
69
+ } ,
70
+ _ => return None ,
71
+ }
72
+ } ;
64
73
65
- Some ( ty)
66
- } ) ;
74
+ Some ( ty)
75
+ } ) ;
67
76
ty
68
77
} )
69
78
. for_each ( |ty| {
@@ -94,7 +103,7 @@ mod tests {
94
103
fn check ( ra_fixture : & str ) {
95
104
let ( analysis, position, expected) = fixture:: annotations ( ra_fixture) ;
96
105
let navs = analysis. goto_type_definition ( position) . unwrap ( ) . unwrap ( ) . info ;
97
- assert_ne ! ( navs. len ( ) , 0 ) ;
106
+ assert ! ( ! navs. is_empty ( ) , "navigation is empty" ) ;
98
107
99
108
let cmp = |& FileRange { file_id, range } : & _ | ( file_id, range. start ( ) ) ;
100
109
let navs = navs
@@ -104,7 +113,7 @@ mod tests {
104
113
. collect :: < Vec < _ > > ( ) ;
105
114
let expected = expected
106
115
. into_iter ( )
107
- . map ( |( FileRange { file_id , range } , _) | FileRange { file_id , range } )
116
+ . map ( |( file_range , _) | file_range )
108
117
. sorted_by_key ( cmp)
109
118
. collect :: < Vec < _ > > ( ) ;
110
119
assert_eq ! ( expected, navs) ;
@@ -198,6 +207,32 @@ id! {
198
207
) ;
199
208
}
200
209
210
+ #[ test]
211
+ fn dont_collect_type_from_token_in_macro_call ( ) {
212
+ check (
213
+ r#"
214
+ struct DontCollectMe;
215
+ struct S;
216
+ //^
217
+
218
+ macro_rules! inner {
219
+ ($t:tt) => { DontCollectMe }
220
+ }
221
+ macro_rules! m {
222
+ ($t:ident) => {
223
+ match $t {
224
+ _ => inner!($t);
225
+ }
226
+ }
227
+ }
228
+
229
+ fn test() {
230
+ m!($0S);
231
+ }
232
+ "# ,
233
+ ) ;
234
+ }
235
+
201
236
#[ test]
202
237
fn goto_type_definition_for_param ( ) {
203
238
check (
0 commit comments