mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
fix: Fix search for associated trait items being inconsistent
This commit is contained in:
parent
84544134f6
commit
bb4bfae422
4 changed files with 125 additions and 43 deletions
|
@ -7,16 +7,14 @@
|
||||||
use std::{convert::TryInto, mem, sync::Arc};
|
use std::{convert::TryInto, mem, sync::Arc};
|
||||||
|
|
||||||
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
|
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
|
||||||
use hir::{
|
use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility};
|
||||||
AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility,
|
|
||||||
};
|
|
||||||
use once_cell::unsync::Lazy;
|
use once_cell::unsync::Lazy;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
|
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
defs::{Definition, NameClass, NameRefClass},
|
defs::{Definition, NameClass, NameRefClass},
|
||||||
traits::convert_to_def_in_trait,
|
traits::{as_trait_assoc_def, convert_to_def_in_trait},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -314,6 +312,7 @@ impl Definition {
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
def: self,
|
def: self,
|
||||||
|
trait_assoc_def: as_trait_assoc_def(sema.db, self),
|
||||||
sema,
|
sema,
|
||||||
scope: None,
|
scope: None,
|
||||||
include_self_kw_refs: None,
|
include_self_kw_refs: None,
|
||||||
|
@ -325,6 +324,8 @@ impl Definition {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FindUsages<'a> {
|
pub struct FindUsages<'a> {
|
||||||
def: Definition,
|
def: Definition,
|
||||||
|
/// If def is an assoc item from a trait or trait impl, this is the corresponding item of the trait definition
|
||||||
|
trait_assoc_def: Option<Definition>,
|
||||||
sema: &'a Semantics<'a, RootDatabase>,
|
sema: &'a Semantics<'a, RootDatabase>,
|
||||||
scope: Option<SearchScope>,
|
scope: Option<SearchScope>,
|
||||||
include_self_kw_refs: Option<hir::Type>,
|
include_self_kw_refs: Option<hir::Type>,
|
||||||
|
@ -375,7 +376,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let sema = self.sema;
|
let sema = self.sema;
|
||||||
|
|
||||||
let search_scope = {
|
let search_scope = {
|
||||||
let base = self.def.search_scope(sema.db);
|
let base = self.trait_assoc_def.unwrap_or(self.def).search_scope(sema.db);
|
||||||
match &self.scope {
|
match &self.scope {
|
||||||
None => base,
|
None => base,
|
||||||
Some(scope) => base.intersection(scope),
|
Some(scope) => base.intersection(scope),
|
||||||
|
@ -621,7 +622,13 @@ impl<'a> FindUsages<'a> {
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
Some(NameRefClass::Definition(def))
|
Some(NameRefClass::Definition(def))
|
||||||
if convert_to_def_in_trait(self.sema.db, def) == self.def =>
|
if match self.trait_assoc_def {
|
||||||
|
Some(trait_assoc_def) => {
|
||||||
|
// we have a trait assoc item, so force resolve all assoc items to their trait version
|
||||||
|
convert_to_def_in_trait(self.sema.db, def) == trait_assoc_def
|
||||||
|
}
|
||||||
|
None => self.def == def,
|
||||||
|
} =>
|
||||||
{
|
{
|
||||||
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
|
@ -711,30 +718,22 @@ impl<'a> FindUsages<'a> {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
// Resolve trait impl function definitions to the trait definition's version if self.def is the trait definition's
|
|
||||||
Some(NameClass::Definition(def)) if def != self.def => {
|
Some(NameClass::Definition(def)) if def != self.def => {
|
||||||
/* poor man's try block */
|
// if the def we are looking for is a trait (impl) assoc item, we'll have to resolve the items to trait definition assoc item
|
||||||
(|| {
|
if !matches!(
|
||||||
let this_trait = self
|
self.trait_assoc_def,
|
||||||
.def
|
Some(trait_assoc_def)
|
||||||
.as_assoc_item(self.sema.db)?
|
if convert_to_def_in_trait(self.sema.db, def) == trait_assoc_def
|
||||||
.containing_trait_or_trait_impl(self.sema.db)?;
|
) {
|
||||||
let trait_ = def
|
return false;
|
||||||
.as_assoc_item(self.sema.db)?
|
}
|
||||||
.containing_trait_or_trait_impl(self.sema.db)?;
|
let FileRange { file_id, range } = self.sema.original_range(name.syntax());
|
||||||
(trait_ == this_trait && self.def.name(self.sema.db) == def.name(self.sema.db))
|
let reference = FileReference {
|
||||||
.then(|| {
|
range,
|
||||||
let FileRange { file_id, range } =
|
name: ast::NameLike::Name(name.clone()),
|
||||||
self.sema.original_range(name.syntax());
|
category: None,
|
||||||
let reference = FileReference {
|
};
|
||||||
range,
|
sink(file_id, reference)
|
||||||
name: ast::NameLike::Name(name.clone()),
|
|
||||||
category: None,
|
|
||||||
};
|
|
||||||
sink(file_id, reference)
|
|
||||||
})
|
|
||||||
})()
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,26 +71,44 @@ pub fn get_missing_assoc_items(
|
||||||
|
|
||||||
/// Converts associated trait impl items to their trait definition counterpart
|
/// Converts associated trait impl items to their trait definition counterpart
|
||||||
pub(crate) fn convert_to_def_in_trait(db: &dyn HirDatabase, def: Definition) -> Definition {
|
pub(crate) fn convert_to_def_in_trait(db: &dyn HirDatabase, def: Definition) -> Definition {
|
||||||
use hir::AssocItem::*;
|
|
||||||
(|| {
|
(|| {
|
||||||
let assoc = def.as_assoc_item(db)?;
|
let assoc = def.as_assoc_item(db)?;
|
||||||
let trait_ = assoc.containing_trait_impl(db)?;
|
let trait_ = assoc.containing_trait_impl(db)?;
|
||||||
let name = match assoc {
|
assoc_item_of_trait(db, assoc, trait_)
|
||||||
Function(it) => it.name(db),
|
|
||||||
Const(it) => it.name(db)?,
|
|
||||||
TypeAlias(it) => it.name(db),
|
|
||||||
};
|
|
||||||
let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
|
|
||||||
(Function(trait_func), Function(_)) => trait_func.name(db) == name,
|
|
||||||
(Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
|
|
||||||
(TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
|
|
||||||
_ => false,
|
|
||||||
})?;
|
|
||||||
Some(Definition::from(item))
|
|
||||||
})()
|
})()
|
||||||
.unwrap_or(def)
|
.unwrap_or(def)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this is an trait (impl) assoc item, returns the assoc item of the corresponding trait definition.
|
||||||
|
pub(crate) fn as_trait_assoc_def(db: &dyn HirDatabase, def: Definition) -> Option<Definition> {
|
||||||
|
let assoc = def.as_assoc_item(db)?;
|
||||||
|
let trait_ = match assoc.container(db) {
|
||||||
|
hir::AssocItemContainer::Trait(_) => return Some(def),
|
||||||
|
hir::AssocItemContainer::Impl(i) => i.trait_(db),
|
||||||
|
}?;
|
||||||
|
assoc_item_of_trait(db, assoc, trait_)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assoc_item_of_trait(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
assoc: hir::AssocItem,
|
||||||
|
trait_: hir::Trait,
|
||||||
|
) -> Option<Definition> {
|
||||||
|
use hir::AssocItem::*;
|
||||||
|
let name = match assoc {
|
||||||
|
Function(it) => it.name(db),
|
||||||
|
Const(it) => it.name(db)?,
|
||||||
|
TypeAlias(it) => it.name(db),
|
||||||
|
};
|
||||||
|
let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
|
||||||
|
(Function(trait_func), Function(_)) => trait_func.name(db) == name,
|
||||||
|
(Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
|
||||||
|
(TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
|
||||||
|
_ => false,
|
||||||
|
})?;
|
||||||
|
Some(Definition::from(item))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use base_db::{fixture::ChangeFixture, FilePosition};
|
use base_db::{fixture::ChangeFixture, FilePosition};
|
||||||
|
|
|
@ -1307,6 +1307,70 @@ fn foo((
|
||||||
//^^^read
|
//^^^read
|
||||||
let foo;
|
let foo;
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hl_trait_impl_methods() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait Trait {
|
||||||
|
fn func$0(self) {}
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for () {
|
||||||
|
fn func(self) {}
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
<()>::func(());
|
||||||
|
//^^^^
|
||||||
|
().func();
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait Trait {
|
||||||
|
fn func(self) {}
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for () {
|
||||||
|
fn func$0(self) {}
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
<()>::func(());
|
||||||
|
//^^^^
|
||||||
|
().func();
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait Trait {
|
||||||
|
fn func(self) {}
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trait for () {
|
||||||
|
fn func(self) {}
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
<()>::func(());
|
||||||
|
//^^^^
|
||||||
|
().func$0();
|
||||||
|
//^^^^
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ pub(crate) fn find_all_refs(
|
||||||
});
|
});
|
||||||
let mut usages =
|
let mut usages =
|
||||||
def.usages(sema).set_scope(search_scope.clone()).include_self_refs().all();
|
def.usages(sema).set_scope(search_scope.clone()).include_self_refs().all();
|
||||||
|
|
||||||
if literal_search {
|
if literal_search {
|
||||||
retain_adt_literal_usages(&mut usages, def, sema);
|
retain_adt_literal_usages(&mut usages, def, sema);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +106,7 @@ pub(crate) fn find_all_refs(
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let search = make_searcher(false);
|
let search = make_searcher(false);
|
||||||
Some(find_defs(sema, &syntax, position.offset)?.into_iter().map(search).collect())
|
Some(find_defs(sema, &syntax, position.offset)?.map(search).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue