1
- use std:: convert:: TryInto ;
1
+ use std:: { convert:: TryInto , iter } ;
2
2
3
3
use either:: Either ;
4
4
use hir:: { AsAssocItem , InFile , ModuleDef , Semantics } ;
@@ -11,7 +11,7 @@ use ide_db::{
11
11
use syntax:: { ast, match_ast, AstNode , AstToken , SyntaxKind :: * , SyntaxToken , TextRange , T } ;
12
12
13
13
use crate :: {
14
- display:: TryToNav ,
14
+ display:: { ToNav , TryToNav } ,
15
15
doc_links:: { doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def} ,
16
16
FilePosition , NavigationTarget , RangeInfo ,
17
17
} ;
@@ -54,41 +54,44 @@ pub(crate) fn goto_definition(
54
54
let nav = resolve_doc_path_for_def ( db, def, & link, ns) ?. try_to_nav ( db) ?;
55
55
return Some ( RangeInfo :: new ( original_token. text_range ( ) , vec ! [ nav] ) ) ;
56
56
}
57
- let nav = match_ast ! {
57
+
58
+ let navs = match_ast ! {
58
59
match parent {
59
60
ast:: NameRef ( name_ref) => {
60
61
reference_definition( & sema, Either :: Right ( & name_ref) )
61
62
} ,
62
63
ast:: Name ( name) => {
63
- let def = match NameClass :: classify( & sema, & name) ? {
64
- NameClass :: Definition ( it) | NameClass :: ConstReference ( it) => it,
65
- NameClass :: PatFieldShorthand { local_def, field_ref: _ } => Definition :: Local ( local_def) ,
66
- } ;
67
- try_find_trait_item_definition( sema. db, & def) . or_else( || def. try_to_nav( sema. db) )
64
+ match NameClass :: classify( & sema, & name) ? {
65
+ NameClass :: Definition ( def) | NameClass :: ConstReference ( def) => {
66
+ try_find_trait_item_definition( sema. db, & def) . unwrap_or_else( || def_to_nav( sema. db, def) )
67
+ }
68
+ NameClass :: PatFieldShorthand { local_def, field_ref } => {
69
+ local_and_field_to_nav( sema. db, local_def, field_ref)
70
+ } ,
71
+ }
68
72
} ,
69
73
ast:: Lifetime ( lt) => if let Some ( name_class) = NameClass :: classify_lifetime( & sema, & lt) {
70
- let def = match name_class {
71
- NameClass :: Definition ( it) | NameClass :: ConstReference ( it) => it,
72
- NameClass :: PatFieldShorthand { local_def, field_ref: _ } => Definition :: Local ( local_def) ,
73
- } ;
74
- def. try_to_nav( sema. db)
74
+ match name_class {
75
+ NameClass :: Definition ( def) => def_to_nav( sema. db, def) ,
76
+ _ => return None ,
77
+ }
75
78
} else {
76
79
reference_definition( & sema, Either :: Left ( & lt) )
77
80
} ,
78
- ast:: TokenTree ( tt) => try_lookup_include_path( sema. db, tt, token, position. file_id) ,
81
+ ast:: TokenTree ( tt) => try_lookup_include_path( sema. db, tt, token, position. file_id) ? ,
79
82
_ => return None ,
80
83
}
81
84
} ;
82
85
83
- Some ( RangeInfo :: new ( original_token. text_range ( ) , nav . into_iter ( ) . collect ( ) ) )
86
+ Some ( RangeInfo :: new ( original_token. text_range ( ) , navs ) )
84
87
}
85
88
86
89
fn try_lookup_include_path (
87
90
db : & RootDatabase ,
88
91
tt : ast:: TokenTree ,
89
92
token : SyntaxToken ,
90
93
file_id : FileId ,
91
- ) -> Option < NavigationTarget > {
94
+ ) -> Option < Vec < NavigationTarget > > {
92
95
let path = ast:: String :: cast ( token) ?. value ( ) ?. into_owned ( ) ;
93
96
let macro_call = tt. syntax ( ) . parent ( ) . and_then ( ast:: MacroCall :: cast) ?;
94
97
let name = macro_call. path ( ) ?. segment ( ) ?. name_ref ( ) ?;
@@ -97,7 +100,7 @@ fn try_lookup_include_path(
97
100
}
98
101
let file_id = db. resolve_path ( AnchoredPath { anchor : file_id, path : & path } ) ?;
99
102
let size = db. file_text ( file_id) . len ( ) . try_into ( ) . ok ( ) ?;
100
- Some ( NavigationTarget {
103
+ Some ( vec ! [ NavigationTarget {
101
104
file_id,
102
105
full_range: TextRange :: new( 0 . into( ) , size) ,
103
106
name: path. into( ) ,
@@ -106,7 +109,7 @@ fn try_lookup_include_path(
106
109
container_name: None ,
107
110
description: None ,
108
111
docs: None ,
109
- } )
112
+ } ] )
110
113
}
111
114
112
115
/// finds the trait definition of an impl'd item
@@ -116,7 +119,10 @@ fn try_lookup_include_path(
116
119
/// struct S;
117
120
/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
118
121
/// ```
119
- fn try_find_trait_item_definition ( db : & RootDatabase , def : & Definition ) -> Option < NavigationTarget > {
122
+ fn try_find_trait_item_definition (
123
+ db : & RootDatabase ,
124
+ def : & Definition ,
125
+ ) -> Option < Vec < NavigationTarget > > {
120
126
let name = def. name ( db) ?;
121
127
let assoc = match def {
122
128
Definition :: ModuleDef ( ModuleDef :: Function ( f) ) => f. as_assoc_item ( db) ,
@@ -135,40 +141,66 @@ fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option
135
141
. items ( db)
136
142
. iter ( )
137
143
. find_map ( |itm| ( itm. name ( db) ? == name) . then ( || itm. try_to_nav ( db) ) . flatten ( ) )
144
+ . map ( |it| vec ! [ it] )
138
145
}
139
146
140
147
pub ( crate ) fn reference_definition (
141
148
sema : & Semantics < RootDatabase > ,
142
149
name_ref : Either < & ast:: Lifetime , & ast:: NameRef > ,
143
- ) -> Option < NavigationTarget > {
144
- let name_kind = name_ref. either (
150
+ ) -> Vec < NavigationTarget > {
151
+ let name_kind = match name_ref. either (
145
152
|lifetime| NameRefClass :: classify_lifetime ( sema, lifetime) ,
146
153
|name_ref| NameRefClass :: classify ( sema, name_ref) ,
147
- ) ?;
148
- let def = match name_kind {
149
- NameRefClass :: Definition ( def) => def,
150
- NameRefClass :: FieldShorthand { local_ref, field_ref : _ } => Definition :: Local ( local_ref) ,
154
+ ) {
155
+ Some ( class) => class,
156
+ None => return Vec :: new ( ) ,
151
157
} ;
152
- def. try_to_nav ( sema. db )
158
+ match name_kind {
159
+ NameRefClass :: Definition ( def) => def_to_nav ( sema. db , def) ,
160
+ NameRefClass :: FieldShorthand { local_ref, field_ref } => {
161
+ local_and_field_to_nav ( sema. db , local_ref, field_ref)
162
+ }
163
+ }
164
+ }
165
+
166
+ fn def_to_nav ( db : & RootDatabase , def : Definition ) -> Vec < NavigationTarget > {
167
+ def. try_to_nav ( db) . map ( |it| vec ! [ it] ) . unwrap_or_default ( )
168
+ }
169
+
170
+ fn local_and_field_to_nav (
171
+ db : & RootDatabase ,
172
+ local : hir:: Local ,
173
+ field : hir:: Field ,
174
+ ) -> Vec < NavigationTarget > {
175
+ iter:: once ( local. to_nav ( db) ) . chain ( field. try_to_nav ( db) ) . collect ( )
153
176
}
154
177
155
178
#[ cfg( test) ]
156
179
mod tests {
157
180
use ide_db:: base_db:: FileRange ;
181
+ use itertools:: Itertools ;
158
182
159
183
use crate :: fixture;
160
184
161
185
fn check ( ra_fixture : & str ) {
162
- let ( analysis, position, expected) = fixture:: nav_target_annotation ( ra_fixture) ;
163
- let mut navs =
164
- analysis. goto_definition ( position) . unwrap ( ) . expect ( "no definition found" ) . info ;
186
+ let ( analysis, position, expected) = fixture:: annotations ( ra_fixture) ;
187
+ let navs = analysis. goto_definition ( position) . unwrap ( ) . expect ( "no definition found" ) . info ;
165
188
if navs. len ( ) == 0 {
166
189
panic ! ( "unresolved reference" )
167
190
}
168
- assert_eq ! ( navs. len( ) , 1 ) ;
169
191
170
- let nav = navs. pop ( ) . unwrap ( ) ;
171
- assert_eq ! ( expected, FileRange { file_id: nav. file_id, range: nav. focus_or_full_range( ) } ) ;
192
+ let cmp = |& FileRange { file_id, range } : & _ | ( file_id, range. start ( ) ) ;
193
+ let navs = navs
194
+ . into_iter ( )
195
+ . map ( |nav| FileRange { file_id : nav. file_id , range : nav. focus_or_full_range ( ) } )
196
+ . sorted_by_key ( cmp)
197
+ . collect :: < Vec < _ > > ( ) ;
198
+ let expected = expected
199
+ . into_iter ( )
200
+ . map ( |( FileRange { file_id, range } , _) | FileRange { file_id, range } )
201
+ . sorted_by_key ( cmp)
202
+ . collect :: < Vec < _ > > ( ) ;
203
+ assert_eq ! ( expected, navs) ;
172
204
}
173
205
174
206
fn check_unresolved ( ra_fixture : & str ) {
@@ -871,6 +903,7 @@ fn bar() {
871
903
check (
872
904
r#"
873
905
struct Foo { x: i32 }
906
+ //^
874
907
fn main() {
875
908
let x = 92;
876
909
//^
@@ -886,6 +919,7 @@ fn main() {
886
919
r#"
887
920
enum Foo {
888
921
Bar { x: i32 }
922
+ //^
889
923
}
890
924
fn baz(foo: Foo) {
891
925
match foo {
@@ -1135,13 +1169,15 @@ fn foo<'foobar>(_: &'foobar ()) {
1135
1169
fn goto_lifetime_hrtb ( ) {
1136
1170
// FIXME: requires the HIR to somehow track these hrtb lifetimes
1137
1171
check_unresolved (
1138
- r#"trait Foo<T> {}
1172
+ r#"
1173
+ trait Foo<T> {}
1139
1174
fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1140
1175
//^^
1141
1176
"# ,
1142
1177
) ;
1143
1178
check_unresolved (
1144
- r#"trait Foo<T> {}
1179
+ r#"
1180
+ trait Foo<T> {}
1145
1181
fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1146
1182
//^^
1147
1183
"# ,
0 commit comments