Skip to content

Commit ab26477

Browse files
committed
Return both field and local references for shorthands in goto_def
1 parent c41f127 commit ab26477

File tree

3 files changed

+92
-56
lines changed

3 files changed

+92
-56
lines changed

crates/ide/src/fixture.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,3 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
5151
.collect();
5252
(host.analysis(), FilePosition { file_id, offset }, annotations)
5353
}
54-
55-
pub(crate) fn nav_target_annotation(ra_fixture: &str) -> (Analysis, FilePosition, FileRange) {
56-
let (analysis, position, mut annotations) = annotations(ra_fixture);
57-
let (expected, data) = annotations.pop().unwrap();
58-
assert!(annotations.is_empty());
59-
assert_eq!(data, "");
60-
(analysis, position, expected)
61-
}

crates/ide/src/goto_declaration.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,47 +24,55 @@ pub(crate) fn goto_declaration(
2424
let def = match_ast! {
2525
match parent {
2626
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
27-
NameRefClass::Definition(def) => def,
28-
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
29-
Definition::Local(local_ref)
30-
}
27+
NameRefClass::Definition(it) => Some(it),
28+
_ => None
3129
},
3230
ast::Name(name) => match NameClass::classify(&sema, &name)? {
33-
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
34-
NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def),
31+
NameClass::Definition(it) => Some(it),
32+
_ => None
3533
},
36-
_ => return None,
34+
_ => None,
3735
}
3836
};
39-
match def {
37+
match def? {
4038
Definition::ModuleDef(hir::ModuleDef::Module(module)) => Some(RangeInfo::new(
4139
original_token.text_range(),
4240
vec![NavigationTarget::from_module_to_decl(db, module)],
4341
)),
44-
_ => return None,
42+
_ => None,
4543
}
4644
}
4745

4846
#[cfg(test)]
4947
mod tests {
5048
use ide_db::base_db::FileRange;
49+
use itertools::Itertools;
5150

5251
use crate::fixture;
5352

5453
fn check(ra_fixture: &str) {
55-
let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
56-
let mut navs = analysis
54+
let (analysis, position, expected) = fixture::annotations(ra_fixture);
55+
let navs = analysis
5756
.goto_declaration(position)
5857
.unwrap()
5958
.expect("no declaration or definition found")
6059
.info;
6160
if navs.len() == 0 {
6261
panic!("unresolved reference")
6362
}
64-
assert_eq!(navs.len(), 1);
6563

66-
let nav = navs.pop().unwrap();
67-
assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
64+
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
65+
let navs = navs
66+
.into_iter()
67+
.map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
68+
.sorted_by_key(cmp)
69+
.collect::<Vec<_>>();
70+
let expected = expected
71+
.into_iter()
72+
.map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
73+
.sorted_by_key(cmp)
74+
.collect::<Vec<_>>();
75+
assert_eq!(expected, navs);
6876
}
6977

7078
#[test]

crates/ide/src/goto_definition.rs

Lines changed: 70 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::convert::TryInto;
1+
use std::{convert::TryInto, iter};
22

33
use either::Either;
44
use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
@@ -11,7 +11,7 @@ use ide_db::{
1111
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
1212

1313
use 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,41 +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 = 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+
}
6872
},
6973
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+
}
7578
} else {
7679
reference_definition(&sema, Either::Left(&lt))
7780
},
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)?,
7982
_ => return None,
8083
}
8184
};
8285

83-
Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
86+
Some(RangeInfo::new(original_token.text_range(), navs))
8487
}
8588

8689
fn try_lookup_include_path(
8790
db: &RootDatabase,
8891
tt: ast::TokenTree,
8992
token: SyntaxToken,
9093
file_id: FileId,
91-
) -> Option<NavigationTarget> {
94+
) -> Option<Vec<NavigationTarget>> {
9295
let path = ast::String::cast(token)?.value()?.into_owned();
9396
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
9497
let name = macro_call.path()?.segment()?.name_ref()?;
@@ -97,7 +100,7 @@ fn try_lookup_include_path(
97100
}
98101
let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
99102
let size = db.file_text(file_id).len().try_into().ok()?;
100-
Some(NavigationTarget {
103+
Some(vec![NavigationTarget {
101104
file_id,
102105
full_range: TextRange::new(0.into(), size),
103106
name: path.into(),
@@ -106,7 +109,7 @@ fn try_lookup_include_path(
106109
container_name: None,
107110
description: None,
108111
docs: None,
109-
})
112+
}])
110113
}
111114

112115
/// finds the trait definition of an impl'd item
@@ -116,7 +119,10 @@ fn try_lookup_include_path(
116119
/// struct S;
117120
/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
118121
/// ```
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>> {
120126
let name = def.name(db)?;
121127
let assoc = match def {
122128
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
135141
.items(db)
136142
.iter()
137143
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
144+
.map(|it| vec![it])
138145
}
139146

140147
pub(crate) fn reference_definition(
141148
sema: &Semantics<RootDatabase>,
142149
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(
145152
|lifetime| NameRefClass::classify_lifetime(sema, lifetime),
146153
|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(),
151157
};
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()
153176
}
154177

155178
#[cfg(test)]
156179
mod tests {
157180
use ide_db::base_db::FileRange;
181+
use itertools::Itertools;
158182

159183
use crate::fixture;
160184

161185
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;
165188
if navs.len() == 0 {
166189
panic!("unresolved reference")
167190
}
168-
assert_eq!(navs.len(), 1);
169191

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);
172204
}
173205

174206
fn check_unresolved(ra_fixture: &str) {
@@ -871,6 +903,7 @@ fn bar() {
871903
check(
872904
r#"
873905
struct Foo { x: i32 }
906+
//^
874907
fn main() {
875908
let x = 92;
876909
//^
@@ -886,6 +919,7 @@ fn main() {
886919
r#"
887920
enum Foo {
888921
Bar { x: i32 }
922+
//^
889923
}
890924
fn baz(foo: Foo) {
891925
match foo {
@@ -1135,13 +1169,15 @@ fn foo<'foobar>(_: &'foobar ()) {
11351169
fn goto_lifetime_hrtb() {
11361170
// FIXME: requires the HIR to somehow track these hrtb lifetimes
11371171
check_unresolved(
1138-
r#"trait Foo<T> {}
1172+
r#"
1173+
trait Foo<T> {}
11391174
fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
11401175
//^^
11411176
"#,
11421177
);
11431178
check_unresolved(
1144-
r#"trait Foo<T> {}
1179+
r#"
1180+
trait Foo<T> {}
11451181
fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
11461182
//^^
11471183
"#,

0 commit comments

Comments
 (0)