diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLazyLightClass.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLazyLightClass.kt index 1c22367755f..bd3c6f974d8 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLazyLightClass.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/KtLazyLightClass.kt @@ -27,6 +27,4 @@ abstract class KtLazyLightClass(manager: PsiManager) : KtLightClassBase(manager) override fun getOwnFields() = lightClassData.getOwnFields(this) override fun getOwnMethods() = lightClassData.getOwnMethods(this) - override fun getSuperTypes() = lightClassData.supertypes - } \ No newline at end of file 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 667961c289b..8dd04776a6d 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 @@ -17,11 +17,15 @@ package org.jetbrains.kotlin.asJava.classes import com.google.common.collect.Lists +import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.project.Project import com.intellij.openapi.util.Comparing import com.intellij.openapi.util.Key import com.intellij.psi.* +import com.intellij.psi.impl.PsiSubstitutorImpl import com.intellij.psi.impl.java.stubs.PsiJavaFileStub +import com.intellij.psi.impl.source.PsiImmediateClassType import com.intellij.psi.scope.PsiScopeProcessor import com.intellij.psi.search.SearchScope import com.intellij.psi.stubs.IStubElementType @@ -31,6 +35,8 @@ import com.intellij.psi.util.CachedValuesManager import com.intellij.util.IncorrectOperationException import com.intellij.util.containers.ContainerUtil import org.jetbrains.annotations.NonNls +import org.jetbrains.kotlin.asJava.ImpreciseResolveResult +import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.UNSURE import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder @@ -174,12 +180,7 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC override fun getName(): String? = classOrObject.nameAsName?.asString() - private val _modifierList: PsiModifierList by lazyPub { - object : KtLightModifierListWithExplicitModifiers(this@KtLightClassForSourceDeclaration, computeModifiers()) { - override val delegate: PsiAnnotationOwner - get() = this@KtLightClassForSourceDeclaration.delegate.modifierList!! - } - } + private val _modifierList: PsiModifierList by lazyPub { KtLightModifierListWithExplicitModifiers(this@KtLightClassForSourceDeclaration, computeModifiers()) } override fun getModifierList(): PsiModifierList? = _modifierList @@ -274,6 +275,8 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC override fun isValid(): Boolean = classOrObject.isValid override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean { + LightClassInheritanceHelper.getService(project).isInheritor(this, baseClass, checkDeep).ifSure { return it } + val qualifiedName: String? if (baseClass is KtLightClassForSourceDeclaration) { val baseDescriptor = baseClass.getDescriptor() @@ -412,6 +415,41 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC private val LOG = Logger.getInstance(KtLightClassForSourceDeclaration::class.java) } + override fun getSupers(): Array { + if (classOrObject.superTypeListEntries.isEmpty()) { + return getSupertypeByPsi()?.let { arrayOf(it.resolve()!!) } ?: emptyArray() + } + return clsDelegate.supers + } + + override fun getSuperTypes(): Array { + if (classOrObject.superTypeListEntries.isEmpty()) { + return getSupertypeByPsi()?.let { arrayOf(it) } ?: emptyArray() + } + return clsDelegate.superTypes + } + + private fun getSupertypeByPsi(): PsiImmediateClassType? { + return classOrObject.defaultJavaAncestorQualifiedName()?.let { + ancestorFqName -> + JavaPsiFacade.getInstance(project).findClass(ancestorFqName, resolveScope)?.let { + PsiImmediateClassType(it, createSubstitutor(it)) + } + } + } + + private fun createSubstitutor(ancestor: PsiClass): PsiSubstitutor { + if (ancestor.qualifiedName != CommonClassNames.JAVA_LANG_ENUM) { + return PsiSubstitutor.EMPTY + } + val javaLangEnumsTypeParameter = ancestor.typeParameters.firstOrNull() ?: return PsiSubstitutor.EMPTY + return PsiSubstitutorImpl.createSubstitutor( + mapOf( + javaLangEnumsTypeParameter to PsiImmediateClassType(this, PsiSubstitutor.EMPTY) + ) + ) + } + override val originKind: LightClassOriginKind get() = LightClassOriginKind.SOURCE } @@ -423,3 +461,30 @@ fun getOutermostClassOrObject(classOrObject: KtClassOrObject): KtClassOrObject { return outermostClass } +interface LightClassInheritanceHelper { + fun isInheritor( + lightClass: KtLightClassForSourceDeclaration, + baseClass: PsiClass, + checkDeep: Boolean + ): ImpreciseResolveResult + + object NoHelp : LightClassInheritanceHelper { + override fun isInheritor(lightClass: KtLightClassForSourceDeclaration, baseClass: PsiClass, checkDeep: Boolean) = UNSURE + } + + companion object { + fun getService(project: Project): LightClassInheritanceHelper = + ServiceManager.getService(project, LightClassInheritanceHelper::class.java) ?: NoHelp + } +} + +fun KtClassOrObject.defaultJavaAncestorQualifiedName(): String? { + if (this !is KtClass) return CommonClassNames.JAVA_LANG_OBJECT + + return when { + isAnnotation() -> CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION + isEnum() -> CommonClassNames.JAVA_LANG_ENUM + isInterface() -> CommonClassNames.JAVA_LANG_OBJECT // see com.intellij.psi.impl.PsiClassImplUtil.getSuperClass + else -> CommonClassNames.JAVA_LANG_OBJECT + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/IdeLightClassInheritanceHelper.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/IdeLightClassInheritanceHelper.kt new file mode 100644 index 00000000000..4ebb94afec8 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/IdeLightClassInheritanceHelper.kt @@ -0,0 +1,64 @@ +/* + * 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. + */ + +package org.jetbrains.kotlin.idea.caches.resolve.lightClasses + +import com.intellij.psi.CommonClassNames +import com.intellij.psi.PsiClass +import org.jetbrains.kotlin.asJava.ImpreciseResolveResult +import org.jetbrains.kotlin.asJava.ImpreciseResolveResult.* +import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.asJava.classes.LightClassInheritanceHelper +import org.jetbrains.kotlin.asJava.classes.defaultJavaAncestorQualifiedName +import org.jetbrains.kotlin.idea.search.PsiBasedClassResolver +import org.jetbrains.kotlin.psi.* + +class IdeLightClassInheritanceHelper : LightClassInheritanceHelper { + override fun isInheritor( + lightClass: KtLightClassForSourceDeclaration, + baseClass: PsiClass, + checkDeep: Boolean + ): ImpreciseResolveResult { + val classOrObject = lightClass.kotlinOrigin + val entries = classOrObject.superTypeListEntries + val hasSuperClass = entries.any { it is KtSuperTypeCallEntry } + if (baseClass.qualifiedName == classOrObject.defaultJavaAncestorQualifiedName() && (!hasSuperClass || checkDeep)) { + return MATCH + } + if (checkDeep && baseClass.qualifiedName == CommonClassNames.JAVA_LANG_OBJECT) { + return MATCH + } + val amongEntries = isAmongEntries(baseClass, entries) + return when { + !checkDeep -> amongEntries + amongEntries == MATCH -> MATCH + else -> UNSURE + } + } + + private fun isAmongEntries(baseClass: PsiClass, entries: List): ImpreciseResolveResult { + val psiBasedResolver = PsiBasedClassResolver(baseClass) + entries@ for (entry in entries) { + val reference: KtSimpleNameExpression = entry.typeAsUserType?.referenceExpression ?: continue@entries + when (psiBasedResolver.canBeTargetReference(reference)) { + MATCH -> return MATCH + NO_MATCH -> continue@entries + UNSURE -> return UNSURE + } + } + return NO_MATCH + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/LazyLightClassDataHolder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/LazyLightClassDataHolder.kt index df31783953c..047bb2e413a 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/LazyLightClassDataHolder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/lightClasses/LazyLightClassDataHolder.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.caches.resolve.lightClasses import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.psi.PsiClass -import com.intellij.psi.PsiClassType import com.intellij.psi.PsiMember import com.intellij.psi.PsiMethod import com.intellij.psi.impl.java.stubs.PsiJavaFileStub @@ -65,7 +64,7 @@ sealed class LazyLightClassDataHolder( // for facade or defaultImpls override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData = - LazyLightClassData(relyOnDummySupertypes = true) { lightClassBuilderResult -> + LazyLightClassData { lightClassBuilderResult -> findDelegate(lightClassBuilderResult.stub) } @@ -73,7 +72,7 @@ sealed class LazyLightClassDataHolder( builder: LightClassBuilder, exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider ) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForClass { override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData = - LazyLightClassData(relyOnDummySupertypes = classOrObject.getSuperTypeList() == null) { lightClassBuilderResult -> + LazyLightClassData { lightClassBuilderResult -> lightClassBuilderResult.stub.findDelegate(classOrObject) } } @@ -83,7 +82,6 @@ sealed class LazyLightClassDataHolder( ) : LazyLightClassDataHolder(builder, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForFacade private inner class LazyLightClassData( - private val relyOnDummySupertypes: Boolean, findDelegate: (LightClassBuilderResult) -> PsiClass ) : LightClassData { override val clsDelegate: PsiClass by lazyPub { findDelegate(exactResult) } @@ -133,17 +131,13 @@ sealed class LazyLightClassDataHolder( } } } - - override val supertypes: Array - get() = if (relyOnDummySupertypes && dummyDelegate != null) dummyDelegate!!.superTypes else clsDelegate.superTypes - } private fun T?.assertMatches(dummyMember: T, containingClass: KtLightClass): T { if (this == null) throw LazyLightClassMemberMatchingError.NoMatch(dummyMember, containingClass) val parameterCountMatches = (this as? PsiMethod)?.parameterList?.parametersCount ?: 0 == - (dummyMember as? PsiMethod)?.parameterList?.parametersCount ?: 0 + (dummyMember as? PsiMethod)?.parameterList?.parametersCount ?: 0 if (this.memberIndex != dummyMember.memberIndex || !parameterCountMatches) { throw LazyLightClassMemberMatchingError.WrongMatch(this, dummyMember, containingClass) } @@ -157,7 +151,6 @@ private fun shouldRollbackOptimization(origin: LightMemberOriginForDeclaration?) if (!kotlinDeclaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false - val possiblyOverridesInternalMember = kotlinDeclaration.name?.let { anyInternalMembersWithThisName(it, kotlinDeclaration.project) } ?: false return possiblyOverridesInternalMember } @@ -194,4 +187,4 @@ private sealed class LazyLightClassMemberMatchingError(message: String, containi private val PsiMember.debugName get() = "${this::class.java.simpleName}:${this.name} ${this.memberIndex}" + if (this is PsiMethod) " (with ${parameterList.parametersCount} parameters)" else "" -var KtClassOrObject.hasLightClassMatchingErrors: Boolean by NotNullableUserDataProperty(Key.create("LIGHT_CLASS_MATCHING_ERRORS"), false) \ No newline at end of file +var KtClassOrObject.hasLightClassMatchingErrors: Boolean by NotNullableUserDataProperty(Key.create("LIGHT_CLASS_MATCHING_ERRORS"), false) diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 8a489f8ea8c..7747550d32f 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -305,6 +305,9 @@ + +