1- use std:: convert:: TryInto ;
1+ use std:: { convert:: TryInto , iter } ;
22
33use either:: Either ;
44use hir:: { AsAssocItem , InFile , ModuleDef , Semantics } ;
@@ -11,7 +11,7 @@ use ide_db::{
1111use syntax:: { ast, match_ast, AstNode , AstToken , SyntaxKind :: * , SyntaxToken , TextRange , T } ;
1212
1313use crate :: {
14- display:: TryToNav ,
14+ display:: { ToNav , TryToNav } ,
1515 doc_links:: { doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def} ,
1616 FilePosition , NavigationTarget , RangeInfo ,
1717} ;
@@ -54,36 +54,44 @@ pub(crate) fn goto_definition(
5454 let nav = resolve_doc_path_for_def ( db, def, & link, ns) ?. try_to_nav ( db) ?;
5555 return Some ( RangeInfo :: new ( original_token. text_range ( ) , vec ! [ nav] ) ) ;
5656 }
57- let nav = match_ast ! {
57+
58+ let navs = match_ast ! {
5859 match parent {
5960 ast:: NameRef ( name_ref) => {
6061 reference_definition( & sema, Either :: Right ( & name_ref) )
6162 } ,
6263 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+ }
6672 } ,
6773 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+ }
7078 } else {
7179 reference_definition( & sema, Either :: Left ( & lt) )
7280 } ,
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) ? ,
7482 _ => return None ,
7583 }
7684 } ;
7785
78- Some ( RangeInfo :: new ( original_token. text_range ( ) , nav . into_iter ( ) . collect ( ) ) )
86+ Some ( RangeInfo :: new ( original_token. text_range ( ) , navs ) )
7987}
8088
8189fn try_lookup_include_path (
8290 db : & RootDatabase ,
8391 tt : ast:: TokenTree ,
8492 token : SyntaxToken ,
8593 file_id : FileId ,
86- ) -> Option < NavigationTarget > {
94+ ) -> Option < Vec < NavigationTarget > > {
8795 let path = ast:: String :: cast ( token) ?. value ( ) ?. into_owned ( ) ;
8896 let macro_call = tt. syntax ( ) . parent ( ) . and_then ( ast:: MacroCall :: cast) ?;
8997 let name = macro_call. path ( ) ?. segment ( ) ?. name_ref ( ) ?;
@@ -92,7 +100,7 @@ fn try_lookup_include_path(
92100 }
93101 let file_id = db. resolve_path ( AnchoredPath { anchor : file_id, path : & path } ) ?;
94102 let size = db. file_text ( file_id) . len ( ) . try_into ( ) . ok ( ) ?;
95- Some ( NavigationTarget {
103+ Some ( vec ! [ NavigationTarget {
96104 file_id,
97105 full_range: TextRange :: new( 0 . into( ) , size) ,
98106 name: path. into( ) ,
@@ -101,7 +109,7 @@ fn try_lookup_include_path(
101109 container_name: None ,
102110 description: None ,
103111 docs: None ,
104- } )
112+ } ] )
105113}
106114
107115/// finds the trait definition of an impl'd item
@@ -111,7 +119,10 @@ fn try_lookup_include_path(
111119/// struct S;
112120/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
113121/// ```
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 > > {
115126 let name = def. name ( db) ?;
116127 let assoc = match def {
117128 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
130141 . items ( db)
131142 . iter ( )
132143 . find_map ( |itm| ( itm. name ( db) ? == name) . then ( || itm. try_to_nav ( db) ) . flatten ( ) )
144+ . map ( |it| vec ! [ it] )
133145}
134146
135147pub ( crate ) fn reference_definition (
136148 sema : & Semantics < RootDatabase > ,
137149 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 (
140152 |lifetime| NameRefClass :: classify_lifetime ( sema, lifetime) ,
141153 |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 ( )
145176}
146177
147178#[ cfg( test) ]
148179mod tests {
149180 use ide_db:: base_db:: FileRange ;
181+ use itertools:: Itertools ;
150182
151183 use crate :: fixture;
152184
153185 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 ;
157188 if navs. len ( ) == 0 {
158189 panic ! ( "unresolved reference" )
159190 }
160- assert_eq ! ( navs. len( ) , 1 ) ;
161191
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) ;
164204 }
165205
166206 fn check_unresolved ( ra_fixture : & str ) {
@@ -863,6 +903,7 @@ fn bar() {
863903 check (
864904 r#"
865905struct Foo { x: i32 }
906+ //^
866907fn main() {
867908 let x = 92;
868909 //^
@@ -878,10 +919,12 @@ fn main() {
878919 r#"
879920enum Foo {
880921 Bar { x: i32 }
881- } //^
922+ //^
923+ }
882924fn baz(foo: Foo) {
883925 match foo {
884926 Foo::Bar { x$0 } => x
927+ //^
885928 };
886929}
887930"# ,
@@ -1126,13 +1169,15 @@ fn foo<'foobar>(_: &'foobar ()) {
11261169 fn goto_lifetime_hrtb ( ) {
11271170 // FIXME: requires the HIR to somehow track these hrtb lifetimes
11281171 check_unresolved (
1129- r#"trait Foo<T> {}
1172+ r#"
1173+ trait Foo<T> {}
11301174fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
11311175 //^^
11321176"# ,
11331177 ) ;
11341178 check_unresolved (
1135- r#"trait Foo<T> {}
1179+ r#"
1180+ trait Foo<T> {}
11361181fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
11371182 //^^
11381183"# ,
0 commit comments