Add accessor symbols & test for property overriding in Java

This commit is contained in:
Mikhail Glukhikh
2019-03-13 16:44:16 +03:00
parent 9abf4062b1
commit cf72b13d84
15 changed files with 201 additions and 50 deletions
@@ -19,9 +19,11 @@ import org.jetbrains.kotlin.fir.java.enhancement.*
import org.jetbrains.kotlin.fir.java.enhancement.EnhancementSignatureParts
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.*
@@ -74,30 +76,43 @@ class JavaClassEnhancementScope(
private fun enhance(
original: ConeVariableSymbol,
name: Name
): FirPropertySymbol {
val firField = (original as FirBasedSymbol<*>).fir as? FirJavaField ?: error("Can't make enhancement for $original")
): ConePropertySymbol {
when (val firElement = (original as FirBasedSymbol<*>).fir) {
is FirJavaField -> {
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firField.annotations)
val newReturnTypeRef = enhanceReturnType(firField, emptyList(), memberContext, null)
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firElement.annotations)
val newReturnTypeRef = enhanceReturnType(firElement, emptyList(), memberContext, null)
val symbol = FirPropertySymbol(original.callableId)
with(firField) {
FirMemberPropertyImpl(
this@JavaClassEnhancementScope.session, null, symbol, name,
visibility, modality, isExpect, isActual, isOverride,
isConst = false, isLateInit = false,
receiverTypeRef = null,
returnTypeRef = newReturnTypeRef,
isVar = isVar, initializer = null,
getter = FirDefaultPropertyGetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
setter = FirDefaultPropertySetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
delegate = null
).apply {
annotations += firField.annotations
status.isStatic = firField.isStatic
val symbol = FirPropertySymbol(original.callableId)
with(firElement) {
FirMemberPropertyImpl(
this@JavaClassEnhancementScope.session, null, symbol, name,
visibility, modality, isExpect, isActual, isOverride,
isConst = false, isLateInit = false,
receiverTypeRef = null,
returnTypeRef = newReturnTypeRef,
isVar = isVar, initializer = null,
getter = FirDefaultPropertyGetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
setter = FirDefaultPropertySetter(this@JavaClassEnhancementScope.session, null, newReturnTypeRef, visibility),
delegate = null
).apply {
annotations += firElement.annotations
status.isStatic = firElement.isStatic
}
}
return symbol
}
is FirJavaMethod -> {
original as FirAccessorSymbol
return enhanceMethod(
firElement, original.accessorId, original.accessorId.callableName, isAccessor = true, propertyId = original.callableId
) as FirAccessorSymbol
}
else -> {
if (original is ConePropertySymbol) return original
error("Can't make enhancement for $original: `${firElement.render()}`")
}
}
return symbol
}
private fun enhance(
@@ -107,7 +122,17 @@ class JavaClassEnhancementScope(
val firMethod = (original as FirFunctionSymbol).fir as? FirFunction
if (firMethod !is FirJavaMethod && firMethod !is FirJavaConstructor || firMethod !is FirCallableMemberDeclaration) return original
return enhanceMethod(firMethod, original.callableId, name) as FirFunctionSymbol
}
private fun enhanceMethod(
firMethod: FirFunction,
methodId: CallableId,
name: Name,
isAccessor: Boolean = false,
propertyId: CallableId? = null
): ConeCallableSymbol {
require(firMethod is FirCallableMemberDeclaration)
val memberContext = context.copyWithNewDefaultTypeQualifiers(typeQualifierResolver, jsr305State, firMethod.annotations)
val predefinedEnhancementInfo =
@@ -144,7 +169,7 @@ class JavaClassEnhancementScope(
)
}
val symbol = FirFunctionSymbol(original.callableId)
val symbol = if (!isAccessor) FirFunctionSymbol(methodId) else FirAccessorSymbol(callableId = propertyId!!, accessorId = methodId)
FirMemberFunctionImpl(
this@JavaClassEnhancementScope.session, null, symbol, name,
newReceiverTypeRef, newReturnTypeRef
@@ -250,8 +275,7 @@ class JavaClassEnhancementScope(
typeInSignature = TypeInSignature.ValueParameter(hasReceiver, index)
).enhance(session, jsr305State, predefinedEnhancementInfo?.parametersInfo?.getOrNull(index))
val firResolvedTypeRef = signatureParts.type
val defaultValue = ownerParameter.getDefaultValueFromAnnotation()
val defaultValueExpression = when (defaultValue) {
val defaultValueExpression = when (val defaultValue = ownerParameter.getDefaultValueFromAnnotation()) {
NullDefaultValue -> FirConstExpressionImpl(session, null, IrConstKind.Null, null)
is StringDefaultValue -> firResolvedTypeRef.type.lexicalCastFrom(session, defaultValue.value)
null -> null
@@ -8,17 +8,21 @@ package org.jetbrains.kotlin.fir.java.scopes
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractProviderBasedScope
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConeVariableSymbol
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirAccessorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.Name
@@ -32,7 +36,7 @@ class JavaClassUseSiteScope(
internal val symbol = klass.symbol
//base symbol as key, overridden as value
private val overriddenByBase = mutableMapOf<ConeFunctionSymbol, ConeFunctionSymbol?>()
private val overriddenByBase = mutableMapOf<ConeCallableSymbol, ConeFunctionSymbol?>()
private val context: ConeTypeContext = session.typeContext
@@ -71,14 +75,46 @@ class JavaClassUseSiteScope(
}
}
internal fun ConeFunctionSymbol.getOverridden(candidates: Set<ConeFunctionSymbol>): ConeCallableSymbol? {
private fun isOverriddenPropertyCheck(overriddenInJava: FirNamedFunction, base: FirProperty): Boolean {
val receiverTypeRef = base.receiverTypeRef
if (receiverTypeRef == null) {
// TODO: setters
return overriddenInJava.valueParameters.isEmpty()
} else {
if (overriddenInJava.valueParameters.size != 1) return false
return isEqualTypes(receiverTypeRef, overriddenInJava.valueParameters.single().returnTypeRef)
}
}
internal fun ConeCallableSymbol.getOverridden(candidates: Set<ConeFunctionSymbol>): ConeCallableSymbol? {
if (overriddenByBase.containsKey(this)) return overriddenByBase[this]
val self = (this as FirFunctionSymbol).fir as FirNamedFunction
val overriding = candidates.firstOrNull {
val member = (it as FirFunctionSymbol).firUnsafe<FirNamedFunction>()
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
} // TODO: two or more overrides for one fun?
val overriding = when (this) {
is FirFunctionSymbol -> {
val self = fir as FirNamedFunction
candidates.firstOrNull {
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
}
}
is FirPropertySymbol -> {
val self = fir as FirProperty
candidates.firstOrNull {
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
self.modality != Modality.FINAL && isOverriddenPropertyCheck(member, self)
}
}
is FirAccessorSymbol -> {
val self = fir as FirNamedFunction
candidates.firstOrNull {
val member = (it as FirFunctionSymbol).fir as FirNamedFunction
self.modality != Modality.FINAL && isOverriddenFunCheck(member, self)
}
}
else -> error("Unexpected callable symbol: $this")
}
// TODO: two or more overrides for one fun?
overriddenByBase[this] = overriding
return overriding
}
@@ -102,14 +138,51 @@ class JavaClassUseSiteScope(
}
}
private fun processAccessorFunctionsByName(
propertyName: Name,
accessorName: Name,
processor: (ConePropertySymbol) -> ProcessorAction
): ProcessorAction {
val overrideCandidates = mutableSetOf<ConeFunctionSymbol>()
if (!declaredMemberScope.processFunctionsByName(accessorName) { functionSymbol ->
overrideCandidates += functionSymbol
val accessorSymbol = FirAccessorSymbol(
accessorId = functionSymbol.callableId,
callableId = CallableId(functionSymbol.callableId.packageName, functionSymbol.callableId.className, propertyName)
)
if (functionSymbol is FirBasedSymbol<*>) {
(functionSymbol.fir as? FirCallableMemberDeclaration)?.let { callableMember -> accessorSymbol.bind(callableMember) }
}
processor(accessorSymbol)
}
) return ProcessorAction.STOP
return superTypesScope.processPropertiesByName(propertyName) {
val firCallableMember = (it as FirBasedSymbol<*>).fir as? FirCallableMemberDeclaration
if (firCallableMember?.isStatic == true) {
ProcessorAction.NEXT
} else {
val overriddenBy = it.getOverridden(overrideCandidates)
if (overriddenBy == null && it is ConePropertySymbol) {
processor(it)
} else {
ProcessorAction.NEXT
}
}
}
}
override fun processPropertiesByName(name: Name, processor: (ConeVariableSymbol) -> ProcessorAction): ProcessorAction {
val seen = mutableSetOf<ConeVariableSymbol>()
if (!declaredMemberScope.processPropertiesByName(name) {
seen += it
processor(it)
}
) return ProcessorAction.STOP
return ProcessorAction.NEXT
val getterName = Name.identifier(getterPrefix + name.asString().capitalize())
return processAccessorFunctionsByName(name, getterName, processor)
}
companion object {
private const val getterPrefix = "get"
}
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.ConeFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.ConeVariableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.name.Name
class FirLocalScope : FirScope {
@@ -22,7 +23,7 @@ class FirLocalScope : FirScope {
fun storeDeclaration(declaration: FirNamedDeclaration) {
when (declaration) {
is FirVariable -> properties[declaration.name] = declaration.symbol
is FirNamedFunction -> functions[declaration.name] = declaration.symbol
is FirNamedFunction -> functions[declaration.name] = declaration.symbol as FirFunctionSymbol
}
}
@@ -27,8 +27,6 @@ interface FirNamedFunction : @VisitedSupertype FirFunction, FirCallableMemberDec
override val isOverride: Boolean get() = status.isOverride
override val symbol: FirFunctionSymbol
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R =
visitor.visitNamedFunction(this, data)
@@ -10,11 +10,9 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.transformInplace
import org.jetbrains.kotlin.fir.transformSingle
@@ -24,12 +22,12 @@ import org.jetbrains.kotlin.name.Name
open class FirMemberFunctionImpl : FirAbstractCallableMember, FirNamedFunction, FirModifiableFunction {
override val symbol: FirFunctionSymbol
override val symbol: FirCallableSymbol
constructor(
session: FirSession,
psi: PsiElement?,
symbol: FirFunctionSymbol,
symbol: FirCallableSymbol,
name: Name,
receiverTypeRef: FirTypeRef?,
returnTypeRef: FirTypeRef
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.symbols.impl
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.ConePropertySymbol
class FirAccessorSymbol(
override val callableId: CallableId,
val accessorId: CallableId
) : ConePropertySymbol, FirCallableSymbol()
@@ -2,10 +2,10 @@ FILE fqName:<root> fileName:/javaSyntheticPropertyAccess.kt
FUN name:test visibility:public modality:FINAL <> (j:<root>.J) returnType:kotlin.Unit
VALUE_PARAMETER name:j index:0 type:<root>.J
BLOCK_BODY
ERROR_CALL 'No getter found for R|/J.foo|' type=kotlin.Int
ERROR_CALL 'Unresolved reference: R|/J.foo|' type=IrErrorType
VAR name:<unary> type:kotlin.Int [val]
ERROR_CALL 'No getter found for R|/J.foo|' type=kotlin.Int
ERROR_CALL 'Unresolved reference: R|/J.foo|' type=IrErrorType
GET_VAR 'val <unary>: kotlin.Int [val] declared in <root>.test' type=kotlin.Int origin=null
ERROR_CALL 'Unresolved reference: R|/J.foo|' type=IrErrorType
ERROR_CALL 'Unresolved reference: <Ambiguity: foo, [/J.foo, /J.foo]>#' type=IrErrorType
ERROR_CALL 'Unresolved reference: <Ambiguity: foo, [/J.foo, /J.foo]>#' type=IrErrorType
VAR name:<unary> type:IrErrorType [val]
ERROR_CALL 'Unresolved reference: <Ambiguity: foo, [/J.foo, /J.foo]>#' type=IrErrorType
ERROR_CALL 'Unresolved reference: <Ambiguity: foo, [/J.foo, /J.foo]>#' type=IrErrorType
GET_VAR 'val <unary>: IrErrorType [val] declared in <root>.test' type=IrErrorType origin=null
ERROR_CALL 'Unresolved reference: <Ambiguity: foo, [/J.foo, /J.foo]>#' type=IrErrorType
@@ -9,6 +9,10 @@ public open class JFrame : R|awt/Frame| {
protected/*protected and package*/ get(): R|ft<kotlin/String, kotlin/String?>|!
protected/*protected and package*/ set(value: R|ft<kotlin/String, kotlin/String?>|!): kotlin/Unit
public/*package*/ open var accessibleContext: R|ft<kotlin/String, kotlin/String?>|!
public/*package*/ get(): R|ft<kotlin/String, kotlin/String?>|!
public/*package*/ set(value: R|ft<kotlin/String, kotlin/String?>|!): kotlin/Unit
public final fun JFrame(): R|test/JFrame|
}
@@ -0,0 +1,4 @@
public open class Inheritor : R|java/lang/Object|, R|Base| {
public open operator fun getX(): R|kotlin/Int|
}
@@ -0,0 +1,3 @@
interface Base {
val x: Int
}
@@ -0,0 +1,6 @@
FILE: Base.kt
public abstract interface Base : R|kotlin/Any| {
public abstract val x: R|kotlin/Int|
public get(): R|kotlin/Int|
}
@@ -0,0 +1,5 @@
public class Inheritor implements Base {
public int getX() {
return 42;
}
}
@@ -0,0 +1,5 @@
class Tester : Inheritor() {
fun test(): Int {
return x
}
}
@@ -0,0 +1,11 @@
FILE: Test.kt
public final class Tester : R|Inheritor| {
public constructor(): R|Tester| {
super<R|Inheritor|>()
}
public final fun test(): R|kotlin/Int| {
^test R|/Inheritor.x|
}
}
@@ -69,6 +69,11 @@ public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleRe
runTest("idea/testData/fir/multiModule/javaInheritsKotlinExtension/");
}
@TestMetadata("javaInheritsKotlinProperty")
public void testJavaInheritsKotlinProperty() throws Exception {
runTest("idea/testData/fir/multiModule/javaInheritsKotlinProperty/");
}
@TestMetadata("javaInheritsRawKotlin")
public void testJavaInheritsRawKotlin() throws Exception {
runTest("idea/testData/fir/multiModule/javaInheritsRawKotlin/");