Auto merge of #13933 - Veykril:assoc-search, r=Veykril

Refine search for const and function assoc items

This changes our searching behavior, before we always associated all usages and definitions of associated items with all implementations of a trait and the trait itself. Now, when searching for references of a an associated trait item, we still do the same and consider all implementations, but when searching for an associated item of an implementation we now only consider the uses of that specific implementations associated item.

This does not affect associated type aliases as we unfortunately are missing information in the IDE layer here still.
This commit is contained in:
bors 2023-01-11 16:21:11 +00:00
commit 09aceea36d
7 changed files with 319 additions and 36 deletions

View file

@ -712,17 +712,17 @@ fn lookup_impl_assoc_item_for_trait_ref(
let table = InferenceTable::new(db, env);
let impl_data = find_matching_impl(impls, table, trait_ref)?;
impl_data.items.iter().find_map(|it| match it {
impl_data.items.iter().find_map(|&it| match it {
AssocItemId::FunctionId(f) => {
(db.function_data(*f).name == *name).then_some(AssocItemId::FunctionId(*f))
(db.function_data(f).name == *name).then_some(AssocItemId::FunctionId(f))
}
AssocItemId::ConstId(c) => db
.const_data(*c)
.const_data(c)
.name
.as_ref()
.map(|n| *n == *name)
.and_then(|result| if result { Some(AssocItemId::ConstId(*c)) } else { None }),
_ => None,
.map(|n| n == name)
.and_then(|result| if result { Some(AssocItemId::ConstId(c)) } else { None }),
AssocItemId::TypeAliasId(_) => None,
})
}

View file

@ -2129,7 +2129,7 @@ pub enum AssocItem {
Const(Const),
TypeAlias(TypeAlias),
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum AssocItemContainer {
Trait(Trait),
Impl(Impl),

View file

@ -504,7 +504,7 @@ impl SourceAnalyzer {
AssocItemId::ConstId(const_id) => {
self.resolve_impl_const_or_trait_def(db, const_id, subs).into()
}
_ => assoc,
assoc => assoc,
};
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
@ -517,7 +517,13 @@ impl SourceAnalyzer {
prefer_value_ns = true;
} else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
let pat_id = self.pat_id(&path_pat.into())?;
if let Some((assoc, _)) = infer.assoc_resolutions_for_pat(pat_id) {
if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) {
let assoc = match assoc {
AssocItemId::ConstId(const_id) => {
self.resolve_impl_const_or_trait_def(db, const_id, subs).into()
}
assoc => assoc,
};
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
}
if let Some(VariantId::EnumVariantId(variant)) =

View file

@ -7,7 +7,9 @@
use std::{mem, sync::Arc};
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility};
use hir::{
AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility,
};
use memchr::memmem::Finder;
use once_cell::unsync::Lazy;
use parser::SyntaxKind;
@ -311,15 +313,15 @@ impl Definition {
pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> {
FindUsages {
def: self,
assoc_item_container: self.as_assoc_item(sema.db).map(|a| a.container(sema.db)),
sema,
scope: None,
include_self_kw_refs: None,
local_repr: match self {
Definition::Local(local) => Some(local.representative(sema.db)),
_ => None,
},
def: self,
trait_assoc_def: as_trait_assoc_def(sema.db, self),
sema,
scope: None,
include_self_kw_refs: None,
search_self_mod: false,
}
}
@ -328,8 +330,7 @@ impl Definition {
#[derive(Clone)]
pub struct FindUsages<'a> {
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>,
assoc_item_container: Option<hir::AssocItemContainer>,
sema: &'a Semantics<'a, RootDatabase>,
scope: Option<SearchScope>,
include_self_kw_refs: Option<hir::Type>,
@ -380,7 +381,9 @@ impl<'a> FindUsages<'a> {
let sema = self.sema;
let search_scope = {
let base = self.trait_assoc_def.unwrap_or(self.def).search_scope(sema.db);
// FIXME: Is the trait scope needed for trait impl assoc items?
let base =
as_trait_assoc_def(sema.db, self.def).unwrap_or(self.def).search_scope(sema.db);
match &self.scope {
None => base,
Some(scope) => base.intersection(scope),
@ -651,13 +654,26 @@ impl<'a> FindUsages<'a> {
sink(file_id, reference)
}
Some(NameRefClass::Definition(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,
} =>
if self.def == def
// is our def a trait assoc item? then we want to find everything
|| matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_)))
&& convert_to_def_in_trait(self.sema.db, def) == self.def =>
{
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let reference = FileReference {
range,
name: ast::NameLike::NameRef(name_ref.clone()),
category: ReferenceCategory::new(&def, name_ref),
};
sink(file_id, reference)
}
// FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions
// so we always resolve all assoc type aliases to both their trait def and impl defs
Some(NameRefClass::Definition(def))
if self.assoc_item_container.is_some()
&& matches!(self.def, Definition::TypeAlias(_))
&& convert_to_def_in_trait(self.sema.db, def)
== convert_to_def_in_trait(self.sema.db, self.def) =>
{
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let reference = FileReference {
@ -748,12 +764,14 @@ impl<'a> FindUsages<'a> {
false
}
Some(NameClass::Definition(def)) if def != self.def => {
// 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!(
self.trait_assoc_def,
Some(trait_assoc_def)
if convert_to_def_in_trait(self.sema.db, def) == trait_assoc_def
) {
// only when looking for trait assoc items, we want to find other assoc items
if !matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_)))
// FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions
// so we always resolve all assoc type aliases to both their trait def and impl defs
&& !(matches!(self.def, Definition::TypeAlias(_))
&& convert_to_def_in_trait(self.sema.db, def)
== convert_to_def_in_trait(self.sema.db, self.def))
{
return false;
}
let FileRange { file_id, range } = self.sema.original_range(name.syntax());

View file

@ -1356,7 +1356,6 @@ fn main() {
r#"
trait Trait {
fn func(self) {}
//^^^^
}
impl Trait for () {
@ -1376,7 +1375,6 @@ fn main() {
r#"
trait Trait {
fn func(self) {}
//^^^^
}
impl Trait for () {

View file

@ -1636,4 +1636,265 @@ pub fn deri$0ve(_stream: TokenStream) -> TokenStream {}
"#]],
);
}
#[test]
fn assoc_items_trait_def() {
check(
r#"
trait Trait {
const CONST$0: usize;
}
impl Trait for () {
const CONST: usize = 0;
}
impl Trait for ((),) {
const CONST: usize = 0;
}
fn f<T: Trait>() {
let _ = <()>::CONST;
let _ = T::CONST;
}
"#,
expect![[r#"
CONST Const FileId(0) 18..37 24..29
FileId(0) 71..76
FileId(0) 125..130
FileId(0) 183..188
FileId(0) 206..211
"#]],
);
check(
r#"
trait Trait {
type TypeAlias$0;
}
impl Trait for () {
type TypeAlias = ();
}
impl Trait for ((),) {
type TypeAlias = ();
}
fn f<T: Trait>() {
let _: <() as Trait>::TypeAlias;
let _: T::TypeAlias;
}
"#,
expect![[r#"
TypeAlias TypeAlias FileId(0) 18..33 23..32
FileId(0) 66..75
FileId(0) 117..126
FileId(0) 181..190
FileId(0) 207..216
"#]],
);
check(
r#"
trait Trait {
fn function$0() {}
}
impl Trait for () {
fn function() {}
}
impl Trait for ((),) {
fn function() {}
}
fn f<T: Trait>() {
let _ = <()>::function;
let _ = T::function;
}
"#,
expect![[r#"
function Function FileId(0) 18..34 21..29
FileId(0) 65..73
FileId(0) 112..120
FileId(0) 166..174
FileId(0) 192..200
"#]],
);
}
#[test]
fn assoc_items_trait_impl_def() {
check(
r#"
trait Trait {
const CONST: usize;
}
impl Trait for () {
const CONST$0: usize = 0;
}
impl Trait for ((),) {
const CONST: usize = 0;
}
fn f<T: Trait>() {
let _ = <()>::CONST;
let _ = T::CONST;
}
"#,
expect![[r#"
CONST Const FileId(0) 65..88 71..76
FileId(0) 183..188
"#]],
);
check(
r#"
trait Trait {
type TypeAlias;
}
impl Trait for () {
type TypeAlias$0 = ();
}
impl Trait for ((),) {
type TypeAlias = ();
}
fn f<T: Trait>() {
let _: <() as Trait>::TypeAlias;
let _: T::TypeAlias;
}
"#,
expect![[r#"
TypeAlias TypeAlias FileId(0) 61..81 66..75
FileId(0) 23..32
FileId(0) 117..126
FileId(0) 181..190
FileId(0) 207..216
"#]],
);
check(
r#"
trait Trait {
fn function() {}
}
impl Trait for () {
fn function$0() {}
}
impl Trait for ((),) {
fn function() {}
}
fn f<T: Trait>() {
let _ = <()>::function;
let _ = T::function;
}
"#,
expect![[r#"
function Function FileId(0) 62..78 65..73
FileId(0) 166..174
"#]],
);
}
#[test]
fn assoc_items_ref() {
check(
r#"
trait Trait {
const CONST: usize;
}
impl Trait for () {
const CONST: usize = 0;
}
impl Trait for ((),) {
const CONST: usize = 0;
}
fn f<T: Trait>() {
let _ = <()>::CONST$0;
let _ = T::CONST;
}
"#,
expect![[r#"
CONST Const FileId(0) 65..88 71..76
FileId(0) 183..188
"#]],
);
check(
r#"
trait Trait {
type TypeAlias;
}
impl Trait for () {
type TypeAlias = ();
}
impl Trait for ((),) {
type TypeAlias = ();
}
fn f<T: Trait>() {
let _: <() as Trait>::TypeAlias$0;
let _: T::TypeAlias;
}
"#,
expect![[r#"
TypeAlias TypeAlias FileId(0) 18..33 23..32
FileId(0) 66..75
FileId(0) 117..126
FileId(0) 181..190
FileId(0) 207..216
"#]],
);
check(
r#"
trait Trait {
fn function() {}
}
impl Trait for () {
fn function() {}
}
impl Trait for ((),) {
fn function() {}
}
fn f<T: Trait>() {
let _ = <()>::function$0;
let _ = T::function;
}
"#,
expect![[r#"
function Function FileId(0) 62..78 65..73
FileId(0) 166..174
"#]],
);
}
}

View file

@ -1044,7 +1044,7 @@ impl Config {
&self.data.cargo_extraEnv
}
pub fn check_on_save_extra_env(&self) -> FxHashMap<String, String> {
pub fn check_extra_env(&self) -> FxHashMap<String, String> {
let mut extra_env = self.data.cargo_extraEnv.clone();
extra_env.extend(self.data.check_extraEnv.clone());
extra_env
@ -1165,7 +1165,7 @@ impl Config {
FlycheckConfig::CustomCommand {
command,
args,
extra_env: self.check_on_save_extra_env(),
extra_env: self.check_extra_env(),
invocation_strategy: match self.data.check_invocationStrategy {
InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
InvocationStrategy::PerWorkspace => {
@ -1210,7 +1210,7 @@ impl Config {
CargoFeaturesDef::Selected(it) => it,
},
extra_args: self.data.check_extraArgs.clone(),
extra_env: self.check_on_save_extra_env(),
extra_env: self.check_extra_env(),
ansi_color_output: self.color_diagnostic_output(),
},
}