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? {
// this method is called from IDEA to resolve dependencies in Java code
// which supposedly shouldn't have errors so the dependencies exist in general
// Most classes are top level classes so we will try to find them fast
// but we must sometimes fallback to support finding inner/nested classes
return qName.toSafeTopLevelClassId()?.let { classId -> findClass(classId, scope) } ?: super.findClass(qName, scope)
// String cannot be reliably converted to ClassId because we don't know where the package name ends and class names begin.
// 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",
// or, for example, for a nested class with the relative qualified name "c.d.e" in the package "a.b".
// 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
// "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> {
@@ -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 interface Bar {
void bar();
}
}
@@ -1,3 +1,5 @@
import test.Foo.Bar
import test.Baz
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)
}
fun testSourcePackagePrefixKnownIssueWithInnerClasses() {
fun testSourcePackagePrefixWithInnerClasses() {
initProject()
val buildResult = buildAllModules()
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"))
buildAllModules().assertSuccessful()
}
fun testKotlinJavaScriptProject() {