diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt index d6a68edde4..577b7149a4 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt @@ -891,9 +891,28 @@ class ResolverAAImpl( } } + // Returns the class/interface that declares this member. For a fake or intersection override, + // closestClassDeclaration() resolves to the receiver's enclosing declaration rather than the real + // supertype that declares the member, which makes computeAsMemberOf()'s subtype precondition below + // fail spuriously. Unwrap to an overridden original whose containing class is a genuine supertype + // of the receiver so the precondition is checked against the actual declaring type. + @OptIn(KaExperimentalApi::class) + private fun KSDeclaration.declaringClassForAsMemberOf(): KSClassDeclaration? { + val symbol = when (this) { + is KSFunctionDeclarationImpl -> ktFunctionSymbol + is KSPropertyDeclarationImpl -> ktPropertySymbol + else -> null + } ?: return closestClassDeclaration() + val declarer = analyze { + val original = symbol.intersectionOverriddenSymbols.firstOrNull() ?: symbol.fakeOverrideOriginal + (original.containingSymbol as? KaNamedClassSymbol)?.let { KSClassDeclarationImpl.getCached(it) } + } + return declarer ?: closestClassDeclaration() + } + @OptIn(KaExperimentalApi::class) internal fun computeAsMemberOf(property: KSPropertyDeclaration, containing: KSType): KSType { - val declaredIn = property.closestClassDeclaration() + val declaredIn = property.declaringClassForAsMemberOf() ?: throw IllegalArgumentException( "Cannot call asMemberOf with a property that is not declared in a class or an interface" ) @@ -943,7 +962,7 @@ class ResolverAAImpl( @OptIn(KaExperimentalApi::class) internal fun computeAsMemberOf(function: KSFunctionDeclaration, containing: KSType): KSFunction { - val propertyDeclaredIn = function.closestClassDeclaration() + val declaredIn = function.declaringClassForAsMemberOf() ?: throw IllegalArgumentException( "Cannot call asMemberOf with a function that is not declared in a class or an interface" ) @@ -953,14 +972,14 @@ class ResolverAAImpl( recordLookupWithSupertypes(containing.type) recordLookupForPropertyOrMethod(function) val isSubTypeOf = analyze { - (propertyDeclaredIn.asStarProjectedType() as? KSTypeImpl)?.type?.let { + (declaredIn.asStarProjectedType() as? KSTypeImpl)?.type?.let { containing.type.isSubtypeOf(it) } ?: false } if (!isSubTypeOf) { throw IllegalArgumentException( "$containing is not a sub type of the class/interface that contains `$function` " + - "($propertyDeclaredIn)" + "($declaredIn)" ) } analyze { diff --git a/kotlin-analysis-api/testData/asMemberOf.kt b/kotlin-analysis-api/testData/asMemberOf.kt index d334058d2f..8fa846b34d 100644 --- a/kotlin-analysis-api/testData/asMemberOf.kt +++ b/kotlin-analysis-api/testData/asMemberOf.kt @@ -122,6 +122,7 @@ // () -> kotlin.Int!! // (kotlin.Int!!) -> kotlin.Unit!! // Baz!! +// C.Both.provideString: () -> kotlin.String!! // END // MODULE: lib // FILE: Test.java @@ -178,6 +179,10 @@ fun fileLevelFunction():Unit = TODO() val fileLevelProperty:Int = 3 val errorType: NonExistingType +class A { interface Foo { fun provideString(): String } } +class B { interface Bar { fun provideString(): String } } +class C { interface Both : A.Foo, B.Bar } + interface KotlinInterface { val x:Int var y:Int diff --git a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt index 655cb5cf51..81e016c6f6 100644 --- a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt +++ b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt @@ -119,6 +119,17 @@ class AsMemberOfProcessor : AbstractTestProcessor() { results.add(f.asMemberOf(usage).returnType!!.toSignature()) } } + + // Intersection override: a member inherited with the same signature from two supertypes nested + // in different classes. asMemberOf() used to throw IllegalArgumentException for it because + // closestClassDeclaration() resolved the owner to the receiver's enclosing class. + resolver.getClassDeclarationByName("C.Both")?.let { both -> + val fn = both.getAllFunctions().single { it.simpleName.asString() == "provideString" } + results.add( + "C.Both.provideString: " + + resolver.asMemberOfSignature(fn, both.asStarProjectedType()) + ) + } return emptyList() }