Add accessor symbols & test for property overriding in Java
This commit is contained in:
+47
-23
@@ -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
|
||||
|
||||
+84
-11
@@ -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)
|
||||
|
||||
|
||||
+4
-6
@@ -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()
|
||||
+7
-7
@@ -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|
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -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/");
|
||||
|
||||
Reference in New Issue
Block a user