Skip to content

Commit

Permalink
Add Resolver.overrides(overrider: KSDeclaration, overridee: KSDeclara…
Browse files Browse the repository at this point in the history
…tion, containingClass: KSClassDeclaration): Boolean

this function checks if a declaration is overriding another declaration in the context of contained in the containing class.

(cherry picked from commit b698c8e)
  • Loading branch information
neetopia committed Feb 16, 2022
1 parent edde9d4 commit db75346
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ interface Resolver {
*/
fun overrides(overrider: KSDeclaration, overridee: KSDeclaration): Boolean

/**
* @param overrider the candidate overriding declaration being checked.
* @param overridee the candidate overridden declaration being checked.
* @param containingClass the containing class of candidate overriding and overridden declaration being checked.
* @return boolean value indicating whether [overrider] overrides [overridee]
* Calling [overrides] is expensive and should be avoided if possible.
*/
fun overrides(overrider: KSDeclaration, overridee: KSDeclaration, containingClass: KSClassDeclaration): Boolean

/**
* Returns the jvm name of the given function.
* This function might fail due to resolution error, in case of error, null is returned.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import org.jetbrains.kotlin.resolve.constants.KClassValue
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
import org.jetbrains.kotlin.resolve.lazy.DeclarationScopeProvider
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
Expand Down Expand Up @@ -462,6 +463,58 @@ class ResolverImpl(
return OverridingUtil.overrides(subDescriptor, superDescriptor, true, true)
}

// check if the candidate is overridden from the original declaration.
private fun isOriginal(original: KSDeclaration, candidate: KSDeclaration): Boolean {
incrementalContext.recordLookupForDeclaration(original)
incrementalContext.recordLookupForDeclaration(candidate)
val originalDescriptor = when (original) {
is KSPropertyDeclaration -> resolvePropertyDeclaration(original)
is KSFunctionDeclaration -> resolveFunctionDeclaration(original)?.propertyIfAccessor
else -> return false
}

val candidateDescriptors = when (candidate) {
is KSPropertyDeclaration -> resolvePropertyDeclaration(candidate)?.overriddenDescriptors
is KSFunctionDeclaration -> resolveFunctionDeclaration(candidate)?.overriddenDescriptors
else -> return false
}
return candidateDescriptors?.any { it == originalDescriptor } ?: false
}

override fun overrides(
overrider: KSDeclaration,
overridee: KSDeclaration,
containingClass: KSClassDeclaration
): Boolean {
incrementalContext.recordLookupForDeclaration(containingClass)
return when (overrider) {
is KSPropertyDeclaration -> containingClass.getAllProperties().singleOrNull {
it.simpleName.asString() == overrider.simpleName.asString() && isOriginal(overrider, it)
}?.let { overrides(it, overridee) } ?: false
is KSFunctionDeclaration -> {
val candidates = containingClass.getAllFunctions().filter {
it.simpleName.asString() == overridee.simpleName.asString()
}
if (overrider.simpleName.asString().startsWith("get") ||
overrider.simpleName.asString().startsWith("set")
) {
candidates.plus(
containingClass.getAllProperties().filter {
val overriderName = overrider.simpleName.asString().substring(3)
.replaceFirstChar { it.lowercase() }
it.simpleName.asString() == overriderName ||
it.simpleName.asString().replaceFirstChar { it.lowercase() } == overriderName
}
// TODO: It is currently not possible to do the overridden descriptor optimization for java overrides.
).any { overrides(it, overridee) }
} else {
candidates.singleOrNull { isOriginal(overrider, it) }?.let { overrides(it, overridee) } ?: false
}
}
else -> false
}
}

fun evaluateConstant(expression: KtExpression?, expectedType: KotlinType): ConstantValue<*>? {
return expression?.let {
if (it is KtClassLiteralExpression && it.receiverExpression != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ class CheckOverrideProcessor : AbstractTestProcessor() {
}

override fun process(resolver: Resolver): List<KSAnnotated> {
fun checkOverride(overrider: KSDeclaration, overridee: KSDeclaration) {
fun checkOverride(overrider: KSDeclaration, overridee: KSDeclaration, containing: KSClassDeclaration? = null) {
results.add(
"${overrider.qualifiedName?.asString()} overrides ${overridee.qualifiedName?.asString()}: " +
"${resolver.overrides(overrider, overridee)}"
"${containing?.let { resolver.overrides(overrider, overridee, containing) }
?: resolver.overrides(overrider, overridee)}"
)
}
val javaList = resolver.getClassDeclarationByName(resolver.getKSNameFromString("JavaList"))
Expand Down Expand Up @@ -96,6 +97,19 @@ class CheckOverrideProcessor : AbstractTestProcessor() {
val diffGetX = JavaDifferentReturnTypes.getDeclaredFunctions()
.first { it.simpleName.asString() == "foo" }
checkOverride(diffGetX, fooFunJava)
val base = resolver.getClassDeclarationByName("Base")!!
val baseF1 = base.declarations.filter { it.simpleName.asString() == "f1" }.single()
val baseProp = base.declarations.filter { it.simpleName.asString() == "prop" }.single()
val myInterface3 = resolver.getClassDeclarationByName("MyInterface3")!!
val myInterfaceF1 = myInterface3.declarations.filter { it.simpleName.asString() == "f1" }.single()
val myInterfaceProp = myInterface3.declarations.filter { it.simpleName.asString() == "prop" }.single()
val baseOverride = resolver.getClassDeclarationByName("BaseOverride")!!
checkOverride(baseF1, myInterfaceF1, baseOverride)
checkOverride(baseProp, myInterfaceProp, baseOverride)
val jbase = resolver.getClassDeclarationByName("JBase")!!
val jBaseOverride = resolver.getClassDeclarationByName("JBaseOverride")!!
val jbaseProp = jbase.declarations.single { it.simpleName.asString() == "getProp" }
checkOverride(jbaseProp, myInterfaceProp, jBaseOverride)
return emptyList()
}
}
33 changes: 32 additions & 1 deletion compiler-plugin/testData/api/checkOverride.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
// MyInterface2ImplWithType.receiveList overrides MyInterface2.receiveList: true
// MyInterface2ImplWithType.receiveList overrides MyInterface2ImplWithoutType.receiveList: true
// JavaDifferentReturnType.foo overrides JavaList.foo: true
// Base.f1 overrides MyInterface3.f1: true
// Base.prop overrides MyInterface3.prop: true
// JBase.getProp overrides MyInterface3.prop: true
// END
// FILE: a.kt

Expand Down Expand Up @@ -113,6 +116,34 @@ interface MyInterface2ImplWithType : MyInterface2ImplWithoutType<EnumType> {
override fun receiveList(argsInParent : List<EnumType>):Unit
}

interface MyInterface3 {
fun f1()
val prop: String
}

open class Base {
val prop: String = ""
fun f1() {
}
}

class BaseOverride: MyInterface3, Base() {
fun f2() {}
}

// FILE: JBaseOverride.java
public class JBaseOverride extends JBase implements MyInterface3 {

}

// FILE: JBase.java

public class JBase {
public String getProp() {
return "JBase";
}
}

// FILE: JavaList.java

import java.util.*;
Expand Down Expand Up @@ -155,4 +186,4 @@ public abstract class JavaDifferentReturnType extends JavaList {
protected String foo() {
return "";
}
}
}

0 comments on commit db75346

Please sign in to comment.