Support nested classes in KotlinCliJavaFileManagerImpl.findClass

findClass(String, GlobalSearchScope) is invoked for example when we're
resolving supertypes of classes in Java libraries. Previously, it never
found nested classes and falled back to CoreJavaFileManager's
implementation, which lacks a fix for the original issue (KT-12664,
which was fixed in JvmDependenciesIndex in 5a533a52 and 164c72e8)

 #KT-16931 Fixed
This commit is contained in:
Alexander Udalov
2017-03-24 18:33:32 +03:00
parent f1a1ebae01
commit c67eb84369
9 changed files with 35 additions and 11 deletions
@@ -53,12 +53,31 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ
} }
} }
// this method is called from IDEA to resolve dependencies in Java code
// which supposedly shouldn't have errors so the dependencies exist in general
override fun findClass(qName: String, scope: GlobalSearchScope): PsiClass? { override fun findClass(qName: String, scope: GlobalSearchScope): PsiClass? {
// this method is called from IDEA to resolve dependencies in Java code // String cannot be reliably converted to ClassId because we don't know where the package name ends and class names begin.
// which supposedly shouldn't have errors so the dependencies exist in general // For example, if qName is "a.b.c.d.e", we should either look for a top level class "e" in the package "a.b.c.d",
// Most classes are top level classes so we will try to find them fast // or, for example, for a nested class with the relative qualified name "c.d.e" in the package "a.b".
// but we must sometimes fallback to support finding inner/nested classes // Below, we start by looking for the top level class "e" in the package "a.b.c.d" first, then for the class "d.e" in the package
return qName.toSafeTopLevelClassId()?.let { classId -> findClass(classId, scope) } ?: super.findClass(qName, scope) // "a.b.c", and so on, until we find something. Most classes are top level, so most of the times the search ends quickly
var classId = qName.toSafeTopLevelClassId() ?: return super.findClass(qName, scope)
while (true) {
findClass(classId, scope)?.let { return it }
val packageFqName = classId.packageFqName
if (packageFqName.isRoot) break
classId = ClassId(
packageFqName.parent(),
FqName(packageFqName.shortName().asString() + "." + classId.relativeClassName.asString()),
false
)
}
return super.findClass(qName, scope)
} }
override fun findClasses(qName: String, scope: GlobalSearchScope): Array<PsiClass> { override fun findClasses(qName: String, scope: GlobalSearchScope): Array<PsiClass> {
@@ -0,0 +1,6 @@
package test;
public class Baz implements Foo.Bar {
@Override
public void bar() {}
}
@@ -2,6 +2,6 @@ package test;
public class Foo { public class Foo {
public interface Bar { public interface Bar {
void bar();
} }
} }
@@ -1,3 +1,5 @@
import test.Foo.Bar import test.Foo.Bar
import test.Baz
val f: Bar? = null val f: Bar? = null
val g: Baz? = Baz().apply { bar() }
@@ -277,12 +277,9 @@ class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() {
assertEquals("Invalid package prefix name is ignored: invalid-prefix.test", warnings.first().messageText) assertEquals("Invalid package prefix name is ignored: invalid-prefix.test", warnings.first().messageText)
} }
fun testSourcePackagePrefixKnownIssueWithInnerClasses() { fun testSourcePackagePrefixWithInnerClasses() {
initProject() initProject()
val buildResult = buildAllModules() buildAllModules().assertSuccessful()
buildResult.assertFailed()
val errors = buildResult.getMessages(BuildMessage.Kind.ERROR).map { it.messageText }
assertTrue("Message wasn't found. $errors", errors.first().contains("class xxx.JavaWithInner.TextRenderer, unresolved supertypes: TableRow"))
} }
fun testKotlinJavaScriptProject() { fun testKotlinJavaScriptProject() {