[Completion] Substitute base type for nested types

Ensure we call `getTypeOfMember` for nested nominals and typealiases.
This commit is contained in:
Hamish Knight
2025-02-25 14:49:13 +00:00
parent be0f6f9491
commit 8975bf6c37
4 changed files with 48 additions and 27 deletions

View File

@@ -603,7 +603,7 @@ Type CompletionLookup::getTypeOfMember(const ValueDecl *VD,
DynamicLookupInfo dynamicLookupInfo) {
switch (dynamicLookupInfo.getKind()) {
case DynamicLookupInfo::None:
return getTypeOfMember(VD, this->ExprType);
return getTypeOfMember(VD, getMemberBaseType());
case DynamicLookupInfo::AnyObject:
return getTypeOfMember(VD, Type());
case DynamicLookupInfo::KeyPathDynamicMember: {
@@ -676,7 +676,14 @@ Type CompletionLookup::getTypeOfMember(const ValueDecl *VD,
}
Type CompletionLookup::getTypeOfMember(const ValueDecl *VD, Type ExprType) {
Type T = VD->getInterfaceType();
Type T;
if (auto *TD = dyn_cast<TypeDecl>(VD)) {
// For a type decl we're interested in the declared interface type, i.e
// we don't want a metatype.
T = TD->getDeclaredInterfaceType();
} else {
T = VD->getInterfaceType();
}
assert(!T.isNull());
if (ExprType) {
@@ -1707,13 +1714,16 @@ void CompletionLookup::addNominalTypeRef(const NominalTypeDecl *NTD,
addLeadingDot(Builder);
Builder.addBaseName(NTD->getName().str());
// Substitute the base type for a nested type if needed.
auto nominalTy = getTypeOfMember(NTD, dynamicLookupInfo);
// "Fake" annotation for custom attribute types.
SmallVector<char, 0> stash;
StringRef customAttributeAnnotation = getTypeAnnotationString(NTD, stash);
if (!customAttributeAnnotation.empty()) {
Builder.addTypeAnnotation(customAttributeAnnotation);
} else {
addTypeAnnotation(Builder, NTD->getDeclaredInterfaceType());
addTypeAnnotation(Builder, nominalTy);
}
// Override the type relation for NominalTypes. Use the better relation
@@ -1723,8 +1733,7 @@ void CompletionLookup::addNominalTypeRef(const NominalTypeDecl *NTD,
// func receiveMetatype(_: Int.Type) {}
//
// We want to suggest 'Int' as 'Identical' for both arguments.
Builder.setResultTypes(
{NTD->getInterfaceType(), NTD->getDeclaredInterfaceType()});
Builder.setResultTypes({MetatypeType::get(nominalTy), nominalTy});
Builder.setTypeContext(expectedTypeContext, CurrDeclContext);
}
@@ -1737,13 +1746,18 @@ void CompletionLookup::addTypeAliasRef(const TypeAliasDecl *TAD,
Builder.setAssociatedDecl(TAD);
addLeadingDot(Builder);
Builder.addBaseName(TAD->getName().str());
if (auto underlyingType = TAD->getUnderlyingType()) {
if (underlyingType->hasError()) {
addTypeAnnotation(Builder, TAD->getDeclaredInterfaceType());
} else {
addTypeAnnotation(Builder, underlyingType);
}
// Substitute the base type for a nested typealias if needed.
auto ty = getTypeOfMember(TAD, dynamicLookupInfo);
// If the underlying type has an error, prefer to print the full typealias,
// otherwise get the underlying type.
if (auto *TA = dyn_cast<TypeAliasType>(ty.getPointer())) {
auto underlyingTy = TA->getSinglyDesugaredType();
if (!underlyingTy->hasError())
ty = underlyingTy;
}
addTypeAnnotation(Builder, ty);
}
void CompletionLookup::addGenericTypeParamRef(

View File

@@ -75,7 +75,7 @@ func foo(s: MyStruct<Int>) {
// META_MYSTRUCT_INT_DOT: Begin completions, 11 items
// META_MYSTRUCT_INT_DOT-DAG: Keyword[self]/CurrNominal: self[#MyStruct<Int>.Type#]; name=self
// META_MYSTRUCT_INT_DOT-DAG: Keyword/CurrNominal: Type[#MyStruct<Int>.Type#]; name=Type
// META_MYSTRUCT_INT_DOT-DAG: Decl[TypeAlias]/CurrNominal: Assoc[#T#]; name=Assoc
// META_MYSTRUCT_INT_DOT-DAG: Decl[TypeAlias]/CurrNominal: Assoc[#Int#]; name=Assoc
// META_MYSTRUCT_INT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#int: U#})[#MyStruct<Int>#]; name=init(int:)
// META_MYSTRUCT_INT_DOT-DAG: Decl[Constructor]/CurrNominal: init({#withConstrainedGenericParam: SomeProto#})[#MyStruct<Int>#]; name=init(withConstrainedGenericParam:)
// META_MYSTRUCT_INT_DOT-DAG: Decl[InstanceMethod]/CurrNominal: methodWithConstrainedGenericParam({#(self): MyStruct<Int>#})[#(x: SomeProto) -> Int#]; name=methodWithConstrainedGenericParam(:)

View File

@@ -90,7 +90,7 @@ do {
let _: Int!.#^SUGARED_IUOPTIONAL_MEMBER_DOT?check=OPTIONAL^#
// OPTIONAL-LABEL: Begin completions, 2 items
// OPTIONAL-LABEL: Decl[TypeAlias]/CurrNominal: Wrappt[#Wrapped#]; name=Wrappt
// OPTIONAL-LABEL: Decl[TypeAlias]/CurrNominal: Wrappt[#Int#]; name=Wrappt
// OPTIONAL-LABEL: Keyword/None: Type[#{{Optional<Int>|Int\?}}.Type#]; name=Type
let _: Array<Int>.#^ARRAY_MEMBER_DOT?check=ARRAY^#
@@ -99,10 +99,10 @@ do {
// ARRAY-LABEL: Begin completions, 8 items
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: Index[#Int#]; name=Index
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: Indices[#Range<Int>#]; name=Indices
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: Iterator[#IndexingIterator<Array<Element>>#]; name=Iterator
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: Element[#Element#]; name=Element
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: ArrayLiteralElement[#Element#]; name=ArrayLiteralElement
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: SubSequence[#ArraySlice<Element>#]; name=SubSequence
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: Iterator[#IndexingIterator<Array<Int>>#]; name=Iterator
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: Element[#Int#]; name=Element
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: ArrayLiteralElement[#Int#]; name=ArrayLiteralElement
// ARRAY-DAG: Decl[TypeAlias]/CurrNominal/IsSystem: SubSequence[#ArraySlice<Int>#]; name=SubSequence
// ARRAY-DAG: Decl[TypeAlias]/Super/NotRecommended/IsSystem: IndexDistance[#Int#]; name=IndexDistance; diagnostics=warning:'IndexDistance' is deprecated: all index distances are now of type Int
// ARRAY-DAG: Keyword/None: Type[#{{Array<Int>|\[Int\]}}.Type#]; name=Type
@@ -110,15 +110,15 @@ do {
let _: [Int : Int].#^SUGARED_DICTIONARY_MEMBER_DOT?check=DICTIONARY^#
// DICTIONARY-LABEL: Begin completions, 11 items
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Element[#(key: Key, value: Value)#]; name=Element
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: SubSequence[#Slice<Dictionary<Key, Value>>#]; name=SubSequence
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Indices[#DefaultIndices<Dictionary<Key, Value>>#]; name=Indices
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Key[#Key#]; name=Key
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Value[#Value#]; name=Value
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Keys[#Dictionary<Key, Value>.Keys#]; name=Keys
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Values[#Dictionary<Key, Value>.Values#]; name=Values
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Index[#Dictionary<Key, Value>.Index#]; name=Index
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Iterator[#Dictionary<Key, Value>.Iterator#]; name=Iterator
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Element[#(key: Int, value: Int)#]; name=Element
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: SubSequence[#Slice<Dictionary<Int, Int>>#]; name=SubSequence
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Indices[#DefaultIndices<Dictionary<Int, Int>>#]; name=Indices
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Key[#Int#]; name=Key
// DICTIONARY-NEXT: Decl[TypeAlias]/CurrNominal/IsSystem: Value[#Int#]; name=Value
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Keys[#Dictionary<Int, Int>.Keys#]; name=Keys
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Values[#Dictionary<Int, Int>.Values#]; name=Values
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Index[#Dictionary<Int, Int>.Index#]; name=Index
// DICTIONARY-NEXT: Decl[Struct]/CurrNominal/IsSystem: Iterator[#Dictionary<Int, Int>.Iterator#]; name=Iterator
// DICTIONARY-NEXT: Decl[TypeAlias]/Super/NotRecommended/IsSystem: IndexDistance[#Int#]; name=IndexDistance; diagnostics=warning
// DICTIONARY-NEXT: Keyword/None: Type[#{{Dictionary<Int, Int>|\[Int : Int\]}}.Type#]; name=Type
}
@@ -160,3 +160,10 @@ do {
// MEMBER_EXISTENTIAL_NO_DOT-NEXT: Keyword/None: .Protocol[#(any Sequence).Type#]; name=Protocol
// MEMBER_EXISTENTIAL_NO_DOT-NEXT: Keyword/None: .Type[#any Sequence.Type#]; name=Type
}
struct TestGenericTypealias<T, U> {
typealias K<V> = TestGenericTypealias<(T, U), V>
}
let _ = TestGenericTypealias<Int, String>.K<Float>.#^GENERIC_TYPEALIAS^#
// GENERIC_TYPEALIAS-DAG: Decl[TypeAlias]/CurrNominal: K[#TestGenericTypealias<((Int, String), Float), V>#]; name=K

View File

@@ -49,4 +49,4 @@ extension ObservableConvertibleType {
// CATCHSEQUENCE_DOT-DAG: Keyword/CurrNominal: Type[#CatchSequence<_>.Type#]; name=Type
// CATCHSEQUENCE_DOT-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#CatchSequence<_>#]; name=init()
// CATCHSEQUENCE_DOT-DAG: Decl[StaticMethod]/Super/TypeRelation[Convertible]: catchError()[#Observable<CatchSequence<_>.T>#]; name=catchError()
// CATCHSEQUENCE_DOT-DAG: Decl[TypeAlias]/Super: T[#T#]; name=T
// CATCHSEQUENCE_DOT-DAG: Decl[TypeAlias]/Super: T[#Observable<_.Element.T>.T#]; name=T