FIR: Fix overridability rule for Java declarations with different return type kinds
See the class at org/jmock/Expectations public <T> T with(Matcher<T> matcher); public boolean with(Matcher<Boolean> matcher); When we extending such class it we start assuming that fake generic override overrides both of the overridden that is wrong from POV of Java and it fails at FIR ultimate build NB: It's hard to write a test because such Expectation-like class is impossible to write in pure Java
This commit is contained in:
committed by
TeamCityServer
parent
2e37ec6f0c
commit
360d67410d
@@ -102,7 +102,9 @@ object JavaScopeProvider : FirScopeProvider() {
|
||||
useSiteSession,
|
||||
JavaOverrideChecker(
|
||||
useSiteSession,
|
||||
regularClass.javaTypeParameterStack
|
||||
regularClass.javaTypeParameterStack,
|
||||
baseScope = null,
|
||||
considerReturnTypeKinds = false,
|
||||
),
|
||||
superTypeScopes,
|
||||
regularClass.defaultType(),
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ class JavaClassStaticUseSiteScope internal constructor(
|
||||
) : FirContainingNamesAwareScope() {
|
||||
private val functions = hashMapOf<Name, Collection<FirNamedFunctionSymbol>>()
|
||||
private val properties = hashMapOf<Name, Collection<FirVariableSymbol<*>>>()
|
||||
private val overrideChecker = JavaOverrideChecker(session, javaTypeParameterStack)
|
||||
private val overrideChecker = JavaOverrideChecker(session, javaTypeParameterStack, baseScope = null, considerReturnTypeKinds = false)
|
||||
|
||||
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
|
||||
functions.getOrPut(name) {
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ class JavaClassUseSiteMemberScope(
|
||||
declaredMemberScope: FirContainingNamesAwareScope
|
||||
) : AbstractFirUseSiteMemberScope(
|
||||
session,
|
||||
JavaOverrideChecker(session, klass.javaTypeParameterStack),
|
||||
JavaOverrideChecker(session, klass.javaTypeParameterStack, superTypesScope, considerReturnTypeKinds = true),
|
||||
superTypesScope,
|
||||
declaredMemberScope
|
||||
) {
|
||||
|
||||
@@ -16,15 +16,20 @@ import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker
|
||||
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptorRepresentation
|
||||
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
|
||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
class JavaOverrideChecker internal constructor(
|
||||
private val session: FirSession,
|
||||
private val javaTypeParameterStack: JavaTypeParameterStack
|
||||
private val javaTypeParameterStack: JavaTypeParameterStack,
|
||||
private val baseScope: FirTypeScope?,
|
||||
private val considerReturnTypeKinds: Boolean,
|
||||
) : FirAbstractOverrideChecker() {
|
||||
private val context: ConeTypeContext = session.typeContext
|
||||
|
||||
@@ -74,7 +79,7 @@ class JavaOverrideChecker internal constructor(
|
||||
val candidateType = candidateTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack)
|
||||
val baseType = baseTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack)
|
||||
|
||||
if (candidateType.isPrimitiveInJava() != baseType.isPrimitiveInJava()) return false
|
||||
if (candidateType.isPrimitiveInJava(isReturnType = false) != baseType.isPrimitiveInJava(isReturnType = false)) return false
|
||||
|
||||
return isEqualTypes(
|
||||
candidateType,
|
||||
@@ -83,8 +88,50 @@ class JavaOverrideChecker internal constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.isPrimitiveInJava(): Boolean = with(context) {
|
||||
!isNullableType() && CompilerConeAttributes.EnhancedNullability !in attributes && isPrimitiveOrNullablePrimitive
|
||||
// In most cases checking erasure of value parameters should be enough, but in some cases there might be semi-valid Java hierarchies
|
||||
// with same value parameters, but different return type kinds, so it's worth distinguishing them as different non-overridable members
|
||||
private fun doesReturnTypesHaveSameKind(
|
||||
candidate: FirSimpleFunction,
|
||||
base: FirSimpleFunction,
|
||||
): Boolean {
|
||||
val candidateTypeRef = candidate.returnTypeRef
|
||||
val baseTypeRef = base.returnTypeRef
|
||||
|
||||
val candidateType = candidateTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack)
|
||||
val baseType = baseTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack)
|
||||
|
||||
val candidateHasPrimitiveReturnType = candidate.hasPrimitiveReturnTypeInJvm(candidateType)
|
||||
if (candidateHasPrimitiveReturnType != base.hasPrimitiveReturnTypeInJvm(baseType)) return false
|
||||
|
||||
// Both candidate and base are not primitive
|
||||
if (!candidateHasPrimitiveReturnType) return true
|
||||
|
||||
return (candidateType as? ConeClassLikeType)?.lookupTag == (baseType as? ConeClassLikeType)?.lookupTag
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.isPrimitiveInJava(isReturnType: Boolean): Boolean = with(context) {
|
||||
if (isNullableType() || CompilerConeAttributes.EnhancedNullability in attributes) return false
|
||||
|
||||
val isVoid = isReturnType && isUnit
|
||||
return isPrimitiveOrNullablePrimitive || isVoid
|
||||
}
|
||||
|
||||
private fun FirSimpleFunction.hasPrimitiveReturnTypeInJvm(returnType: ConeKotlinType): Boolean {
|
||||
if (!returnType.isPrimitiveInJava(isReturnType = true)) return false
|
||||
|
||||
var foundNonPrimitiveOverridden = false
|
||||
|
||||
baseScope?.processOverriddenFunctions(symbol) {
|
||||
val type = it.fir.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack)
|
||||
if (!type.isPrimitiveInJava(isReturnType = true)) {
|
||||
foundNonPrimitiveOverridden = true
|
||||
ProcessorAction.STOP
|
||||
} else {
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
}
|
||||
|
||||
return !foundNonPrimitiveOverridden
|
||||
}
|
||||
|
||||
private fun isEqualArrayElementTypeProjections(
|
||||
@@ -176,9 +223,16 @@ class JavaOverrideChecker internal constructor(
|
||||
|
||||
if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false
|
||||
val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration)
|
||||
return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
|
||||
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor)
|
||||
|
||||
if (!overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
|
||||
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor)
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (considerReturnTypeKinds && !doesReturnTypesHaveSameKind(overrideCandidate, baseDeclaration)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isOverriddenProperty(overrideCandidate: FirCallableDeclaration, baseDeclaration: FirProperty): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user