Support DefaultImpls in light-classes and indices

#KT-4647 Fixed
This commit is contained in:
Denis Zharkov
2015-09-29 13:27:00 +03:00
parent 8b8fa40348
commit 22d58239e3
13 changed files with 192 additions and 7 deletions
@@ -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<PsiClass> answer) {
findInterfaceDefaultImpls(qualifiedName, scope, answer);
Collection<JetClassOrObject> classOrObjectDeclarations =
lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName, scope);
@@ -141,6 +145,22 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
}
}
private void findInterfaceDefaultImpls(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> 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<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
@@ -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<KotlinClassOrObjectStub<out JetClassOrObject>> {
private var delegate: PsiClass? = null
@@ -268,7 +268,7 @@ public open class KotlinLightClassForExplicitDeclaration(
override fun getModifierList(): PsiModifierList = _modifierList
private fun computeModifiers(): Array<String> {
protected open fun computeModifiers(): Array<String> {
val psiModifiers = hashSetOf<String>()
// 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)
}
@@ -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<PsiTypeParameter> = emptyArray()
override fun computeModifiers(): Array<String> = 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")
}
}
@@ -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<PsiNamedElement>()
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)
@@ -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)
}
@@ -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);
}
@@ -0,0 +1,7 @@
public class Testing {
public static void test() {
mockLib.foo.MyInterface.<caret>
}
}
// EXIST: DefaultImpls
@@ -0,0 +1,8 @@
import mockLib.foo.MyInterface.DefaultImpls;
public class Testing {
public static void test() {
DefaultImpls.<caret>
}
}
// EXIST: foo
@@ -0,0 +1,7 @@
public class Testing {
public static void test() {
mockLib.foo.MyInterface.DefaultImpls.<caret>
}
}
// EXIST: foo
@@ -0,0 +1,8 @@
import static mockLib.foo.MyInterface.DefaultImpls.*;
public class Testing {
public static void test() {
f<caret>
}
}
// EXIST: foo
+4
View File
@@ -48,4 +48,8 @@ class F() {
}
}
}
}
interface MyInterface {
fun foo() = 1
}
@@ -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");
@@ -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");