Skip to content

Commit 30b992e

Browse files
krobelusVeykril
authored andcommitted
Deduplicate references to macro argument
Commit 6a06f6f (Deduplicate reference search results, 2022-11-07) deduplicates references within each definition. There is an edge case when requesting references of a macro argument. Apparently, our descend_into_macros() stanza in references.rs produces a cartesian product of - references inside the macro times - times references outside the macro. Since the above deduplication only applies to the references within a single definition, we return them all, leading to many redundant references. Work around this by deduplicating definitions as well. Perhaps there is a better fix to not produce this cartesian product in the first place; but I think at least for definitions the problem would remain; a macro can contain multiple definitions of the same name, but since the navigation target will be the unresolved location, it's the same for all of them. We can't use unique() because we don't want to drop references that don't have a declaration (though I dont' have an example for this case). I discovered this working with the "bitflags" macro from the crate of the same name. Fixes #16357
1 parent 60982dc commit 30b992e

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

crates/rust-analyzer/src/handlers/request.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! Protocol. This module specifically handles requests.
33
44
use std::{
5+
collections::HashSet,
56
fs,
67
io::Write as _,
78
path::PathBuf,
@@ -13,7 +14,8 @@ use anyhow::Context;
1314
use ide::{
1415
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
1516
HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, RangeLimit,
16-
ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
17+
ReferenceCategory, ReferenceSearchResult, Runnable, RunnableKind, SingleResolve, SourceChange,
18+
TextEdit,
1719
};
1820
use ide_db::SymbolKind;
1921
use lsp_server::ErrorCode;
@@ -28,6 +30,8 @@ use lsp_types::{
2830
};
2931
use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
3032
use serde_json::json;
33+
#[allow(unused_imports)]
34+
use stdx::IsNoneOr;
3135
use stdx::{format_to, never};
3236
use syntax::{algo, ast, AstNode, TextRange, TextSize};
3337
use triomphe::Arc;
@@ -1055,10 +1059,10 @@ pub(crate) fn handle_references(
10551059
let exclude_imports = snap.config.find_all_refs_exclude_imports();
10561060
let exclude_tests = snap.config.find_all_refs_exclude_tests();
10571061

1058-
let refs = match snap.analysis.find_all_refs(position, None)? {
1059-
None => return Ok(None),
1060-
Some(refs) => refs,
1062+
let Some(mut refs) = snap.analysis.find_all_refs(position, None)? else {
1063+
return Ok(None);
10611064
};
1065+
deduplicate_declarations(&mut refs);
10621066

10631067
let include_declaration = params.context.include_declaration;
10641068
let locations = refs
@@ -1090,6 +1094,17 @@ pub(crate) fn handle_references(
10901094
Ok(Some(locations))
10911095
}
10921096

1097+
fn deduplicate_declarations(refs: &mut Vec<ReferenceSearchResult>) {
1098+
if refs.iter().filter(|decl| decl.declaration.is_some()).take(2).count() > 1 {
1099+
let mut seen_navigation_targets = HashSet::new();
1100+
refs.retain(|res| {
1101+
res.declaration
1102+
.as_ref()
1103+
.is_none_or(|decl| seen_navigation_targets.insert(decl.nav.clone()))
1104+
});
1105+
}
1106+
}
1107+
10931108
pub(crate) fn handle_formatting(
10941109
snap: GlobalStateSnapshot,
10951110
params: lsp_types::DocumentFormattingParams,
@@ -1794,7 +1809,10 @@ fn show_ref_command_link(
17941809
position: &FilePosition,
17951810
) -> Option<lsp_ext::CommandLinkGroup> {
17961811
if snap.config.hover_actions().references && snap.config.client_commands().show_reference {
1797-
if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
1812+
if let Some(mut ref_search_res) =
1813+
snap.analysis.find_all_refs(*position, None).unwrap_or(None)
1814+
{
1815+
deduplicate_declarations(&mut ref_search_res);
17981816
let uri = to_proto::url(snap, position.file_id);
17991817
let line_index = snap.file_line_index(position.file_id).ok()?;
18001818
let position = to_proto::position(&line_index, position.offset);

crates/stdx/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,22 @@ pub fn slice_tails<T>(this: &[T]) -> impl Iterator<Item = &[T]> {
302302
(0..this.len()).map(|i| &this[i..])
303303
}
304304

305+
pub trait IsNoneOr {
306+
type Type;
307+
#[allow(clippy::wrong_self_convention)]
308+
fn is_none_or(self, s: impl FnOnce(Self::Type) -> bool) -> bool;
309+
}
310+
#[allow(unstable_name_collisions)]
311+
impl<T> IsNoneOr for Option<T> {
312+
type Type = T;
313+
fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool {
314+
match self {
315+
Some(v) => f(v),
316+
None => true,
317+
}
318+
}
319+
}
320+
305321
#[cfg(test)]
306322
mod tests {
307323
use super::*;

0 commit comments

Comments
 (0)