Skip to content

Commit

Permalink
Deduplicate references to macro argument
Browse files Browse the repository at this point in the history
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 produces a cartesian
product of references inside the macro times references outside the
macro. Since the above deduplication only applies to the references
within a 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.

We can't use unique() because we don't want to drop references without
declaration (though I dont' have an example).

I discovered this working with the "bitflags" macro from the crate
of the same name.

Fixes #16357
  • Loading branch information
krobelus committed Jan 17, 2024
1 parent 0333646 commit 02d551c
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 2 deletions.
64 changes: 62 additions & 2 deletions crates/ide/src/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! at the index that the match starts at and its tree parent is
//! resolved to the search element definition, we get a reference.
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

use hir::{DescendPreference, PathResolution, Semantics};
use ide_db::{
Expand All @@ -20,6 +20,8 @@ use ide_db::{
};
use itertools::Itertools;
use nohash_hasher::IntMap;
#[allow(unused_imports)]
use stdx::IsNoneOr;
use syntax::{
ast::{self, HasName},
match_ast, AstNode,
Expand Down Expand Up @@ -118,7 +120,17 @@ pub(crate) fn find_all_refs(
}
None => {
let search = make_searcher(false);
Some(find_defs(sema, &syntax, position.offset)?.into_iter().map(search).collect())
let mut defs: Vec<ReferenceSearchResult> =
find_defs(sema, &syntax, position.offset)?.into_iter().map(search).collect();
if defs.iter().filter(|decl| decl.declaration.is_some()).take(2).count() > 1 {
let mut seen_declarations = HashSet::new();
defs.retain(|res| {
res.declaration
.as_ref()
.is_none_or(|decl| seen_declarations.insert(decl.nav.clone()))
});
}
Some(defs)
}
}
}
Expand Down Expand Up @@ -2122,4 +2134,52 @@ fn test() {
"#]],
);
}

#[test]
fn multiple_definitions_inside_macro() {
check(
r#"
macro_rules! use_name {
($name:ident) => {
let $name = ();
let $name = ();
};
}
fn test() {
let name = ();
use_name!(name$0);
let _ = name;
}
"#,
expect![[r#"
name Local FileId(0) 149..153 149..153
FileId(0) 168..172 Read
"#]],
);
}

#[test]
fn multiple_references_inside_macro() {
check(
r#"
macro_rules! use_name {
($name:ident) => {
let _ = $name;
let _ = $name;
let _ = $name;
};
}
fn test() {
let name = ();
use_name!(name$0);
}
"#,
expect![[r#"
name Local FileId(0) 145..149 145..149
FileId(0) 170..174 Read
"#]],
);
}
}
16 changes: 16 additions & 0 deletions crates/stdx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,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::*;
Expand Down

0 comments on commit 02d551c

Please sign in to comment.