Skip to content
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

fix: auto-completion for associated type constants #19118

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
66 changes: 57 additions & 9 deletions crates/hir-ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use chalk_ir::{
use either::Either;
use hir_def::{
builtin_type::BuiltinType,
data::{adt::StructKind, TraitFlags},
data::{adt::StructKind, TraitFlags, TypeAliasData},
expander::Expander,
generics::{
GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
Expand Down Expand Up @@ -1578,22 +1578,70 @@ pub(crate) fn type_for_type_alias_with_diagnostics_query(
t: TypeAliasId,
) -> (Binders<Ty>, Diagnostics) {
let generics = generics(db.upcast(), t.into());
let type_alias_data = find_effective_type_alias_data(db, &generics, t);

let resolver = t.resolver(db.upcast());
let type_alias_data = db.type_alias_data(t);
let mut ctx = TyLoweringContext::new(db, &resolver, &type_alias_data.types_map, t.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let inner = if type_alias_data.is_extern {
TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner)
} else {
type_alias_data
.type_ref
.map(|type_ref| ctx.lower_ty(type_ref))
.unwrap_or_else(|| TyKind::Error.intern(Interner))

let inner = match (type_alias_data.is_extern, type_alias_data.type_ref.as_ref()) {
(true, _) => TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner),
(false, Some(type_ref)) => ctx.lower_ty(*type_ref),
(false, None) => TyKind::Error.intern(Interner),
};

(make_binders(db, &generics, inner), create_diagnostics(ctx.diagnostics))
}

// Attempt to find and type alias data from the trait implementation.
// Falls back to the original type alias if no suitable implementation is found.
fn find_effective_type_alias_data(
db: &dyn HirDatabase,
generics: &Generics,
t: TypeAliasId,
) -> Arc<TypeAliasData> {
let original_data = db.type_alias_data(t);

let parent = match generics.parent_generics() {
Some(parent) => parent,
None => return original_data,
};

let trait_id = match parent.def() {
GenericDefId::TraitId(id) => id,
_ => return original_data,
};

let krate = trait_id.lookup(db.upcast()).container.krate();
let impls = db.trait_impls_in_crate(krate);
let trait_type_alias_name = original_data.name.clone();

let impl_type_alias_id = impls.all_impls().find_map(|impl_id| {
let impl_data = db.impl_data(impl_id);
impl_data.items.iter().find_map(|(name, item)| {
if name != &trait_type_alias_name {
return None;
}
match item {
AssocItemId::TypeAliasId(id) => Some(*id),
_ => None,
}
})
});

let Some(impl_type_alias_id) = impl_type_alias_id else {
return original_data;
};

let impl_data = db.type_alias_data(impl_type_alias_id);
if impl_data.name.as_str() == "Assoc" {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sure there is a better way to do this check but was having a hard time figuring it out. Open to any suggestions but wanted to get eyes on this change to make sure this was the ideal way to solve

original_data
} else {
impl_data
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TyDefId {
BuiltinType(BuiltinType),
Expand Down
27 changes: 27 additions & 0 deletions crates/ide-completion/src/tests/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2036,3 +2036,30 @@ fn foo() {
"#]],
);
}

#[test]
fn completion_associated_type() {
check(
r#"
pub trait FieldPaths {
type T;
}
pub struct Person;
pub struct PersonPaths;
impl FieldPaths for Person {
type T = PersonPaths;
}
impl PersonPaths {
pub const NAME: &'static str = "name";
pub fn get_default() -> String { String::new() }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String isn't available in our tests (that's why you have {unknown}), you can use primitive types like i32 or &str.

}
fn main() {
let x = <Person as FieldPaths>::T::$0
}
"#,
expect![[r#"
ct NAME pub const NAME: &'static str
fn get_default() fn() -> {unknown}
"#]],
);
}