don't perform full resolve and stub building for isInheritor() checks

#KT-8656 Fixed
This commit is contained in:
Dmitry Jemerov
2015-08-19 20:19:06 +02:00
parent 6c32f655f9
commit c00fbb236f
6 changed files with 120 additions and 25 deletions
@@ -204,6 +204,12 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport
return UtilsPackage.<PsiClass>emptyOrSingletonList(KotlinLightClassForPackage.Factory.create(psiManager, packageFqName, scope, filesWithCallables));
}
@Nullable
@Override
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) {
return bindingContext.get(BindingContext.CLASS, classOrObject);
}
@NotNull
@Override
public BindingTraceContext createTrace() {
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.JetLanguage
import org.jetbrains.kotlin.lexer.JetTokens.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -150,8 +151,12 @@ public open class KotlinLightClassForExplicitDeclaration(
private fun getJavaFileStub(): PsiJavaFileStub = getLightClassData().javaFileStub
protected fun getDescriptor(): ClassDescriptor? {
val data = getLightClassDataExactly(classOrObject)
return data?.descriptor
val cachedValue = getLightClassCachedValue(classOrObject)
if (cachedValue.hasUpToDateValue()) {
return cachedValue.value.dataForClass(classOrObject)?.descriptor
}
return LightClassGenerationSupport.getInstance(project).resolveClassToDescriptor(classOrObject)
}
private fun getLightClassData(): OutermostKotlinClassLightClassData {
@@ -321,22 +326,17 @@ public open class KotlinLightClassForExplicitDeclaration(
override fun isValid(): Boolean = classOrObject.isValid
override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean {
// Java inheritor check doesn't work when trait (interface in Java) subclasses Java class and for Kotlin local classes
if (baseClass is KotlinLightClassForExplicitDeclaration || (isInterface && !baseClass.isInterface)) {
val qualifiedName: String?
if (baseClass is KotlinLightClassForExplicitDeclaration) {
val baseDescriptor = baseClass.getDescriptor()
qualifiedName = if (baseDescriptor != null) DescriptorUtils.getFqName(baseDescriptor).asString() else null
}
else {
qualifiedName = baseClass.qualifiedName
}
val thisDescriptor = getDescriptor()
return qualifiedName != null && thisDescriptor != null && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep)
val qualifiedName: String?
if (baseClass is KotlinLightClassForExplicitDeclaration) {
val baseDescriptor = baseClass.getDescriptor()
qualifiedName = if (baseDescriptor != null) DescriptorUtils.getFqName(baseDescriptor).asString() else null
}
else {
qualifiedName = baseClass.qualifiedName
}
return super.isInheritor(baseClass, checkDeep)
val thisDescriptor = getDescriptor()
return qualifiedName != null && thisDescriptor != null && checkSuperTypeByFQName(thisDescriptor, qualifiedName, checkDeep)
}
throws(IncorrectOperationException::class)
@@ -399,17 +399,23 @@ public open class KotlinLightClassForExplicitDeclaration(
}
public fun getLightClassData(classOrObject: JetClassOrObject): OutermostKotlinClassLightClassData {
return getLightClassCachedValue(classOrObject).value
}
public fun getLightClassCachedValue(classOrObject: JetClassOrObject): CachedValue<OutermostKotlinClassLightClassData> {
val outermostClassOrObject = getOutermostClassOrObject(classOrObject)
return CachedValuesManager.getManager(classOrObject.project).getCachedValue(
outermostClassOrObject,
JAVA_API_STUB,
KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject),
/*trackValue = */false)
var value = outermostClassOrObject.getUserData(JAVA_API_STUB)
if (value == null) {
value = CachedValuesManager.getManager(classOrObject.project).createCachedValue(
KotlinJavaFileStubProvider.createForDeclaredClass(outermostClassOrObject), false)
value = outermostClassOrObject.putUserDataIfAbsent(JAVA_API_STUB, value)!!
}
return value
}
private fun getLightClassDataExactly(classOrObject: JetClassOrObject): LightClassDataForKotlinClass? {
val data = getLightClassData(classOrObject)
return if (data.classOrObject == classOrObject) data else data.allInnerClasses.get(classOrObject)
return data.dataForClass(classOrObject)
}
private fun getOutermostClassOrObject(classOrObject: JetClassOrObject): JetClassOrObject {
@@ -424,11 +430,16 @@ public open class KotlinLightClassForExplicitDeclaration(
if (qualifiedName == DescriptorUtils.getFqName(classDescriptor).asString()) return true
val mappedDescriptor = JavaToKotlinClassMap.INSTANCE.mapJavaToKotlin(FqName(qualifiedName))
val mappedQName = if (mappedDescriptor == null) null else DescriptorUtils.getFqName(mappedDescriptor).asString()
if (qualifiedName == mappedQName) return true
for (superType in classDescriptor.typeConstructor.supertypes) {
val superDescriptor = superType.constructor.declarationDescriptor
if (superDescriptor is ClassDescriptor) {
if (qualifiedName == DescriptorUtils.getFqName(superDescriptor).asString()) return true
val superQName = DescriptorUtils.getFqName(superDescriptor).asString()
if (superQName == qualifiedName || superQName == mappedQName) return true
if (deep) {
if (checkSuperTypeByFQName(superDescriptor, qualifiedName, true)) {
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.JetClassOrObject;
import org.jetbrains.kotlin.psi.JetFile;
@@ -71,4 +72,7 @@ public abstract class LightClassGenerationSupport {
@NotNull
public abstract Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope);
@Nullable
public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject);
}
@@ -18,9 +18,9 @@ package org.jetbrains.kotlin.asJava
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.name.FqName
interface LightClassData
@@ -53,4 +53,9 @@ data class OutermostKotlinClassLightClassData(
override val classOrObject: JetClassOrObject,
override val descriptor: ClassDescriptor?,
val allInnerClasses: Map<JetClassOrObject, InnerKotlinClassLightClassData>
): LightClassDataForKotlinClass, WithFileStubAndExtraDiagnostics
): LightClassDataForKotlinClass, WithFileStubAndExtraDiagnostics {
fun dataForClass(cls: JetClassOrObject): LightClassDataForKotlinClass? =
if (cls == classOrObject) this else allInnerClasses[cls]
}
@@ -304,6 +304,17 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return result;
}
@Nullable
@Override
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) {
try {
return (ClassDescriptor) ResolvePackage.resolveToDescriptor(classOrObject);
}
catch (IllegalStateException e) {
return null;
}
}
@Nullable
private static PsiClass getLightClassForDecompiledPackage(@NotNull FqName packageFqName, @NotNull List<JetFile> filesWithCallables) {
JetFile firstFile = filesWithCallables.iterator().next();
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2015 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.asJava
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.JetLightProjectDescriptor
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetFile
import org.junit.Assert
public class KotlinLightClassInheritorTest : JetLightCodeInsightFixtureTestCase() {
public fun testAnnotation() {
doTestInheritorByText("annotation class A", "java.lang.annotation.Annotation", false)
}
public fun testEnum() {
doTestInheritorByText("enum class A", "java.lang.Enum", false)
}
public fun testIterable() {
doTestInheritorByText("abstract class A<T> : Iterable<T>", "java.lang.Iterable", false)
}
public fun testIterableDeep() {
doTestInheritorByText("abstract class A<T> : List<T>", "java.lang.Iterable", true)
}
public fun testObjectDeep() {
doTestInheritorByText("abstract class A<T> : List<T>", "java.lang.Object", true)
}
private fun doTestInheritorByText(text: String, superQName: String, checkDeep: Boolean) {
val file = myFixture.configureByText("A.kt", text) as JetFile
val jetClass = file.declarations.filterIsInstance<JetClass>().single()
val psiClass = LightClassGenerationSupport.getInstance(project).getPsiClass(jetClass)!!
val baseClass = JavaPsiFacade.getInstance(project).findClass(superQName, GlobalSearchScope.allScope(project))!!
Assert.assertTrue(psiClass.isInheritor(baseClass, checkDeep))
}
override fun getProjectDescriptor(): LightProjectDescriptor = JetLightProjectDescriptor.INSTANCE
}