From 8c65a9a2aa9346a1430835765ae4f5a01452c0e4 Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Thu, 17 Oct 2019 23:28:44 +0300 Subject: [PATCH] Move AllOpen UL support to separate extension --- .../UltraLightClassModifierExtension.kt | 7 ++ .../KtLightClassForSourceDeclaration.kt | 13 ++-- .../kotlin/asJava/classes/ultraLightClass.kt | 8 +-- .../classes/ultraLightMembersCreator.kt | 13 ++-- .../kotlin/asJava/classes/ultraLightUtils.kt | 6 ++ idea/resources/META-INF/jvm.xml | 1 + idea/resources/META-INF/jvm.xml.183 | 1 + plugins/allopen/allopen-ide/build.gradle.kts | 1 + ...AllOpenUltraLightClassModifierExtension.kt | 67 +++++++++++++++++++ .../test/TestAllOpenForLightClass.kt | 8 +-- 10 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 plugins/allopen/allopen-ide/src/AllOpenUltraLightClassModifierExtension.kt diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassModifierExtension.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassModifierExtension.kt index 0b4dd8ed678..3c4c1957015 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassModifierExtension.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/UltraLightClassModifierExtension.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.asJava +import com.intellij.psi.PsiModifier import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass import org.jetbrains.kotlin.asJava.elements.KtLightField import org.jetbrains.kotlin.asJava.elements.KtLightMethod @@ -32,4 +33,10 @@ interface UltraLightClassModifierExtension { containingDeclaration: KtUltraLightClass, fieldsList: MutableList ) = Unit + + fun interceptModalityBuilding( + declaration: KtDeclaration, + descriptor: Lazy, + modifier: String + ) = modifier } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt index eab06651767..4c09ea985b5 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassForSourceDeclaration.kt @@ -42,10 +42,7 @@ import org.jetbrains.kotlin.asJava.elements.KtLightModifierList import org.jetbrains.kotlin.asJava.elements.KtLightPsiReferenceList import org.jetbrains.kotlin.asJava.hasInterfaceDefaultImpls import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind import org.jetbrains.kotlin.name.FqNameUnsafe @@ -230,10 +227,12 @@ abstract class KtLightClassForSourceDeclaration( if (isAbstract() || isSealed()) { psiModifiers.add(PsiModifier.ABSTRACT) } else if (!(classOrObject.hasModifier(OPEN_KEYWORD) || (classOrObject is KtClass && classOrObject.isEnum()))) { - // AllOpen can affect on modality of the member. We ought to check if the extension could override the modality - // Resolver will produce correct descriptor corresponding to modality from AllOpen. - // The easiest way to get new modality is to resolve the descriptor - if (!kotlinOrigin.isOrdinaryClass || getDescriptor()?.modality != Modality.OPEN) { + val descriptor = lazy { getDescriptor() } + var modifier = PsiModifier.FINAL + project.applyCompilerPlugins { + modifier = it.interceptModalityBuilding(kotlinOrigin, descriptor, modifier) + } + if (modifier == PsiModifier.FINAL) { psiModifiers.add(PsiModifier.FINAL) } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt index 49c773f51d5..cb33226bff9 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightClass.kt @@ -197,7 +197,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor fun ArrayList.updateWithCompilerPlugins() = also { val lazyDescriptor = lazy { getDescriptor() } - applyCompilerPlugins { + project.applyCompilerPlugins { it.interceptFieldsBuilding( declaration = kotlinOrigin, descriptor = lazyDescriptor, @@ -309,7 +309,7 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor addDelegatesToInterfaceMethods(result) val lazyDescriptor = lazy { getDescriptor() } - applyCompilerPlugins { + project.applyCompilerPlugins { it.interceptMethodsBuilding( declaration = kotlinOrigin, descriptor = lazyDescriptor, @@ -321,10 +321,6 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor return result } - private inline fun applyCompilerPlugins(body: (UltraLightClassModifierExtension) -> Unit) { - UltraLightClassModifierExtension.getInstances(project).forEach { body(it) } - } - private val _ownMethods: CachedValue> = CachedValuesManager.getManager(project).createCachedValue( { CachedValueProvider.Result.create( diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt index d0a6c0b76f9..627b34883ea 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt @@ -246,14 +246,15 @@ internal class UltraLightMembersCreator( override fun hasModifierProperty(name: String): Boolean { - val modifierResult = hasModifier(name) + if (name != PsiModifier.FINAL || !outerDeclaration.isOrdinaryClass) return hasModifier(name) //AllOpen can affect on modality of the member. We ought to check if the extension could override the modality - if (name != PsiModifier.FINAL) return modifierResult - if (!outerDeclaration.isOrdinaryClass) return modifierResult - // Resolver will produce correct descriptor corresponding to modality from AllOpen. - // The easiest way to get new modality is to resolve the descriptor - return (declaration.resolve() as? CallableMemberDescriptor)?.modality == Modality.FINAL + val descriptor = lazy { declaration.resolve() } + var modifier = PsiModifier.FINAL + project.applyCompilerPlugins { + modifier = it.interceptModalityBuilding(declaration, descriptor, modifier) + } + return modifier == PsiModifier.FINAL } private fun hasModifier(name: String): Boolean { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt index a5a3ce5f549..ef8484e5605 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.asJava.classes import com.google.common.collect.Lists +import com.intellij.openapi.project.Project import com.intellij.psi.* import com.intellij.psi.impl.cache.ModifierFlags import com.intellij.psi.impl.cache.TypeInfo @@ -16,6 +17,7 @@ import com.intellij.psi.impl.light.* import com.intellij.util.BitUtil.isSet import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.asJava.LightClassGenerationSupport +import org.jetbrains.kotlin.asJava.UltraLightClassModifierExtension import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration import org.jetbrains.kotlin.asJava.elements.KotlinLightTypeParameterListBuilder import org.jetbrains.kotlin.asJava.elements.KtLightMethod @@ -390,5 +392,9 @@ fun KotlinType.tryResolveMarkerInterfaceFQName(): String? { return null } +internal inline fun Project.applyCompilerPlugins(body: (UltraLightClassModifierExtension) -> Unit) { + UltraLightClassModifierExtension.getInstances(this).forEach { body(it) } +} + internal fun L.invalidAccess(): Nothing = error("Cls delegate shouldn't be loaded for not too complex ultra-light classes! Qualified name: ${javaClass.name}") diff --git a/idea/resources/META-INF/jvm.xml b/idea/resources/META-INF/jvm.xml index 26fad8bca9e..3d5c4b6d738 100644 --- a/idea/resources/META-INF/jvm.xml +++ b/idea/resources/META-INF/jvm.xml @@ -198,6 +198,7 @@ + diff --git a/idea/resources/META-INF/jvm.xml.183 b/idea/resources/META-INF/jvm.xml.183 index 7bd461223b7..8ba116c5793 100644 --- a/idea/resources/META-INF/jvm.xml.183 +++ b/idea/resources/META-INF/jvm.xml.183 @@ -198,6 +198,7 @@ + diff --git a/plugins/allopen/allopen-ide/build.gradle.kts b/plugins/allopen/allopen-ide/build.gradle.kts index 628ae7af620..ca78ea10774 100644 --- a/plugins/allopen/allopen-ide/build.gradle.kts +++ b/plugins/allopen/allopen-ide/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { testRuntimeOnly(intellijDep()) Platform[192].orHigher { + compileOnly(intellijPluginDep("java")) testCompileOnly(intellijPluginDep("java")) testRuntimeOnly(intellijPluginDep("java")) } diff --git a/plugins/allopen/allopen-ide/src/AllOpenUltraLightClassModifierExtension.kt b/plugins/allopen/allopen-ide/src/AllOpenUltraLightClassModifierExtension.kt new file mode 100644 index 00000000000..8e815a0bca1 --- /dev/null +++ b/plugins/allopen/allopen-ide/src/AllOpenUltraLightClassModifierExtension.kt @@ -0,0 +1,67 @@ +/* + * 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.allopen.ide + +import org.jetbrains.kotlin.descriptors.* + +/* + * 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. + */ + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiModifier +import org.jetbrains.kotlin.annotation.plugin.ide.CachedAnnotationNames +import org.jetbrains.kotlin.annotation.plugin.ide.getAnnotationNames +import org.jetbrains.kotlin.asJava.UltraLightClassModifierExtension +import org.jetbrains.kotlin.extensions.AnnotationBasedExtension +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClass +import org.jetbrains.kotlin.psi.psiUtil.isPropertyParameter +import org.jetbrains.kotlin.util.isAnnotated +import org.jetbrains.kotlin.util.isOrdinaryClass + +class AllOpenUltraLightClassModifierExtension(project: Project) : + AnnotationBasedExtension, + UltraLightClassModifierExtension { + + private val cachedAnnotationsNames = CachedAnnotationNames(project, ALL_OPEN_ANNOTATION_OPTION_PREFIX) + + override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List = + cachedAnnotationsNames.getAnnotationNames(modifierListOwner) + + private val KtDeclaration.isMethodOrProperty get() = this is KtProperty || this is KtPropertyAccessor || this is KtFunction || (this is KtParameter && this.isPropertyParameter()) + + private fun isSuitableDeclaration(declaration: KtDeclaration): Boolean { + + if (getAnnotationFqNames(declaration).isEmpty()) return false + + val declarationToCheck = if (declaration.isMethodOrProperty) declaration.containingClass() else declaration + declarationToCheck ?: return false + + if (!declarationToCheck.isOrdinaryClass || declarationToCheck !is KtClassOrObject) return false + + if (declarationToCheck.superTypeListEntries.isEmpty() && !declarationToCheck.isAnnotated) return false + + return true + } + + override fun interceptModalityBuilding( + declaration: KtDeclaration, + descriptor: Lazy, + modifier: String + ): String { + + //Final can be altered to Open only + if (modifier != PsiModifier.FINAL) return modifier + + if (!isSuitableDeclaration(declaration)) return modifier + + // Resolver will produce correct descriptor corresponding to modality from AllOpen. + // The easiest way to get new modality is to resolve the descriptor + return if ((descriptor.value as? MemberDescriptor)?.modality == Modality.OPEN) PsiModifier.OPEN else modifier + } +} \ No newline at end of file diff --git a/plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt b/plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt index a28df8af19b..7a22497f822 100644 --- a/plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt +++ b/plugins/allopen/allopen-ide/test/TestAllOpenForLightClass.kt @@ -3,11 +3,10 @@ * 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 +package org.jetbrains.kotlin.allopen 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 @@ -17,11 +16,8 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner import org.junit.runner.RunWith - - - @RunWith(JUnit3WithIdeaConfigurationRunner::class) -class TestNoArgForLightClass : KotlinLightCodeInsightFixtureTestCase() { +class TestAllOpenForLightClass : KotlinLightCodeInsightFixtureTestCase() { companion object { val allOpenAnnotationName = AbstractAllOpenDeclarationAttributeAltererExtension.ANNOTATIONS_FOR_TESTS.first()