Skip to content

Commit 93f3c17

Browse files
Merge #10103
10103: fix: make "find references" multi-token mapping aware r=jonas-schievink a=jonas-schievink Part of #10070 I was hoping that this would fix "find references" on salsa queries, but salsa emits multiple defs sharing the same span (the trait method, and an impl of that trait). Co-authored-by: Jonas Schievink <[email protected]>
2 parents 81ab52c + bdba35c commit 93f3c17

File tree

7 files changed

+156
-87
lines changed

7 files changed

+156
-87
lines changed

crates/ide/src/annotations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation)
134134
AnnotationKind::HasReferences { position, data } => {
135135
*data = find_all_refs(&Semantics::new(db), *position, None).map(|result| {
136136
result
137-
.references
138137
.into_iter()
138+
.flat_map(|res| res.references)
139139
.map(|(file_id, access)| {
140140
access.into_iter().map(move |(range, _)| FileRange { file_id, range })
141141
})

crates/ide/src/call_hierarchy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
4646

4747
let mut calls = CallLocations::default();
4848

49-
for (file_id, references) in refs.references {
49+
for (file_id, references) in refs.into_iter().flat_map(|refs| refs.references) {
5050
let file = sema.parse(file_id);
5151
let file = file.syntax();
5252
for (relative_range, token) in references

crates/ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ impl Analysis {
405405
&self,
406406
position: FilePosition,
407407
search_scope: Option<SearchScope>,
408-
) -> Cancellable<Option<ReferenceSearchResult>> {
408+
) -> Cancellable<Option<Vec<ReferenceSearchResult>>> {
409409
self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
410410
}
411411

crates/ide/src/references.rs

Lines changed: 134 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
//! at the index that the match starts at and its tree parent is
1010
//! resolved to the search element definition, we get a reference.
1111
12+
use std::iter;
13+
14+
use either::Either;
1215
use hir::{PathResolution, Semantics};
1316
use ide_db::{
1417
base_db::FileId,
@@ -52,76 +55,91 @@ pub(crate) fn find_all_refs(
5255
sema: &Semantics<RootDatabase>,
5356
position: FilePosition,
5457
search_scope: Option<SearchScope>,
55-
) -> Option<ReferenceSearchResult> {
58+
) -> Option<Vec<ReferenceSearchResult>> {
5659
let _p = profile::span("find_all_refs");
5760
let syntax = sema.parse(position.file_id).syntax().clone();
5861

5962
let mut is_literal_search = false;
60-
let def = if let Some(name) = name_for_constructor_search(&syntax, position) {
61-
is_literal_search = true;
62-
match NameClass::classify(sema, &name)? {
63-
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
64-
NameClass::PatFieldShorthand { local_def: _, field_ref } => {
65-
Definition::Field(field_ref)
66-
}
63+
let defs = match name_for_constructor_search(&syntax, position) {
64+
Some(name) => {
65+
is_literal_search = true;
66+
let def = match NameClass::classify(sema, &name)? {
67+
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
68+
NameClass::PatFieldShorthand { local_def: _, field_ref } => {
69+
Definition::Field(field_ref)
70+
}
71+
};
72+
Either::Left(iter::once(def))
6773
}
68-
} else {
69-
find_def(sema, &syntax, position.offset)?
74+
None => Either::Right(find_defs(sema, &syntax, position.offset)),
7075
};
7176

72-
let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all();
73-
let declaration = match def {
74-
Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
75-
Some(NavigationTarget::from_module_to_decl(sema.db, module))
76-
}
77-
def => def.try_to_nav(sema.db),
78-
}
79-
.map(|nav| {
80-
let decl_range = nav.focus_or_full_range();
81-
Declaration { nav, access: decl_access(&def, &syntax, decl_range) }
82-
});
83-
if is_literal_search {
84-
retain_adt_literal_usages(&mut usages, def, sema);
85-
}
86-
87-
let references = usages
88-
.into_iter()
89-
.map(|(file_id, refs)| {
90-
(file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect())
91-
})
92-
.collect();
77+
Some(
78+
defs.into_iter()
79+
.map(|def| {
80+
let mut usages =
81+
def.usages(sema).set_scope(search_scope.clone()).include_self_refs().all();
82+
let declaration = match def {
83+
Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
84+
Some(NavigationTarget::from_module_to_decl(sema.db, module))
85+
}
86+
def => def.try_to_nav(sema.db),
87+
}
88+
.map(|nav| {
89+
let decl_range = nav.focus_or_full_range();
90+
Declaration { nav, access: decl_access(&def, &syntax, decl_range) }
91+
});
92+
if is_literal_search {
93+
retain_adt_literal_usages(&mut usages, def, sema);
94+
}
9395

94-
Some(ReferenceSearchResult { declaration, references })
96+
let references = usages
97+
.into_iter()
98+
.map(|(file_id, refs)| {
99+
(
100+
file_id,
101+
refs.into_iter()
102+
.map(|file_ref| (file_ref.range, file_ref.access))
103+
.collect(),
104+
)
105+
})
106+
.collect();
107+
108+
ReferenceSearchResult { declaration, references }
109+
})
110+
.collect(),
111+
)
95112
}
96113

97-
pub(crate) fn find_def(
98-
sema: &Semantics<RootDatabase>,
114+
pub(crate) fn find_defs<'a>(
115+
sema: &'a Semantics<RootDatabase>,
99116
syntax: &SyntaxNode,
100117
offset: TextSize,
101-
) -> Option<Definition> {
102-
let def = match sema.find_node_at_offset_with_descend(syntax, offset)? {
103-
ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
104-
NameRefClass::Definition(def) => def,
105-
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
106-
Definition::Local(local_ref)
107-
}
108-
},
109-
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
110-
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
111-
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
112-
Definition::Local(local_def)
113-
}
114-
},
115-
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
116-
.and_then(|class| match class {
117-
NameRefClass::Definition(it) => Some(it),
118-
_ => None,
119-
})
120-
.or_else(|| {
121-
NameClass::classify_lifetime(sema, &lifetime).and_then(NameClass::defined)
122-
})?,
123-
};
124-
Some(def)
118+
) -> impl Iterator<Item = Definition> + 'a {
119+
sema.find_nodes_at_offset_with_descend(syntax, offset).filter_map(move |node| {
120+
Some(match node {
121+
ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
122+
NameRefClass::Definition(def) => def,
123+
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
124+
Definition::Local(local_ref)
125+
}
126+
},
127+
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
128+
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
129+
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
130+
Definition::Local(local_def)
131+
}
132+
},
133+
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
134+
.and_then(|class| match class {
135+
NameRefClass::Definition(it) => Some(it),
136+
_ => None,
137+
})
138+
.or_else(|| {
139+
NameClass::classify_lifetime(sema, &lifetime).and_then(NameClass::defined)
140+
})?,
141+
})
142+
})
125143
}
126144

