Skip to content

fix: Do reference search on all downmapped tokens with the same kind only #12001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 50 additions & 31 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.descend_into_macros(token)
}

/// Descend the token into macrocalls to all its mapped counterparts.
///
/// Returns the original non descended token if none of the mapped counterparts have the same syntax kind.
pub fn descend_into_macros_with_same_kind(
&self,
token: SyntaxToken,
) -> SmallVec<[SyntaxToken; 1]> {
self.imp.descend_into_macros_with_same_kind(token)
}

/// Maps a node down by mapping its first and last token down.
pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
self.imp.descend_node_into_attributes(node)
Expand Down Expand Up @@ -599,25 +609,19 @@ impl<'db> SemanticsImpl<'db> {
};

if first == last {
self.descend_into_macros_impl(
first,
&mut |InFile { value, .. }| {
if let Some(node) = value.ancestors().find_map(N::cast) {
res.push(node)
}
},
false,
);
self.descend_into_macros_impl(first, &mut |InFile { value, .. }| {
if let Some(node) = value.ancestors().find_map(N::cast) {
res.push(node)
}
false
});
} else {
// Descend first and last token, then zip them to look for the node they belong to
let mut scratch: SmallVec<[_; 1]> = smallvec![];
self.descend_into_macros_impl(
first,
&mut |token| {
scratch.push(token);
},
false,
);
self.descend_into_macros_impl(first, &mut |token| {
scratch.push(token);
false
});

let mut scratch = scratch.into_iter();
self.descend_into_macros_impl(
Expand All @@ -638,30 +642,50 @@ impl<'db> SemanticsImpl<'db> {
}
}
}
false
},
false,
);
}
res
}

fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
let mut res = smallvec![];
self.descend_into_macros_impl(token, &mut |InFile { value, .. }| res.push(value), false);
self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
res.push(value);
false
});
res
}

fn descend_into_macros_with_same_kind(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
let kind = token.kind();
let mut res = smallvec![];
self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
if value.kind() == kind {
res.push(value);
}
false
});
if res.is_empty() {
res.push(token);
}
res
}

fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
let mut res = token.clone();
self.descend_into_macros_impl(token, &mut |InFile { value, .. }| res = value, true);
self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
res = value;
true
});
res
}

fn descend_into_macros_impl(
&self,
token: SyntaxToken,
f: &mut dyn FnMut(InFile<SyntaxToken>),
single: bool,
f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool,
) {
let _p = profile::span("descend_into_macros");
let parent = match token.parent() {
Expand All @@ -688,16 +712,11 @@ impl<'db> SemanticsImpl<'db> {
self.cache(value, file_id);
}

let mut mapped_tokens =
expansion_info.map_token_down(self.db.upcast(), item, token)?;

let mapped_tokens = expansion_info.map_token_down(self.db.upcast(), item, token)?;
let len = stack.len();

// requeue the tokens we got from mapping our current token down
if single {
stack.extend(mapped_tokens.next());
} else {
stack.extend(mapped_tokens);
}
stack.extend(mapped_tokens);
// if the length changed we have found a mapping for the token
(stack.len() != len).then(|| ())
};
Expand Down Expand Up @@ -787,8 +806,8 @@ impl<'db> SemanticsImpl<'db> {
})()
.is_none();

if was_not_remapped {
f(token)
if was_not_remapped && f(token) {
break;
}
}
}
Expand Down
72 changes: 45 additions & 27 deletions crates/ide/src/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use rustc_hash::FxHashMap;
use syntax::{
algo::find_node_at_offset,
ast::{self, HasName},
match_ast, AstNode, SyntaxNode, TextRange, TextSize, T,
match_ast, AstNode,
SyntaxKind::*,
SyntaxNode, TextRange, TextSize, T,
};

use crate::{FilePosition, NavigationTarget, TryToNav};
Expand Down Expand Up @@ -104,7 +106,7 @@ pub(crate) fn find_all_refs(
}
None => {
let search = make_searcher(false);
Some(find_defs(sema, &syntax, position.offset).into_iter().map(search).collect())
Some(find_defs(sema, &syntax, position.offset)?.into_iter().map(search).collect())
}
}
}
Expand All @@ -113,31 +115,47 @@ pub(crate) fn find_defs<'a>(
sema: &'a Semantics<RootDatabase>,
syntax: &SyntaxNode,
offset: TextSize,
) -> impl Iterator<Item = Definition> + 'a {
sema.find_nodes_at_offset_with_descend(syntax, offset).filter_map(move |name_like| {
let def = match name_like {
ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
NameRefClass::Definition(def) => def,
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
Definition::Local(local_ref)
}
},
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
Definition::Local(local_def)
}
},
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
.and_then(|class| match class {
NameRefClass::Definition(it) => Some(it),
_ => None,
})
.or_else(|| {
NameClass::classify_lifetime(sema, &lifetime).and_then(NameClass::defined)
})?,
};
Some(def)
) -> Option<impl Iterator<Item = Definition> + 'a> {
let token = syntax.token_at_offset(offset).find(|t| {
matches!(
t.kind(),
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self]
)
});
token.map(|token| {
sema.descend_into_macros_with_same_kind(token)
.into_iter()
.filter_map(|it| ast::NameLike::cast(it.parent()?))
.filter_map(move |name_like| {
let def = match name_like {
ast::NameLike::NameRef(name_ref) => {
match NameRefClass::classify(sema, &name_ref)? {
NameRefClass::Definition(def) => def,
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
Definition::Local(local_ref)
}
}
}
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
Definition::Local(local_def)
}
},
ast::NameLike::Lifetime(lifetime) => {
NameRefClass::classify_lifetime(sema, &lifetime)
.and_then(|class| match class {
NameRefClass::Definition(it) => Some(it),
_ => None,
})
.or_else(|| {
NameClass::classify_lifetime(sema, &lifetime)
.and_then(NameClass::defined)
})?
}
};
Some(def)
})
})
}

Expand Down
6 changes: 5 additions & 1 deletion crates/ide/src/runnables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ fn find_related_tests(
search_scope: Option<SearchScope>,
tests: &mut FxHashSet<Runnable>,
) {
let defs = references::find_defs(sema, syntax, position.offset);
// FIXME: why is this using references::find_defs, this should use ide_db::search
let defs = match references::find_defs(sema, syntax, position.offset) {
Some(defs) => defs,
None => return,
};
for def in defs {
let defs = def
.usages(sema)
Expand Down