[KAPT] KT-44706 Support @JvmRecord-annotated classes
Don't parse Java stubs in tests if no validation is needed Merge-request: KT-MR-14308 Merged-by: Pavel Mikhailovskii <Pavel.Mikhailovskii@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
6288bdbd3f
commit
12552e4e04
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* public final annotation class A : kotlin/Annotation {
|
||||
*
|
||||
* // signature: <init>()V
|
||||
* public constructor()
|
||||
*
|
||||
* // module name: main
|
||||
* }
|
||||
*/
|
||||
@kotlin.Metadata()
|
||||
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
public abstract @interface A {
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
|
||||
/**
|
||||
* public abstract interface I : kotlin/Any {
|
||||
*
|
||||
* // signature: foo()I
|
||||
* public abstract fun foo(): kotlin/Int
|
||||
*
|
||||
* // module name: main
|
||||
* }
|
||||
*/
|
||||
@kotlin.Metadata()
|
||||
public abstract interface I {
|
||||
|
||||
public abstract int foo();
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
|
||||
/**
|
||||
* public final data class R<T#0 (* T *)> : I, java/lang/Record {
|
||||
*
|
||||
* // signature: <init>(ILjava/lang/Object;)V
|
||||
* public constructor(x: kotlin/Int, y: T#0)
|
||||
*
|
||||
* // signature: <init>(Ljava/lang/Object;)V
|
||||
* public (* secondary *) constructor(y: T#0)
|
||||
*
|
||||
* // signature: component1()I
|
||||
* public final (* synthesized *) operator fun component1(): kotlin/Int
|
||||
*
|
||||
* // signature: equals(Ljava/lang/Object;)Z
|
||||
* public open (* synthesized *) operator fun equals(other: kotlin/Any?): kotlin/Boolean
|
||||
*
|
||||
* // signature: foo()I
|
||||
* public open fun foo(): kotlin/Int
|
||||
*
|
||||
* // signature: hashCode()I
|
||||
* public open (* synthesized *) fun hashCode(): kotlin/Int
|
||||
*
|
||||
* public final (* synthesized *) operator fun component2(): T#0
|
||||
*
|
||||
* public final (* synthesized *) fun copy(x: kotlin/Int (* = ... *), y: T#0 (* = ... *)): R<T#0>
|
||||
*
|
||||
* // signature: toString()Ljava/lang/String;
|
||||
* public open (* synthesized *) fun toString(): kotlin/String
|
||||
*
|
||||
* // field: x:I
|
||||
* public final val x: kotlin/Int
|
||||
* public final get
|
||||
*
|
||||
* // field: y:Ljava/lang/Object;
|
||||
* public final val y: T#0
|
||||
* public final get
|
||||
*
|
||||
* // module name: main
|
||||
* }
|
||||
*/
|
||||
@kotlin.Metadata()
|
||||
@A()
|
||||
@kotlin.jvm.JvmRecord()
|
||||
public record R<T extends java.lang.Object>(@A() int x, T y) implements I {
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final R<T> copy(@A() int x, T y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean equals(@org.jetbrains.annotations.Nullable() java.lang.Object other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public java.lang.String toString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final int component1() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final int x() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final T component2() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final T y() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public R(T y) {
|
||||
this(0, null);
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public int foo() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// JDK_KIND: FULL_JDK_21
|
||||
// NO_VALIDATION
|
||||
annotation class A
|
||||
|
||||
interface I {
|
||||
fun foo(): Int
|
||||
}
|
||||
|
||||
@A
|
||||
@JvmRecord
|
||||
data class R<T>(@A val x: Int, val y: T): I {
|
||||
constructor(y: T) : this(0, y)
|
||||
|
||||
override fun foo(): Int = 0
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
/**
|
||||
* public final annotation class A : kotlin/Annotation {
|
||||
*
|
||||
* // signature: <init>()V
|
||||
* public constructor()
|
||||
*
|
||||
* // module name: main
|
||||
* }
|
||||
*/
|
||||
@kotlin.Metadata()
|
||||
public abstract @interface A {
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* public abstract interface I : kotlin/Any {
|
||||
*
|
||||
* // signature: foo()I
|
||||
* public abstract fun foo(): kotlin/Int
|
||||
*
|
||||
* // module name: main
|
||||
* }
|
||||
*/
|
||||
@kotlin.Metadata()
|
||||
public abstract interface I {
|
||||
|
||||
public abstract int foo();
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
@A()
|
||||
/**
|
||||
* public final data class R<T#0 (* T *)> : I, java/lang/Record {
|
||||
*
|
||||
* // signature: <init>(ILjava/lang/Object;)V
|
||||
* public constructor(x: kotlin/Int, y: T#0)
|
||||
*
|
||||
* // signature: <init>(Ljava/lang/Object;)V
|
||||
* public (* secondary *) constructor(y: T#0)
|
||||
*
|
||||
* // signature: component1()I
|
||||
* public final (* synthesized *) operator fun component1(): kotlin/Int
|
||||
*
|
||||
* // signature: component2()Ljava/lang/Object;
|
||||
* public final (* synthesized *) operator fun component2(): T#0
|
||||
*
|
||||
* // signature: copy(ILjava/lang/Object;)LR;
|
||||
* public final (* synthesized *) fun copy(x: kotlin/Int (* = ... *), y: T#0 (* = ... *)): R<T#0>
|
||||
*
|
||||
* // signature: equals(Ljava/lang/Object;)Z
|
||||
* public open (* synthesized *) operator fun equals(other: kotlin/Any?): kotlin/Boolean
|
||||
*
|
||||
* // signature: foo()I
|
||||
* public open fun foo(): kotlin/Int
|
||||
*
|
||||
* // signature: hashCode()I
|
||||
* public open (* synthesized *) fun hashCode(): kotlin/Int
|
||||
*
|
||||
* // signature: toString()Ljava/lang/String;
|
||||
* public open (* synthesized *) fun toString(): kotlin/String
|
||||
*
|
||||
* // field: x:I
|
||||
* // getter: x()I
|
||||
* public final val x: kotlin/Int
|
||||
* public final get
|
||||
*
|
||||
* // field: y:Ljava/lang/Object;
|
||||
* // getter: y()Ljava/lang/Object;
|
||||
* public final val y: T#0
|
||||
* public final get
|
||||
*
|
||||
* // module name: main
|
||||
* }
|
||||
*/
|
||||
@kotlin.Metadata()
|
||||
@kotlin.jvm.JvmRecord()
|
||||
public final class R<T extends java.lang.Object> extends java.lang.Record implements I {
|
||||
private final int x = 0;
|
||||
private final T y = null;
|
||||
|
||||
public R(@A()
|
||||
int x, T y) {
|
||||
super();
|
||||
}
|
||||
|
||||
public final int x() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final T y() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public R(T y) {
|
||||
super();
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public int foo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final int component1() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public final T component2() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final R<T> copy(@A()
|
||||
int x, T y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public boolean equals(@org.jetbrains.annotations.Nullable()
|
||||
java.lang.Object other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public java.lang.String toString() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+6
@@ -389,6 +389,12 @@ public class IrClassFileToSourceStubConverterTestGenerated extends AbstractIrCla
|
||||
runTest("plugins/kapt3/kapt3-compiler/testData/converter/jvmOverloads.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRecord.kt")
|
||||
public void testJvmRecord() {
|
||||
runTest("plugins/kapt3/kapt3-compiler/testData/converter/jvmRecord.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRepeatableAnnotation.kt")
|
||||
public void testJvmRepeatableAnnotation() {
|
||||
|
||||
@@ -218,33 +218,40 @@ private class StubGenerator(
|
||||
|
||||
printComment(psiClass)
|
||||
|
||||
calculateMetadata(psiClass)?.let { printMetadata(it) }
|
||||
printModifiers(psiClass)
|
||||
val classWord = when {
|
||||
psiClass.isAnnotationType -> "@interface"
|
||||
psiClass.isInterface -> "interface"
|
||||
psiClass.isEnum -> "enum"
|
||||
psiClass.isRecord -> "record"
|
||||
else -> "class"
|
||||
}
|
||||
calculateMetadata(psiClass)?.let { printMetadata(it) }
|
||||
printModifiers(psiClass)
|
||||
printWithNoIndent(classWord, " ", simpleName)
|
||||
printTypeParams(psiClass.typeParameters)
|
||||
|
||||
psiClass.extendsList
|
||||
?.referencedTypes
|
||||
?.asList()
|
||||
?.let { if (!psiClass.isInterface) it.take(1) else it }
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let { superClasses ->
|
||||
printWithNoIndent(" extends ")
|
||||
superClasses.forEachIndexed { index, type ->
|
||||
if (index > 0) printWithNoIndent(", ")
|
||||
printType(type)
|
||||
if (psiClass.isRecord) {
|
||||
printParameters(psiClass.constructors.first())
|
||||
}
|
||||
|
||||
if (!psiClass.isRecord) {
|
||||
psiClass.extendsList
|
||||
?.referencedTypes
|
||||
?.asList()
|
||||
?.let { if (!psiClass.isInterface) it.take(1) else it }
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let { superClasses ->
|
||||
printWithNoIndent(" extends ")
|
||||
superClasses.forEachIndexed { index, type ->
|
||||
if (index > 0) printWithNoIndent(", ")
|
||||
printType(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
psiClass.implementsList
|
||||
?.referencedTypes
|
||||
?.filterNot { it.canonicalText.startsWith("kotlin.collections.") }
|
||||
?.filterNot { it.qualifiedName.startsWith("kotlin.collections.") || it.qualifiedName == "java.lang.Record" }
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let { interfaces ->
|
||||
printWithNoIndent(" implements ")
|
||||
@@ -253,6 +260,7 @@ private class StubGenerator(
|
||||
printType(type)
|
||||
}
|
||||
}
|
||||
|
||||
printlnWithNoIndent(" {")
|
||||
pushIndent()
|
||||
|
||||
@@ -278,9 +286,12 @@ private class StubGenerator(
|
||||
.onEach { lineMappings.registerField(psiClass, it) }
|
||||
.associateWith { MemberData(it.name, it.signature, lineMappings.getPosition(psiClass, it)) }
|
||||
|
||||
fieldsPositions.keys.sortedWith(MembersPositionComparator(classPosition, fieldsPositions)).forEachIndexed { index, field ->
|
||||
if (index > 0) printlnWithNoIndent()
|
||||
printField(field)
|
||||
if (!psiClass.isRecord) {
|
||||
fieldsPositions.keys.sortedWith(MembersPositionComparator(classPosition, fieldsPositions))
|
||||
.forEachIndexed { index, field ->
|
||||
if (index > 0) printlnWithNoIndent()
|
||||
printField(field)
|
||||
}
|
||||
}
|
||||
|
||||
val methodsPositions = psiClass.methods
|
||||
@@ -292,12 +303,13 @@ private class StubGenerator(
|
||||
.onEach { lineMappings.registerMethod(psiClass, it) }
|
||||
.associateWith { MemberData(it.name, it.signature, lineMappings.getPosition(psiClass, it)) }
|
||||
|
||||
if (methodsPositions.isNotEmpty()) printlnWithNoIndent()
|
||||
methodsPositions.keys.sortedWith(MembersPositionComparator(classPosition, methodsPositions))
|
||||
.forEachIndexed { index, method ->
|
||||
.forEach { method ->
|
||||
lineMappings.registerSignature(javacSignature(method), method)
|
||||
if (index > 0) printlnWithNoIndent()
|
||||
printMethod(method)
|
||||
if (!psiClass.isRecord || method != psiClass.constructors.firstOrNull()) {
|
||||
printlnWithNoIndent()
|
||||
printMethod(method)
|
||||
}
|
||||
}
|
||||
|
||||
if (psiClass.innerClasses.isNotEmpty() && (fieldsPositions.isNotEmpty() || methodsPositions.isNotEmpty())) println()
|
||||
@@ -350,14 +362,8 @@ private class StubGenerator(
|
||||
printType(it)
|
||||
printWithNoIndent(" ")
|
||||
}
|
||||
printWithNoIndent(method.name, "(")
|
||||
method.parameterList.parameters.filter { isValidIdentifier(paramName(it)) }.forEachIndexed { index, param ->
|
||||
if (index > 0) printWithNoIndent(", ")
|
||||
printModifiers(param)
|
||||
printType(param.type)
|
||||
printWithNoIndent(" ", paramName(param))
|
||||
}
|
||||
printWithNoIndent(")")
|
||||
printWithNoIndent(method.name)
|
||||
printParameters(method)
|
||||
(method as? PsiAnnotationMethod)?.defaultValue?.let {
|
||||
printWithNoIndent(" default ")
|
||||
printAnnotationMemberValue(it)
|
||||
@@ -378,10 +384,10 @@ private class StubGenerator(
|
||||
pushIndent()
|
||||
|
||||
if (method.isConstructor && !psiClass.isEnum) {
|
||||
val superConstructor = method.containingClass?.superClass?.constructors?.firstOrNull { !it.isPrivate }
|
||||
if (superConstructor != null) {
|
||||
print("super(")
|
||||
val args = superConstructor.parameterList.parameters.map { defaultValue(it.type) }
|
||||
val delegateTo = (if (psiClass.isRecord) psiClass else psiClass.superClass)?.constructors?.firstOrNull { !it.isPrivate }
|
||||
if (delegateTo != null) {
|
||||
print(if (psiClass.isRecord) "this(" else "super(")
|
||||
val args = delegateTo.parameterList.parameters.map { defaultValue(it.type) }
|
||||
args.forEachIndexed { index, arg ->
|
||||
if (index > 0) printWithNoIndent(", ")
|
||||
printWithNoIndent(arg)
|
||||
@@ -396,6 +402,17 @@ private class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun Printer.printParameters(method: PsiMethod) {
|
||||
printWithNoIndent("(")
|
||||
method.parameterList.parameters.filter { isValidIdentifier(paramName(it)) }.forEachIndexed { index, param ->
|
||||
if (index > 0) printWithNoIndent(", ")
|
||||
printModifiers(param)
|
||||
printType(param.type)
|
||||
printWithNoIndent(" ", paramName(param))
|
||||
}
|
||||
printWithNoIndent(")")
|
||||
}
|
||||
|
||||
private fun javacSignature(method: PsiMethod) = printToString {
|
||||
print(method.name, "(")
|
||||
method.parameterList.parameters.forEachIndexed{ index, parameter ->
|
||||
@@ -525,6 +542,7 @@ private class StubGenerator(
|
||||
onError("Support for interface methods with bodies in Kapt requires -Xjvm-default=all or -Xjvm-default=all-compatibility compiler option")
|
||||
}
|
||||
|
||||
if (modifier == PsiModifier.FINAL && modifierListOwner is PsiClass && modifierListOwner.isRecord) continue
|
||||
if ((modifier != PsiModifier.FINAL && modifier != PsiModifier.ABSTRACT) || !(modifierListOwner is PsiClass && modifierListOwner.isEnum)) {
|
||||
printWithNoIndent(modifier, " ")
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeTokenProvider
|
||||
import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementLifetimeTokenProvider
|
||||
import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.kapt3.base.KaptContext
|
||||
@@ -47,12 +46,12 @@ internal class Kapt4Facade(private val testServices: TestServices) :
|
||||
val configuration = configurationProvider.getCompilerConfiguration(module)
|
||||
configuration.addKotlinSourceRoots(module.files.filter { it.isKtFile }.map { it.realFile().absolutePath })
|
||||
val options = testServices.kaptOptionsProvider[module]
|
||||
val (context, stubMap) = run(
|
||||
val (context, stubs) = run(
|
||||
configuration,
|
||||
options,
|
||||
configurationProvider.testRootDisposable
|
||||
)
|
||||
return Kapt4ContextBinaryArtifact(context, stubMap.values.filterNotNull())
|
||||
return Kapt4ContextBinaryArtifact(context, stubs)
|
||||
}
|
||||
|
||||
private fun TestFile.realFile(): File {
|
||||
@@ -69,7 +68,7 @@ private fun run(
|
||||
configuration: CompilerConfiguration,
|
||||
options: KaptOptions,
|
||||
projectDisposable: Disposable,
|
||||
): Pair<KaptContext, Map<KtLightClass, KaptStub?>> {
|
||||
): Pair<KaptContext, List<KaptStub>> {
|
||||
val standaloneAnalysisAPISession = buildStandaloneAnalysisAPISession(projectDisposable) {
|
||||
(project as MockProject).registerService(
|
||||
KtLifetimeTokenProvider::class.java,
|
||||
@@ -111,7 +110,8 @@ private fun run(
|
||||
}
|
||||
}
|
||||
|
||||
return context to generateStubs(module, files, options, logger, metadataRenderer = { renderMetadata(it) })
|
||||
val stubsMap = generateStubs(module, files, options, logger, metadataRenderer = { renderMetadata(it) })
|
||||
return context to stubsMap.entries.sortedBy { it.key.qualifiedName }.mapNotNull { it.value }
|
||||
}
|
||||
|
||||
internal data class Kapt4ContextBinaryArtifact(
|
||||
|
||||
@@ -41,26 +41,24 @@ internal class Kapt4Handler(testServices: TestServices) : AnalysisHandler<Kapt4C
|
||||
get() = Kapt4ContextBinaryArtifact.Kind
|
||||
|
||||
override fun processModule(module: TestModule, info: Kapt4ContextBinaryArtifact) {
|
||||
val validate = KaptTestDirectives.NO_VALIDATION !in module.directives
|
||||
|
||||
val (kaptContext) = info
|
||||
val convertedFiles = getJavaFiles(info)
|
||||
kaptContext.javaLog.interceptorData.files = convertedFiles.associateBy { it.sourceFile }
|
||||
if (validate) kaptContext.compiler.enterTrees(convertedFiles)
|
||||
|
||||
val actualRaw = convertedFiles
|
||||
.sortedBy { it.sourceFile.name }
|
||||
.joinToString(ClassFileToSourceKaptStubHandler.FILE_SEPARATOR) { (it.sourceFile as KaptJavaFileObject).file!!.readText() }
|
||||
|
||||
val stubs = info.kaptStubs.map { it.source }
|
||||
val actualRaw = stubs.joinToString(ClassFileToSourceKaptStubHandler.FILE_SEPARATOR)
|
||||
val actual = StringUtil.convertLineSeparators(actualRaw.trim { it <= ' ' })
|
||||
.trimTrailingWhitespacesAndAddNewlineAtEOF()
|
||||
.let { removeMetadataAnnotationContents(it) }
|
||||
|
||||
val validate = KaptTestDirectives.NO_VALIDATION !in module.directives
|
||||
if (validate) {
|
||||
val (kaptContext) = info
|
||||
val convertedFiles = getJavaFiles(info)
|
||||
kaptContext.javaLog.interceptorData.files = convertedFiles.associateBy { it.sourceFile }
|
||||
kaptContext.compiler.enterTrees(convertedFiles)
|
||||
}
|
||||
assertions.assertAll(
|
||||
{ assertions.checkTxt(module, actual) },
|
||||
{
|
||||
if (kaptContext.compiler.shouldStop(CompileStates.CompileState.ENTER)) {
|
||||
checkJavaCompilerErrors(module, kaptContext, actual)
|
||||
if (info.kaptContext.compiler.shouldStop(CompileStates.CompileState.ENTER)) {
|
||||
checkJavaCompilerErrors(module, info.kaptContext, actual)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
+6
@@ -389,6 +389,12 @@ public class KotlinKapt4ContextTestGenerated extends AbstractKotlinKapt4ContextT
|
||||
runTest("plugins/kapt4/../kapt3/kapt3-compiler/testData/converter/jvmOverloads.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRecord.kt")
|
||||
public void testJvmRecord() {
|
||||
runTest("plugins/kapt4/../kapt3/kapt3-compiler/testData/converter/jvmRecord.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jvmRepeatableAnnotation.kt")
|
||||
public void testJvmRepeatableAnnotation() {
|
||||
|
||||
Reference in New Issue
Block a user