127145
pub(crate) fn decl_access(
@@ -609,6 +627,7 @@ impl Foo {
609627
expect![[r#"
610628
f Function FileId(0) 27..43 30..31
611629
630+
(no references)
612631
"#]],
613632
);
614633
}
@@ -626,6 +645,7 @@ enum Foo {
626645
expect![[r#"
627646
B Variant FileId(0) 22..23 22..23
628647
648+
(no references)
629649
"#]],
630650
);
631651
}
@@ -643,6 +663,7 @@ enum Foo {
643663
expect![[r#"
644664
field Field FileId(0) 26..35 26..31
645665
666+
(no references)
646667
"#]],
647668
);
648669
}
@@ -744,6 +765,7 @@ use self$0;
744765
expect![[r#"
745766
Module FileId(0) 0..10
746767
768+
(no references)
747769
"#]],
748770
);
749771
}
@@ -1065,21 +1087,29 @@ impl Foo {
10651087
let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
10661088

10671089
let mut actual = String::new();
1068-
if let Some(decl) = refs.declaration {
1069-
format_to!(actual, "{}", decl.nav.debug_render());
1070-
if let Some(access) = decl.access {
1071-
format_to!(actual, " {:?}", access)
1072-
}
1090+
for refs in refs {
10731091
actual += "\n\n";
1074-
}
10751092

1076-
for (file_id, references) in refs.references {
1077-
for (range, access) in references {
1078-
format_to!(actual, "{:?} {:?}", file_id, range);
1079-
if let Some(access) = access {
1080-
format_to!(actual, " {:?}", access);
1093+
if let Some(decl) = refs.declaration {
1094+
format_to!(actual, "{}", decl.nav.debug_render());
1095+
if let Some(access) = decl.access {
1096+
format_to!(actual, " {:?}", access)
10811097
}
1082-
actual += "\n";
1098+
actual += "\n\n";
1099+
}
1100+
1101+
for (file_id, references) in &refs.references {
1102+
for (range, access) in references {
1103+
format_to!(actual, "{:?} {:?}", file_id, range);
1104+
if let Some(access) = access {
1105+
format_to!(actual, " {:?}", access);
1106+
}
1107+
actual += "\n";
1108+
}
1109+
}
1110+
1111+
if refs.references.is_empty() {
1112+
actual += "(no references)\n";
10831113
}
10841114
}
10851115
expect.assert_eq(actual.trim_start())
@@ -1440,4 +1470,38 @@ m$0!();
14401470
"#]],
14411471
);
14421472
}
1473+
1474+
#[test]
1475+
fn multi_def() {
1476+
check(
1477+
r#"
1478+
macro_rules! m {
1479+
($name:ident) => {
1480+
mod module {
1481+
pub fn $name() {}
1482+
}
1483+
1484+
pub fn $name() {}
1485+
}
1486+
}
1487+
1488+
m!(func$0);
1489+
1490+
fn f() {
1491+
func();
1492+
module::func();
1493+
}
1494+
"#,
1495+
expect![[r#"
1496+
func Function FileId(0) 137..146 140..144
1497+
1498+
FileId(0) 161..165
1499+
1500+
1501+
func Function FileId(0) 137..146 140..144
1502+
1503+
FileId(0) 181..185
1504+
"#]],
1505+
)
1506+
}
14431507
}

crates/ide/src/runnables.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ fn find_related_tests(
226226
tests: &mut FxHashSet<Runnable>,
227227
) {
228228
if let Some(refs) = references::find_all_refs(sema, position, search_scope) {
229-
for (file_id, refs) in refs.references {
229+
for (file_id, refs) in refs.into_iter().flat_map(|refs| refs.references) {
230230
let file = sema.parse(file_id);
231231
let file = file.syntax();
232232
let functions = refs.iter().filter_map(|(range, _)| {

crates/ide_db/src/search.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub enum ReferenceAccess {
7171
/// For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates.
7272
/// In some cases, the location of the references is known to within a `TextRange`,
7373
/// e.g. for things like local variables.
74+
#[derive(Clone)]
7475
pub struct SearchScope {
7576
entries: FxHashMap<FileId, Option<TextRange>>,
7677
}

crates/rust-analyzer/src/handlers.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -930,21 +930,25 @@ pub(crate) fn handle_references(
930930
Some(refs) => refs,
931931
};
932932

933-
let decl = if params.context.include_declaration {
934-
refs.declaration.map(|decl| FileRange {
935-
file_id: decl.nav.file_id,
936-
range: decl.nav.focus_or_full_range(),
937-
})
938-
} else {
939-
None
940-
};
933+
let include_declaration = params.context.include_declaration;
941934
let locations = refs
942-
.references
943935
.into_iter()
944-
.flat_map(|(file_id, refs)| {
945-
refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
936+
.flat_map(|refs| {
937+
let decl = if include_declaration {
938+
refs.declaration.map(|decl| FileRange {
939+
file_id: decl.nav.file_id,
940+
range: decl.nav.focus_or_full_range(),
941+
})
942+
} else {
943+
None
944+
};
945+
refs.references
946+
.into_iter()
947+
.flat_map(|(file_id, refs)| {
948+
refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
949+
})
950+
.chain(decl)
946951
})
947-
.chain(decl)
948952
.filter_map(|frange| to_proto::location(&snap, frange).ok())
949953
.collect();
950954

@@ -1515,8 +1519,8 @@ fn show_ref_command_link(
15151519
let line_index = snap.file_line_index(position.file_id).ok()?;
15161520
let position = to_proto::position(&line_index, position.offset);
15171521
let locations: Vec<_> = ref_search_res
1518-
.references
15191522
.into_iter()
1523+
.flat_map(|res| res.references)
15201524
.flat_map(|(file_id, ranges)| {
15211525
ranges.into_iter().filter_map(move |(range, _)| {
15221526
to_proto::location(snap, FileRange { file_id, range }).ok()

0 commit comments

Comments
 (0)