8283: Resolve associated types r=flodiebold a=Veykril

Prior we were only resolving paths until the first type was found, then discarding the result if the path wasn't fully consumed. That of course causes associated types to not resolve. Fixes #5003

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-04-01 21:11:06 +00:00 committed by GitHub
commit 5ef0c7a213
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 7 deletions

View File

@ -76,9 +76,11 @@ impl PathResolution {
pub fn assoc_type_shorthand_candidates<R>(
&self,
db: &dyn HirDatabase,
mut cb: impl FnMut(TypeAlias) -> Option<R>,
mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
) -> Option<R> {
associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into()))
associated_type_shorthand_candidates(db, self.in_type_ns()?, |name, _, id| {
cb(name, id.into())
})
}
}

View File

@ -20,7 +20,7 @@ use hir_def::{
use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
use hir_ty::{
diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
InferenceResult, Substitution,
InferenceResult, Substitution, TyLoweringContext,
};
use syntax::{
ast::{self, AstNode},
@ -466,7 +466,21 @@ fn resolve_hir_path_(
prefer_value_ns: bool,
) -> Option<PathResolution> {
let types = || {
resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty {
let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => {
let (_, res) = TyLoweringContext::new(db, resolver).lower_ty_ext(type_ref);
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
let (ty, remaining) =
resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
match remaining {
Some(remaining) if remaining > 1 => None,
_ => Some((ty, path.segments().get(1))),
}
}
}?;
let res = match ty {
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
@ -476,7 +490,17 @@ fn resolve_hir_path_(
TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
})
};
match unresolved {
Some(unresolved) => res
.assoc_type_shorthand_candidates(db, |name, alias| {
(name == unresolved.name).then(|| alias)
})
.map(TypeAlias::from)
.map(Into::into)
.map(PathResolution::Def),
None => Some(res),
}
};
let body_owner = resolver.body_owner();

View File

@ -146,7 +146,7 @@ impl<'a> TyLoweringContext<'a> {
self.lower_ty_ext(type_ref).0
}
fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
let mut res = None;
let ty = match type_ref {
TypeRef::Never => TyKind::Never.intern(&Interner),

View File

@ -3834,4 +3834,67 @@ fn foo() {}
"#]],
);
}
#[test]
fn hover_generic_assoc() {
check(
r#"
fn foo<T: A>() where T::Assoc$0: {}
trait A {
type Assoc;
}"#,
expect![[r#"
*Assoc*
```rust
test
```
```rust
type Assoc
```
"#]],
);
check(
r#"
fn foo<T: A>() {
let _: <T>::Assoc$0;
}
trait A {
type Assoc;
}"#,
expect![[r#"
*Assoc*
```rust
test
```
```rust
type Assoc
```
"#]],
);
check(
r#"
trait A where
Self::Assoc$0: ,
{
type Assoc;
}"#,
expect![[r#"
*Assoc*
```rust
test
```
```rust
type Assoc
```
"#]],
);
}
}

View File

@ -24,7 +24,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
};
// Add associated types on type parameters and `Self`.
resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
acc.add_type_alias(ctx, alias);
None::<()>
});