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,36 +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 = NameClass :: classify( & sema, & name) ?. referenced_or_defined( ) ;
64
- try_find_trait_item_definition( sema. db, & def)
65
- . 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
+ }
66
72
} ,
67
73
ast:: Lifetime ( lt) => if let Some ( name_class) = NameClass :: classify_lifetime( & sema, & lt) {
68
- let def = name_class. referenced_or_defined( ) ;
69
- def. try_to_nav( sema. db)
74
+ match name_class {
75
+ NameClass :: Definition ( def) => def_to_nav( sema. db, def) ,
76
+ _ => return None ,
77
+ }
70
78
} else {
71
79
reference_definition( & sema, Either :: Left ( & lt) )
72
80
} ,
73
- 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) ? ,
74
82
_ => return None ,
75
83
}
76
84
} ;
77
85
78
- Some ( RangeInfo :: new ( original_token. text_range ( ) , nav . into_iter ( ) . collect ( ) ) )
86
+ Some ( RangeInfo :: new ( original_token. text_range ( ) , navs ) )
79
87
}
80
88
81
89
fn try_lookup_include_path (
82
90
db : & RootDatabase ,
83
91
tt : ast:: TokenTree ,
84
92
token : SyntaxToken ,
85
93
file_id : FileId ,
86
- ) -> Option < NavigationTarget > {
94
+ ) -> Option < Vec < NavigationTarget > > {
87
95
let path = ast:: String :: cast ( token) ?. value ( ) ?. into_owned ( ) ;
88
96
let macro_call = tt. syntax ( ) . parent ( ) . and_then ( ast:: MacroCall :: cast) ?;
89
97
let name = macro_call. path ( ) ?. segment ( ) ?. name_ref ( ) ?;
@@ -92,7 +100,7 @@ fn try_lookup_include_path(
92
100
}
93
101
let file_id = db. resolve_path ( AnchoredPath { anchor : file_id, path : & path } ) ?;
94
102
let size = db. file_text ( file_id) . len ( ) . try_into ( ) . ok ( ) ?;
95
- Some ( NavigationTarget {
103
+ Some ( vec ! [ NavigationTarget {
96
104
file_id,
97
105
full_range: TextRange :: new( 0 . into( ) , size) ,
98
106
name: path. into( ) ,
@@ -101,7 +109,7 @@ fn try_lookup_include_path(
101
109
container_name: None ,
102
110
description: None ,
103
111
docs: None ,
104
- } )
112
+ } ] )
105
113
}
106
114
107
115
/// finds the trait definition of an impl'd item
@@ -111,7 +119,10 @@ fn try_lookup_include_path(
111
119
/// struct S;
112
120
/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
113
121
/// ```
114
- 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 > > {
115
126
let name = def. name ( db) ?;
116
127
let assoc = match def {
117
128
Definition :: ModuleDef ( ModuleDef :: Function ( f) ) => f. as_assoc_item ( db) ,
@@ -130,37 +141,66 @@ fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option
130
141
. items ( db)
131
142
. iter ( )
132
143
. find_map ( |itm| ( itm. name ( db) ? == name) . then ( || itm. try_to_nav ( db) ) . flatten ( ) )
144
+ . map ( |it| vec ! [ it] )
133
145
}
134
146
135
147
pub ( crate ) fn reference_definition (
136
148
sema : & Semantics < RootDatabase > ,
137
149
name_ref : Either < & ast:: Lifetime , & ast:: NameRef > ,
138
- ) -> Option < NavigationTarget > {
139
- let name_kind = name_ref. either (
150
+ ) -> Vec < NavigationTarget > {
151
+ let name_kind = match name_ref. either (
140
152
|lifetime| NameRefClass :: classify_lifetime ( sema, lifetime) ,
141
153
|name_ref| NameRefClass :: classify ( sema, name_ref) ,
142
- ) ?;
143
- let def = name_kind. referenced ( ) ;
144
- def. try_to_nav ( sema. db )
154
+ ) {
155
+ Some ( class) => class,
156
+ None => return Vec :: new ( ) ,
157
+ } ;
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 ( )
145
176
}
146
177
147
178
#[ cfg( test) ]
148
179
mod tests {
149
180
use ide_db:: base_db:: FileRange ;
181
+ use itertools:: Itertools ;
150
182
151
183
use crate :: fixture;
152
184
153
185
fn check ( ra_fixture : & str ) {
154
- let ( analysis, position, expected) = fixture:: nav_target_annotation ( ra_fixture) ;
155
- let mut navs =
156
- 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 ;
157
188
if navs. len ( ) == 0 {
158
189
panic ! ( "unresolved reference" )
159
190
}
160
- assert_eq ! ( navs. len( ) , 1 ) ;
161
191
162
- let nav = navs. pop ( ) . unwrap ( ) ;
163
- 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) ;
164
204
}
165
205
166
206
fn check_unresolved ( ra_fixture : & str ) {
@@ -863,6 +903,7 @@ fn bar() {
863
903
check (
864
904
r#"
865
905
struct Foo { x: i32 }
906
+ //^
866
907
fn main() {
867
908
let x = 92;
868
909
//^
@@ -878,10 +919,12 @@ fn main() {
878
919
r#"
879
920
enum Foo {
880
921
Bar { x: i32 }
881
- } //^
922
+ //^
923
+ }
882
924
fn baz(foo: Foo) {
883
925
match foo {
884
926
Foo::Bar { x$0 } => x
927
+ //^
885
928
};
886
929
}
887
930
"# ,
@@ -1126,13 +1169,15 @@ fn foo<'foobar>(_: &'foobar ()) {
1126
1169
fn goto_lifetime_hrtb ( ) {
1127
1170
// FIXME: requires the HIR to somehow track these hrtb lifetimes
1128
1171
check_unresolved (
1129
- r#"trait Foo<T> {}
1172
+ r#"
1173
+ trait Foo<T> {}
1130
1174
fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1131
1175
//^^
1132
1176
"# ,
1133
1177
) ;
1134
1178
check_unresolved (
1135
- r#"trait Foo<T> {}
1179
+ r#"
1180
+ trait Foo<T> {}
1136
1181
fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1137
1182
//^^
1138
1183
"# ,
0 commit comments