Add CodegenApplicabilityCheckerExtension and use it to fallback to Heavy LigthClasses

+ Fixed #KT-33584
This commit is contained in:
Igor Yakovlev
2019-09-05 21:43:08 +03:00
parent f3b7d2fca9
commit 2b7dee6f8d
23 changed files with 528 additions and 138 deletions
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.types.TypeUtils
interface AnnotationBasedExtension {
fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String>
fun DeclarationDescriptor.hasSpecialAnnotation(modifierListOwner: KtModifierListOwner?): Boolean {
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2019 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.extensions
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtDeclaration
enum class LightClassApplicabilityType {
LightClass,
UltraLightClass
}
/**
* This extension point needs to be implemented by the Kotlin compiler plugins in case they change the requested declaration (i.e. producing synthetic parts)
* with backend extension points. The light class provider will request this EP to check either to create LightClass or UltraLight class implementations.
* @see org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
* @see org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
*/
interface LightClassApplicabilityCheckExtension {
companion object : ProjectExtensionDescriptor<LightClassApplicabilityCheckExtension>(
"org.jetbrains.kotlin.lightClassApplicabilityCheckExtension",
LightClassApplicabilityCheckExtension::class.java
)
/**
* This method should return LightClass if any changes in Kotlin declarations is going to be produced during JVM-backend code generation.
* This method ought to be as fast as possible but never return UltraLightClass when not sure.
* Next advice could be given to make best:
* 1) Try to cover as much cases as possible without forcing descriptor evaluation
* 2) After you've forced descriptor evaluation, ideally, you should never return LightClass while in fact no synthetic parts going to be generated
* 3) So, you should force descriptor evaluation only if you're sure that you will be able to return UltraLightClass significantly more often
* @see org.jetbrains.kotlin.noarg.ide.IdeNoArgApplicabilityExtension
* @see org.jetbrains.kotlin.android.parcel.IDEParcelableApplicabilityExtension
*/
fun checkApplicabilityType(declaration: KtDeclaration, descriptor: Lazy<DeclarationDescriptor?>): LightClassApplicabilityType
}
@@ -43,6 +43,8 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.extensions.LightClassApplicabilityType
import org.jetbrains.kotlin.extensions.LightClassApplicabilityCheckExtension
import org.jetbrains.kotlin.idea.caches.lightClasses.IDELightClassContexts
import org.jetbrains.kotlin.idea.caches.lightClasses.LazyLightClassDataHolder
import org.jetbrains.kotlin.idea.facet.KotlinFacet
@@ -53,7 +55,6 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -62,7 +63,6 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.keysToMap
import java.util.concurrent.ConcurrentMap
class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() {
@@ -78,12 +78,29 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
override val isReleasedCoroutine
get() = module.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
override fun isTooComplexForUltraLightGeneration(element: KtDeclaration): Boolean {
private fun KtDeclaration.mayBeModifiedByCompilerPlugins(): Boolean {
val facet = KotlinFacet.get(module)
val pluginClasspaths = facet?.configuration?.settings?.compilerArguments?.pluginClasspaths
if (!pluginClasspaths.isNullOrEmpty()) {
val stringifiedClasspaths = pluginClasspaths.joinToString()
LOG.debug { "Using heavy light classes for ${element.forLogString()} because of compiler plugins $stringifiedClasspaths" }
if (pluginClasspaths.isNullOrEmpty()) return false
val resolvedDescriptor = lazy(LazyThreadSafetyMode.NONE) {
resolveToDescriptorIfAny(
getResolutionFacade(),
bodyResolveMode = BodyResolveMode.PARTIAL
)
}
return LightClassApplicabilityCheckExtension.getInstances(project).any {
it.checkApplicabilityType(this, resolvedDescriptor) == LightClassApplicabilityType.LightClass
}
}
override fun isTooComplexForUltraLightGeneration(element: KtDeclaration): Boolean {
val codegenExtensionsEnabled = element.mayBeModifiedByCompilerPlugins()
if (codegenExtensionsEnabled) {
LOG.debug { "Using heavy light classes because of compiler plugins" }
return true
}
+1
View File
@@ -96,6 +96,7 @@
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableCodegenExtension"/>
<syntheticResolveExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableResolveExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableApplicabilityExtension"/>
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension"/>
<quickFixContributor implementation="org.jetbrains.kotlin.android.parcel.quickfixes.ParcelableQuickFixContributor"/>
@@ -7,6 +7,10 @@
interface="org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension"
area="IDEA_PROJECT"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.lightClassApplicabilityCheckExtension"
interface="org.jetbrains.kotlin.extensions.LightClassApplicabilityCheckExtension"
area="IDEA_PROJECT"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.idePlatformKindResolution"
interface="org.jetbrains.kotlin.caches.resolve.IdePlatformKindResolution"/>
+2
View File
@@ -198,9 +198,11 @@
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.samWithReceiver.ide.IdeSamWithReceiverComponentContributor"/>
<declarationAttributeAltererExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenDeclarationAttributeAltererExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenApplicabilityExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgComponentContainerContributor"/>
<expressionCodegenExtension implementation="org.jetbrains.kotlin.noarg.NoArgExpressionCodegenExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgApplicabilityExtension"/>
<defaultErrorMessages implementation="org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg"/>
<completionExtension implementation="org.jetbrains.kotlin.idea.properties.PropertyKeyCompletion"/>
+2
View File
@@ -198,9 +198,11 @@
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.samWithReceiver.ide.IdeSamWithReceiverComponentContributor"/>
<declarationAttributeAltererExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenDeclarationAttributeAltererExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenApplicabilityExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgComponentContainerContributor"/>
<expressionCodegenExtension implementation="org.jetbrains.kotlin.noarg.NoArgExpressionCodegenExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgApplicabilityExtension"/>
<defaultErrorMessages implementation="org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg"/>
<completionExtension implementation="org.jetbrains.kotlin.idea.properties.PropertyKeyCompletion"/>
@@ -5,5 +5,6 @@
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEJsExtension"/>
<irGenerationExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEIrExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEContainerContributor"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlinx.serialization.idea.IdeSerializationPluginApplicabilityExtension"/>
</extensions>
</idea-plugin>
+33 -1
View File
@@ -7,6 +7,7 @@ plugins {
}
dependencies {
testRuntime(project(":kotlin-reflect"))
compile(project(":kotlin-allopen-compiler-plugin"))
compile(project(":compiler:util"))
compile(project(":compiler:frontend"))
@@ -18,11 +19,42 @@ dependencies {
compileOnly(intellijDep())
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
compileOnly(intellijPluginDep("gradle"))
testCompileOnly(project(":kotlin-serialization"))
testCompileOnly(project(":plugins:lint"))
testCompileOnly(project(":plugins:kapt3-idea"))
testCompileOnly(project(":plugins:android-extensions-compiler"))
testCompileOnly(project(":kotlin-android-extensions"))
testCompileOnly(project(":kotlin-android-extensions-runtime"))
testCompileOnly(project(":plugins:android-extensions-ide"))
testCompileOnly(project(":kotlin-allopen-compiler-plugin"))
testCompileOnly(project(":allopen-ide-plugin"))
testCompileOnly(project(":kotlin-imports-dumper-compiler-plugin"))
testCompileOnly(project(":kotlin-source-sections-compiler-plugin"))
testCompileOnly(project(":kotlinx-serialization-compiler-plugin"))
testCompileOnly(project(":kotlinx-serialization-ide-plugin"))
testCompileOnly(project(":kotlin-sam-with-receiver-compiler-plugin"))
testCompileOnly(project(":noarg-ide-plugin"))
testCompileOnly(project(":sam-with-receiver-ide-plugin"))
testCompileOnly(project(":idea:idea-native"))
testCompileOnly(project(":idea:idea-gradle-native"))
testCompileOnly(projectTests(":idea:idea-test-framework"))
testCompileOnly(intellijDep())
testRuntimeOnly(intellijDep())
Platform[192].orHigher {
testCompileOnly(intellijPluginDep("java"))
testRuntimeOnly(intellijPluginDep("java"))
}
}
sourceSets {
"main" { projectDefault() }
"test" {}
"test" { projectDefault() }
}
runtimeJar()
projectTest(parallel = true) {
}
@@ -17,45 +17,27 @@
package org.jetbrains.kotlin.allopen.ide
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.allopen.AbstractAllOpenDeclarationAttributeAltererExtension
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor.Companion.PLUGIN_ID
import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor.Companion.ANNOTATION_OPTION
import org.jetbrains.kotlin.annotation.plugin.ide.getSpecialAnnotations
import java.util.concurrent.ConcurrentMap
import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor.Companion.PLUGIN_ID
import org.jetbrains.kotlin.annotation.plugin.ide.AnnotationBasedLightClassApplicabilityExtension
import org.jetbrains.kotlin.annotation.plugin.ide.CachedAnnotationNames
import org.jetbrains.kotlin.annotation.plugin.ide.getAnnotationNames
import org.jetbrains.kotlin.psi.KtModifierListOwner
class IdeAllOpenDeclarationAttributeAltererExtension(val project: Project) : AbstractAllOpenDeclarationAttributeAltererExtension() {
private companion object {
val ANNOTATION_OPTION_PREFIX = "plugin:$PLUGIN_ID:${ANNOTATION_OPTION.optionName}="
}
internal val ALL_OPEN_ANNOTATION_OPTION_PREFIX = "plugin:$PLUGIN_ID:${ANNOTATION_OPTION.optionName}="
private val cache: CachedValue<ConcurrentMap<Module, List<String>>> = cachedValue(project) {
CachedValueProvider.Result.create(
ContainerUtil.createConcurrentWeakMap<Module, List<String>>(),
ProjectRootModificationTracker.getInstance(project)
)
}
class IdeAllOpenApplicabilityExtension(project: Project) :
AnnotationBasedLightClassApplicabilityExtension(project, ALL_OPEN_ANNOTATION_OPTION_PREFIX)
class IdeAllOpenDeclarationAttributeAltererExtension(val project: Project) :
AbstractAllOpenDeclarationAttributeAltererExtension() {
private val cachedAnnotationsNames = CachedAnnotationNames(project, ALL_OPEN_ANNOTATION_OPTION_PREFIX)
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> {
if (ApplicationManager.getApplication().isUnitTestMode) {
return ANNOTATIONS_FOR_TESTS
}
if (modifierListOwner == null) return emptyList()
val module = ModuleUtilCore.findModuleForPsiElement(modifierListOwner) ?: return emptyList()
return cache.value.getOrPut(module) { module.getSpecialAnnotations(ANNOTATION_OPTION_PREFIX) }
}
private fun <T> cachedValue(project: Project, result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
return CachedValuesManager.getManager(project).createCachedValue(result, false)
return if (ApplicationManager.getApplication().isUnitTestMode) ANNOTATIONS_FOR_TESTS
else cachedAnnotationsNames.getAnnotationNames(modifierListOwner)
}
}
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2019 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.noarg
import com.intellij.lang.jvm.JvmModifier
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.kotlin.allopen.AbstractAllOpenDeclarationAttributeAltererExtension
import org.jetbrains.kotlin.allopen.ide.ALL_OPEN_ANNOTATION_OPTION_PREFIX
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinProjectDescriptorWithFacet
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.junit.runner.RunWith
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class TestNoArgForLightClass : KotlinLightCodeInsightFixtureTestCase() {
companion object {
val allOpenAnnotationName = AbstractAllOpenDeclarationAttributeAltererExtension.ANNOTATIONS_FOR_TESTS.first()
const val targetClassName = "TargetClassName"
}
override fun getProjectDescriptor(): LightProjectDescriptor =
KotlinProjectDescriptorWithFacet(LanguageVersion.LATEST_STABLE, multiPlatform = false)
override fun setUp() {
super.setUp()
val facet = KotlinFacet.get(module) ?: error { "Facet not found" }
val configurationArguments = facet.configuration.settings.compilerArguments ?: error { "CompilerArguments not found" }
configurationArguments.pluginClasspaths = arrayOf("SomeClasspath")
configurationArguments.pluginOptions = arrayOf("$ALL_OPEN_ANNOTATION_OPTION_PREFIX$allOpenAnnotationName")
}
fun testAllOpenAnnotation() {
val file = myFixture.configureByText(
"A.kt",
"annotation class $allOpenAnnotationName\n"
+ "@$allOpenAnnotationName class $targetClassName(val e: Int)\n {"
+ " fun a() {}\n"
+ " val b = 32\n"
+ "}"
) as KtFile
val classes = file.classes
assertEquals(2, classes.size)
val targetClass = classes.firstOrNull { it.name == targetClassName }
?: error { "Expected class $targetClassName not found" }
assertFalse(targetClass.hasModifier(JvmModifier.FINAL))
targetClass.methods
.filter { !it.isConstructor }
.forEach {
assertFalse(it.hasModifier(JvmModifier.FINAL))
}
}
}
@@ -41,7 +41,6 @@ import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -57,6 +56,7 @@ import org.jetbrains.org.objectweb.asm.Type
import java.io.FileDescriptor
open class ParcelableCodegenExtension : ExpressionCodegenExtension {
private companion object {
private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
private val CREATOR_NAME = Name.identifier("CREATOR")
@@ -38,6 +38,7 @@ val ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME = FqName("android.os.Parcelable.Crea
val ANDROID_PARCEL_CLASS_FQNAME = FqName("android.os.Parcel")
class ParcelableDeclarationChecker : DeclarationChecker {
private companion object {
private val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.canonicalName)
}
@@ -65,25 +66,29 @@ class ParcelableDeclarationChecker : DeclarationChecker {
}
private fun checkParcelableClassMethod(
method: SimpleFunctionDescriptor,
containingClass: ClassDescriptor,
declaration: KtFunction,
diagnosticHolder: DiagnosticSink
method: SimpleFunctionDescriptor,
containingClass: ClassDescriptor,
declaration: KtFunction,
diagnosticHolder: DiagnosticSink
) {
if (!containingClass.isParcelize) return
if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val reportElement = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) ?: declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid)
val reportElement =
declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) ?: declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
)
}
}
private fun checkParcelableClassProperty(
property: PropertyDescriptor,
containingClass: ClassDescriptor,
declaration: KtProperty,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext
property: PropertyDescriptor,
containingClass: ClassDescriptor,
declaration: KtProperty,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext
) {
fun hasIgnoredOnParcel(): Boolean {
fun Annotations.hasIgnoredOnParcel() = any { it.fqName == IGNORED_ON_PARCEL_FQNAME }
@@ -104,17 +109,20 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val outerClass = containingClass.containingDeclaration as? ClassDescriptor
if (outerClass != null && outerClass.isParcelize) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
)
}
}
}
private fun checkParcelableClass(
descriptor: ClassDescriptor,
declaration: KtDeclaration,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext,
languageVersionSettings: LanguageVersionSettings
descriptor: ClassDescriptor,
declaration: KtDeclaration,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext,
languageVersionSettings: LanguageVersionSettings
) {
if (!descriptor.isParcelize) return
@@ -132,13 +140,20 @@ class ParcelableDeclarationChecker : DeclarationChecker {
for (companion in declaration.companionObjects) {
if (companion.name == "CREATOR") {
val reportElement = companion.nameIdentifier ?: companion
diagnosticHolder.reportFromPlugin(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
)
}
}
val sealedOrAbstract = declaration.modifierList?.let { it.getModifier(KtTokens.ABSTRACT_KEYWORD) ?: it.getModifier(KtTokens.SEALED_KEYWORD) }
val sealedOrAbstract =
declaration.modifierList?.let { it.getModifier(KtTokens.ABSTRACT_KEYWORD) ?: it.getModifier(KtTokens.SEALED_KEYWORD) }
if (sealedOrAbstract != null) {
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract), DefaultErrorMessagesAndroid)
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract),
DefaultErrorMessagesAndroid
)
}
if (declaration is KtClass && declaration.isInner()) {
@@ -163,17 +178,26 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue
if (type.isParcelable()) {
val reportElement = supertypeEntry.byKeywordNode?.psi ?: delegateExpression
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement),
DefaultErrorMessagesAndroid
)
}
}
val primaryConstructor = declaration.primaryConstructor
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement),
DefaultErrorMessagesAndroid
)
} else if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement),
DefaultErrorMessagesAndroid
)
}
val typeMapper = KotlinTypeMapper(
@@ -183,21 +207,22 @@ class ParcelableDeclarationChecker : DeclarationChecker {
languageVersionSettings
)
for (parameter in primaryConstructor?.valueParameters.orEmpty()) {
for (parameter in primaryConstructor?.valueParameters.orEmpty<KtParameter>()) {
checkParcelableClassProperty(parameter, descriptor, diagnosticHolder, typeMapper)
}
}
private fun checkParcelableClassProperty(
parameter: KtParameter,
containerClass: ClassDescriptor,
diagnosticHolder: DiagnosticSink,
typeMapper: KotlinTypeMapper
parameter: KtParameter,
containerClass: ClassDescriptor,
diagnosticHolder: DiagnosticSink,
typeMapper: KotlinTypeMapper
) {
if (!parameter.hasValOrVar()) {
val reportElement = parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement), DefaultErrorMessagesAndroid)
ErrorsAndroid.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement), DefaultErrorMessagesAndroid
)
}
val descriptor = typeMapper.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter] ?: return
@@ -209,17 +234,20 @@ class ParcelableDeclarationChecker : DeclarationChecker {
try {
val parcelers = getTypeParcelers(descriptor.annotations) + getTypeParcelers(containerClass.annotations)
val context = ParcelSerializer.ParcelSerializerContext(
typeMapper,
typeMapper.mapType(containerClass.defaultType),
parcelers,
FrameMap())
typeMapper,
typeMapper.mapType(containerClass.defaultType),
parcelers,
FrameMap()
)
ParcelSerializer.get(type, asmType, context, strict = true)
}
catch (e: IllegalArgumentException) {
} catch (e: IllegalArgumentException) {
// get() throws IllegalArgumentException on unknown types
val reportElement = parameter.typeReference ?: parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin(ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement), DefaultErrorMessagesAndroid)
diagnosticHolder.reportFromPlugin(
ErrorsAndroid.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement),
DefaultErrorMessagesAndroid
)
}
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2019 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.android.parcel
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.extensions.LightClassApplicabilityCheckExtension
import org.jetbrains.kotlin.extensions.LightClassApplicabilityType
import org.jetbrains.kotlin.psi.KtDeclaration
class IDEParcelableApplicabilityExtension : LightClassApplicabilityCheckExtension {
override fun checkApplicabilityType(declaration: KtDeclaration, descriptor: Lazy<DeclarationDescriptor?>): LightClassApplicabilityType {
if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return LightClassApplicabilityType.UltraLightClass
val descriptorValue = descriptor.value ?: return LightClassApplicabilityType.UltraLightClass
val classDescriptor = (descriptorValue as? ClassDescriptor)
?: descriptorValue.containingDeclaration as? ClassDescriptor
?: return LightClassApplicabilityType.UltraLightClass
return if (classDescriptor.isParcelize) LightClassApplicabilityType.LightClass else LightClassApplicabilityType.UltraLightClass
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2019 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.annotation.plugin.ide
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
import org.jetbrains.kotlin.extensions.LightClassApplicabilityCheckExtension
import org.jetbrains.kotlin.extensions.LightClassApplicabilityType
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtModifierListOwner
abstract class AnnotationBasedLightClassApplicabilityExtension(project: Project, annotationOptionPrefix: String) :
LightClassApplicabilityCheckExtension,
AnnotationBasedExtension
{
private val cachedAnnotationsNames = CachedAnnotationNames(project, annotationOptionPrefix)
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> =
cachedAnnotationsNames.getAnnotationNames(modifierListOwner)
override fun checkApplicabilityType(declaration: KtDeclaration, descriptor: Lazy<DeclarationDescriptor?>): LightClassApplicabilityType {
if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return LightClassApplicabilityType.UltraLightClass
if (cachedAnnotationsNames.getAnnotationNames(declaration).isEmpty()) return LightClassApplicabilityType.UltraLightClass
val descriptorValue = descriptor.value ?: return LightClassApplicabilityType.UltraLightClass
val classDescriptor = (descriptorValue as? ClassDescriptor)
?: descriptorValue.containingDeclaration as? ClassDescriptor
?: return LightClassApplicabilityType.UltraLightClass
val hasSpecialAnnotation = run { classDescriptor.hasSpecialAnnotation(declaration) }
return if (hasSpecialAnnotation) LightClassApplicabilityType.LightClass else LightClassApplicabilityType.UltraLightClass
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2019 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.annotation.plugin.ide
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.psi.KtElement
import java.util.concurrent.ConcurrentMap
fun CachedAnnotationNames.getAnnotationNames(element: KtElement?): List<String> {
if (element === null) return emptyList()
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return emptyList()
return getNamesForModule(module)
}
class CachedAnnotationNames(project: Project, private val annotationOptionPrefix: String) {
private val cache: CachedValue<ConcurrentMap<Module, List<String>>> = cachedValue(project) {
CachedValueProvider.Result.create(
ContainerUtil.createConcurrentWeakMap<Module, List<String>>(),
ProjectRootModificationTracker.getInstance(project)
)
}
fun getNamesForModule(module: Module): List<String> {
return cache.value.getOrPut(module) { module.getSpecialAnnotations(annotationOptionPrefix) }
}
private fun <T> cachedValue(project: Project, result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
return CachedValuesManager.getManager(project).createCachedValue(result, false)
}
}
@@ -19,6 +19,9 @@ package org.jetbrains.kotlin.annotation.plugin.ide
import com.intellij.openapi.module.Module
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration
import java.io.File
fun Module.getSpecialAnnotations(prefix: String): List<String> {
@@ -26,9 +29,9 @@ fun Module.getSpecialAnnotations(prefix: String): List<String> {
val commonArgs = kotlinFacet.configuration.settings.compilerArguments ?: return emptyList()
return commonArgs.pluginOptions
?.filter { it.startsWith(prefix) }
?.map { it.substring(prefix.length) }
?: emptyList()
?.filter { it.startsWith(prefix) }
?.map { it.substring(prefix.length) }
?: emptyList()
}
class AnnotationBasedCompilerPluginSetup(val options: List<PluginOption>, val classpath: List<String>) {
@@ -36,10 +39,10 @@ class AnnotationBasedCompilerPluginSetup(val options: List<PluginOption>, val cl
}
internal fun modifyCompilerArgumentsForPlugin(
facet: KotlinFacet,
setup: AnnotationBasedCompilerPluginSetup?,
compilerPluginId: String,
pluginName: String
facet: KotlinFacet,
setup: AnnotationBasedCompilerPluginSetup?,
compilerPluginId: String,
pluginName: String
) {
val facetSettings = facet.configuration.settings
@@ -49,7 +52,8 @@ internal fun modifyCompilerArgumentsForPlugin(
/** See [CommonCompilerArguments.PLUGIN_OPTION_FORMAT] **/
val newOptionsForPlugin = setup?.options?.map { "plugin:$compilerPluginId:${it.key}=${it.value}" } ?: emptyList()
val oldAllPluginOptions = (commonArguments.pluginOptions ?: emptyArray()).filterTo(mutableListOf()) { !it.startsWith("plugin:$compilerPluginId:") }
val oldAllPluginOptions =
(commonArguments.pluginOptions ?: emptyArray()).filterTo(mutableListOf()) { !it.startsWith("plugin:$compilerPluginId:") }
val newAllPluginOptions = oldAllPluginOptions + newOptionsForPlugin
val oldPluginClasspaths = (commonArguments.pluginClasspaths ?: emptyArray()).filterTo(mutableListOf()) {
@@ -66,4 +70,12 @@ internal fun modifyCompilerArgumentsForPlugin(
commonArguments.pluginClasspaths = newPluginClasspaths.toTypedArray()
facetSettings.compilerArguments = commonArguments
}
}
val KtDeclaration.isOrdinaryClass
get() = this is KtClass &&
!this.hasModifier(KtTokens.INLINE_KEYWORD) &&
!this.isAnnotation() &&
!this.isInterface()
val KtDeclaration.isAnnotated get() = this.annotationEntries.isNotEmpty()
@@ -223,5 +223,4 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
)
}
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2019 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.kotlinx.serialization.idea
import org.jetbrains.kotlin.annotation.plugin.ide.isAnnotated
import org.jetbrains.kotlin.annotation.plugin.ide.isOrdinaryClass
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.extensions.LightClassApplicabilityCheckExtension
import org.jetbrains.kotlin.extensions.LightClassApplicabilityType
import org.jetbrains.kotlin.psi.KtDeclaration
class IdeSerializationPluginApplicabilityExtension : LightClassApplicabilityCheckExtension {
override fun checkApplicabilityType(declaration: KtDeclaration, descriptor: Lazy<DeclarationDescriptor?>): LightClassApplicabilityType {
if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return LightClassApplicabilityType.UltraLightClass
return (descriptor.value as? ClassDescriptor)?.let {
getIfEnabledOn(it) { LightClassApplicabilityType.LightClass }
} ?: LightClassApplicabilityType.UltraLightClass
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2019 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.noarg
@@ -49,18 +38,19 @@ class NoArgExpressionCodegenExtension(val invokeInitializers: Boolean = false) :
// If a parent sealed class has not a zero-parameter constructor, user must write @NoArg annotation for the parent class as well,
// and then we generate <init>()V
val isParentASealedClassWithDefaultConstructor =
superClass.modality == Modality.SEALED && superClass.constructors.any { it.isZeroParameterConstructor() }
superClass.modality == Modality.SEALED && superClass.constructors.any { it.isZeroParameterConstructor() }
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, constructorDescriptor, object: CodegenBased(state) {
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, constructorDescriptor, object : CodegenBased(state) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
codegen.v.load(0, AsmTypes.OBJECT_TYPE)
if (isParentASealedClassWithDefaultConstructor) {
codegen.v.aconst(null)
codegen.v.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>",
"(Lkotlin/jvm/internal/DefaultConstructorMarker;)V", false)
}
else {
codegen.v.visitMethodInsn(
Opcodes.INVOKESPECIAL, superClassInternalName, "<init>",
"(Lkotlin/jvm/internal/DefaultConstructorMarker;)V", false
)
} else {
codegen.v.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>", "()V", false)
}
@@ -74,8 +64,10 @@ class NoArgExpressionCodegenExtension(val invokeInitializers: Boolean = false) :
private fun createNoArgConstructorDescriptor(containingClass: ClassDescriptor): ConstructorDescriptor {
return ClassConstructorDescriptorImpl.createSynthesized(containingClass, Annotations.EMPTY, false, SourceElement.NO_SOURCE).apply {
initialize(null, calculateDispatchReceiverParameter(), emptyList(), emptyList(),
containingClass.builtIns.unitType, Modality.OPEN, Visibilities.PUBLIC)
initialize(
null, calculateDispatchReceiverParameter(), emptyList(), emptyList(),
containingClass.builtIns.unitType, Modality.OPEN, Visibilities.PUBLIC
)
}
}
@@ -96,4 +88,6 @@ class NoArgExpressionCodegenExtension(val invokeInitializers: Boolean = false) :
return parameters.isEmpty() ||
(parameters.all { it.declaresDefaultValue() } && (isPrimary || findJvmOverloadsAnnotation() != null))
}
override val shouldGenerateClassSyntheticPartsInLightClassesMode = true
}
+32 -1
View File
@@ -7,6 +7,7 @@ plugins {
}
dependencies {
testRuntime(project(":kotlin-reflect"))
compile(project(":kotlin-noarg-compiler-plugin"))
compile(project(":compiler:util"))
compile(project(":compiler:frontend"))
@@ -19,11 +20,41 @@ dependencies {
compileOnly(intellijDep())
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
compileOnly(intellijPluginDep("gradle"))
testCompileOnly(project(":kotlin-serialization"))
testCompileOnly(project(":plugins:lint"))
testCompileOnly(project(":plugins:kapt3-idea"))
testCompileOnly(project(":plugins:android-extensions-compiler"))
testCompileOnly(project(":kotlin-android-extensions"))
testCompileOnly(project(":kotlin-android-extensions-runtime"))
testCompileOnly(project(":plugins:android-extensions-ide"))
testCompileOnly(project(":kotlin-allopen-compiler-plugin"))
testCompileOnly(project(":allopen-ide-plugin"))
testCompileOnly(project(":kotlin-imports-dumper-compiler-plugin"))
testCompileOnly(project(":kotlin-source-sections-compiler-plugin"))
testCompileOnly(project(":kotlinx-serialization-compiler-plugin"))
testCompileOnly(project(":kotlinx-serialization-ide-plugin"))
testCompileOnly(project(":kotlin-sam-with-receiver-compiler-plugin"))
testCompileOnly(project(":sam-with-receiver-ide-plugin"))
testCompileOnly(project(":idea:idea-native"))
testCompileOnly(project(":idea:idea-gradle-native"))
testCompileOnly(projectTests(":idea:idea-test-framework"))
testCompileOnly(intellijDep())
testRuntimeOnly(intellijDep())
Platform[192].orHigher {
testCompileOnly(intellijPluginDep("java"))
testRuntimeOnly(intellijPluginDep("java"))
}
}
sourceSets {
"main" { projectDefault() }
"test" {}
"test" { projectDefault() }
}
runtimeJar()
projectTest(parallel = true) {
}
@@ -16,41 +16,24 @@
package org.jetbrains.kotlin.noarg.ide
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.annotation.plugin.ide.getSpecialAnnotations
import org.jetbrains.kotlin.noarg.NoArgCommandLineProcessor.Companion.ANNOTATION_OPTION
import org.jetbrains.kotlin.noarg.NoArgCommandLineProcessor.Companion.PLUGIN_ID
import org.jetbrains.kotlin.annotation.plugin.ide.AnnotationBasedLightClassApplicabilityExtension
import org.jetbrains.kotlin.annotation.plugin.ide.CachedAnnotationNames
import org.jetbrains.kotlin.annotation.plugin.ide.getAnnotationNames
import org.jetbrains.kotlin.noarg.NoArgCommandLineProcessor
import org.jetbrains.kotlin.noarg.diagnostic.AbstractNoArgDeclarationChecker
import org.jetbrains.kotlin.psi.KtModifierListOwner
import java.util.concurrent.ConcurrentMap
class IdeNoArgDeclarationChecker(val project: Project) : AbstractNoArgDeclarationChecker() {
private companion object {
val ANNOTATION_OPTION_PREFIX = "plugin:$PLUGIN_ID:${ANNOTATION_OPTION.optionName}="
}
internal val NO_ARG_ANNOTATION_OPTION_PREFIX =
"plugin:${NoArgCommandLineProcessor.PLUGIN_ID}:${NoArgCommandLineProcessor.ANNOTATION_OPTION.optionName}="
private val cache: CachedValue<ConcurrentMap<Module, List<String>>> = cachedValue(project) {
CachedValueProvider.Result.create(
ContainerUtil.createConcurrentWeakMap<Module, List<String>>(),
ProjectRootModificationTracker.getInstance(project)
)
}
class IdeNoArgApplicabilityExtension(project: Project) :
AnnotationBasedLightClassApplicabilityExtension(project, NO_ARG_ANNOTATION_OPTION_PREFIX)
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> {
if (modifierListOwner == null) return emptyList()
val module = ModuleUtilCore.findModuleForPsiElement(modifierListOwner) ?: return emptyList()
class IdeNoArgDeclarationChecker(project: Project) : AbstractNoArgDeclarationChecker() {
return cache.value.getOrPut(module) { module.getSpecialAnnotations(ANNOTATION_OPTION_PREFIX) }
}
private val cachedAnnotationNames = CachedAnnotationNames(project, NO_ARG_ANNOTATION_OPTION_PREFIX)
private fun <T> cachedValue(project: Project, result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
return CachedValuesManager.getManager(project).createCachedValue(result, false)
}
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> =
cachedAnnotationNames.getAnnotationNames(modifierListOwner)
}
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2019 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.noarg
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinProjectDescriptorWithFacet
import org.jetbrains.kotlin.noarg.ide.NO_ARG_ANNOTATION_OPTION_PREFIX
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.junit.runner.RunWith
private const val targetClassName = "TargetClassName"
private const val noArgAnnotationName = "HelloNoArg"
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class TestNoArgForLightClass : KotlinLightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor =
KotlinProjectDescriptorWithFacet(LanguageVersion.LATEST_STABLE, multiPlatform = false)
override fun setUp() {
super.setUp()
val facet = KotlinFacet.get(module) ?: error { "Facet not found" }
val configurationArguments = facet.configuration.settings.compilerArguments ?: error { "CompilerArguments not found" }
configurationArguments.pluginClasspaths = arrayOf("SomeClasspath")
configurationArguments.pluginOptions = arrayOf("$NO_ARG_ANNOTATION_OPTION_PREFIX$noArgAnnotationName")
}
fun testNoArgAnnotation() {
val file = myFixture.configureByText(
"A.kt",
"annotation class $noArgAnnotationName\n"
+ "@$noArgAnnotationName class $targetClassName(val e: Int)"
) as KtFile
//We have to call it because NoArg checker need to be executed and mark needed PSI for CodegenExtension
file.analyzeWithAllCompilerChecks()
val classes = file.classes
assertEquals(2, classes.size)
val targetClass = classes.firstOrNull { it.name == targetClassName }
?: error { "Expected class $targetClassName not found" }
val constructors = targetClass.constructors
assertEquals(constructors.size, 2)
assertTrue(constructors.any { it.parameters.isEmpty() })
}
}