mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
Auto merge of #16358 - krobelus:fix-redundant-references-with-macros, r=Veykril
Deduplicate references when some of them are in macro expansions
EDIT: I wonder if this is a regression, I'll try to investigate.
Commit 6a06f6f72
(Deduplicate reference search results, 2022-11-07)
deduplicates references within each definition.
Apparently our descend_into_macros() stanza returns
one definition for each time a name is used in a macro.
Each of those definitions has the same set of references.
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 duplicate definitions in the first
place.
I discovered this working with the "bitflags" macro from the crate
of the same name.
Fixes #16357
This commit is contained in:
commit
d8c8ccc380
3 changed files with 27 additions and 9 deletions
|
@ -16,6 +16,7 @@ use ide::{
|
||||||
ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
|
ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
|
||||||
};
|
};
|
||||||
use ide_db::SymbolKind;
|
use ide_db::SymbolKind;
|
||||||
|
use itertools::Itertools;
|
||||||
use lsp_server::ErrorCode;
|
use lsp_server::ErrorCode;
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
|
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
|
||||||
|
@ -1055,9 +1056,8 @@ pub(crate) fn handle_references(
|
||||||
let exclude_imports = snap.config.find_all_refs_exclude_imports();
|
let exclude_imports = snap.config.find_all_refs_exclude_imports();
|
||||||
let exclude_tests = snap.config.find_all_refs_exclude_tests();
|
let exclude_tests = snap.config.find_all_refs_exclude_tests();
|
||||||
|
|
||||||
let refs = match snap.analysis.find_all_refs(position, None)? {
|
let Some(refs) = snap.analysis.find_all_refs(position, None)? else {
|
||||||
None => return Ok(None),
|
return Ok(None);
|
||||||
Some(refs) => refs,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let include_declaration = params.context.include_declaration;
|
let include_declaration = params.context.include_declaration;
|
||||||
|
@ -1084,6 +1084,7 @@ pub(crate) fn handle_references(
|
||||||
})
|
})
|
||||||
.chain(decl)
|
.chain(decl)
|
||||||
})
|
})
|
||||||
|
.unique()
|
||||||
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -1802,10 +1803,10 @@ fn show_ref_command_link(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|res| res.references)
|
.flat_map(|res| res.references)
|
||||||
.flat_map(|(file_id, ranges)| {
|
.flat_map(|(file_id, ranges)| {
|
||||||
ranges.into_iter().filter_map(move |(range, _)| {
|
ranges.into_iter().map(move |(range, _)| FileRange { file_id, range })
|
||||||
to_proto::location(snap, FileRange { file_id, range }).ok()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
.unique()
|
||||||
|
.filter_map(|range| to_proto::location(snap, range).ok())
|
||||||
.collect();
|
.collect();
|
||||||
let title = to_proto::reference_title(locations.len());
|
let title = to_proto::reference_title(locations.len());
|
||||||
let command = to_proto::command::show_references(title, &uri, position, locations);
|
let command = to_proto::command::show_references(title, &uri, position, locations);
|
||||||
|
|
|
@ -904,15 +904,16 @@ pub(crate) fn goto_definition_response(
|
||||||
if snap.config.location_link() {
|
if snap.config.location_link() {
|
||||||
let links = targets
|
let links = targets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.unique_by(|nav| (nav.file_id, nav.full_range, nav.focus_range))
|
||||||
.map(|nav| location_link(snap, src, nav))
|
.map(|nav| location_link(snap, src, nav))
|
||||||
.collect::<Cancellable<Vec<_>>>()?;
|
.collect::<Cancellable<Vec<_>>>()?;
|
||||||
Ok(links.into())
|
Ok(links.into())
|
||||||
} else {
|
} else {
|
||||||
let locations = targets
|
let locations = targets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|nav| {
|
.map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
|
||||||
location(snap, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
|
.unique()
|
||||||
})
|
.map(|range| location(snap, range))
|
||||||
.collect::<Cancellable<Vec<_>>>()?;
|
.collect::<Cancellable<Vec<_>>>()?;
|
||||||
Ok(locations.into())
|
Ok(locations.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,6 +302,22 @@ pub fn slice_tails<T>(this: &[T]) -> impl Iterator<Item = &[T]> {
|
||||||
(0..this.len()).map(|i| &this[i..])
|
(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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue