Add CodegenApplicabilityCheckerExtension and use it to fallback to Heavy LigthClasses
+ Fixed #KT-33584
This commit is contained in:
@@ -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 {
|
||||
|
||||
+39
@@ -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
|
||||
}
|
||||
+23
-6
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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")
|
||||
|
||||
+64
-36
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -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
|
||||
}
|
||||
}
|
||||
+41
@@ -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()
|
||||
|
||||
-1
@@ -223,5 +223,4 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+25
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user