From fa4dadf9b768a6270f12dc1643ef65d47dd9a049 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 3 Mar 2022 19:13:02 +0900 Subject: [PATCH] Update KotlinClassInnerStuffCache implementation 'KotlinClassInnerStuffCache' was copy-pasted from the platform's 'ClassInnerStuffCache' several years ago. After J2K and other changes, it became quite dirty. Besides cleaning things up, enum synthetic method handling have significantly improved. Before, such methods were handled only in 'processDeclarationsInEnum', and 'getMethods()' didn't return them. This led to inconsistent behavior and was reason for providing special ad-hoc support in '1b6ded600518176b21cc308ed3e032ef8d04cc90'. --- .../KtLightClassForDecompiledDeclaration.kt | 3 - ...tLightClassForDecompiledDeclarationBase.kt | 5 +- .../symbol/classes/FirLightClassBase.kt | 11 +- .../classes/KotlinClassInnerStuffCache.kt | 374 ++++++++++++------ .../kotlin/asJava/classes/KtLightClass.kt | 3 + .../kotlin/asJava/classes/KtLightClassBase.kt | 18 +- .../AnnotatedParameterInEnumConstructor.java | 6 + .../lightClasses/DeprecatedEnumEntry.java | 6 + .../compilationErrors/EnumNameOverride.java | 6 + .../asJava/ultraLightClasses/enums.fir.java | 24 ++ .../asJava/ultraLightClasses/enums.lib.java | 6 + .../ultraLightClasses/inlineClasses.java | 6 + 12 files changed, 323 insertions(+), 145 deletions(-) diff --git a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclaration.kt b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclaration.kt index 2d3e3645756..9abfd0fface 100644 --- a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclaration.kt +++ b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclaration.kt @@ -95,9 +95,6 @@ open class KtLightClassForDecompiledDeclaration( lastParent: PsiElement?, place: PsiElement ): Boolean { - if (isEnum) { - if (!KotlinClassInnerStuffCache.processDeclarationsInEnum(processor, state, myInnersCache)) return false - } return PsiClassImplUtil.processDeclarationsInClass( this, processor, state, null, lastParent, place, PsiUtil.getLanguageLevel(place), false diff --git a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclarationBase.kt b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclarationBase.kt index cbfb5732de0..63d29b9f657 100644 --- a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclarationBase.kt +++ b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/KtLightClassForDecompiledDeclarationBase.kt @@ -4,8 +4,7 @@ package org.jetbrains.kotlin.analysis.decompiled.light.classes import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement -import com.intellij.psi.impl.source.PsiExtensibleClass -import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.classes.KtExtensibleLightClass import org.jetbrains.kotlin.asJava.elements.KtLightElementBase import org.jetbrains.kotlin.psi.KtClassOrObject @@ -13,4 +12,4 @@ abstract class KtLightClassForDecompiledDeclarationBase( override val clsDelegate: PsiClass, clsParent: PsiElement, final override val kotlinOrigin: KtClassOrObject? -) : KtLightElementBase(clsParent), PsiClass, KtLightClass, PsiExtensibleClass \ No newline at end of file +) : KtLightElementBase(clsParent), PsiClass, KtExtensibleLightClass \ No newline at end of file diff --git a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/FirLightClassBase.kt b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/FirLightClassBase.kt index a525eee8cb3..7912a84a027 100644 --- a/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/FirLightClassBase.kt +++ b/analysis/symbol-light-classes/src/org/jetbrains/kotlin/light/classes/symbol/classes/FirLightClassBase.kt @@ -24,14 +24,12 @@ import com.intellij.openapi.util.TextRange import com.intellij.psi.* import com.intellij.psi.impl.* import com.intellij.psi.impl.light.LightElement -import com.intellij.psi.impl.source.PsiExtensibleClass import com.intellij.psi.javadoc.PsiDocComment import com.intellij.psi.scope.PsiScopeProcessor import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.PsiUtil import org.jetbrains.annotations.NonNls import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker -import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache.Companion.processDeclarationsInEnum import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.analysis.api.tokens.HackToForceAllowRunningAnalyzeOnEDT import org.jetbrains.kotlin.analysis.api.tokens.hackyAllowRunningOnEdt @@ -39,9 +37,9 @@ import org.jetbrains.kotlin.asJava.classes.* import org.jetbrains.kotlin.light.classes.symbol.classes.checkIsInheritor import javax.swing.Icon -abstract class FirLightClassBase protected constructor(manager: PsiManager) : LightElement(manager, KotlinLanguage.INSTANCE), PsiClass, - KtLightClass, PsiExtensibleClass { - +abstract class FirLightClassBase protected constructor( + manager: PsiManager +) : LightElement(manager, KotlinLanguage.INSTANCE), PsiClass, KtExtensibleLightClass { override val clsDelegate: PsiClass get() = invalidAccess() @@ -96,9 +94,6 @@ abstract class FirLightClassBase protected constructor(manager: PsiManager) : Li override fun processDeclarations( processor: PsiScopeProcessor, state: ResolveState, lastParent: PsiElement?, place: PsiElement ): Boolean { - - if (isEnum && !processDeclarationsInEnum(processor, state, myInnersCache)) return false - return PsiClassImplUtil.processDeclarationsInClass( this, processor, diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KotlinClassInnerStuffCache.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KotlinClassInnerStuffCache.kt index cc534c77946..dabb59fac62 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KotlinClassInnerStuffCache.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KotlinClassInnerStuffCache.kt @@ -6,20 +6,38 @@ package org.jetbrains.kotlin.asJava.classes import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.TextRange import com.intellij.psi.* import com.intellij.psi.impl.PsiClassImplUtil import com.intellij.psi.impl.PsiImplUtil -import com.intellij.psi.impl.light.LightMethod -import com.intellij.psi.impl.source.PsiExtensibleClass -import com.intellij.psi.scope.ElementClassHint -import com.intellij.psi.scope.NameHint -import com.intellij.psi.scope.PsiScopeProcessor +import com.intellij.psi.impl.PsiSuperMethodImplUtil +import com.intellij.psi.impl.compiled.ClsClassImpl +import com.intellij.psi.impl.compiled.InnerClassSourceStrategy +import com.intellij.psi.impl.compiled.StubBuildingVisitor +import com.intellij.psi.impl.java.stubs.PsiClassStub +import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl +import com.intellij.psi.impl.light.* +import com.intellij.psi.javadoc.PsiDocComment +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.StubElement +import com.intellij.psi.util.* import com.intellij.util.ArrayUtil +import com.intellij.util.IncorrectOperationException import gnu.trove.THashMap -import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.annotations.NotNull +import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.elements.KtLightParameter +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.org.objectweb.asm.Opcodes +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.HashMap class KotlinClassInnerStuffCache( - private val myClass: PsiExtensibleClass, + private val myClass: KtExtensibleLightClass, private val dependencies: List, private val lazyCreator: LazyCreator, ) { @@ -27,182 +45,306 @@ class KotlinClassInnerStuffCache( abstract fun get(initializer: () -> T, dependencies: List): Lazy } - private fun get(initializer: () -> T): Lazy = lazyCreator.get(initializer, dependencies) + private fun cache(initializer: () -> T): Lazy = lazyCreator.get(initializer, dependencies) - private val _getConstructors: Array by get { PsiImplUtil.getConstructors(myClass) } + private val constructorsCache = cache { PsiImplUtil.getConstructors(myClass) } val constructors: Array - get() = _getConstructors + get() = copy(constructorsCache.value) - private val _getFields: Array by get { this.getAllFields() } + private val fieldsCache = cache { + val own = myClass.ownFields + val ext = collectAugments(myClass, PsiField::class.java) + ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY) + } val fields: Array - get() = _getFields + get() = copy(fieldsCache.value) - private val _getMethods: Array by get { this.getAllMethods() } + private val methodsCache = cache { + val own = myClass.ownMethods + var ext = collectAugments(myClass, PsiMethod::class.java) + if (myClass.isEnum) { + ext = ArrayList(ext.size + 2).also { + it += ext + it.addIfNotNull(getValuesMethod()) + it.addIfNotNull(getValueOfMethod()) + } + } + ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY) + } val methods: Array - get() = _getMethods + get() = copy(methodsCache.value) - private val _getAllInnerClasses: Array by get { this.getAllInnerClasses() } + private val innerClassesCache = cache { + val own = myClass.ownInnerClasses + val ext = collectAugments(myClass, PsiClass::class.java) + ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY) + } val innerClasses: Array - get() = _getAllInnerClasses + get() = copy(innerClassesCache.value) - private val _getFieldsMap: Map by get { this.getFieldsMap() } + private val recordComponentsCache = cache { + val header = myClass.recordHeader + header?.recordComponents ?: PsiRecordComponent.EMPTY_ARRAY + } + + val recordComponents: Array + get() = copy(recordComponentsCache.value) + + private val fieldByNameCache = cache { + val fields = this.fields.takeIf { it.isNotEmpty() } ?: return@cache emptyMap() + Collections.unmodifiableMap(THashMap(fields.size).apply { + for (field in fields) { + putIfAbsent(field.name, field) + } + }) + } fun findFieldByName(name: String, checkBases: Boolean): PsiField? { return if (checkBases) { PsiClassImplUtil.findFieldByName(myClass, name, true) } else { - _getFieldsMap[name] + fieldByNameCache.value[name] } } - private val _getMethodsMap: Map> by get { this.getMethodsMap() } + private val methodByNameCache = cache { + val methods = this.methods.takeIf { it.isNotEmpty() } ?: return@cache emptyMap() + Collections.unmodifiableMap(THashMap>().apply { + for ((key, list) in methods.groupByTo(HashMap()) { it.name }) { + put(key, list.toTypedArray()) + } + }) + } fun findMethodsByName(name: String, checkBases: Boolean): Array { return if (checkBases) { PsiClassImplUtil.findMethodsByName(myClass, name, true) } else { - copy(_getMethodsMap[name] ?: PsiMethod.EMPTY_ARRAY) + copy(methodByNameCache.value[name] ?: PsiMethod.EMPTY_ARRAY) } } - private val _getInnerClassesMap: Map by get { this.getInnerClassesMap() } + private val innerClassByNameCache = cache { + val classes = this.innerClasses.takeIf { it.isNotEmpty() } ?: return@cache emptyMap() + + Collections.unmodifiableMap(THashMap().apply { + for (psiClass in classes) { + val name = psiClass.name + if (name == null) { + Logger.getInstance(KotlinClassInnerStuffCache::class.java).error(psiClass) + } else if (psiClass !is ExternallyDefinedPsiElement || !containsKey(name)) { + put(name, psiClass) + } + } + }) + } fun findInnerClassByName(name: String, checkBases: Boolean): PsiClass? { return if (checkBases) { PsiClassImplUtil.findInnerByName(myClass, name, true) } else { - _getInnerClassesMap[name] + innerClassByNameCache.value[name] } } - private val _makeValuesMethod: PsiMethod by get { this.makeValuesMethod() } + private val valuesMethodCache = cache { KotlinEnumSyntheticMethod(myClass, KotlinEnumSyntheticMethod.Kind.VALUES) } - fun getValuesMethod(): PsiMethod? = if (myClass.isEnum && myClass.name != null) _makeValuesMethod else null + private fun getValuesMethod(): PsiMethod? { + if (myClass.isEnum && !myClass.isAnonymous && !isClassNameSealed()) { + return valuesMethodCache.value + } - private val _makeValueOfMethod: PsiMethod by get { this.makeValueOfMethod() } - - fun getValueOfMethod(): PsiMethod? = if (myClass.isEnum && myClass.name != null) _makeValueOfMethod else null - - private fun copy(value: Array): Array { - return if (value.isEmpty()) value else value.clone() + return null } - private fun getAllFields(): Array { - val own = myClass.ownFields - val ext = collectAugments(myClass, PsiField::class.java) - return ArrayUtil.mergeCollections(own, ext, PsiField.ARRAY_FACTORY) + private val valueOfMethodCache = cache { KotlinEnumSyntheticMethod(myClass, KotlinEnumSyntheticMethod.Kind.VALUE_OF) } + + fun getValueOfMethod(): PsiMethod? { + if (myClass.isEnum && !myClass.isAnonymous) { + return valueOfMethodCache.value + } + + return null } - private fun getAllMethods(): Array { - val own = myClass.ownMethods - val ext = collectAugments(myClass, PsiMethod::class.java) - return ArrayUtil.mergeCollections(own, ext, PsiMethod.ARRAY_FACTORY) + private fun isClassNameSealed(): Boolean { + return myClass.name == PsiKeyword.SEALED && PsiUtil.getLanguageLevel(myClass).toJavaVersion().feature >= 16 + } +} + +private class KotlinEnumSyntheticMethod( + private val enumClass: KtExtensibleLightClass, + private val kind: Kind +) : LightElement(enumClass.manager, enumClass.language), KtLightMethod, SyntheticElement { + enum class Kind(val methodName: String) { + VALUE_OF("valueOf"), VALUES("values") } - private fun getAllInnerClasses(): Array { - val own = myClass.ownInnerClasses - val ext = collectAugments(myClass, PsiClass::class.java) - return ArrayUtil.mergeCollections(own, ext, PsiClass.ARRAY_FACTORY) + private val returnType = run { + val enumType = JavaPsiFacade.getElementFactory(project).createType(enumClass) + .annotate { arrayOf(makeNotNullAnnotation(enumClass)) } + + when (kind) { + Kind.VALUE_OF -> enumType + Kind.VALUES -> enumType.createArrayType().annotate { arrayOf(makeNotNullAnnotation(enumClass)) } + } } - private fun getFieldsMap(): Map { - val fields = this.fields - if (fields.isEmpty()) return emptyMap() + private val parameterList = LightParameterListBuilder(manager, language).apply { + if (kind == Kind.VALUE_OF) { + val stringType = PsiType.getJavaLangString(manager, GlobalSearchScope.allScope(project)) + val nameParameter = object : LightParameter("name", stringType, this, language, false), KtLightParameter { + override val method: KtLightMethod get() = this@KotlinEnumSyntheticMethod + override val kotlinOrigin: KtParameter? get() = null + override val clsDelegate: PsiParameter get() = this@KotlinEnumSyntheticMethod.clsDelegate.parameterList.parameters.single() - val cachedFields = THashMap() - for (field in fields) { - val name = field.name - if (!cachedFields.containsKey(name)) { - cachedFields[name] = field + override fun getParent(): PsiElement = this@KotlinEnumSyntheticMethod + override fun getContainingFile(): PsiFile = this@KotlinEnumSyntheticMethod.containingFile + + override fun getText(): String = name + override fun getTextRange(): TextRange = TextRange.EMPTY_RANGE } + nameParameter.setModifierList(NotNullModifierList(manager)) + addParameter(nameParameter) } - return cachedFields } - private fun getMethodsMap(): Map> { - val methods = this.methods - if (methods.isEmpty()) return emptyMap() + private val modifierList = object : LightModifierList(manager, language, PsiModifier.PUBLIC, PsiModifier.STATIC) { + override fun getParent() = this@KotlinEnumSyntheticMethod - val collectedMethods = hashMapOf>() - for (method in methods) { - var list: MutableList? = collectedMethods[method.name] - if (list == null) { - list = SmartList() - collectedMethods[method.name] = list - } - list.add(method) - } + private val annotations = arrayOf(makeNotNullAnnotation(enumClass)) - val cachedMethods = THashMap>() - for ((key, list) in collectedMethods) { - cachedMethods[key] = list.toTypedArray() - } - return cachedMethods + override fun findAnnotation(fqn: String): PsiAnnotation? = annotations.firstOrNull { it.hasQualifiedName(fqn) } + override fun getAnnotations(): Array = copy(annotations) } - private fun getInnerClassesMap(): Map { - val classes = this.innerClasses - if (classes.isEmpty()) return emptyMap() + override fun getTextOffset(): Int = enumClass.textOffset + override fun toString(): String = enumClass.toString() - val cachedInners = THashMap() - for (psiClass in classes) { - val name = psiClass.name - if (name == null) { - Logger.getInstance(KotlinClassInnerStuffCache::class.java).error(psiClass) - } else if (psiClass !is ExternallyDefinedPsiElement || !cachedInners.containsKey(name)) { - cachedInners[name] = psiClass - } - } - return cachedInners + override fun equals(other: Any?): Boolean { + return this === other || (other is KotlinEnumSyntheticMethod && enumClass == other.enumClass && kind == other.kind) } - private fun makeValuesMethod(): PsiMethod { - return getSyntheticMethod("public static " + myClass.name + "[] values() { }") - } + override fun hashCode() = Objects.hash(enumClass, kind) - private fun makeValueOfMethod(): PsiMethod { - return getSyntheticMethod("public static " + myClass.name + " valueOf(java.lang.String name) throws java.lang.IllegalArgumentException { }") - } + override fun isDeprecated(): Boolean = false + override fun getDocComment(): PsiDocComment? = null + override fun getReturnType(): PsiType = returnType + override fun getReturnTypeElement(): PsiTypeElement? = null + override fun getParameterList(): PsiParameterList = parameterList - private fun getSyntheticMethod(text: String): PsiMethod { - val factory = JavaPsiFacade.getElementFactory(myClass.project) - val method = factory.createMethodFromText(text, myClass) - return object : LightMethod(myClass.manager, method, myClass) { - override fun getTextOffset(): Int { - return myClass.textOffset + override fun getThrowsList(): PsiReferenceList { + return LightReferenceListBuilder(manager, language, PsiReferenceList.Role.THROWS_LIST).apply { + if (kind == Kind.VALUE_OF) { + addReference("java.lang.IllegalArgumentException") } } } - companion object { - private const val VALUES_METHOD = "values" - private const val VALUE_OF_METHOD = "valueOf" + override fun getParent(): PsiElement = enumClass + override fun getContainingClass(): KtExtensibleLightClass = enumClass + override fun getContainingFile(): PsiFile = enumClass.containingFile - // Copy of PsiClassImplUtil.processDeclarationsInEnum for own cache class - @JvmStatic - fun processDeclarationsInEnum( - processor: PsiScopeProcessor, - state: ResolveState, - innerStuffCache: KotlinClassInnerStuffCache - ): Boolean { - val classHint = processor.getHint(ElementClassHint.KEY) - if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) { - val nameHint = processor.getHint(NameHint.KEY) - if (nameHint == null || VALUES_METHOD == nameHint.getName(state)) { - val method = innerStuffCache.getValuesMethod() - if (method != null && !processor.execute(method, ResolveState.initial())) return false - } - if (nameHint == null || VALUE_OF_METHOD == nameHint.getName(state)) { - val method = innerStuffCache.getValueOfMethod() - if (method != null && !processor.execute(method, ResolveState.initial())) return false - } - } + override fun getBody(): PsiCodeBlock? = null + override fun isConstructor(): Boolean = false + override fun isVarArgs(): Boolean = false + override fun getSignature(substitutor: PsiSubstitutor): MethodSignature = MethodSignatureBackedByPsiMethod.create(this, substitutor) + override fun getNameIdentifier(): PsiIdentifier = LightIdentifier(manager, name) + override fun getName() = kind.methodName - return true + override fun findSuperMethods(): Array = PsiSuperMethodImplUtil.findSuperMethods(this) + override fun findSuperMethods(checkAccess: Boolean): Array = PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess) + override fun findSuperMethods(parentClass: PsiClass): Array = PsiSuperMethodImplUtil.findSuperMethods(this, parentClass) + + override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean): List { + return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess) + } + + @Suppress("OVERRIDE_DEPRECATION") + override fun findDeepestSuperMethod(): PsiMethod? = PsiSuperMethodImplUtil.findDeepestSuperMethod(this) + + override fun findDeepestSuperMethods(): Array = PsiMethod.EMPTY_ARRAY + override fun getModifierList(): PsiModifierList = modifierList + override fun hasModifierProperty(name: String) = name == PsiModifier.PUBLIC || name == PsiModifier.STATIC + override fun setName(name: String): PsiElement = throw IncorrectOperationException() + override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this) + override fun getDefaultValue(): PsiAnnotationMemberValue? = null + + override fun hasTypeParameters(): Boolean = false + override fun getTypeParameterList(): PsiTypeParameterList? = null + override fun getTypeParameters(): Array = PsiTypeParameter.EMPTY_ARRAY + + override val isMangled: Boolean get() = false + override val lightMemberOrigin: LightMemberOrigin? get() = null + override val kotlinOrigin: KtDeclaration? get() = null + + override fun getText(): String = "" + override fun getTextRange(): TextRange = TextRange.EMPTY_RANGE + + // As you can see, the implementation is quite dumb, yet it is still better than throwing an exception, + // as 'clsDelegate' is used for verifying light class consistency. + // See 'org.jetbrains.kotlin.idea.caches.resolve.LightClassLazinessChecker.LazinessInfo#checkConsistency' for more information. + override val clsDelegate: PsiMethod by lazy { + val emptyStrategy = object : InnerClassSourceStrategy { + override fun findInnerClass(innerName: String, outerClass: PsiClass): PsiClass? = null + override fun accept(innerClass: PsiClass, visitor: StubBuildingVisitor) {} + } + + val containingFile = enumClass.containingFile as PsiJavaFile + var parent = PsiJavaFileStubImpl(containingFile, containingFile.packageName, null, true) as StubElement<*> + + fun process(clazz: PsiClass): PsiClassStub<*> { + clazz.containingClass?.let { process(it) } + + val access = getAccess(clazz) + val superName = clazz.superClass?.let { ClassUtil.getJVMClassName(it) } ?: "java/lang/Object" + val interfaceNames = clazz.interfaces.mapNotNull { ClassUtil.getJVMClassName(it) }.toTypedArray() + + val stub = StubBuildingVisitor(clazz, emptyStrategy, parent, access, clazz.name).apply { + visit(Opcodes.V1_8, access, ClassUtil.getJVMClassName(clazz), null, superName, interfaceNames) + visitEnd() + }.result + + parent = stub + return stub + } + + ClsClassImpl(process(enumClass)).methods.single { it.name == name } + } + + private companion object { + private fun getAccess(clazz: PsiClass): Int { + var result = 0 + if (clazz.hasModifierProperty(PsiModifier.PUBLIC)) result = result or Opcodes.ACC_PUBLIC + if (clazz.hasModifierProperty(PsiModifier.PROTECTED)) result = result or Opcodes.ACC_PROTECTED + if (clazz.hasModifierProperty(PsiModifier.PRIVATE)) result = result or Opcodes.ACC_PRIVATE + if (clazz.hasModifierProperty(PsiModifier.FINAL)) result = result or Opcodes.ACC_FINAL + if (clazz.hasModifierProperty(PsiModifier.ABSTRACT)) result = result or Opcodes.ACC_ABSTRACT + if (clazz.isDeprecated) result = result or Opcodes.ACC_DEPRECATED + if (clazz.isEnum) result = result or Opcodes.ACC_ENUM + if (clazz.isAnnotationType) result = result or Opcodes.ACC_ANNOTATION + if (clazz.isInterface) result = result or Opcodes.ACC_INTERFACE + return result + } + + private fun makeNotNullAnnotation(context: PsiClass): PsiAnnotation { + return PsiElementFactory.getInstance(context.project).createAnnotationFromText("@" + NotNull::class.java.name, context) } } +} + +private val PsiClass.isAnonymous: Boolean + get() = name == null || this is PsiAnonymousClass + +private class NotNullModifierList(manager: PsiManager) : LightModifierList(manager) { + private val annotation = PsiElementFactory.getInstance(project).createAnnotationFromText("@" + NotNull::class.java.name, context) + override fun getAnnotations() = arrayOf(annotation) +} + +private fun copy(value: Array): Array { + return if (value.isEmpty()) value else value.clone() } \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClass.kt index 5594fbd06b2..9e9e0c4efc2 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClass.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClass.kt @@ -17,8 +17,11 @@ package org.jetbrains.kotlin.asJava.classes import com.intellij.psi.PsiClass +import com.intellij.psi.impl.source.PsiExtensibleClass import org.jetbrains.kotlin.asJava.KtLightClassMarker import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration import org.jetbrains.kotlin.psi.KtClassOrObject interface KtLightClass : PsiClass, KtLightDeclaration, KtLightClassMarker + +interface KtExtensibleLightClass : KtLightClass, PsiExtensibleClass \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassBase.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassBase.kt index e79ec8b167f..a30457fecdb 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassBase.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLightClassBase.kt @@ -20,16 +20,14 @@ import com.intellij.navigation.ItemPresentationProviders import com.intellij.psi.* import com.intellij.psi.impl.PsiClassImplUtil import com.intellij.psi.impl.light.AbstractLightClass -import com.intellij.psi.impl.source.PsiExtensibleClass -import com.intellij.psi.scope.PsiScopeProcessor import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService -import org.jetbrains.kotlin.asJava.classes.KotlinClassInnerStuffCache.Companion.processDeclarationsInEnum import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl import org.jetbrains.kotlin.idea.KotlinLanguage -abstract class KtLightClassBase protected constructor(manager: PsiManager) - : AbstractLightClass(manager, KotlinLanguage.INSTANCE), KtLightClass, PsiExtensibleClass { +abstract class KtLightClassBase protected constructor( + manager: PsiManager +) : AbstractLightClass(manager, KotlinLanguage.INSTANCE), KtExtensibleLightClass { protected open val myInnersCache = KotlinClassInnerStuffCache( myClass = this, dependencies = listOf(KotlinModificationTrackerService.getInstance(manager.project).outOfBlockModificationTracker), @@ -62,16 +60,6 @@ abstract class KtLightClassBase protected constructor(manager: PsiManager) override fun getOwnMethods(): List = KtLightMethodImpl.fromClsMethods(delegate, this) - override fun processDeclarations( - processor: PsiScopeProcessor, state: ResolveState, lastParent: PsiElement?, place: PsiElement - ): Boolean { - if (isEnum) { - if (!processDeclarationsInEnum(processor, state, myInnersCache)) return false - } - - return super.processDeclarations(processor, state, lastParent, place) - } - override fun getText(): String { val origin = kotlinOrigin return if (origin == null) "" else origin.text diff --git a/compiler/testData/asJava/lightClasses/AnnotatedParameterInEnumConstructor.java b/compiler/testData/asJava/lightClasses/AnnotatedParameterInEnumConstructor.java index 38682f8f4a7..fa04b508453 100644 --- a/compiler/testData/asJava/lightClasses/AnnotatedParameterInEnumConstructor.java +++ b/compiler/testData/asJava/lightClasses/AnnotatedParameterInEnumConstructor.java @@ -1,6 +1,12 @@ public enum AnnotatedParameterInEnumConstructor /* test.AnnotatedParameterInEnumConstructor*/ { A; + @org.jetbrains.annotations.NotNull() + public static test.AnnotatedParameterInEnumConstructor valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static test.AnnotatedParameterInEnumConstructor[] values();// values() + private AnnotatedParameterInEnumConstructor(@test.Anno(x = "a") java.lang.String, @test.Anno(x = "b") java.lang.String);// .ctor(java.lang.String, java.lang.String) } \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/DeprecatedEnumEntry.java b/compiler/testData/asJava/lightClasses/DeprecatedEnumEntry.java index 70d38298344..75a7fd99266 100644 --- a/compiler/testData/asJava/lightClasses/DeprecatedEnumEntry.java +++ b/compiler/testData/asJava/lightClasses/DeprecatedEnumEntry.java @@ -3,6 +3,12 @@ public enum E /* p.E*/ { Entry2, Entry3; + @org.jetbrains.annotations.NotNull() + public static p.E valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static p.E[] values();// values() + private E();// .ctor() } \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/compilationErrors/EnumNameOverride.java b/compiler/testData/asJava/lightClasses/compilationErrors/EnumNameOverride.java index bc55d50cd3f..d0d6cd2975f 100644 --- a/compiler/testData/asJava/lightClasses/compilationErrors/EnumNameOverride.java +++ b/compiler/testData/asJava/lightClasses/compilationErrors/EnumNameOverride.java @@ -1,6 +1,12 @@ public enum EnumNameOverride /* EnumNameOverride*/ implements Bar { ; + @org.jetbrains.annotations.NotNull() + public static EnumNameOverride valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static EnumNameOverride[] values();// values() + private EnumNameOverride();// .ctor() } \ No newline at end of file diff --git a/compiler/testData/asJava/ultraLightClasses/enums.fir.java b/compiler/testData/asJava/ultraLightClasses/enums.fir.java index f4cd5c7836c..e75d33682a4 100644 --- a/compiler/testData/asJava/ultraLightClasses/enums.fir.java +++ b/compiler/testData/asJava/ultraLightClasses/enums.fir.java @@ -4,6 +4,12 @@ public enum Direction /* Direction*/ { WEST, EAST; + @org.jetbrains.annotations.NotNull() + public static Direction valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static Direction[] values();// values() + private Direction();// .ctor() } @@ -15,6 +21,12 @@ public enum Color /* Color*/ { private final int rgb; + @org.jetbrains.annotations.NotNull() + public static Color valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static Color[] values();// values() + private Color(int);// .ctor(int) private Color(java.lang.String);// .ctor(java.lang.String) @@ -42,6 +54,12 @@ public enum ProtocolState /* ProtocolState*/ { @org.jetbrains.annotations.NotNull() public abstract ProtocolState signal();// signal() + @org.jetbrains.annotations.NotNull() + public static ProtocolState valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static ProtocolState[] values();// values() + private ProtocolState();// .ctor() } @@ -79,6 +97,12 @@ public enum IntArithmetics /* IntArithmetics*/ { @org.jetbrains.annotations.NotNull() public IntArithmetics applyAsInt(int, int);// applyAsInt(int, int) + @org.jetbrains.annotations.NotNull() + public static IntArithmetics valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static IntArithmetics[] values();// values() + private IntArithmetics();// .ctor() } diff --git a/compiler/testData/asJava/ultraLightClasses/enums.lib.java b/compiler/testData/asJava/ultraLightClasses/enums.lib.java index 9f1cc754dfc..1e0ea49be3d 100644 --- a/compiler/testData/asJava/ultraLightClasses/enums.lib.java +++ b/compiler/testData/asJava/ultraLightClasses/enums.lib.java @@ -5,6 +5,12 @@ public abstract enum ProtocolState /* ProtocolState*/ { @org.jetbrains.annotations.NotNull() public abstract ProtocolState signal();// signal() + @org.jetbrains.annotations.NotNull() + public static ProtocolState valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static ProtocolState[] values();// values() + private ProtocolState();// .ctor() diff --git a/compiler/testData/asJava/ultraLightClasses/inlineClasses.java b/compiler/testData/asJava/ultraLightClasses/inlineClasses.java index b0260f362bf..7f081d245f2 100644 --- a/compiler/testData/asJava/ultraLightClasses/inlineClasses.java +++ b/compiler/testData/asJava/ultraLightClasses/inlineClasses.java @@ -4,6 +4,12 @@ public final class UInt /* UInt*/ { public enum Foo /* Foo*/ { ; + @org.jetbrains.annotations.NotNull() + public static Foo valueOf(@org.jetbrains.annotations.NotNull() java.lang.String) throws java.lang.IllegalArgumentException;// valueOf(java.lang.String) + + @org.jetbrains.annotations.NotNull() + public static Foo[] values();// values() + public final int getX();// getX() }