[FIR] Generate java declarations for record components of java records
^KT-53964 Fixed
This commit is contained in:
+2
-2
@@ -180,7 +180,7 @@ internal class KtFirScopeProvider(
|
||||
return KtCompositeTypeScope(
|
||||
listOfNotNull(
|
||||
convertToKtTypeScope(firTypeScope),
|
||||
FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(firSession, firTypeScope)?.let { convertToKtTypeScope(it) }
|
||||
FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(firSession, type.coneType, firTypeScope)?.let { convertToKtTypeScope(it) }
|
||||
),
|
||||
token
|
||||
)
|
||||
@@ -278,4 +278,4 @@ private class EnumEntryContainingNamesAwareScope(private val originalScope: FirC
|
||||
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
|
||||
// enum entries does not have constructors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -32013,6 +32013,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/irrelevantFields.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithExplicitComponent.kt")
|
||||
public void testJavaRecordWithExplicitComponent() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithGeneric.kt")
|
||||
public void testJavaRecordWithGeneric() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithGeneric.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRecordDescriptorStructure.kt")
|
||||
public void testJvmRecordDescriptorStructure() throws Exception {
|
||||
|
||||
+12
@@ -32013,6 +32013,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/irrelevantFields.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithExplicitComponent.kt")
|
||||
public void testJavaRecordWithExplicitComponent() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithGeneric.kt")
|
||||
public void testJavaRecordWithGeneric() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithGeneric.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRecordDescriptorStructure.kt")
|
||||
public void testJvmRecordDescriptorStructure() throws Exception {
|
||||
|
||||
+12
@@ -32013,6 +32013,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/irrelevantFields.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithExplicitComponent.kt")
|
||||
public void testJavaRecordWithExplicitComponent() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithGeneric.kt")
|
||||
public void testJavaRecordWithGeneric() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithGeneric.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRecordDescriptorStructure.kt")
|
||||
public void testJvmRecordDescriptorStructure() throws Exception {
|
||||
|
||||
Generated
+16
@@ -942,6 +942,22 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/jvmRecord")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class JvmRecord {
|
||||
@Test
|
||||
public void testAllFilesPresentInJvmRecord() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/ir/irText/declarations/jvmRecord"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordComponentAccess.kt")
|
||||
public void testJavaRecordComponentAccess() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/declarations/jvmRecord/javaRecordComponentAccess.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/multiplatform")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+16
@@ -942,6 +942,22 @@ public class LightTreeFir2IrTextTestGenerated extends AbstractLightTreeFir2IrTex
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/jvmRecord")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class JvmRecord {
|
||||
@Test
|
||||
public void testAllFilesPresentInJvmRecord() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/ir/irText/declarations/jvmRecord"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordComponentAccess.kt")
|
||||
public void testJavaRecordComponentAccess() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/declarations/jvmRecord/javaRecordComponentAccess.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/multiplatform")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.java
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElement
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.*
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
|
||||
@@ -46,7 +45,6 @@ import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaElementImpl
|
||||
import org.jetbrains.kotlin.name.*
|
||||
import org.jetbrains.kotlin.toKtPsiSourceElement
|
||||
import org.jetbrains.kotlin.types.Variance.INVARIANT
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
@@ -231,7 +229,7 @@ abstract class FirJavaFacade(
|
||||
val classIsAnnotation = javaClass.classKind == ClassKind.ANNOTATION_CLASS
|
||||
val moduleData = getModuleDataForClass(javaClass)
|
||||
return buildJavaClass {
|
||||
source = (javaClass as? JavaElementImpl<*>)?.psi?.toKtPsiSourceElement()
|
||||
source = javaClass.toSourceElement()
|
||||
this.moduleData = moduleData
|
||||
symbol = classSymbol
|
||||
name = javaClass.name
|
||||
@@ -292,6 +290,7 @@ abstract class FirJavaFacade(
|
||||
for (javaMethod in javaClass.methods) {
|
||||
if (javaMethod.isObjectMethodInInterface()) continue
|
||||
val firJavaMethod = convertJavaMethodToFir(
|
||||
javaClass,
|
||||
javaMethod,
|
||||
classId,
|
||||
javaTypeParameterStack,
|
||||
@@ -360,6 +359,17 @@ abstract class FirJavaFacade(
|
||||
moduleData = moduleData,
|
||||
)
|
||||
}
|
||||
if (javaClass.isRecord) {
|
||||
createDeclarationsForJavaRecord(
|
||||
javaClass,
|
||||
classId,
|
||||
moduleData,
|
||||
javaTypeParameterStack,
|
||||
dispatchReceiver,
|
||||
classTypeParameters,
|
||||
declarations
|
||||
)
|
||||
}
|
||||
}.apply {
|
||||
if (modality == Modality.SEALED) {
|
||||
val inheritors = javaClass.permittedTypes.mapNotNull { classifierType ->
|
||||
@@ -378,6 +388,80 @@ abstract class FirJavaFacade(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (javaClass.isRecord) {
|
||||
this.isJavaRecord = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDeclarationsForJavaRecord(
|
||||
javaClass: JavaClass,
|
||||
classId: ClassId,
|
||||
moduleData: FirModuleData,
|
||||
javaTypeParameterStack: JavaTypeParameterStack,
|
||||
classType: ConeClassLikeType,
|
||||
classTypeParameters: List<FirTypeParameter>,
|
||||
destination: MutableList<FirDeclaration>
|
||||
) {
|
||||
val functionsByName = destination.filterIsInstance<FirJavaMethod>().groupBy { it.name }
|
||||
|
||||
for (recordComponent in javaClass.recordComponents) {
|
||||
val name = recordComponent.name
|
||||
if (functionsByName[name].orEmpty().any { it.valueParameters.isEmpty() }) continue
|
||||
|
||||
val componentId = CallableId(classId, name)
|
||||
destination += buildJavaMethod {
|
||||
this.moduleData = moduleData
|
||||
source = recordComponent.toSourceElement(KtFakeSourceElementKind.JavaRecordComponentFunction)
|
||||
symbol = FirNamedFunctionSymbol(componentId)
|
||||
this.name = name
|
||||
isFromSource = recordComponent.isFromSource
|
||||
returnTypeRef = recordComponent.type.toFirJavaTypeRef(session, javaTypeParameterStack)
|
||||
annotationBuilder = { emptyList() }
|
||||
status = FirResolvedDeclarationStatusImpl(
|
||||
Visibilities.Public,
|
||||
Modality.FINAL,
|
||||
EffectiveVisibility.Public
|
||||
)
|
||||
dispatchReceiverType = classType
|
||||
}.apply {
|
||||
isJavaRecordComponent = true
|
||||
}
|
||||
}
|
||||
|
||||
destination += buildJavaConstructor {
|
||||
source = javaClass.toSourceElement(KtFakeSourceElementKind.ImplicitJavaRecordConstructor)
|
||||
this.moduleData = moduleData
|
||||
isFromSource = javaClass.isFromSource
|
||||
|
||||
val constructorId = CallableId(classId, classId.shortClassName)
|
||||
symbol = FirConstructorSymbol(constructorId)
|
||||
status = FirResolvedDeclarationStatusImpl(
|
||||
Visibilities.Public,
|
||||
Modality.FINAL,
|
||||
EffectiveVisibility.Public
|
||||
)
|
||||
visibility = Visibilities.Public
|
||||
isPrimary = true
|
||||
returnTypeRef = classType.toFirResolvedTypeRef()
|
||||
dispatchReceiverType = null
|
||||
typeParameters += classTypeParameters.toRefs()
|
||||
annotationBuilder = { emptyList() }
|
||||
|
||||
javaClass.recordComponents.mapTo(valueParameters) { component ->
|
||||
buildJavaValueParameter {
|
||||
source = component.toSourceElement(KtFakeSourceElementKind.ImplicitRecordConstructorParameter)
|
||||
this.moduleData = moduleData
|
||||
isFromSource = component.isFromSource
|
||||
returnTypeRef = component.type.toFirJavaTypeRef(session, javaTypeParameterStack)
|
||||
name = component.name
|
||||
isVararg = component.isVararg
|
||||
annotationBuilder = { emptyList() }
|
||||
}
|
||||
}
|
||||
}.apply {
|
||||
containingClassForStaticMemberAttr = classType.lookupTag
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +477,7 @@ abstract class FirJavaFacade(
|
||||
val returnType = javaField.type
|
||||
return when {
|
||||
javaField.isEnumEntry -> buildEnumEntry {
|
||||
source = (javaField as? JavaElementImpl<*>)?.psi?.toKtPsiSourceElement()
|
||||
source = javaField.toSourceElement()
|
||||
this.moduleData = moduleData
|
||||
symbol = FirEnumEntrySymbol(fieldId)
|
||||
name = fieldName
|
||||
@@ -416,7 +500,7 @@ abstract class FirJavaFacade(
|
||||
containingClassForStaticMemberAttr = ConeClassLikeLookupTagImpl(classId)
|
||||
}
|
||||
else -> buildJavaField {
|
||||
source = (javaField as? JavaElementImpl<*>)?.psi?.toKtPsiSourceElement()
|
||||
source = javaField.toSourceElement()
|
||||
this.moduleData = moduleData
|
||||
symbol = FirFieldSymbol(fieldId)
|
||||
name = fieldName
|
||||
@@ -456,6 +540,7 @@ abstract class FirJavaFacade(
|
||||
}
|
||||
|
||||
private fun convertJavaMethodToFir(
|
||||
containingClass: JavaClass,
|
||||
javaMethod: JavaMethod,
|
||||
classId: ClassId,
|
||||
javaTypeParameterStack: JavaTypeParameterStack,
|
||||
@@ -468,7 +553,7 @@ abstract class FirJavaFacade(
|
||||
val returnType = javaMethod.returnType
|
||||
return buildJavaMethod {
|
||||
this.moduleData = moduleData
|
||||
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toKtPsiSourceElement()
|
||||
source = javaMethod.toSourceElement()
|
||||
symbol = methodSymbol
|
||||
name = methodName
|
||||
isFromSource = javaMethod.isFromSource
|
||||
@@ -505,6 +590,9 @@ abstract class FirJavaFacade(
|
||||
if (javaMethod.isStatic) {
|
||||
containingClassForStaticMemberAttr = ConeClassLikeLookupTagImpl(classId)
|
||||
}
|
||||
if (containingClass.isRecord && valueParameters.isEmpty() && containingClass.recordComponents.any { it.name == methodName }) {
|
||||
isJavaRecordComponent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,8 +602,7 @@ abstract class FirJavaFacade(
|
||||
moduleData: FirModuleData,
|
||||
): FirJavaValueParameter =
|
||||
buildJavaValueParameter {
|
||||
source = (javaMethod as? JavaElementImpl<*>)?.psi
|
||||
?.toKtPsiSourceElement(KtFakeSourceElementKind.ImplicitJavaAnnotationConstructor)
|
||||
source = javaMethod.toSourceElement(KtFakeSourceElementKind.ImplicitJavaAnnotationConstructor)
|
||||
this.moduleData = moduleData
|
||||
isFromSource = javaMethod.isFromSource
|
||||
returnTypeRef = firJavaMethod.returnTypeRef
|
||||
@@ -536,7 +623,7 @@ abstract class FirJavaFacade(
|
||||
): FirJavaConstructor {
|
||||
val constructorSymbol = FirConstructorSymbol(constructorId)
|
||||
return buildJavaConstructor {
|
||||
source = (javaConstructor as? JavaElementImpl<*>)?.psi?.toKtPsiSourceElement()
|
||||
source = javaConstructor?.toSourceElement()
|
||||
this.moduleData = moduleData
|
||||
isFromSource = javaClass.isFromSource
|
||||
symbol = constructorSymbol
|
||||
@@ -559,7 +646,7 @@ abstract class FirJavaFacade(
|
||||
type = ownerClassBuilder.buildSelfTypeRef()
|
||||
}
|
||||
dispatchReceiverType = if (isThisInner) outerClassSymbol?.defaultType() else null
|
||||
typeParameters += classTypeParameters.map { buildConstructedClassTypeParameterRef { symbol = it.symbol } }
|
||||
typeParameters += classTypeParameters.toRefs()
|
||||
|
||||
if (javaConstructor != null) {
|
||||
this.typeParameters += javaConstructor.typeParameters.convertTypeParameters(javaTypeParameterStack, constructorSymbol, moduleData)
|
||||
@@ -612,4 +699,12 @@ abstract class FirJavaFacade(
|
||||
|
||||
private fun FqName.topLevelName() =
|
||||
asString().substringBefore(".")
|
||||
|
||||
private fun JavaElement.toSourceElement(sourceElementKind: KtSourceElementKind = KtRealSourceElementKind): KtSourceElement? {
|
||||
return (this as? JavaElementImpl<*>)?.psi?.toKtPsiSourceElement(sourceElementKind)
|
||||
}
|
||||
|
||||
private fun List<FirTypeParameter>.toRefs(): List<FirTypeParameterRef> {
|
||||
return this.map { buildConstructedClassTypeParameterRef { symbol = it.symbol } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ package org.jetbrains.kotlin.fir.java
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.FirArrayOfCall
|
||||
|
||||
+8
-1
@@ -253,6 +253,8 @@ class FirSignatureEnhancement(
|
||||
annotations += valueParameter.annotations
|
||||
}
|
||||
}
|
||||
var isJavaRecordComponent = false
|
||||
|
||||
val function = when (firMethod) {
|
||||
is FirJavaConstructor -> {
|
||||
val symbol = FirConstructorSymbol(methodId)
|
||||
@@ -296,6 +298,7 @@ class FirSignatureEnhancement(
|
||||
}
|
||||
}
|
||||
is FirJavaMethod -> {
|
||||
isJavaRecordComponent = firMethod.isJavaRecordComponent ?: false
|
||||
FirSimpleFunctionBuilder().apply {
|
||||
source = firMethod.source
|
||||
moduleData = this@FirSignatureEnhancement.moduleData
|
||||
@@ -316,7 +319,11 @@ class FirSignatureEnhancement(
|
||||
}.apply {
|
||||
annotations += firMethod.annotations
|
||||
deprecationsProvider = annotations.getDeprecationsProviderFromAnnotations(fromJava = true, session.firCachesFactory)
|
||||
}.build()
|
||||
}.build().apply {
|
||||
if (isJavaRecordComponent) {
|
||||
this.isJavaRecordComponent = true
|
||||
}
|
||||
}
|
||||
|
||||
return function.symbol
|
||||
}
|
||||
|
||||
@@ -12,15 +12,9 @@ import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeNullability.NOT_NULL
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.withNullability
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
@@ -46,28 +40,51 @@ class FirSyntheticFunctionSymbol(
|
||||
class FirSyntheticPropertiesScope private constructor(
|
||||
val session: FirSession,
|
||||
private val baseScope: FirTypeScope,
|
||||
private val dispatchReceiverType: ConeKotlinType,
|
||||
private val syntheticNamesProvider: FirSyntheticNamesProvider
|
||||
) : FirContainingNamesAwareScope() {
|
||||
companion object {
|
||||
fun createIfSyntheticNamesProviderIsDefined(
|
||||
session: FirSession,
|
||||
dispatchReceiverType: ConeKotlinType,
|
||||
baseScope: FirTypeScope
|
||||
): FirSyntheticPropertiesScope? {
|
||||
val syntheticNamesProvider = session.syntheticNamesProvider
|
||||
return if (syntheticNamesProvider != null)
|
||||
FirSyntheticPropertiesScope(session, baseScope, syntheticNamesProvider)
|
||||
else
|
||||
null
|
||||
val syntheticNamesProvider = session.syntheticNamesProvider ?: return null
|
||||
return FirSyntheticPropertiesScope(
|
||||
session,
|
||||
baseScope,
|
||||
dispatchReceiverType,
|
||||
syntheticNamesProvider
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
|
||||
val getterNames = syntheticNamesProvider.possibleGetterNamesByPropertyName(name)
|
||||
var getterFound = false
|
||||
for (getterName in getterNames) {
|
||||
baseScope.processFunctionsByName(getterName) {
|
||||
checkGetAndCreateSynthetic(name, getterName, it, processor)
|
||||
checkGetAndCreateSynthetic(name, getterName, it, needCheckForSetter = true, processor)
|
||||
getterFound = true
|
||||
}
|
||||
}
|
||||
if (!getterFound && shouldSearchForJavaRecordComponents()) {
|
||||
baseScope.processFunctionsByName(name) {
|
||||
if (it.fir.isJavaRecordComponent == true) {
|
||||
checkGetAndCreateSynthetic(name, name, it, needCheckForSetter = false, processor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldSearchForJavaRecordComponents(): Boolean {
|
||||
/*
|
||||
* Fast path: if dispatch receiver type is simple type and corresponding
|
||||
* class is not a java record then there is no need to additional
|
||||
* search for record components
|
||||
*/
|
||||
val dispatchSymbol = dispatchReceiverType.toRegularClassSymbol(session) ?: return true
|
||||
return dispatchSymbol.fir.isJavaRecord ?: false
|
||||
}
|
||||
|
||||
override fun getCallableNames(): Set<Name> = baseScope.getCallableNames().flatMapTo(hashSetOf()) { propertyName ->
|
||||
@@ -80,6 +97,7 @@ class FirSyntheticPropertiesScope private constructor(
|
||||
propertyName: Name,
|
||||
getterName: Name,
|
||||
getterSymbol: FirFunctionSymbol<*>,
|
||||
needCheckForSetter: Boolean,
|
||||
processor: (FirVariableSymbol<*>) -> Unit
|
||||
) {
|
||||
if (getterSymbol !is FirNamedFunctionSymbol) return
|
||||
@@ -94,7 +112,7 @@ class FirSyntheticPropertiesScope private constructor(
|
||||
if (!getterSymbol.hasJavaOverridden()) return
|
||||
|
||||
var matchingSetter: FirSimpleFunction? = null
|
||||
if (getterReturnType != null) {
|
||||
if (needCheckForSetter && getterReturnType != null) {
|
||||
val setterName = syntheticNamesProvider.setterNameByGetterName(getterName)
|
||||
baseScope.processFunctionsByName(setterName, fun(setterSymbol: FirNamedFunctionSymbol) {
|
||||
if (matchingSetter != null) return
|
||||
|
||||
+5
-1
@@ -105,7 +105,11 @@ class MemberScopeTowerLevel(
|
||||
}
|
||||
|
||||
if (givenExtensionReceiverOptions.isEmpty()) {
|
||||
val withSynthetic = FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(session, scope)
|
||||
val withSynthetic = FirSyntheticPropertiesScope.createIfSyntheticNamesProviderIsDefined(
|
||||
session,
|
||||
dispatchReceiverValue.type,
|
||||
scope
|
||||
)
|
||||
withSynthetic?.processScopeMembers { symbol ->
|
||||
empty = false
|
||||
output.consumeCandidate(symbol, dispatchReceiverValue, givenExtensionReceiverOptions = emptyList(), scope)
|
||||
|
||||
@@ -131,3 +131,10 @@ val FirCallableDeclaration.propertyIfAccessor: FirCallableDeclaration
|
||||
|
||||
val FirCallableDeclaration.propertyIfBackingField: FirCallableDeclaration
|
||||
get() = (this as? FirBackingField)?.propertySymbol?.fir ?: this
|
||||
|
||||
private object IsJavaRecordKey : FirDeclarationDataKey()
|
||||
var FirRegularClass.isJavaRecord: Boolean? by FirDeclarationDataRegistry.data(IsJavaRecordKey)
|
||||
|
||||
private object IsJavaRecordComponentKey : FirDeclarationDataKey()
|
||||
var FirFunction.isJavaRecordComponent: Boolean? by FirDeclarationDataRegistry.data(IsJavaRecordComponentKey)
|
||||
|
||||
|
||||
@@ -191,6 +191,22 @@ sealed class KtFakeSourceElementKind : KtSourceElementKind() {
|
||||
// with a fake source which refers to declared annotation methods
|
||||
object ImplicitAnnotationAnnotationConstructorParameter : KtFakeSourceElementKind()
|
||||
|
||||
// for java records implicit constructor is generated
|
||||
// with a fake source which refers to containing class
|
||||
object ImplicitJavaRecordConstructor : KtFakeSourceElementKind()
|
||||
|
||||
// for java record constructor implicit parameters are generated
|
||||
// with a fake source which refers to declared record components
|
||||
object ImplicitRecordConstructorParameter : KtFakeSourceElementKind()
|
||||
|
||||
// for java records implicit component functions are generated
|
||||
// with a fake source which refers to corresponding component
|
||||
object JavaRecordComponentFunction : KtFakeSourceElementKind()
|
||||
|
||||
// for java records implicit component fields are generated
|
||||
// with a fake source which refers to corresponding component
|
||||
object JavaRecordComponentField : KtFakeSourceElementKind()
|
||||
|
||||
// for the implicit field storing the delegated object for class delegation
|
||||
// with a fake source that refers to the KtExpression that creates the delegate
|
||||
object ClassDelegationField : KtFakeSourceElementKind()
|
||||
|
||||
Vendored
-1
@@ -1,5 +1,4 @@
|
||||
// ENABLE_JVM_PREVIEW
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// FILE: MyRec.java
|
||||
public record MyRec(String name) implements KI {
|
||||
public String getName() {
|
||||
|
||||
Vendored
-1
@@ -1,5 +1,4 @@
|
||||
// ENABLE_JVM_PREVIEW
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// FILE: MyRec.java
|
||||
public record MyRec(String name) {
|
||||
public String getName() {
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +JvmRecordSupport
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// ENABLE_JVM_PREVIEW
|
||||
// FILE: MyRec.java
|
||||
public record MyRec(String name) {}
|
||||
|
||||
Vendored
-1
@@ -1,5 +1,4 @@
|
||||
// WITH_REFLECT
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// ISSUE: KT-47760
|
||||
|
||||
// FILE: MyRecord.java
|
||||
|
||||
+5
-5
@@ -14,11 +14,11 @@ class MyRec(
|
||||
)
|
||||
|
||||
fun foo(jr: JRecord) {
|
||||
JRecord(<!TOO_MANY_ARGUMENTS!>1<!>, <!TOO_MANY_ARGUMENTS!>""<!>)
|
||||
JRecord(1, "")
|
||||
|
||||
jr.<!UNRESOLVED_REFERENCE!>x<!>()
|
||||
jr.<!UNRESOLVED_REFERENCE!>y<!>()
|
||||
jr.x()
|
||||
jr.y()
|
||||
|
||||
jr.<!UNRESOLVED_REFERENCE!>x<!>
|
||||
jr.<!UNRESOLVED_REFERENCE!>y<!>
|
||||
jr.x
|
||||
jr.y
|
||||
}
|
||||
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_IDE_IGNORE
|
||||
// API_VERSION: 1.5
|
||||
// LANGUAGE: +JvmRecordSupport
|
||||
// SCOPE_DUMP: MyRecord:x
|
||||
|
||||
// FILE: MyRecord.java
|
||||
public record MyRecord(String x) {
|
||||
public String x() {
|
||||
System.out.println("hello");
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun takeString(s: String) {}
|
||||
|
||||
fun foo(mr: MyRecord) {
|
||||
takeString(mr.x)
|
||||
takeString(mr.x())
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
MyRecord:
|
||||
[Enhancement]: public open fun x(): R|kotlin/String!| from Java enhancement scope for /MyRecord [id: 0]
|
||||
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ mr: MyRecord): kotlin.Unit
|
||||
public fun takeString(/*0*/ s: kotlin.String): kotlin.Unit
|
||||
|
||||
/*record*/ public final class MyRecord : java.lang.Record {
|
||||
public constructor MyRecord(/*0*/ x: kotlin.String!)
|
||||
public abstract override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public abstract override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
/*record component*/ public open fun x(): kotlin.String!
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_IDE_IGNORE
|
||||
|
||||
// FILE: MyInterface.java
|
||||
public interface MyInterface<T> {
|
||||
T x();
|
||||
}
|
||||
|
||||
// FILE: MyRecord.java
|
||||
public record MyRecord<T>(T x) implements MyInterface<T> {}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun takeInt(x: Int) {}
|
||||
fun takeString(s: String) {}
|
||||
fun takeAny(a: Any) {}
|
||||
|
||||
fun test_1(mr: MyRecord<Int>) {
|
||||
takeInt(mr.x)
|
||||
takeInt(mr.x())
|
||||
}
|
||||
|
||||
fun test_2(mr: MyRecord<String>) {
|
||||
takeString(mr.x)
|
||||
takeString(mr.x())
|
||||
}
|
||||
|
||||
fun test_3(mr: MyRecord<*>) {
|
||||
takeAny(mr.x)
|
||||
takeAny(mr.x())
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package
|
||||
|
||||
public fun takeAny(/*0*/ a: kotlin.Any): kotlin.Unit
|
||||
public fun takeInt(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
public fun takeString(/*0*/ s: kotlin.String): kotlin.Unit
|
||||
public fun test_1(/*0*/ mr: MyRecord<kotlin.Int>): kotlin.Unit
|
||||
public fun test_2(/*0*/ mr: MyRecord<kotlin.String>): kotlin.Unit
|
||||
public fun test_3(/*0*/ mr: MyRecord<*>): kotlin.Unit
|
||||
|
||||
public interface MyInterface</*0*/ T : kotlin.Any!> {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
public abstract fun x(): T!
|
||||
}
|
||||
|
||||
/*record*/ public final class MyRecord</*0*/ T : kotlin.Any!> : java.lang.Record, MyInterface<T!> {
|
||||
public constructor MyRecord</*0*/ T : kotlin.Any!>(/*0*/ x: T!)
|
||||
public abstract override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public abstract override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
/*record component*/ public open override /*1*/ fun x(): T!
|
||||
}
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
// FIR_IDE_IGNORE
|
||||
// !API_VERSION: 1.5
|
||||
// !LANGUAGE: +JvmRecordSupport
|
||||
// FILE: MyRecord.java
|
||||
public record MyRecord(int x, CharSequence y) {
|
||||
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun foo(mr: MyRecord) {
|
||||
MyRecord(<!TOO_MANY_ARGUMENTS!>1<!>, <!TOO_MANY_ARGUMENTS!>""<!>)
|
||||
|
||||
mr.<!UNRESOLVED_REFERENCE!>x<!>()
|
||||
mr.<!UNRESOLVED_REFERENCE!>y<!>()
|
||||
|
||||
mr.<!UNRESOLVED_REFERENCE!>x<!>
|
||||
mr.<!UNRESOLVED_REFERENCE!>y<!>
|
||||
}
|
||||
+17
-10
@@ -1,19 +1,26 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_IDE_IGNORE
|
||||
// !API_VERSION: 1.5
|
||||
// !LANGUAGE: +JvmRecordSupport
|
||||
// FILE: MyRecord.java
|
||||
public record MyRecord(int x, CharSequence y) {
|
||||
// API_VERSION: 1.5
|
||||
// LANGUAGE: +JvmRecordSupport
|
||||
// SCOPE_DUMP: MyRecord:x;y;z
|
||||
|
||||
}
|
||||
// FILE: MyRecord.java
|
||||
public record MyRecord(CharSequence x, int y, String... z) {}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun takeInt(x: Int) {}
|
||||
fun takeCharSequence(s: CharSequence) {}
|
||||
fun takeStringArray(a: Array<out String>) {}
|
||||
|
||||
fun foo(mr: MyRecord) {
|
||||
MyRecord(1, "")
|
||||
MyRecord("", 1, "a", "b")
|
||||
|
||||
mr.x()
|
||||
mr.y()
|
||||
takeCharSequence(mr.x())
|
||||
takeInt(mr.y())
|
||||
takeStringArray(mr.z())
|
||||
|
||||
mr.x
|
||||
mr.y
|
||||
takeCharSequence(mr.x)
|
||||
takeInt(mr.y)
|
||||
takeStringArray(mr.z)
|
||||
}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
MyRecord:
|
||||
[Enhancement]: public final fun x(): R|kotlin/CharSequence!| from Java enhancement scope for /MyRecord [id: 0]
|
||||
[Enhancement]: public final fun y(): R|kotlin/Int| from Java enhancement scope for /MyRecord [id: 0]
|
||||
[Enhancement]: public final fun z(): R|ft<kotlin/Array<kotlin/String!>, kotlin/Array<out kotlin/String!>?>| from Java enhancement scope for /MyRecord [id: 0]
|
||||
|
||||
+8
-3
@@ -1,12 +1,17 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ mr: MyRecord): kotlin.Unit
|
||||
public fun takeCharSequence(/*0*/ s: kotlin.CharSequence): kotlin.Unit
|
||||
public fun takeInt(/*0*/ x: kotlin.Int): kotlin.Unit
|
||||
public fun takeStringArray(/*0*/ a: kotlin.Array<out kotlin.String>): kotlin.Unit
|
||||
|
||||
/*record*/ public final class MyRecord : java.lang.Record {
|
||||
public constructor MyRecord(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.CharSequence!)
|
||||
public constructor MyRecord(/*0*/ x: kotlin.CharSequence!, /*1*/ y: kotlin.Int, /*2*/ vararg z: kotlin.String! /*kotlin.Array<(out) kotlin.String!>!*/)
|
||||
public abstract override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public abstract override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
/*record component*/ public open fun x(): kotlin.Int
|
||||
/*record component*/ public open fun y(): kotlin.CharSequence!
|
||||
/*record component*/ public open fun x(): kotlin.CharSequence!
|
||||
/*record component*/ public open fun y(): kotlin.Int
|
||||
/*record component*/ public open fun z(): kotlin.Array<(out) kotlin.String!>!
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
FILE fqName:<root> fileName:/recordPropertyAccess.kt
|
||||
FUN name:test_1 visibility:public modality:FINAL <> (rec:<root>.MyRec) returnType:kotlin.Unit
|
||||
VALUE_PARAMETER name:rec index:0 type:<root>.MyRec
|
||||
BLOCK_BODY
|
||||
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
|
||||
CALL 'public final fun name (): @[FlexibleNullability] kotlin.String? declared in <root>.MyRec' type=@[FlexibleNullability] kotlin.String? origin=GET_PROPERTY
|
||||
$this: GET_VAR 'rec: <root>.MyRec declared in <root>.test_1' type=<root>.MyRec origin=null
|
||||
FUN name:test_2 visibility:public modality:FINAL <> (rec:<root>.MyRec) returnType:kotlin.Unit
|
||||
VALUE_PARAMETER name:rec index:0 type:<root>.MyRec
|
||||
BLOCK_BODY
|
||||
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
|
||||
CALL 'public final fun name (): @[FlexibleNullability] kotlin.String? declared in <root>.MyRec' type=@[FlexibleNullability] kotlin.String? origin=null
|
||||
$this: GET_VAR 'rec: <root>.MyRec declared in <root>.test_2' type=<root>.MyRec origin=null
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test_1(rec: MyRec) {
|
||||
rec.name() /*~> Unit */
|
||||
}
|
||||
|
||||
fun test_2(rec: MyRec) {
|
||||
rec.name() /*~> Unit */
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
FILE fqName:<root> fileName:/recordPropertyAccess.kt
|
||||
FUN name:test_1 visibility:public modality:FINAL <> (rec:<root>.MyRec) returnType:kotlin.Unit
|
||||
VALUE_PARAMETER name:rec index:0 type:<root>.MyRec
|
||||
BLOCK_BODY
|
||||
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
|
||||
CALL 'public open fun name (): @[FlexibleNullability] kotlin.String? declared in <root>.MyRec' type=@[FlexibleNullability] kotlin.String? origin=GET_PROPERTY
|
||||
$this: GET_VAR 'rec: <root>.MyRec declared in <root>.test_1' type=<root>.MyRec origin=null
|
||||
FUN name:test_2 visibility:public modality:FINAL <> (rec:<root>.MyRec) returnType:kotlin.Unit
|
||||
VALUE_PARAMETER name:rec index:0 type:<root>.MyRec
|
||||
BLOCK_BODY
|
||||
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
|
||||
CALL 'public open fun name (): @[FlexibleNullability] kotlin.String? declared in <root>.MyRec' type=@[FlexibleNullability] kotlin.String? origin=null
|
||||
$this: GET_VAR 'rec: <root>.MyRec declared in <root>.test_2' type=<root>.MyRec origin=null
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// JDK_KIND: FULL_JDK_17
|
||||
// FILE: MyRec.java
|
||||
public record MyRec(String name) {}
|
||||
|
||||
// FILE: recordPropertyAccess.kt
|
||||
fun test_1(rec: MyRec) {
|
||||
rec.name
|
||||
}
|
||||
|
||||
fun test_2(rec: MyRec) {
|
||||
rec.name()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test_1(rec: MyRec) {
|
||||
rec.name() /*~> Unit */
|
||||
}
|
||||
|
||||
fun test_2(rec: MyRec) {
|
||||
rec.name() /*~> Unit */
|
||||
}
|
||||
Generated
+12
@@ -32103,6 +32103,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/irrelevantFields.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithExplicitComponent.kt")
|
||||
public void testJavaRecordWithExplicitComponent() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithExplicitComponent.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordWithGeneric.kt")
|
||||
public void testJavaRecordWithGeneric() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/testsWithJava17/jvmRecord/javaRecordWithGeneric.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRecordDescriptorStructure.kt")
|
||||
public void testJvmRecordDescriptorStructure() throws Exception {
|
||||
|
||||
Generated
+16
@@ -942,6 +942,22 @@ public class IrTextTestGenerated extends AbstractIrTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/jvmRecord")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class JvmRecord {
|
||||
@Test
|
||||
public void testAllFilesPresentInJvmRecord() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/ir/irText/declarations/jvmRecord"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaRecordComponentAccess.kt")
|
||||
public void testJavaRecordComponentAccess() throws Exception {
|
||||
runTest("compiler/testData/ir/irText/declarations/jvmRecord/javaRecordComponentAccess.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/multiplatform")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
@@ -687,6 +687,19 @@ public class KlibTextTestCaseGenerated extends AbstractKlibTextTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/jvmRecord")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class JvmRecord extends AbstractKlibTextTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JS_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInJvmRecord() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/ir/irText/declarations/jvmRecord"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/ir/irText/declarations/multiplatform")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user