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 package org.jetbrains.kotlin.idea.core.script
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.NonClasspathClassFinder import com.intellij.psi.NonClasspathClassFinder
import com.intellij.psi.PsiClass 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.EverythingGlobalScope
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.StubIndex
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
@@ -23,33 +24,46 @@ class KotlinScriptDependenciesClassFinder(
.getAllScriptsDependenciesClassFiles().filter { it.isValid }.toList() .getAllScriptsDependenciesClassFiles().filter { it.isValid }.toList()
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? { override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? {
tailrec fun findClassInner(parentQualifier: String, inners: List<String> = emptyList()): PsiClass? { val scriptDependencies =
if (parentQualifier.isEmpty()) return null ScriptConfigurationManager.getInstance(project).getAllScriptsDependenciesClassFiles()
ProgressManager.checkCanceled() val firstJarInDependencies = scriptDependencies.firstOrNull() ?: return null
val parentClass = super.findClass(parentQualifier, scope)
if (parentClass != null) { if (!scope.contains(firstJarInDependencies)) return null
if (inners.isNotEmpty()) {
val innerClass = inners.fold(parentClass) { c: PsiClass?, name: String -> val classByFileName = super.findClass(qualifiedName, scope)
c?.findInnerClassByName(name, false) if (classByFileName != null) {
} return classByFileName.isInScope(scope)
if (innerClass != null) return innerClass
} else return parentClass
}
return findClassInner(
parentQualifier.substringBeforeLast('.', ""),
listOf(parentQualifier.substringAfterLast('.')) + inners
)
} }
return findClassInner(qualifiedName)?.let { aClass -> // Following code is needed because NonClasspathClassFinder cannot find inner classes
if (scope is EverythingGlobalScope) return aClass // JavaFullClassNameIndex cannot be used directly, because it filter only classes in source roots
val classes = StubIndex.getElements(
val file = aClass.containingFile?.virtualFile ?: return null JavaFullClassNameIndex.getInstance().key,
val index = ProjectFileIndex.SERVICE.getInstance(myProject) qualifiedName.hashCode(),
if (index.isInContent(file) || index.isInLibraryClasses(file) || index.isInLibrarySource(file)) { project,
return null scope,
} PsiClass::class.java
return aClass ).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.* import lib.*
fun f( fun javaFun(
<warning>g</warning>: JavaInterface.G, <warning>g</warning>: JavaInterface.G,
<warning>f</warning>: JavaInterface.F, <warning>f</warning>: JavaInterface.F,
<warning>i</warning>: JavaInterface.F.I <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.* // DEPENDENCIES: classpath:lib-classes; imports:custom.library.*
@@ -100,16 +100,16 @@ public class ScriptConfigurationHighlightingTestGenerated extends AbstractScript
runTest("idea/testData/script/definition/highlighting/implicitReceiver/"); runTest("idea/testData/script/definition/highlighting/implicitReceiver/");
} }
@TestMetadata("javaNestedClass")
public void testJavaNestedClass() throws Exception {
runTest("idea/testData/script/definition/highlighting/javaNestedClass/");
}
@TestMetadata("multiModule") @TestMetadata("multiModule")
public void testMultiModule() throws Exception { public void testMultiModule() throws Exception {
runTest("idea/testData/script/definition/highlighting/multiModule/"); runTest("idea/testData/script/definition/highlighting/multiModule/");
} }
@TestMetadata("nestedClass")
public void testNestedClass() throws Exception {
runTest("idea/testData/script/definition/highlighting/nestedClass/");
}
@TestMetadata("noResolver") @TestMetadata("noResolver")
public void testNoResolver() throws Exception { public void testNoResolver() throws Exception {
runTest("idea/testData/script/definition/highlighting/noResolver/"); runTest("idea/testData/script/definition/highlighting/noResolver/");