[FIR plugin] Add test for plugin which generates annotations on IR
^KT-58638
This commit is contained in:
committed by
Space Team
parent
fd670d33cb
commit
24e07fdfe0
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.assignment.plugin.AbstractFirLightTreeBlackBoxCodege
|
||||
import org.jetbrains.kotlin.assignment.plugin.AbstractFirPsiAssignmentPluginDiagnosticTest
|
||||
import org.jetbrains.kotlin.assignment.plugin.AbstractIrBlackBoxCodegenTestAssignmentPlugin
|
||||
import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirLightTreePluginBlackBoxCodegenTest
|
||||
import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirLoadK2CompiledWithPluginJsKotlinTest
|
||||
import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirLoadK2CompiledWithPluginJvmKotlinTest
|
||||
import org.jetbrains.kotlin.fir.plugin.runners.AbstractFirPsiPluginDiagnosticTest
|
||||
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
|
||||
@@ -265,6 +266,10 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractFirLoadK2CompiledWithPluginJvmKotlinTest> {
|
||||
model("firLoadK2Compiled")
|
||||
}
|
||||
|
||||
testClass<AbstractFirLoadK2CompiledWithPluginJsKotlinTest> {
|
||||
model("firLoadK2Compiled")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup(
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
testApi(projectTests(":compiler:test-infrastructure"))
|
||||
testApi(projectTests(":compiler:test-infrastructure-utils"))
|
||||
testApi(projectTests(":compiler:fir:analysis-tests"))
|
||||
testApi(projectTests(":js:js.tests"))
|
||||
testApi(project(":compiler:fir:checkers"))
|
||||
testApi(project(":compiler:fir:checkers:checkers.jvm"))
|
||||
testApi(project(":compiler:fir:checkers:checkers.js"))
|
||||
|
||||
+4
@@ -39,3 +39,7 @@ annotation class MetaSupertype
|
||||
annotation class MyComposable
|
||||
|
||||
annotation class AllPropertiesConstructor
|
||||
|
||||
annotation class AddAnnotations
|
||||
|
||||
annotation class AnnotationToAdd
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@ class GeneratedDeclarationsIrBodyFiller : IrGenerationExtension {
|
||||
TransformerForCompanionGenerator(pluginContext),
|
||||
TransformerForAdditionalMembersGenerator(pluginContext),
|
||||
TransformerForTopLevelDeclarationsGenerator(pluginContext),
|
||||
AllPropertiesConstructorIrGenerator(pluginContext)
|
||||
AllPropertiesConstructorIrGenerator(pluginContext),
|
||||
TransformerForAddingAnnotations(pluginContext),
|
||||
)
|
||||
|
||||
for (transformer in transformers) {
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.ir.plugin
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class TransformerForAddingAnnotations(val context: IrPluginContext) : IrElementVisitorVoid {
|
||||
companion object {
|
||||
private val markerAnnotationFqName = FqName("org.jetbrains.kotlin.fir.plugin.AddAnnotations")
|
||||
private val annotationToAddId = ClassId(FqName("org.jetbrains.kotlin.fir.plugin"), Name.identifier("AnnotationToAdd"))
|
||||
private val annotationToAddFqName = annotationToAddId.asSingleFqName()
|
||||
}
|
||||
|
||||
private val annotationsAdder = AnnotationsAdder()
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
when (element) {
|
||||
is IrFile,
|
||||
is IrModuleFragment -> element.acceptChildrenVoid(this)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
if (declaration.hasAnnotation(markerAnnotationFqName)) {
|
||||
declaration.acceptVoid(annotationsAdder)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AnnotationsAdder : IrElementVisitorVoid {
|
||||
val annotationClass = context.referenceClass(annotationToAddId)?.takeIf { it.owner.isAnnotationClass }
|
||||
|
||||
override fun visitElement(element: IrElement, data: Nothing?) {}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
addAnnotation(declaration)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
if (declaration.isFakeOverride) return
|
||||
visitDeclaration(declaration)
|
||||
}
|
||||
|
||||
private fun addAnnotation(declaration: IrDeclarationBase) {
|
||||
if (declaration.hasAnnotation(annotationToAddFqName)) return
|
||||
val annotationClass = annotationClass ?: return
|
||||
val annotationConstructor = annotationClass.owner.constructors.first()
|
||||
val annotationCall = IrConstructorCallImpl.fromSymbolOwner(
|
||||
type = annotationClass.defaultType,
|
||||
constructorSymbol = annotationConstructor.symbol
|
||||
)
|
||||
declaration.annotations += annotationCall
|
||||
}
|
||||
}
|
||||
}
|
||||
plugins/fir-plugin-prototype/testData/firLoadK2Compiled/annotationsGeneratedInBackend.fir.k2.jvm.txt
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AddAnnotations|() @R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() public final class Some : R|kotlin/Any| {
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() public final fun foo(): R|kotlin/Unit|
|
||||
|
||||
@PROPERTY:R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() field:@FIELD:R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() public final val x: R|kotlin/Int|
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() public get(): R|kotlin/Int|
|
||||
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() public constructor(@R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() x: R|kotlin/Int|): R|test/Some|
|
||||
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() public final class Derived : R|kotlin/Any| {
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AnnotationToAdd|() public constructor(): R|test/Some.Derived|
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AddAnnotations|() public final class Some : R|kotlin/Any| {
|
||||
public final fun foo(): R|kotlin/Unit|
|
||||
|
||||
public final val x: R|kotlin/Int|
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public constructor(x: R|kotlin/Int|): R|test/Some|
|
||||
|
||||
public final class Derived : R|kotlin/Any| {
|
||||
public constructor(): R|test/Some.Derived|
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
package test
|
||||
|
||||
@AddAnnotations
|
||||
@AnnotationToAdd
|
||||
class Some {
|
||||
@AnnotationToAdd
|
||||
constructor(@AnnotationToAdd x: Int) /* primary */ {
|
||||
super/*Any*/()
|
||||
/* <init>() */
|
||||
|
||||
}
|
||||
|
||||
@AnnotationToAdd
|
||||
val x: Int
|
||||
field = x
|
||||
@AnnotationToAdd
|
||||
get
|
||||
|
||||
@AnnotationToAdd
|
||||
fun foo() {
|
||||
}
|
||||
|
||||
@AnnotationToAdd
|
||||
class Derived {
|
||||
@AnnotationToAdd
|
||||
constructor() /* primary */ {
|
||||
super/*Any*/()
|
||||
/* <init>() */
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// PLATFORM_DEPENDANT_METADATA
|
||||
// DUMP_KT_IR
|
||||
package test
|
||||
|
||||
import org.jetbrains.kotlin.fir.plugin.AddAnnotations
|
||||
|
||||
@AddAnnotations
|
||||
class Some(val x: Int) {
|
||||
fun foo() {}
|
||||
|
||||
class Derived
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.plugin.runners;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateTestsKt}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("plugins/fir-plugin-prototype/testData/firLoadK2Compiled")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class FirLoadK2CompiledWithPluginJsKotlinTestGenerated extends AbstractFirLoadK2CompiledWithPluginJsKotlinTest {
|
||||
@Test
|
||||
public void testAllFilesPresentInFirLoadK2Compiled() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/fir-plugin-prototype/testData/firLoadK2Compiled"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationsGeneratedInBackend.kt")
|
||||
public void testAnnotationsGeneratedInBackend() throws Exception {
|
||||
runTest("plugins/fir-plugin-prototype/testData/firLoadK2Compiled/annotationsGeneratedInBackend.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simple-lang-ver-2.1.kt")
|
||||
public void testSimple_lang_ver_2_1() throws Exception {
|
||||
runTest("plugins/fir-plugin-prototype/testData/firLoadK2Compiled/simple-lang-ver-2.1.kt");
|
||||
}
|
||||
}
|
||||
+6
@@ -25,6 +25,12 @@ public class FirLoadK2CompiledWithPluginJvmKotlinTestGenerated extends AbstractF
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/fir-plugin-prototype/testData/firLoadK2Compiled"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("annotationsGeneratedInBackend.kt")
|
||||
public void testAnnotationsGeneratedInBackend() throws Exception {
|
||||
runTest("plugins/fir-plugin-prototype/testData/firLoadK2Compiled/annotationsGeneratedInBackend.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
|
||||
+21
@@ -7,7 +7,11 @@ package org.jetbrains.kotlin.fir.plugin.runners
|
||||
|
||||
import org.jetbrains.kotlin.fir.plugin.services.ExtensionRegistrarConfigurator
|
||||
import org.jetbrains.kotlin.fir.plugin.services.PluginAnnotationsProvider
|
||||
import org.jetbrains.kotlin.fir.plugin.services.PluginRuntimeAnnotationsProvider
|
||||
import org.jetbrains.kotlin.js.test.fir.AbstractFirLoadK2CompiledJsKotlinTest
|
||||
import org.jetbrains.kotlin.test.backend.handlers.IrPrettyKotlinDumpHandler
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.builders.configureIrHandlersStep
|
||||
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.ENABLE_PLUGIN_PHASES
|
||||
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.FIR_DUMP
|
||||
import org.jetbrains.kotlin.test.runners.AbstractFirLoadK2CompiledJvmKotlinTest
|
||||
@@ -30,6 +34,18 @@ abstract class AbstractFirPsiPluginDiagnosticTest : AbstractFirPsiDiagnosticTest
|
||||
}
|
||||
|
||||
open class AbstractFirLoadK2CompiledWithPluginJvmKotlinTest : AbstractFirLoadK2CompiledJvmKotlinTest() {
|
||||
override fun configure(builder: TestConfigurationBuilder) {
|
||||
super.configure(builder)
|
||||
with(builder) {
|
||||
commonFirWithPluginFrontendConfiguration()
|
||||
configureIrHandlersStep {
|
||||
useHandlers(::IrPrettyKotlinDumpHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class AbstractFirLoadK2CompiledWithPluginJsKotlinTest : AbstractFirLoadK2CompiledJsKotlinTest() {
|
||||
override fun configure(builder: TestConfigurationBuilder) {
|
||||
super.configure(builder)
|
||||
builder.commonFirWithPluginFrontendConfiguration()
|
||||
@@ -48,4 +64,9 @@ fun TestConfigurationBuilder.commonFirWithPluginFrontendConfiguration() {
|
||||
::PluginAnnotationsProvider,
|
||||
::ExtensionRegistrarConfigurator
|
||||
)
|
||||
|
||||
useCustomRuntimeClasspathProviders(
|
||||
::PluginRuntimeAnnotationsProvider
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+68
-24
@@ -7,38 +7,82 @@ package org.jetbrains.kotlin.fir.plugin.services
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.platform.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.assertions
|
||||
import java.io.File
|
||||
import java.io.FilenameFilter
|
||||
|
||||
class PluginAnnotationsProvider(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||
companion object {
|
||||
private const val ANNOTATIONS_JAR_DIR = "plugins/fir-plugin-prototype/plugin-annotations/build/libs/"
|
||||
private val ANNOTATIONS_JAR_FILTER = FilenameFilter { _, name -> name.startsWith("plugin-annotations") && name.endsWith(".jar") }
|
||||
}
|
||||
|
||||
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule) {
|
||||
val pluginAnnotationsJar = findJarFromProperty()
|
||||
?: findJarByPath()
|
||||
?: error("Jar with annotations does not exist. Please run :plugins:fir-plugin-prototype:plugin-annotations:jar or specify firPluginAnnotations.path system property")
|
||||
configuration.addJvmClasspathRoot(pluginAnnotationsJar)
|
||||
}
|
||||
|
||||
private fun findJarByPath(): File? {
|
||||
val libDir = File(ANNOTATIONS_JAR_DIR)
|
||||
if (!libDir.exists() || !libDir.isDirectory) return null
|
||||
return libDir.listFiles(ANNOTATIONS_JAR_FILTER)?.firstOrNull()
|
||||
}
|
||||
|
||||
private fun findJarFromProperty(): File? {
|
||||
val firPluginAnnotationsPath = System.getProperty("firPluginAnnotations.path") ?: return null
|
||||
return File(firPluginAnnotationsPath).takeIf {
|
||||
it.isFile &&
|
||||
it.name.startsWith("plugin-annotations") &&
|
||||
it.name.endsWith(".jar")
|
||||
// TODO: handle property
|
||||
val platform = module.targetPlatform
|
||||
when {
|
||||
platform.isJvm() -> {
|
||||
val jar = findJvmLib()
|
||||
configuration.addJvmClasspathRoot(jar)
|
||||
}
|
||||
platform.isJs() -> {
|
||||
val jar = findJsLib()
|
||||
val libraries = configuration.getList(JSConfigurationKeys.LIBRARIES)
|
||||
configuration.put(JSConfigurationKeys.LIBRARIES, libraries + jar.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PluginRuntimeAnnotationsProvider(testServices: TestServices) : RuntimeClasspathProvider(testServices) {
|
||||
override fun runtimeClassPaths(module: TestModule): List<File> {
|
||||
if (!module.targetPlatform.isJs()) return emptyList()
|
||||
val jar = findJsLib()
|
||||
return listOf(jar)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private const val ANNOTATIONS_JAR_DIR = "plugins/fir-plugin-prototype/plugin-annotations/build/libs/"
|
||||
private val JVM_ANNOTATIONS_JAR_FILTER = createFilter("plugin-annotations-jvm", ".jar")
|
||||
private val JS_ANNOTATIONS_KLIB_FILTER = createFilter("plugin-annotations-js", ".klib")
|
||||
|
||||
private fun findJvmLib(): File {
|
||||
return findLib("jvm", ".jar", JVM_ANNOTATIONS_JAR_FILTER)
|
||||
}
|
||||
|
||||
private fun findJsLib(): File {
|
||||
return findLib("js", ".klib", JS_ANNOTATIONS_KLIB_FILTER)
|
||||
}
|
||||
|
||||
@Suppress("warnings") // TODO
|
||||
private fun findLib(platform: String, extension: String, filter: FilenameFilter): File {
|
||||
return findLibFromProperty(platform, extension)
|
||||
?: findLibByPath(filter)
|
||||
?: error("Lib with annotations does not exist. Please run :plugins:fir-plugin-prototype:plugin-annotations:distAnnotations or specify firPluginAnnotations.path system property")
|
||||
}
|
||||
|
||||
private fun createFilter(pattern: String, extension: String): FilenameFilter {
|
||||
return FilenameFilter { _, name -> name.startsWith(pattern) && name.endsWith(extension) }
|
||||
}
|
||||
|
||||
private fun findLibByPath(filter: FilenameFilter): File? {
|
||||
val libDir = File(ANNOTATIONS_JAR_DIR)
|
||||
if (!libDir.exists() || !libDir.isDirectory) return null
|
||||
return libDir.listFiles(filter)?.firstOrNull()
|
||||
}
|
||||
|
||||
/*
|
||||
* Possible properties:
|
||||
* - firPluginAnnotations.jvm.path
|
||||
* - firPluginAnnotations.js.path
|
||||
*/
|
||||
private fun findLibFromProperty(platform: String, extension: String): File? {
|
||||
val firPluginAnnotationsPath = System.getProperty("firPluginAnnotations.${platform}.path") ?: return null
|
||||
return File(firPluginAnnotationsPath).takeIf {
|
||||
it.isFile &&
|
||||
it.name.startsWith("plugin-annotations") &&
|
||||
it.name.endsWith(extension)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user