From 2b7dee6f8dacdfc360a265f28a6e91e142e6ea8b Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Thu, 5 Sep 2019 21:43:08 +0300 Subject: [PATCH] Add CodegenApplicabilityCheckerExtension and use it to fallback to Heavy LigthClasses + Fixed #KT-33584 --- .../extensions/AnnotationBasedExtension.kt | 1 + .../LightClassApplicabilityCheckExtension.kt | 39 +++++++ .../resolve/IDELightClassGenerationSupport.kt | 29 +++-- idea/resources/META-INF/android.xml | 1 + idea/resources/META-INF/extensions/ide.xml | 4 + idea/resources/META-INF/jvm.xml | 2 + idea/resources/META-INF/jvm.xml.183 | 2 + .../META-INF/kotlinx-serialization.xml | 1 + plugins/allopen/allopen-ide/build.gradle.kts | 34 +++++- ...penDeclarationAttributeAltererExtension.kt | 48 +++------ .../test/TestAllOpenForLightClass.kt | 70 ++++++++++++ .../parcel/ParcelableCodegenExtension.kt | 2 +- .../parcel/ParcelableDeclarationChecker.kt | 100 +++++++++++------- .../IDEParcelableApplicabilityExtension.kt | 27 +++++ ...onBasedLightClassApplicabilityExtension.kt | 41 +++++++ .../src/CachedAnnotationNames.kt | 41 +++++++ .../src/idePluginUtils.kt | 30 ++++-- .../SerializationPluginDeclarationChecker.kt | 1 - ...rializationPluginApplicabilityExtension.kt | 25 +++++ .../src/NoArgExpressionCodegenExtension.kt | 36 +++---- plugins/noarg/noarg-ide/build.gradle.kts | 33 +++++- .../src/IdeNoArgDeclarationChecker.kt | 41 +++---- .../kotlin/noarg/TestNoArgForLightClass.kt | 58 ++++++++++ 23 files changed, 528 insertions(+), 138 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/extensions/LightClassApplicabilityCheckExtension.kt create mode 100644 plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt create mode 100644 plugins/annotation-based-compiler-plugins-ide-support/src/AnnotationBasedLightClassApplicabilityExtension.kt create mode 100644 plugins/annotation-based-compiler-plugins-ide-support/src/CachedAnnotationNames.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/IdeSerializationPluginApplicabilityExtension.kt create mode 100644 plugins/noarg/noarg-ide/test/org/jetbrains/kotlin/noarg/TestNoArgForLightClass.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/extensions/AnnotationBasedExtension.kt b/compiler/frontend/src/org/jetbrains/kotlin/extensions/AnnotationBasedExtension.kt index 587c0127699..11ed0a7d29a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/extensions/AnnotationBasedExtension.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/extensions/AnnotationBasedExtension.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.types.TypeUtils interface AnnotationBasedExtension { + fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List fun DeclarationDescriptor.hasSpecialAnnotation(modifierListOwner: KtModifierListOwner?): Boolean { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/extensions/LightClassApplicabilityCheckExtension.kt b/compiler/frontend/src/org/jetbrains/kotlin/extensions/LightClassApplicabilityCheckExtension.kt new file mode 100644 index 00000000000..8ddd5cf3473 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/extensions/LightClassApplicabilityCheckExtension.kt @@ -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( + "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): LightClassApplicabilityType +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index 6dbbd511189..06bf32c5861 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -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 } diff --git a/idea/resources/META-INF/android.xml b/idea/resources/META-INF/android.xml index 272eac57ce5..9f021ecda0b 100644 --- a/idea/resources/META-INF/android.xml +++ b/idea/resources/META-INF/android.xml @@ -96,6 +96,7 @@ + diff --git a/idea/resources/META-INF/extensions/ide.xml b/idea/resources/META-INF/extensions/ide.xml index 09a01395241..f8ea8028a50 100644 --- a/idea/resources/META-INF/extensions/ide.xml +++ b/idea/resources/META-INF/extensions/ide.xml @@ -7,6 +7,10 @@ interface="org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension" area="IDEA_PROJECT"/> + + diff --git a/idea/resources/META-INF/jvm.xml b/idea/resources/META-INF/jvm.xml index 7cf00181d75..866d3914cd6 100644 --- a/idea/resources/META-INF/jvm.xml +++ b/idea/resources/META-INF/jvm.xml @@ -198,9 +198,11 @@ + + diff --git a/idea/resources/META-INF/jvm.xml.183 b/idea/resources/META-INF/jvm.xml.183 index 9dbb3323d2f..2c18b264ff8 100644 --- a/idea/resources/META-INF/jvm.xml.183 +++ b/idea/resources/META-INF/jvm.xml.183 @@ -198,9 +198,11 @@ + + diff --git a/idea/resources/META-INF/kotlinx-serialization.xml b/idea/resources/META-INF/kotlinx-serialization.xml index 4ad16af7a6a..fafe0401264 100644 --- a/idea/resources/META-INF/kotlinx-serialization.xml +++ b/idea/resources/META-INF/kotlinx-serialization.xml @@ -5,5 +5,6 @@ + diff --git a/plugins/allopen/allopen-ide/build.gradle.kts b/plugins/allopen/allopen-ide/build.gradle.kts index 814f6f2e4bb..628ae7af620 100644 --- a/plugins/allopen/allopen-ide/build.gradle.kts +++ b/plugins/allopen/allopen-ide/build.gradle.kts @@ -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) { + +} \ No newline at end of file diff --git a/plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt b/plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt index f537d3ce6ff..312d03e36f7 100644 --- a/plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt +++ b/plugins/allopen/allopen-ide/src/IdeAllOpenDeclarationAttributeAltererExtension.kt @@ -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>> = cachedValue(project) { - CachedValueProvider.Result.create( - ContainerUtil.createConcurrentWeakMap>(), - 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 { - 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 cachedValue(project: Project, result: () -> CachedValueProvider.Result): CachedValue { - return CachedValuesManager.getManager(project).createCachedValue(result, false) + return if (ApplicationManager.getApplication().isUnitTestMode) ANNOTATIONS_FOR_TESTS + else cachedAnnotationsNames.getAnnotationNames(modifierListOwner) } } \ No newline at end of file diff --git a/plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt b/plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt new file mode 100644 index 00000000000..a28df8af19b --- /dev/null +++ b/plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt @@ -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)) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt index 9c3618f1aba..84539917e64 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt @@ -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") diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableDeclarationChecker.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableDeclarationChecker.kt index fb79f637a5a..26f19cfe0e2 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableDeclarationChecker.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableDeclarationChecker.kt @@ -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()) { 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 + ) } } } diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt new file mode 100644 index 00000000000..6a3ce9bdc66 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/IDEParcelableApplicabilityExtension.kt @@ -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): 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 + } +} \ No newline at end of file diff --git a/plugins/annotation-based-compiler-plugins-ide-support/src/AnnotationBasedLightClassApplicabilityExtension.kt b/plugins/annotation-based-compiler-plugins-ide-support/src/AnnotationBasedLightClassApplicabilityExtension.kt new file mode 100644 index 00000000000..2feb2f7e3a0 --- /dev/null +++ b/plugins/annotation-based-compiler-plugins-ide-support/src/AnnotationBasedLightClassApplicabilityExtension.kt @@ -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 = + cachedAnnotationsNames.getAnnotationNames(modifierListOwner) + + override fun checkApplicabilityType(declaration: KtDeclaration, descriptor: Lazy): 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 + } +} \ No newline at end of file diff --git a/plugins/annotation-based-compiler-plugins-ide-support/src/CachedAnnotationNames.kt b/plugins/annotation-based-compiler-plugins-ide-support/src/CachedAnnotationNames.kt new file mode 100644 index 00000000000..11da445a845 --- /dev/null +++ b/plugins/annotation-based-compiler-plugins-ide-support/src/CachedAnnotationNames.kt @@ -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 { + 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>> = cachedValue(project) { + CachedValueProvider.Result.create( + ContainerUtil.createConcurrentWeakMap>(), + ProjectRootModificationTracker.getInstance(project) + ) + } + + fun getNamesForModule(module: Module): List { + return cache.value.getOrPut(module) { module.getSpecialAnnotations(annotationOptionPrefix) } + } + + private fun cachedValue(project: Project, result: () -> CachedValueProvider.Result): CachedValue { + return CachedValuesManager.getManager(project).createCachedValue(result, false) + } +} \ No newline at end of file diff --git a/plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt b/plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt index 01a5b11f973..883eee83f8d 100644 --- a/plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt +++ b/plugins/annotation-based-compiler-plugins-ide-support/src/idePluginUtils.kt @@ -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 { @@ -26,9 +29,9 @@ fun Module.getSpecialAnnotations(prefix: String): List { 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, val classpath: List) { @@ -36,10 +39,10 @@ class AnnotationBasedCompilerPluginSetup(val options: List, 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 -} \ No newline at end of file +} + +val KtDeclaration.isOrdinaryClass + get() = this is KtClass && + !this.hasModifier(KtTokens.INLINE_KEYWORD) && + !this.isAnnotation() && + !this.isInterface() + +val KtDeclaration.isAnnotated get() = this.annotationEntries.isNotEmpty() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt index ae45ba478e7..46657849923 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt @@ -223,5 +223,4 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { ) } } - } diff --git a/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/IdeSerializationPluginApplicabilityExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/IdeSerializationPluginApplicabilityExtension.kt new file mode 100644 index 00000000000..804ef0567de --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/IdeSerializationPluginApplicabilityExtension.kt @@ -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): LightClassApplicabilityType { + + if (!declaration.isOrdinaryClass || !declaration.isAnnotated) return LightClassApplicabilityType.UltraLightClass + + return (descriptor.value as? ClassDescriptor)?.let { + getIfEnabledOn(it) { LightClassApplicabilityType.LightClass } + } ?: LightClassApplicabilityType.UltraLightClass + } +} \ No newline at end of file diff --git a/plugins/noarg/noarg-cli/src/NoArgExpressionCodegenExtension.kt b/plugins/noarg/noarg-cli/src/NoArgExpressionCodegenExtension.kt index a6c9aa51f0f..86775c82fff 100644 --- a/plugins/noarg/noarg-cli/src/NoArgExpressionCodegenExtension.kt +++ b/plugins/noarg/noarg-cli/src/NoArgExpressionCodegenExtension.kt @@ -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 ()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, "", - "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V", false) - } - else { + codegen.v.visitMethodInsn( + Opcodes.INVOKESPECIAL, superClassInternalName, "", + "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V", false + ) + } else { codegen.v.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "", "()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 } diff --git a/plugins/noarg/noarg-ide/build.gradle.kts b/plugins/noarg/noarg-ide/build.gradle.kts index 6e8731dc6af..6b4abc83d99 100644 --- a/plugins/noarg/noarg-ide/build.gradle.kts +++ b/plugins/noarg/noarg-ide/build.gradle.kts @@ -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) { + +} diff --git a/plugins/noarg/noarg-ide/src/IdeNoArgDeclarationChecker.kt b/plugins/noarg/noarg-ide/src/IdeNoArgDeclarationChecker.kt index c8b82bf9b5f..01538f18e3c 100644 --- a/plugins/noarg/noarg-ide/src/IdeNoArgDeclarationChecker.kt +++ b/plugins/noarg/noarg-ide/src/IdeNoArgDeclarationChecker.kt @@ -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>> = cachedValue(project) { - CachedValueProvider.Result.create( - ContainerUtil.createConcurrentWeakMap>(), - ProjectRootModificationTracker.getInstance(project) - ) - } +class IdeNoArgApplicabilityExtension(project: Project) : + AnnotationBasedLightClassApplicabilityExtension(project, NO_ARG_ANNOTATION_OPTION_PREFIX) - override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List { - 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 cachedValue(project: Project, result: () -> CachedValueProvider.Result): CachedValue { - return CachedValuesManager.getManager(project).createCachedValue(result, false) - } + override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List = + cachedAnnotationNames.getAnnotationNames(modifierListOwner) } \ No newline at end of file diff --git a/plugins/noarg/noarg-ide/test/org/jetbrains/kotlin/noarg/TestNoArgForLightClass.kt b/plugins/noarg/noarg-ide/test/org/jetbrains/kotlin/noarg/TestNoArgForLightClass.kt new file mode 100644 index 00000000000..50a1235721d --- /dev/null +++ b/plugins/noarg/noarg-ide/test/org/jetbrains/kotlin/noarg/TestNoArgForLightClass.kt @@ -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() }) + } +} \ No newline at end of file