Skip to content

Commit fe00358

Browse files
bors[bot]Veykril
andauthored
Merge #9569
9569: internal: Explicitly check for reference locals or fields in Name classification r=Veykril a=Veykril Closes #7524 Inlines all the calls to reference related name classification as well as emits both goto definition targets for field shorthands. Co-authored-by: Lukas Wirth <[email protected]>
2 parents 3fc5f01 + ab26477 commit fe00358

File tree

12 files changed

+202
-112
lines changed

12 files changed

+202
-112
lines changed

crates/ide/src/doc_links.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,16 @@ pub(crate) fn external_docs(
112112
let node = token.parent()?;
113113
let definition = match_ast! {
114114
match node {
115-
ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced())?,
116-
ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined())?,
115+
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
116+
NameRefClass::Definition(def) => def,
117+
NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
118+
Definition::Field(field_ref)
119+
}
120+
},
121+
ast::Name(name) => match NameClass::classify(&sema, &name)? {
122+
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
123+
NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref),
124+
},
117125
_ => return None,
118126
}
119127
};

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: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,45 +23,56 @@ pub(crate) fn goto_declaration(
2323
let parent = token.parent()?;
2424
let def = match_ast! {
2525
match parent {
26-
ast::NameRef(name_ref) => {
27-
let name_kind = NameRefClass::classify(&sema, &name_ref)?;
28-
name_kind.referenced()
26+
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
27+
NameRefClass::Definition(it) => Some(it),
28+
_ => None
2929
},
30-
ast::Name(name) => {
31-
NameClass::classify(&sema, &name)?.referenced_or_defined()
30+
ast::Name(name) => match NameClass::classify(&sema, &name)? {
31+
NameClass::Definition(it) => Some(it),
32+
_ => None
3233
},
33-
_ => return None,
34+
_ => None,
3435
}
3536
};
36-
match def {
37+
match def? {
3738
Definition::ModuleDef(hir::ModuleDef::Module(module)) => Some(RangeInfo::new(
3839
original_token.text_range(),
3940
vec![NavigationTarget::from_module_to_decl(db, module)],
4041
)),
41-
_ => return None,
42+
_ => None,
4243
}
4344
}
4445

4546
#[cfg(test)]
4647
mod tests {
4748
use ide_db::base_db::FileRange;
49+
use itertools::Itertools;
4850

4951
use crate::fixture;
5052

5153
fn check(ra_fixture: &str) {
52-
let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
53-
let mut navs = analysis
54+
let (analysis, position, expected) = fixture::annotations(ra_fixture);
55+
let navs = analysis
5456
.goto_declaration(position)
5557
.unwrap()
5658
.expect("no declaration or definition found")
5759
.info;
5860
if navs.len() == 0 {
5961
panic!("unresolved reference")
6062
}
61-
assert_eq!(navs.len(), 1);
6263

63-
let nav = navs.pop().unwrap();
64-
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);
6576
}
6677

6778
#[test]

crates/ide/src/goto_definition.rs

Lines changed: 73 additions & 28 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,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

8189
fn 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

135147
pub(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)]
148179
mod 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#"
865905
struct Foo { x: i32 }
906+
//^
866907
fn main() {
867908
let x = 92;
868909
//^
@@ -878,10 +919,12 @@ fn main() {
878919
r#"
879920
enum Foo {
880921
Bar { x: i32 }
881-
} //^
922+
//^
923+
}
882924
fn 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> {}
11301174
fn 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> {}
11361181
fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
11371182
//^^
11381183
"#,

crates/ide/src/goto_implementation.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,19 @@ pub(crate) fn goto_implementation(
2828

2929
let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?;
3030
let def = match &node {
31-
ast::NameLike::Name(name) => {
32-
NameClass::classify(&sema, name).map(|class| class.referenced_or_defined())
33-
}
31+
ast::NameLike::Name(name) => NameClass::classify(&sema, name).map(|class| match class {
32+
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
33+
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
34+
Definition::Local(local_def)
35+
}
36+
}),
3437
ast::NameLike::NameRef(name_ref) => {
35-
NameRefClass::classify(&sema, name_ref).map(|class| class.referenced())
38+
NameRefClass::classify(&sema, name_ref).map(|class| match class {
39+
NameRefClass::Definition(def) => def,
40+
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
41+
Definition::Local(local_ref)
42+
}
43+
})
3644
}
3745
ast::NameLike::Lifetime(_) => None,
3846
}?;

crates/ide/src/hover.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,23 @@ pub(crate) fn hover(
9696
match node {
9797
// we don't use NameClass::referenced_or_defined here as we do not want to resolve
9898
// field pattern shorthands to their definition
99-
ast::Name(name) => NameClass::classify(&sema, &name).and_then(|class| match class {
100-
NameClass::ConstReference(def) => Some(def),
101-
def => def.defined(),
99+
ast::Name(name) => NameClass::classify(&sema, &name).map(|class| match class {
100+
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
101+
NameClass::PatFieldShorthand { local_def, field_ref: _ } => Definition::Local(local_def),
102+
}),
103+
ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|class| match class {
104+
NameRefClass::Definition(def) => def,
105+
NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
106+
Definition::Field(field_ref)
107+
}
102108
}),
103-
ast::NameRef(name_ref) => {
104-
NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced())
105-
},
106109
ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime).map_or_else(
107-
|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced()),
110+
|| NameRefClass::classify_lifetime(&sema, &lifetime).and_then(|class| match class {
111+
NameRefClass::Definition(it) => Some(it),
112+
_ => None,
113+
}),
108114
|d| d.defined(),
109115
),
110-
111116
_ => {
112117
if ast::Comment::cast(token.clone()).is_some() {
113118
cov_mark::hit!(no_highlight_on_comment_hover);

0 commit comments

Comments
 (0)