From 22d58239e34918161a5679596ba697f001823470 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 29 Sep 2015 13:27:00 +0300 Subject: [PATCH] Support `DefaultImpls` in light-classes and indices #KT-4647 Fixed --- .../kotlin/asJava/JavaElementFinder.java | 20 ++++++++ .../KotlinLightClassForExplicitDeclaration.kt | 21 ++++++-- ...otlinLightClassForInterfaceDefaultImpls.kt | 50 +++++++++++++++++++ .../kotlin/asJava/lightClassUtils.kt | 10 ++++ .../kotlin/idea/caches/JetShortNamesCache.kt | 10 +++- .../idea/stubindex/IdeStubIndexService.java | 6 ++- .../testData/injava/InterfaceDefaultImpl.java | 7 +++ .../InterfaceDefaultImplImportedMembers.java | 8 +++ .../injava/InterfaceDefaultImplMembers.java | 7 +++ ...rfaceDefaultImplStaticImportedMembers.java | 8 +++ .../testData/injava/mockLib/main.kt | 4 ++ ...edKotlinInJavaCompletionTestGenerated.java | 24 +++++++++ ...inSourceInJavaCompletionTestGenerated.java | 24 +++++++++ 13 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForInterfaceDefaultImpls.kt create mode 100644 idea/idea-completion/testData/injava/InterfaceDefaultImpl.java create mode 100644 idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java create mode 100644 idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java create mode 100644 idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java index 4bb5703ca06..c9c455728f5 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java @@ -31,6 +31,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.SLRUCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.NamePackage; @@ -127,7 +128,10 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM } // Finds explicitly declared classes and objects, not package classes + // Also DefaultImpls classes of interfaces private void findClassesAndObjects(FqName qualifiedName, GlobalSearchScope scope, List answer) { + findInterfaceDefaultImpls(qualifiedName, scope, answer); + Collection classOrObjectDeclarations = lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName, scope); @@ -141,6 +145,22 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM } } + private void findInterfaceDefaultImpls(FqName qualifiedName, GlobalSearchScope scope, List answer) { + if (!qualifiedName.shortName().asString().equals(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)) return; + + for (JetClassOrObject classOrObject : lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName.parent(), scope)) { + if (LightClassUtilsKt.getHasInterfaceDefaultImpls(classOrObject)) { + PsiClass interfaceClass = LightClassUtil.INSTANCE$.getPsiClass(classOrObject); + if (interfaceClass != null) { + PsiClass implsClass = interfaceClass.findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false); + if (implsClass != null) { + answer.add(implsClass); + } + } + } + } + } + @NotNull @Override public Set getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt index ec8f1067dfd..82ec15ed192 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForExplicitDeclaration.kt @@ -50,7 +50,7 @@ import javax.swing.Icon public open class KotlinLightClassForExplicitDeclaration( manager: PsiManager, - private val classFqName: FqName, // FqName of (possibly inner) class + protected val classFqName: FqName, // FqName of (possibly inner) class protected val classOrObject: JetClassOrObject) : KotlinWrappingLightClass(manager), JetJavaMirrorMarker, StubBasedPsiElement> { private var delegate: PsiClass? = null @@ -268,7 +268,7 @@ public open class KotlinLightClassForExplicitDeclaration( override fun getModifierList(): PsiModifierList = _modifierList - private fun computeModifiers(): Array { + protected open fun computeModifiers(): Array { val psiModifiers = hashSetOf() // PUBLIC, PROTECTED, PRIVATE, ABSTRACT, FINAL @@ -377,7 +377,7 @@ public open class KotlinLightClassForExplicitDeclaration( return getDelegate().innerClasses .map { val declaration = ClsWrapperStubPsiFactory.getOriginalDeclaration(it) as JetClassOrObject? - if (declaration != null) create(myManager, declaration) else null + if (declaration != null) create(myManager, declaration, it) else null } .filterNotNull() } @@ -397,7 +397,12 @@ public open class KotlinLightClassForExplicitDeclaration( FINAL_KEYWORD to PsiModifier.FINAL) - public fun create(manager: PsiManager, classOrObject: JetClassOrObject): KotlinLightClassForExplicitDeclaration? { + @JvmOverloads + public fun create( + manager: PsiManager, + classOrObject: JetClassOrObject, + psiClass: PsiClass? = null + ): KotlinLightClassForExplicitDeclaration? { if (LightClassUtil.belongsToKotlinBuiltIns(classOrObject.getContainingJetFile())) { return null } @@ -407,6 +412,14 @@ public open class KotlinLightClassForExplicitDeclaration( if (classOrObject is JetObjectDeclaration && classOrObject.isObjectLiteral()) { return KotlinLightClassForAnonymousDeclaration(manager, fqName, classOrObject) } + + if (classOrObject.hasInterfaceDefaultImpls) { + val implsFqName = fqName.defaultImplsChild() + if (implsFqName.asString() == psiClass?.qualifiedName) { + return KotlinLightClassForInterfaceDefaultImpls(manager, implsFqName, classOrObject) + } + } + return KotlinLightClassForExplicitDeclaration(manager, fqName, classOrObject) } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForInterfaceDefaultImpls.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForInterfaceDefaultImpls.kt new file mode 100644 index 00000000000..b2907320f08 --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForInterfaceDefaultImpls.kt @@ -0,0 +1,50 @@ +/* + * 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.* +import com.intellij.util.IncorrectOperationException +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.JetClassOrObject + +public open class KotlinLightClassForInterfaceDefaultImpls( + manager: PsiManager, + classFqName: FqName, + classOrObject: JetClassOrObject) +: KotlinLightClassForExplicitDeclaration(manager, classFqName, classOrObject){ + + override fun copy(): PsiElement { + return KotlinLightClassForInterfaceDefaultImpls(manager, classFqName, classOrObject.copy() as JetClassOrObject) + } + + override fun getTypeParameterList(): PsiTypeParameterList? = null + override fun getTypeParameters(): Array = emptyArray() + + override fun computeModifiers(): Array = arrayOf(PsiModifier.PUBLIC, PsiModifier.STATIC, PsiModifier.FINAL) + + override fun isInterface(): Boolean = false + override fun isDeprecated(): Boolean = false + override fun isAnnotationType(): Boolean = false + override fun isEnum(): Boolean = false + override fun hasTypeParameters(): Boolean = false + override fun isInheritor(baseClass: PsiClass, checkDeep: Boolean): Boolean = false + + @Throws(IncorrectOperationException::class) + override fun setName(name: String): PsiElement { + throw IncorrectOperationException("Impossible to rename DefaultImpls") + } +} diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt index 61caa7ac058..eaad210e621 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt @@ -17,6 +17,9 @@ package org.jetbrains.kotlin.asJava import com.intellij.psi.* +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration @@ -98,3 +101,10 @@ public val PsiElement.unwrapped: PsiElement? public val PsiElement.namedUnwrappedElement: PsiNamedElement? get() = unwrapped?.getNonStrictParentOfType() + + +val JetClassOrObject.hasInterfaceDefaultImpls: Boolean + get() = this is JetClass && isInterface() + +private val DEFAULT_IMPLS_CLASS_NAME = Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME) +fun FqName.defaultImplsChild() = child(DEFAULT_IMPLS_CLASS_NAME) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/JetShortNamesCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/JetShortNamesCache.kt index 88e92f3cc24..d5fddaab9b9 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/JetShortNamesCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/JetShortNamesCache.kt @@ -26,11 +26,13 @@ import com.intellij.util.Processor import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.HashSet import org.jetbrains.kotlin.asJava.JavaElementFinder +import org.jetbrains.kotlin.asJava.defaultImplsChild import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName import org.jetbrains.kotlin.idea.stubindex.JetClassShortNameIndex import org.jetbrains.kotlin.idea.stubindex.JetFileFacadeShortNameIndex import org.jetbrains.kotlin.idea.stubindex.JetFunctionShortNameIndex import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil +import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.FqName import java.util.* @@ -68,10 +70,14 @@ public class JetShortNamesCache(private val project: Project) : PsiShortNamesCac for (fqName in allFqNames) { if (fqName == null) continue - assert(fqName.shortName().asString() == name) { + val isInterfaceDefaultImpl = name == JvmAbi.DEFAULT_IMPLS_CLASS_NAME && fqName.shortName().asString() != name + assert(fqName.shortName().asString() == name || isInterfaceDefaultImpl) { "A declaration obtained from index has non-matching name:\nin index: $name\ndeclared: ${fqName.shortName()}($fqName)" } - val psiClass = JavaElementFinder.getInstance(project).findClass(fqName.asString(), scope) + + val fqNameToSearch = if (isInterfaceDefaultImpl) fqName.defaultImplsChild() else fqName + + val psiClass = JavaElementFinder.getInstance(project).findClass(fqNameToSearch.asString(), scope) if (psiClass != null) { result.add(psiClass) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java index 9eb6457f1ef..2ebf84ddd97 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/stubindex/IdeStubIndexService.java @@ -24,6 +24,7 @@ import com.intellij.util.io.StringRef; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo; import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; +import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; @@ -35,7 +36,6 @@ import org.jetbrains.kotlin.util.TypeIndexUtilKt; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class IdeStubIndexService extends StubIndexService { @@ -95,6 +95,10 @@ public class IdeStubIndexService extends StubIndexService { } } + if (stub.isInterface()) { + sink.occurrence(JetClassShortNameIndex.getInstance().getKey(), JvmAbi.DEFAULT_IMPLS_CLASS_NAME); + } + indexSuperNames(stub, sink); } diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImpl.java b/idea/idea-completion/testData/injava/InterfaceDefaultImpl.java new file mode 100644 index 00000000000..bbdce551935 --- /dev/null +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImpl.java @@ -0,0 +1,7 @@ +public class Testing { + public static void test() { + mockLib.foo.MyInterface. + } +} + +// EXIST: DefaultImpls diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java b/idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java new file mode 100644 index 00000000000..a201edca4c1 --- /dev/null +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java @@ -0,0 +1,8 @@ +import mockLib.foo.MyInterface.DefaultImpls; +public class Testing { + public static void test() { + DefaultImpls. + } +} + +// EXIST: foo diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java b/idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java new file mode 100644 index 00000000000..8bb70e273f1 --- /dev/null +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java @@ -0,0 +1,7 @@ +public class Testing { + public static void test() { + mockLib.foo.MyInterface.DefaultImpls. + } +} + +// EXIST: foo diff --git a/idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java b/idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java new file mode 100644 index 00000000000..dcf64ba552b --- /dev/null +++ b/idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java @@ -0,0 +1,8 @@ +import static mockLib.foo.MyInterface.DefaultImpls.*; +public class Testing { + public static void test() { + f + } +} + +// EXIST: foo diff --git a/idea/idea-completion/testData/injava/mockLib/main.kt b/idea/idea-completion/testData/injava/mockLib/main.kt index c930c6ad962..0e10b5201e7 100644 --- a/idea/idea-completion/testData/injava/mockLib/main.kt +++ b/idea/idea-completion/testData/injava/mockLib/main.kt @@ -48,4 +48,8 @@ class F() { } } } +} + +interface MyInterface { + fun foo() = 1 } \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/CompiledKotlinInJavaCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/CompiledKotlinInJavaCompletionTestGenerated.java index 3f3f4b22174..e4db7b2536a 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/CompiledKotlinInJavaCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/CompiledKotlinInJavaCompletionTestGenerated.java @@ -71,6 +71,30 @@ public class CompiledKotlinInJavaCompletionTestGenerated extends AbstractCompile doTest(fileName); } + @TestMetadata("InterfaceDefaultImpl.java") + public void testInterfaceDefaultImpl() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImpl.java"); + doTest(fileName); + } + + @TestMetadata("InterfaceDefaultImplImportedMembers.java") + public void testInterfaceDefaultImplImportedMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java"); + doTest(fileName); + } + + @TestMetadata("InterfaceDefaultImplMembers.java") + public void testInterfaceDefaultImplMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java"); + doTest(fileName); + } + + @TestMetadata("InterfaceDefaultImplStaticImportedMembers.java") + public void testInterfaceDefaultImplStaticImportedMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java"); + doTest(fileName); + } + @TestMetadata("MultiFileFacade.java") public void testMultiFileFacade() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/MultiFileFacade.java"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinSourceInJavaCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinSourceInJavaCompletionTestGenerated.java index d31f346c74f..e82758946a9 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinSourceInJavaCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/KotlinSourceInJavaCompletionTestGenerated.java @@ -71,6 +71,30 @@ public class KotlinSourceInJavaCompletionTestGenerated extends AbstractKotlinSou doTest(fileName); } + @TestMetadata("InterfaceDefaultImpl.java") + public void testInterfaceDefaultImpl() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImpl.java"); + doTest(fileName); + } + + @TestMetadata("InterfaceDefaultImplImportedMembers.java") + public void testInterfaceDefaultImplImportedMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImplImportedMembers.java"); + doTest(fileName); + } + + @TestMetadata("InterfaceDefaultImplMembers.java") + public void testInterfaceDefaultImplMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImplMembers.java"); + doTest(fileName); + } + + @TestMetadata("InterfaceDefaultImplStaticImportedMembers.java") + public void testInterfaceDefaultImplStaticImportedMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/InterfaceDefaultImplStaticImportedMembers.java"); + doTest(fileName); + } + @TestMetadata("MultiFileFacade.java") public void testMultiFileFacade() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/injava/MultiFileFacade.java");