Preprocess script dependencies before running UnusedSymbolInspection

^KT-29474
This commit is contained in:
Natalia Selezneva
2019-02-15 11:21:12 +03:00
parent 47a4500eaa
commit f98429c509
3 changed files with 46 additions and 26 deletions
@@ -19,9 +19,7 @@ package org.jetbrains.kotlin.idea.search
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.FileIndexFacade
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
@@ -32,15 +30,17 @@ import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.PsiSearchHelper
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.Processor
import com.intellij.util.CommonProcessors
import com.intellij.util.indexing.FileBasedIndex
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.util.compat.psiSearchHelperInstance
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.compat.psiSearchHelperInstance
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.findScriptDefinition
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import java.util.*
infix fun SearchScope.and(otherScope: SearchScope): SearchScope = intersectWith(otherScope)
infix fun SearchScope.or(otherScope: SearchScope): SearchScope = union(otherScope)
@@ -121,32 +121,23 @@ fun PsiSearchHelper.isCheapEnoughToSearchConsideringOperators(
return PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES
}
if (!isCheapToSearchUsagesInScripts(scope.restrictToKotlinSources(), name)) {
return PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES
}
return isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress)
}
private fun isCheapToSearchUsagesInScripts(scope: GlobalSearchScope, name: String): Boolean {
val project = scope.project ?: return true
fun findScriptsWithUsages(declaration: KtNamedDeclaration): List<VirtualFile> {
val project = declaration.project
val scope = psiSearchHelperInstance(project).getUseScope(declaration) as? GlobalSearchScope
?: return emptyList()
var scriptsCount = 0
val processor = object : Processor<VirtualFile> {
override fun process(file: VirtualFile): Boolean {
ProgressManager.checkCanceled()
if (file.findScriptDefinition(project) == null) return true
return scriptsCount++ < 3
}
}
val index = FileIndexFacade.getInstance(project)
return runReadAction {
FileBasedIndex.getInstance().processFilesContainingAllKeys(
val name = declaration.name.takeIf { it?.isNotBlank() == true } ?: return emptyList()
val collector = CommonProcessors.CollectProcessor(ArrayList<VirtualFile>())
runReadAction {
FileBasedIndex.getInstance().getFilesWithKey(
IdIndex.NAME,
listOf(IdIndexEntry(name, true)),
scope,
null,
{ file -> !index.shouldBeFound(scope, file) || processor.process(file) })
setOf(IdIndexEntry(name, true)),
collector,
scope
)
}
return collector.results.filter { it.findScriptDefinition(project) != null }.toList()
}
@@ -75,6 +75,22 @@ class ScriptDependenciesUpdater(
return cache[file] ?: ScriptDependencies.Empty
}
fun updateDependenciesIfNeeded(files: List<VirtualFile>): Boolean {
val definitionsManager = ScriptDefinitionsManager.getInstance(project)
if (definitionsManager.isReady() && areDependenciesCached(files)) {
return false
}
for (file in files) {
val scriptDef = file.findScriptDefinition(project) ?: continue
updateDependencies(file, scriptDef)
}
makeRootsChangeIfNeeded()
return true
}
private fun updateDependencies(file: VirtualFile, scriptDef: KotlinScriptDefinition) {
val loader = when (scriptDef.dependencyResolver) {
is AsyncDependenciesResolver, is LegacyResolverWrapper -> asyncLoader
@@ -154,6 +170,10 @@ class ScriptDependenciesUpdater(
return cache[file] != null
}
private fun areDependenciesCached(files: List<VirtualFile>): Boolean {
return files.all { areDependenciesCached(it) }
}
companion object {
@JvmStatic
fun getInstance(project: Project): ScriptDependenciesUpdater =
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.isInheritable
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater
import org.jetbrains.kotlin.idea.core.toDescriptor
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
@@ -52,6 +53,7 @@ import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.quickfix.RemoveUnusedFunctionParameterFix
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.idea.search.findScriptsWithUsages
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.isCheapEnoughToSearchConsideringOperators
@@ -122,6 +124,13 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
val project = declaration.project
val psiSearchHelper = psiSearchHelperInstance(project)
val usedScripts = findScriptsWithUsages(declaration)
if (usedScripts.isNotEmpty()) {
if (ScriptDependenciesUpdater.getInstance(declaration.project).updateDependenciesIfNeeded(usedScripts)) {
return TOO_MANY_OCCURRENCES
}
}
val useScope = psiSearchHelper.getUseScope(declaration)
if (useScope is GlobalSearchScope) {
var zeroOccurrences = true