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 import org.jetbrains.kotlin.types.TypeUtils
interface AnnotationBasedExtension { interface AnnotationBasedExtension {
fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String>
fun DeclarationDescriptor.hasSpecialAnnotation(modifierListOwner: KtModifierListOwner?): Boolean { 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.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor 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.IDELightClassContexts
import org.jetbrains.kotlin.idea.caches.lightClasses.LazyLightClassDataHolder import org.jetbrains.kotlin.idea.caches.lightClasses.LazyLightClassDataHolder
import org.jetbrains.kotlin.idea.facet.KotlinFacet 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.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe 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.lazy.NoDescriptorForDeclarationException
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.keysToMap
import java.util.concurrent.ConcurrentMap import java.util.concurrent.ConcurrentMap
class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() { class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() {
@@ -78,12 +78,29 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
override val isReleasedCoroutine override val isReleasedCoroutine
get() = module.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) get() = module.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
override fun isTooComplexForUltraLightGeneration(element: KtDeclaration): Boolean { private fun KtDeclaration.mayBeModifiedByCompilerPlugins(): Boolean {
val facet = KotlinFacet.get(module) val facet = KotlinFacet.get(module)
val pluginClasspaths = facet?.configuration?.settings?.compilerArguments?.pluginClasspaths val pluginClasspaths = facet?.configuration?.settings?.compilerArguments?.pluginClasspaths
if (!pluginClasspaths.isNullOrEmpty()) { if (pluginClasspaths.isNullOrEmpty()) return false
val stringifiedClasspaths = pluginClasspaths.joinToString()
LOG.debug { "Using heavy light classes for ${element.forLogString()} because of compiler plugins $stringifiedClasspaths" } 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 return true
} }
+1
View File
@@ -96,6 +96,7 @@
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableCodegenExtension"/> <expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableCodegenExtension"/>
<syntheticResolveExtension implementation="org.jetbrains.kotlin.android.parcel.IDEParcelableResolveExtension"/> <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"/> <classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension"/>
<quickFixContributor implementation="org.jetbrains.kotlin.android.parcel.quickfixes.ParcelableQuickFixContributor"/> <quickFixContributor implementation="org.jetbrains.kotlin.android.parcel.quickfixes.ParcelableQuickFixContributor"/>
@@ -7,6 +7,10 @@
interface="org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension" interface="org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension"
area="IDEA_PROJECT"/> area="IDEA_PROJECT"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.lightClassApplicabilityCheckExtension"
interface="org.jetbrains.kotlin.extensions.LightClassApplicabilityCheckExtension"
area="IDEA_PROJECT"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.idePlatformKindResolution" <extensionPoint qualifiedName="org.jetbrains.kotlin.idePlatformKindResolution"
interface="org.jetbrains.kotlin.caches.resolve.IdePlatformKindResolution"/> interface="org.jetbrains.kotlin.caches.resolve.IdePlatformKindResolution"/>
+2
View File
@@ -198,9 +198,11 @@
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.samWithReceiver.ide.IdeSamWithReceiverComponentContributor"/> <storageComponentContainerContributor implementation="org.jetbrains.kotlin.samWithReceiver.ide.IdeSamWithReceiverComponentContributor"/>
<declarationAttributeAltererExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenDeclarationAttributeAltererExtension"/> <declarationAttributeAltererExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenDeclarationAttributeAltererExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenApplicabilityExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgComponentContainerContributor"/> <storageComponentContainerContributor implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgComponentContainerContributor"/>
<expressionCodegenExtension implementation="org.jetbrains.kotlin.noarg.NoArgExpressionCodegenExtension"/> <expressionCodegenExtension implementation="org.jetbrains.kotlin.noarg.NoArgExpressionCodegenExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgApplicabilityExtension"/>
<defaultErrorMessages implementation="org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg"/> <defaultErrorMessages implementation="org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg"/>
<completionExtension implementation="org.jetbrains.kotlin.idea.properties.PropertyKeyCompletion"/> <completionExtension implementation="org.jetbrains.kotlin.idea.properties.PropertyKeyCompletion"/>
+2
View File
@@ -198,9 +198,11 @@
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.samWithReceiver.ide.IdeSamWithReceiverComponentContributor"/> <storageComponentContainerContributor implementation="org.jetbrains.kotlin.samWithReceiver.ide.IdeSamWithReceiverComponentContributor"/>
<declarationAttributeAltererExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenDeclarationAttributeAltererExtension"/> <declarationAttributeAltererExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenDeclarationAttributeAltererExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.allopen.ide.IdeAllOpenApplicabilityExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgComponentContainerContributor"/> <storageComponentContainerContributor implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgComponentContainerContributor"/>
<expressionCodegenExtension implementation="org.jetbrains.kotlin.noarg.NoArgExpressionCodegenExtension"/> <expressionCodegenExtension implementation="org.jetbrains.kotlin.noarg.NoArgExpressionCodegenExtension"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlin.noarg.ide.IdeNoArgApplicabilityExtension"/>
<defaultErrorMessages implementation="org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg"/> <defaultErrorMessages implementation="org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg"/>
<completionExtension implementation="org.jetbrains.kotlin.idea.properties.PropertyKeyCompletion"/> <completionExtension implementation="org.jetbrains.kotlin.idea.properties.PropertyKeyCompletion"/>
@@ -5,5 +5,6 @@
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEJsExtension"/> <jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEJsExtension"/>
<irGenerationExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEIrExtension"/> <irGenerationExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEIrExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEContainerContributor"/> <storageComponentContainerContributor implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEContainerContributor"/>
<lightClassApplicabilityCheckExtension implementation="org.jetbrains.kotlinx.serialization.idea.IdeSerializationPluginApplicabilityExtension"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>
+33 -1
View File
@@ -7,6 +7,7 @@ plugins {
} }
dependencies { dependencies {
testRuntime(project(":kotlin-reflect"))
compile(project(":kotlin-allopen-compiler-plugin")) compile(project(":kotlin-allopen-compiler-plugin"))
compile(project(":compiler:util")) compile(project(":compiler:util"))
compile(project(":compiler:frontend")) compile(project(":compiler:frontend"))
@@ -18,11 +19,42 @@ dependencies {
compileOnly(intellijDep()) compileOnly(intellijDep())
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) } excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
compileOnly(intellijPluginDep("gradle")) 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 { sourceSets {
"main" { projectDefault() } "main" { projectDefault() }
"test" {} "test" { projectDefault() }
} }
runtimeJar() runtimeJar()
projectTest(parallel = true) {
}
@@ -17,45 +17,27 @@
package org.jetbrains.kotlin.allopen.ide package org.jetbrains.kotlin.allopen.ide
import com.intellij.openapi.application.ApplicationManager 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.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.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.allopen.AllOpenCommandLineProcessor.Companion.ANNOTATION_OPTION
import org.jetbrains.kotlin.annotation.plugin.ide.getSpecialAnnotations import org.jetbrains.kotlin.allopen.AllOpenCommandLineProcessor.Companion.PLUGIN_ID
import java.util.concurrent.ConcurrentMap 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() { internal val ALL_OPEN_ANNOTATION_OPTION_PREFIX = "plugin:$PLUGIN_ID:${ANNOTATION_OPTION.optionName}="
private companion object {
val ANNOTATION_OPTION_PREFIX = "plugin:$PLUGIN_ID:${ANNOTATION_OPTION.optionName}="
}
private val cache: CachedValue<ConcurrentMap<Module, List<String>>> = cachedValue(project) { class IdeAllOpenApplicabilityExtension(project: Project) :
CachedValueProvider.Result.create( AnnotationBasedLightClassApplicabilityExtension(project, ALL_OPEN_ANNOTATION_OPTION_PREFIX)
ContainerUtil.createConcurrentWeakMap<Module, List<String>>(),
ProjectRootModificationTracker.getInstance(project) class IdeAllOpenDeclarationAttributeAltererExtension(val project: Project) :
) AbstractAllOpenDeclarationAttributeAltererExtension() {
}
private val cachedAnnotationsNames = CachedAnnotationNames(project, ALL_OPEN_ANNOTATION_OPTION_PREFIX)
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> { override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> {
if (ApplicationManager.getApplication().isUnitTestMode) { return if (ApplicationManager.getApplication().isUnitTestMode) ANNOTATIONS_FOR_TESTS
return ANNOTATIONS_FOR_TESTS else cachedAnnotationsNames.getAnnotationNames(modifierListOwner)
}
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)
} }
} }
@@ -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
import org.jetbrains.kotlin.incremental.components.NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS import org.jetbrains.kotlin.incremental.components.NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -57,6 +56,7 @@ import org.jetbrains.org.objectweb.asm.Type
import java.io.FileDescriptor import java.io.FileDescriptor
open class ParcelableCodegenExtension : ExpressionCodegenExtension { open class ParcelableCodegenExtension : ExpressionCodegenExtension {
private companion object { private companion object {
private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName) private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
private val CREATOR_NAME = Name.identifier("CREATOR") 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") val ANDROID_PARCEL_CLASS_FQNAME = FqName("android.os.Parcel")
class ParcelableDeclarationChecker : DeclarationChecker { class ParcelableDeclarationChecker : DeclarationChecker {
private companion object { private companion object {
private val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.canonicalName) private val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.canonicalName)
} }
@@ -65,25 +66,29 @@ class ParcelableDeclarationChecker : DeclarationChecker {
} }
private fun checkParcelableClassMethod( private fun checkParcelableClassMethod(
method: SimpleFunctionDescriptor, method: SimpleFunctionDescriptor,
containingClass: ClassDescriptor, containingClass: ClassDescriptor,
declaration: KtFunction, declaration: KtFunction,
diagnosticHolder: DiagnosticSink diagnosticHolder: DiagnosticSink
) { ) {
if (!containingClass.isParcelize) return if (!containingClass.isParcelize) return
if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val reportElement = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) ?: declaration.nameIdentifier ?: declaration val reportElement =
diagnosticHolder.reportFromPlugin(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement), DefaultErrorMessagesAndroid) 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( private fun checkParcelableClassProperty(
property: PropertyDescriptor, property: PropertyDescriptor,
containingClass: ClassDescriptor, containingClass: ClassDescriptor,
declaration: KtProperty, declaration: KtProperty,
diagnosticHolder: DiagnosticSink, diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext bindingContext: BindingContext
) { ) {
fun hasIgnoredOnParcel(): Boolean { fun hasIgnoredOnParcel(): Boolean {
fun Annotations.hasIgnoredOnParcel() = any { it.fqName == IGNORED_ON_PARCEL_FQNAME } fun Annotations.hasIgnoredOnParcel() = any { it.fqName == IGNORED_ON_PARCEL_FQNAME }
@@ -104,17 +109,20 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val outerClass = containingClass.containingDeclaration as? ClassDescriptor val outerClass = containingClass.containingDeclaration as? ClassDescriptor
if (outerClass != null && outerClass.isParcelize) { if (outerClass != null && outerClass.isParcelize) {
val reportElement = declaration.nameIdentifier ?: declaration 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( private fun checkParcelableClass(
descriptor: ClassDescriptor, descriptor: ClassDescriptor,
declaration: KtDeclaration, declaration: KtDeclaration,
diagnosticHolder: DiagnosticSink, diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext, bindingContext: BindingContext,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
) { ) {
if (!descriptor.isParcelize) return if (!descriptor.isParcelize) return
@@ -132,13 +140,20 @@ class ParcelableDeclarationChecker : DeclarationChecker {
for (companion in declaration.companionObjects) { for (companion in declaration.companionObjects) {
if (companion.name == "CREATOR") { if (companion.name == "CREATOR") {
val reportElement = companion.nameIdentifier ?: companion 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) { 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()) { if (declaration is KtClass && declaration.isInner()) {
@@ -163,17 +178,26 @@ class ParcelableDeclarationChecker : DeclarationChecker {
val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue
if (type.isParcelable()) { if (type.isParcelable()) {
val reportElement = supertypeEntry.byKeywordNode?.psi ?: delegateExpression 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 val primaryConstructor = declaration.primaryConstructor
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) { if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration 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()) { } else if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration 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( val typeMapper = KotlinTypeMapper(
@@ -183,21 +207,22 @@ class ParcelableDeclarationChecker : DeclarationChecker {
languageVersionSettings languageVersionSettings
) )
for (parameter in primaryConstructor?.valueParameters.orEmpty()) { for (parameter in primaryConstructor?.valueParameters.orEmpty<KtParameter>()) {
checkParcelableClassProperty(parameter, descriptor, diagnosticHolder, typeMapper) checkParcelableClassProperty(parameter, descriptor, diagnosticHolder, typeMapper)
} }
} }
private fun checkParcelableClassProperty( private fun checkParcelableClassProperty(
parameter: KtParameter, parameter: KtParameter,
containerClass: ClassDescriptor, containerClass: ClassDescriptor,
diagnosticHolder: DiagnosticSink, diagnosticHolder: DiagnosticSink,
typeMapper: KotlinTypeMapper typeMapper: KotlinTypeMapper
) { ) {
if (!parameter.hasValOrVar()) { if (!parameter.hasValOrVar()) {
val reportElement = parameter.nameIdentifier ?: parameter val reportElement = parameter.nameIdentifier ?: parameter
diagnosticHolder.reportFromPlugin( 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 val descriptor = typeMapper.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter] ?: return
@@ -209,17 +234,20 @@ class ParcelableDeclarationChecker : DeclarationChecker {
try { try {
val parcelers = getTypeParcelers(descriptor.annotations) + getTypeParcelers(containerClass.annotations) val parcelers = getTypeParcelers(descriptor.annotations) + getTypeParcelers(containerClass.annotations)
val context = ParcelSerializer.ParcelSerializerContext( val context = ParcelSerializer.ParcelSerializerContext(
typeMapper, typeMapper,
typeMapper.mapType(containerClass.defaultType), typeMapper.mapType(containerClass.defaultType),
parcelers, parcelers,
FrameMap()) FrameMap()
)
ParcelSerializer.get(type, asmType, context, strict = true) ParcelSerializer.get(type, asmType, context, strict = true)
} } catch (e: IllegalArgumentException) {
catch (e: IllegalArgumentException) {
// get() throws IllegalArgumentException on unknown types // get() throws IllegalArgumentException on unknown types
val reportElement = parameter.typeReference ?: parameter.nameIdentifier ?: parameter 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 com.intellij.openapi.module.Module
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.idea.facet.KotlinFacet 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 import java.io.File
fun Module.getSpecialAnnotations(prefix: String): List<String> { 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() val commonArgs = kotlinFacet.configuration.settings.compilerArguments ?: return emptyList()
return commonArgs.pluginOptions return commonArgs.pluginOptions
?.filter { it.startsWith(prefix) } ?.filter { it.startsWith(prefix) }
?.map { it.substring(prefix.length) } ?.map { it.substring(prefix.length) }
?: emptyList() ?: emptyList()
} }
class AnnotationBasedCompilerPluginSetup(val options: List<PluginOption>, val classpath: List<String>) { 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( internal fun modifyCompilerArgumentsForPlugin(
facet: KotlinFacet, facet: KotlinFacet,
setup: AnnotationBasedCompilerPluginSetup?, setup: AnnotationBasedCompilerPluginSetup?,
compilerPluginId: String, compilerPluginId: String,
pluginName: String pluginName: String
) { ) {
val facetSettings = facet.configuration.settings val facetSettings = facet.configuration.settings
@@ -49,7 +52,8 @@ internal fun modifyCompilerArgumentsForPlugin(
/** See [CommonCompilerArguments.PLUGIN_OPTION_FORMAT] **/ /** See [CommonCompilerArguments.PLUGIN_OPTION_FORMAT] **/
val newOptionsForPlugin = setup?.options?.map { "plugin:$compilerPluginId:${it.key}=${it.value}" } ?: emptyList() 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 newAllPluginOptions = oldAllPluginOptions + newOptionsForPlugin
val oldPluginClasspaths = (commonArguments.pluginClasspaths ?: emptyArray()).filterTo(mutableListOf()) { val oldPluginClasspaths = (commonArguments.pluginClasspaths ?: emptyArray()).filterTo(mutableListOf()) {
@@ -66,4 +70,12 @@ internal fun modifyCompilerArgumentsForPlugin(
commonArguments.pluginClasspaths = newPluginClasspaths.toTypedArray() commonArguments.pluginClasspaths = newPluginClasspaths.toTypedArray()
facetSettings.compilerArguments = commonArguments 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. * 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.
* 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.
*/ */
package org.jetbrains.kotlin.noarg 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, // 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 // and then we generate <init>()V
val isParentASealedClassWithDefaultConstructor = 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) { override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
codegen.v.load(0, AsmTypes.OBJECT_TYPE) codegen.v.load(0, AsmTypes.OBJECT_TYPE)
if (isParentASealedClassWithDefaultConstructor) { if (isParentASealedClassWithDefaultConstructor) {
codegen.v.aconst(null) codegen.v.aconst(null)
codegen.v.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>", codegen.v.visitMethodInsn(
"(Lkotlin/jvm/internal/DefaultConstructorMarker;)V", false) Opcodes.INVOKESPECIAL, superClassInternalName, "<init>",
} "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V", false
else { )
} else {
codegen.v.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>", "()V", false) 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 { private fun createNoArgConstructorDescriptor(containingClass: ClassDescriptor): ConstructorDescriptor {
return ClassConstructorDescriptorImpl.createSynthesized(containingClass, Annotations.EMPTY, false, SourceElement.NO_SOURCE).apply { return ClassConstructorDescriptorImpl.createSynthesized(containingClass, Annotations.EMPTY, false, SourceElement.NO_SOURCE).apply {
initialize(null, calculateDispatchReceiverParameter(), emptyList(), emptyList(), initialize(
containingClass.builtIns.unitType, Modality.OPEN, Visibilities.PUBLIC) null, calculateDispatchReceiverParameter(), emptyList(), emptyList(),
containingClass.builtIns.unitType, Modality.OPEN, Visibilities.PUBLIC
)
} }
} }
@@ -96,4 +88,6 @@ class NoArgExpressionCodegenExtension(val invokeInitializers: Boolean = false) :
return parameters.isEmpty() || return parameters.isEmpty() ||
(parameters.all { it.declaresDefaultValue() } && (isPrimary || findJvmOverloadsAnnotation() != null)) (parameters.all { it.declaresDefaultValue() } && (isPrimary || findJvmOverloadsAnnotation() != null))
} }
override val shouldGenerateClassSyntheticPartsInLightClassesMode = true
} }
+32 -1
View File
@@ -7,6 +7,7 @@ plugins {
} }
dependencies { dependencies {
testRuntime(project(":kotlin-reflect"))
compile(project(":kotlin-noarg-compiler-plugin")) compile(project(":kotlin-noarg-compiler-plugin"))
compile(project(":compiler:util")) compile(project(":compiler:util"))
compile(project(":compiler:frontend")) compile(project(":compiler:frontend"))
@@ -19,11 +20,41 @@ dependencies {
compileOnly(intellijDep()) compileOnly(intellijDep())
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) } excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
compileOnly(intellijPluginDep("gradle")) 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 { sourceSets {
"main" { projectDefault() } "main" { projectDefault() }
"test" {} "test" { projectDefault() }
} }
runtimeJar() runtimeJar()
projectTest(parallel = true) {
}
@@ -16,41 +16,24 @@
package org.jetbrains.kotlin.noarg.ide 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.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker import org.jetbrains.kotlin.annotation.plugin.ide.AnnotationBasedLightClassApplicabilityExtension
import com.intellij.psi.util.CachedValue import org.jetbrains.kotlin.annotation.plugin.ide.CachedAnnotationNames
import com.intellij.psi.util.CachedValueProvider import org.jetbrains.kotlin.annotation.plugin.ide.getAnnotationNames
import com.intellij.psi.util.CachedValuesManager import org.jetbrains.kotlin.noarg.NoArgCommandLineProcessor
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.noarg.diagnostic.AbstractNoArgDeclarationChecker import org.jetbrains.kotlin.noarg.diagnostic.AbstractNoArgDeclarationChecker
import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.KtModifierListOwner
import java.util.concurrent.ConcurrentMap
class IdeNoArgDeclarationChecker(val project: Project) : AbstractNoArgDeclarationChecker() { internal val NO_ARG_ANNOTATION_OPTION_PREFIX =
private companion object { "plugin:${NoArgCommandLineProcessor.PLUGIN_ID}:${NoArgCommandLineProcessor.ANNOTATION_OPTION.optionName}="
val ANNOTATION_OPTION_PREFIX = "plugin:$PLUGIN_ID:${ANNOTATION_OPTION.optionName}="
}
private val cache: CachedValue<ConcurrentMap<Module, List<String>>> = cachedValue(project) { class IdeNoArgApplicabilityExtension(project: Project) :
CachedValueProvider.Result.create( AnnotationBasedLightClassApplicabilityExtension(project, NO_ARG_ANNOTATION_OPTION_PREFIX)
ContainerUtil.createConcurrentWeakMap<Module, List<String>>(),
ProjectRootModificationTracker.getInstance(project)
)
}
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> { class IdeNoArgDeclarationChecker(project: Project) : AbstractNoArgDeclarationChecker() {
if (modifierListOwner == null) return emptyList()
val module = ModuleUtilCore.findModuleForPsiElement(modifierListOwner) ?: return emptyList()
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> { override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> =
return CachedValuesManager.getManager(project).createCachedValue(result, false) 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() })
}
}