diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/FakeLightClassForFileOfPackage.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/FakeLightClassForFileOfPackage.java index 8c7aa5c1e6f..d8dd275ab95 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/FakeLightClassForFileOfPackage.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/FakeLightClassForFileOfPackage.java @@ -95,4 +95,17 @@ public class FakeLightClassForFileOfPackage extends AbstractLightClass implement public Language getLanguage() { return JetLanguage.INSTANCE; } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof FakeLightClassForFileOfPackage)) return false; + + FakeLightClassForFileOfPackage other = (FakeLightClassForFileOfPackage) obj; + return file == other.file && delegate.equals(other.delegate); + } + + @Override + public int hashCode() { + return file.hashCode() * 31 + delegate.hashCode(); + } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinWrappingLightClass.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinWrappingLightClass.java index 44b8b55bd85..44c86be46a2 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinWrappingLightClass.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinWrappingLightClass.java @@ -202,4 +202,10 @@ public abstract class KotlinWrappingLightClass extends AbstractLightClass implem public ItemPresentation getPresentation() { return ItemPresentationProviders.getItemPresentation(this); } + + @Override + public abstract boolean equals(Object obj); + + @Override + public abstract int hashCode(); } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinLightClassForDecompiledDeclaration.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinLightClassForDecompiledDeclaration.kt index 1010d58c7be..3dfe85655bf 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinLightClassForDecompiledDeclaration.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinLightClassForDecompiledDeclaration.kt @@ -16,11 +16,11 @@ package org.jetbrains.kotlin.idea.caches.resolve -import org.jetbrains.kotlin.asJava.KotlinWrappingLightClass import com.intellij.psi.PsiClass import com.intellij.psi.impl.compiled.ClsClassImpl -import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.asJava.KotlinWrappingLightClass import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.JetClassOrObject class KotlinLightClassForDecompiledDeclaration( private val clsClass: ClsClassImpl, @@ -43,4 +43,11 @@ class KotlinLightClassForDecompiledDeclaration( override fun getOrigin() = origin override fun getFqName() = origin?.getFqName() ?: FqName(clsClass.getQualifiedName()) + + override fun equals(other: Any?): Boolean = + other is KotlinLightClassForDecompiledDeclaration && + getFqName() == other.getFqName() + + override fun hashCode(): Int = + getFqName().hashCode() } diff --git a/idea/tests/org/jetbrains/kotlin/idea/lightClasses/LightClassEqualsTest.java b/idea/tests/org/jetbrains/kotlin/idea/lightClasses/LightClassEqualsTest.java new file mode 100644 index 00000000000..f6bb89087cf --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/lightClasses/LightClassEqualsTest.java @@ -0,0 +1,73 @@ +/* + * 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.idea.lightClasses; + +import com.intellij.psi.PsiClass; +import com.intellij.testFramework.LightProjectDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.asJava.KotlinLightClass; +import org.jetbrains.kotlin.asJava.KotlinLightClassForExplicitDeclaration; +import org.jetbrains.kotlin.asJava.LightClassUtil; +import org.jetbrains.kotlin.idea.caches.resolve.KotlinLightClassForDecompiledDeclaration; +import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase; +import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor; +import org.jetbrains.kotlin.psi.JetClassOrObject; + +public class LightClassEqualsTest extends JetLightCodeInsightFixtureTestCase { + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE; + } + + public void testEqualsForExplicitDeclaration() throws Exception { + myFixture.configureByText("a.kt", "class A"); + + PsiClass theClass = myFixture.getJavaFacade().findClass("A"); + assertNotNull(theClass); + assertInstanceOf(theClass, KotlinLightClassForExplicitDeclaration.class); + + doTestEquals(((KotlinLightClass) theClass).getOrigin()); + } + + public void testEqualsForDecompiledClass() throws Exception { + myFixture.configureByText("a.kt", ""); + + PsiClass theClass = myFixture.getJavaFacade().findClass("kotlin.Unit"); + assertNotNull(theClass); + assertInstanceOf(theClass, KotlinLightClassForDecompiledDeclaration.class); + + doTestEquals(((KotlinLightClass) theClass).getOrigin()); + } + + private static void doTestEquals(@Nullable JetClassOrObject origin) { + assertNotNull(origin); + + PsiClass lightClass1 = LightClassUtil.getPsiClass(origin); + PsiClass lightClass2 = LightClassUtil.getPsiClass(origin); + assertNotNull(lightClass1); + assertNotNull(lightClass2); + + // If the same light class is returned twice, it means some caching was introduced and this test no longer makes sense. + // Any other way of obtaining light classes should be used, which bypasses caches + assertNotSame(lightClass1, lightClass2); + + assertEquals(lightClass1, lightClass2); + assertEquals(lightClass1.hashCode(), lightClass2.hashCode()); + } +}