[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
@@ -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