Do not run unused symbol inspection if declaration in used in more than 3 scripts

This commit is contained in:
Natalia Selezneva
2019-01-29 16:14:05 +03:00
parent c29332e290
commit 74909c4d09
2 changed files with 74 additions and 13 deletions
@@ -19,18 +19,27 @@ package org.jetbrains.kotlin.idea.search
import com.intellij.openapi.fileTypes.FileType import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeRegistry import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project 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.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.impl.cache.impl.id.IdIndex
import com.intellij.psi.impl.cache.impl.id.IdIndexEntry
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.PsiSearchHelper import com.intellij.psi.search.PsiSearchHelper
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.Processor
import com.intellij.util.indexing.FileBasedIndex
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.util.compat.psiSearchHelperInstance import org.jetbrains.kotlin.idea.util.compat.psiSearchHelperInstance
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.findScriptDefinition
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
infix fun SearchScope.and(otherScope: SearchScope): SearchScope = intersectWith(otherScope) infix fun SearchScope.and(otherScope: SearchScope): SearchScope = intersectWith(otherScope)
@@ -108,7 +117,36 @@ fun PsiSearchHelper.isCheapEnoughToSearchConsideringOperators(
fileToIgnoreOccurrencesIn: PsiFile?, fileToIgnoreOccurrencesIn: PsiFile?,
progress: ProgressIndicator? progress: ProgressIndicator?
): PsiSearchHelper.SearchCostResult { ): PsiSearchHelper.SearchCostResult {
if (OperatorConventions.isConventionName(Name.identifier(name))) return PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES if (OperatorConventions.isConventionName(Name.identifier(name))) {
return PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES
}
if (!isCheapToSearchUsagesInScripts(scope.restrictToKotlinSources(), name)) {
return PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES
}
return isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress) return isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress)
} }
private fun isCheapToSearchUsagesInScripts(scope: GlobalSearchScope, name: String): Boolean {
val project = scope.project ?: return true
var scriptsCount = 0
val processor = object : Processor<VirtualFile> {
override fun process(file: VirtualFile): Boolean {
ProgressManager.checkCanceled()
if (findScriptDefinition(file, project) == null) return true
return scriptsCount++ < 3
}
}
val index = FileIndexFacade.getInstance(project)
return runReadAction {
FileBasedIndex.getInstance().processFilesContainingAllKeys(
IdIndex.NAME,
listOf(IdIndexEntry(name, true)),
scope,
null,
{ file -> !index.shouldBeFound(scope, file) || processor.process(file) })
}
}
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiSearchHelper.SearchCostResult
import com.intellij.psi.search.PsiSearchHelper.SearchCostResult.* import com.intellij.psi.search.PsiSearchHelper.SearchCostResult.*
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.DefinitionsScopedSearch import com.intellij.psi.search.searches.DefinitionsScopedSearch
@@ -109,7 +110,34 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
} }
else -> return false else -> return false
} }
return lightElement != null && javaInspection.isEntryPoint(lightElement)
if (lightElement == null) return false
if (isCheapEnoughToSearchUsages(declaration) == TOO_MANY_OCCURRENCES) return false
return javaInspection.isEntryPoint(lightElement)
}
private fun isCheapEnoughToSearchUsages(declaration: KtNamedDeclaration): SearchCostResult {
val project = declaration.project
val psiSearchHelper = psiSearchHelperInstance(project)
val useScope = psiSearchHelper.getUseScope(declaration)
if (useScope is GlobalSearchScope) {
var zeroOccurrences = true
for (name in listOf(declaration.name) + declaration.getAccessorNames() + listOfNotNull(declaration.getClassNameForCompanionObject())) {
if (name == null) continue
when (psiSearchHelper.isCheapEnoughToSearchConsideringOperators(name, useScope, null, null)) {
ZERO_OCCURRENCES -> {
} // go on, check other names
FEW_OCCURRENCES -> zeroOccurrences = false
TOO_MANY_OCCURRENCES -> return TOO_MANY_OCCURRENCES // searching usages is too expensive; behave like it is used
}
}
if (zeroOccurrences) return ZERO_OCCURRENCES
}
return FEW_OCCURRENCES
} }
private fun KtProperty.isSerializationImplicitlyUsedField(): Boolean { private fun KtProperty.isSerializationImplicitlyUsedField(): Boolean {
@@ -223,18 +251,13 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
val project = declaration.project val project = declaration.project
val psiSearchHelper = psiSearchHelperInstance(project) val psiSearchHelper = psiSearchHelperInstance(project)
val useScope = declaration.useScope val useScope = psiSearchHelper.getUseScope(declaration)
val restrictedScope = if (useScope is GlobalSearchScope) { val restrictedScope = if (useScope is GlobalSearchScope) {
var zeroOccurrences = true val enoughToSearchUsages = isCheapEnoughToSearchUsages(declaration)
val zeroOccurrences = when (enoughToSearchUsages) {
for (name in listOf(declaration.name) + declaration.getAccessorNames() + listOfNotNull(declaration.getClassNameForCompanionObject())) { ZERO_OCCURRENCES -> true
if (name == null) continue FEW_OCCURRENCES -> false
when (psiSearchHelper.isCheapEnoughToSearchConsideringOperators(name, useScope, null, null)) { TOO_MANY_OCCURRENCES -> return true // searching usages is too expensive; behave like it is used
ZERO_OCCURRENCES -> {
} // go on, check other names
FEW_OCCURRENCES -> zeroOccurrences = false
TOO_MANY_OCCURRENCES -> return true // searching usages is too expensive; behave like it is used
}
} }
if (zeroOccurrences && !declaration.hasActualModifier()) { if (zeroOccurrences && !declaration.hasActualModifier()) {