Optimize KotlinScriptDependenciesClassFinder

Do not call processDirectories for all package prefixes checking inner classes

^KT-39796 Fixed
This commit is contained in:
Natalia Selezneva
2020-06-26 06:29:50 +03:00
parent f46970219f
commit a73dac75d4
5 changed files with 63 additions and 32 deletions
@@ -5,14 +5,15 @@
package org.jetbrains.kotlin.idea.core.script
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.NonClasspathClassFinder
import com.intellij.psi.PsiClass
import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex
import com.intellij.psi.search.EverythingGlobalScope
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StubIndex
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
@@ -23,33 +24,46 @@ class KotlinScriptDependenciesClassFinder(
.getAllScriptsDependenciesClassFiles().filter { it.isValid }.toList()
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? {
tailrec fun findClassInner(parentQualifier: String, inners: List<String> = emptyList()): PsiClass? {
if (parentQualifier.isEmpty()) return null
ProgressManager.checkCanceled()
val parentClass = super.findClass(parentQualifier, scope)
if (parentClass != null) {
if (inners.isNotEmpty()) {
val innerClass = inners.fold(parentClass) { c: PsiClass?, name: String ->
c?.findInnerClassByName(name, false)
}
if (innerClass != null) return innerClass
} else return parentClass
}
return findClassInner(
parentQualifier.substringBeforeLast('.', ""),
listOf(parentQualifier.substringAfterLast('.')) + inners
)
val scriptDependencies =
ScriptConfigurationManager.getInstance(project).getAllScriptsDependenciesClassFiles()
val firstJarInDependencies = scriptDependencies.firstOrNull() ?: return null
if (!scope.contains(firstJarInDependencies)) return null
val classByFileName = super.findClass(qualifiedName, scope)
if (classByFileName != null) {
return classByFileName.isInScope(scope)
}
return findClassInner(qualifiedName)?.let { aClass ->
if (scope is EverythingGlobalScope) return aClass
val file = aClass.containingFile?.virtualFile ?: return null
val index = ProjectFileIndex.SERVICE.getInstance(myProject)
if (index.isInContent(file) || index.isInLibraryClasses(file) || index.isInLibrarySource(file)) {
return null
}
return aClass
// Following code is needed because NonClasspathClassFinder cannot find inner classes
// JavaFullClassNameIndex cannot be used directly, because it filter only classes in source roots
val classes = StubIndex.getElements(
JavaFullClassNameIndex.getInstance().key,
qualifiedName.hashCode(),
project,
scope,
PsiClass::class.java
).filter {
it.qualifiedName == qualifiedName
}
val found = when (classes.size) {
0 -> null
1 -> classes.single()
else -> classes.first() // todo: check when this happens
}
return found?.isInScope(scope)
}
private fun PsiClass.isInScope(scope: GlobalSearchScope): PsiClass? {
if (scope is EverythingGlobalScope) return this
val file = this.containingFile?.virtualFile ?: return null
val index = ProjectFileIndex.SERVICE.getInstance(myProject)
if (index.isInContent(file) || index.isInLibrary(file)) {
return null
}
return this
}
}
@@ -0,0 +1,9 @@
package lib
interface KotlinInterface {
interface F {
interface I
}
class G
}
@@ -1,6 +1,6 @@
import lib.*
fun f(
fun javaFun(
<warning>g</warning>: JavaInterface.G,
<warning>f</warning>: JavaInterface.F,
<warning>i</warning>: JavaInterface.F.I
@@ -8,4 +8,12 @@ fun f(
}
fun kotlinFun(
<warning>g</warning>: KotlinInterface.G,
<warning>f</warning>: KotlinInterface.F,
<warning>i</warning>: KotlinInterface.F.I
) {
}
// DEPENDENCIES: classpath:lib-classes; imports:custom.library.*
@@ -100,16 +100,16 @@ public class ScriptConfigurationHighlightingTestGenerated extends AbstractScript
runTest("idea/testData/script/definition/highlighting/implicitReceiver/");
}
@TestMetadata("javaNestedClass")
public void testJavaNestedClass() throws Exception {
runTest("idea/testData/script/definition/highlighting/javaNestedClass/");
}
@TestMetadata("multiModule")
public void testMultiModule() throws Exception {
runTest("idea/testData/script/definition/highlighting/multiModule/");
}
@TestMetadata("nestedClass")
public void testNestedClass() throws Exception {
runTest("idea/testData/script/definition/highlighting/nestedClass/");
}
@TestMetadata("noResolver")
public void testNoResolver() throws Exception {
runTest("idea/testData/script/definition/highlighting/noResolver/");