Fix generic type substitution in impl trait with assoc const

This commit is contained in:
Petr Nevyhoštěný 2022-01-07 16:41:39 +01:00
parent d9b3242bcd
commit a710b87b1b
2 changed files with 71 additions and 14 deletions

View File

@ -1245,6 +1245,62 @@ impl Behavior<u32> for Impl {
fn reproduce(&self, foo: <u32 as bar::Types2>::Bar) {
${0:todo!()}
}
}"#,
);
}
#[test]
fn test_transform_path_in_path_expr() {
check_assist(
add_missing_default_members,
r#"
pub trait Const {
const FOO: u32;
}
pub trait Trait<T: Const> {
fn foo() -> bool {
match T::FOO {
0 => true,
_ => false,
}
}
}
impl Const for u32 {
const FOO: u32 = 1;
}
struct Impl;
impl Trait<u32> for Impl { $0 }"#,
r#"
pub trait Const {
const FOO: u32;
}
pub trait Trait<T: Const> {
fn foo() -> bool {
match T::FOO {
0 => true,
_ => false,
}
}
}
impl Const for u32 {
const FOO: u32 = 1;
}
struct Impl;
impl Trait<u32> for Impl {
$0fn foo() -> bool {
match <u32 as Const>::FOO {
0 => true,
_ => false,
}
}
}"#,
);
}

View File

@ -154,14 +154,14 @@ impl<'a> Ctx<'a> {
let parent = path.syntax().parent()?;
if let Some(parent) = ast::Path::cast(parent.clone()) {
// Path inside path means that there is an associated
// type on the type parameter. It is necessary to fully
// qualify the type with `as Trait`. Even though it
// might be unnecessary if `subst` is generic type,
// always fully qualifying the path is safer because of
// potential clash of associated types from multiple
// traits
// type/constant on the type parameter. It is necessary
// to fully qualify the type with `as Trait`. Even
// though it might be unnecessary if `subst` is generic
// type, always fully qualifying the path is safer
// because of potential clash of associated types from
// multiple traits
let trait_ref = find_trait_for_assoc_type(
let trait_ref = find_trait_for_assoc_item(
self.source_scope,
tp,
parent.segment()?.name_ref()?,
@ -252,24 +252,25 @@ fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<
Some(result)
}
fn find_trait_for_assoc_type(
fn find_trait_for_assoc_item(
scope: &SemanticsScope,
type_param: hir::TypeParam,
assoc_type: ast::NameRef,
assoc_item: ast::NameRef,
) -> Option<hir::Trait> {
let db = scope.db;
let trait_bounds = type_param.trait_bounds(db);
let assoc_type_name = assoc_type.text();
let assoc_item_name = assoc_item.text();
for trait_ in trait_bounds {
let type_aliases = trait_.items(db).into_iter().filter_map(|item| match item {
hir::AssocItem::TypeAlias(ta) => Some(ta),
let names = trait_.items(db).into_iter().filter_map(|item| match item {
hir::AssocItem::TypeAlias(ta) => Some(ta.name(db)),
hir::AssocItem::Const(cst) => cst.name(db),
_ => None,
});
for type_alias in type_aliases {
if assoc_type_name.as_str() == type_alias.name(db).as_text()?.as_str() {
for name in names {
if assoc_item_name.as_str() == name.as_text()?.as_str() {
// It is fine to return the first match because in case of
// multiple possibilities, the exact trait must be disambiguated
// in the definition of trait being implemented, so this search