Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs28
-rw-r--r--crates/stdx/src/lib.rs16
2 files changed, 39 insertions, 5 deletions
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index eb9d4bf0f0..a677cea31b 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -2,6 +2,7 @@
//! Protocol. This module specifically handles requests.
use std::{
+ collections::HashSet,
fs,
io::Write as _,
path::PathBuf,
@@ -13,7 +14,8 @@ use anyhow::Context;
use ide::{
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, RangeLimit,
- ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
+ ReferenceCategory, ReferenceSearchResult, Runnable, RunnableKind, SingleResolve, SourceChange,
+ TextEdit,
};
use ide_db::SymbolKind;
use lsp_server::ErrorCode;
@@ -28,6 +30,8 @@ use lsp_types::{
};
use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
use serde_json::json;
+#[allow(unused_imports)]
+use stdx::IsNoneOr;
use stdx::{format_to, never};
use syntax::{algo, ast, AstNode, TextRange, TextSize};
use triomphe::Arc;
@@ -1055,10 +1059,10 @@ pub(crate) fn handle_references(
let exclude_imports = snap.config.find_all_refs_exclude_imports();
let exclude_tests = snap.config.find_all_refs_exclude_tests();
- let refs = match snap.analysis.find_all_refs(position, None)? {
- None => return Ok(None),
- Some(refs) => refs,
+ let Some(mut refs) = snap.analysis.find_all_refs(position, None)? else {
+ return Ok(None);
};
+ deduplicate_declarations(&mut refs);
let include_declaration = params.context.include_declaration;
let locations = refs
@@ -1090,6 +1094,17 @@ pub(crate) fn handle_references(
Ok(Some(locations))
}
+fn deduplicate_declarations(refs: &mut Vec<ReferenceSearchResult>) {
+ if refs.iter().filter(|decl| decl.declaration.is_some()).take(2).count() > 1 {
+ let mut seen_navigation_targets = HashSet::new();
+ refs.retain(|res| {
+ res.declaration
+ .as_ref()
+ .is_none_or(|decl| seen_navigation_targets.insert(decl.nav.clone()))
+ });
+ }
+}
+
pub(crate) fn handle_formatting(
snap: GlobalStateSnapshot,
params: lsp_types::DocumentFormattingParams,
@@ -1794,7 +1809,10 @@ fn show_ref_command_link(
position: &FilePosition,
) -> Option<lsp_ext::CommandLinkGroup> {
if snap.config.hover_actions().references && snap.config.client_commands().show_reference {
- if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
+ if let Some(mut ref_search_res) =
+ snap.analysis.find_all_refs(*position, None).unwrap_or(None)
+ {
+ deduplicate_declarations(&mut ref_search_res);
let uri = to_proto::url(snap, position.file_id);
let line_index = snap.file_line_index(position.file_id).ok()?;
let position = to_proto::position(&line_index, position.offset);
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 9a9ebae74e..0504ca50b8 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -302,6 +302,22 @@ pub fn slice_tails<T>(this: &[T]) -> impl Iterator<Item = &[T]> {
(0..this.len()).map(|i| &this[i..])
}
+pub trait IsNoneOr {
+ type Type;
+ #[allow(clippy::wrong_self_convention)]
+ fn is_none_or(self, s: impl FnOnce(Self::Type) -> bool) -> bool;
+}
+#[allow(unstable_name_collisions)]
+impl<T> IsNoneOr for Option<T> {
+ type Type = T;
+ fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool {
+ match self {
+ Some(v) => f(v),
+ None => true,
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;