Ignore sub-queries for other operators with the same receiver (KT-18566)
ExpressionsOfTypeProcessor searches for all occurence of expression with given type. It start from usages of the class, searches for sub-classes, declarations that return those classes, usages of these declarations, and so on. During this search, find usages for all operators that return the subject type is executed as sub-queries. Full search for such queries can't give addition types. And it also shouldn't give additional scopes for search, because same scopes should be located by operands. In other words, if sub-query can spot the scope of usage starting from the same type, the original query should also process same scope. #KT-18566 Fixed
This commit is contained in:
+9
-19
@@ -43,7 +43,6 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.search.excludeFileTypes
|
||||
@@ -67,6 +66,7 @@ import java.util.*
|
||||
|
||||
class ExpressionsOfTypeProcessor(
|
||||
private val typeToSearch: FuzzyType,
|
||||
private val classToSearch: PsiClass?,
|
||||
private val searchScope: SearchScope,
|
||||
private val project: Project,
|
||||
private val possibleMatchHandler: (KtExpression) -> Unit,
|
||||
@@ -120,7 +120,7 @@ class ExpressionsOfTypeProcessor(
|
||||
}
|
||||
|
||||
private val tasks = ArrayDeque<Task>()
|
||||
private val taskSet = HashSet<Any>()
|
||||
private val taskSet = HashSet<Task>()
|
||||
|
||||
private val scopesToUsePlainSearch = LinkedHashMap<KtFile, ArrayList<PsiElement>>()
|
||||
|
||||
@@ -130,7 +130,7 @@ class ExpressionsOfTypeProcessor(
|
||||
ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN -> true
|
||||
ExpressionsOfTypeProcessor.Mode.PLAIN_WHEN_NEEDED -> searchScope is LocalSearchScope // for local scope it's faster to use plain search
|
||||
}
|
||||
if (usePlainSearch) {
|
||||
if (usePlainSearch || classToSearch == null) {
|
||||
possibleMatchesInScopeHandler(searchScope)
|
||||
return
|
||||
}
|
||||
@@ -138,15 +138,13 @@ class ExpressionsOfTypeProcessor(
|
||||
// optimization
|
||||
if (runReadAction { searchScope is GlobalSearchScope && !FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, searchScope) }) return
|
||||
|
||||
val psiClass = runReadAction { detectClassToSearch() }
|
||||
|
||||
// for class from library always use plain search because we cannot search usages in compiled code (we could though)
|
||||
if (psiClass == null || !runReadAction { psiClass.isValid && ProjectRootsUtil.isInProjectSource(psiClass) }) {
|
||||
if (classToSearch == null || !runReadAction { classToSearch.isValid && ProjectRootsUtil.isInProjectSource(classToSearch) }) {
|
||||
possibleMatchesInScopeHandler(searchScope)
|
||||
return
|
||||
}
|
||||
|
||||
addClassToProcess(psiClass)
|
||||
addClassToProcess(classToSearch)
|
||||
|
||||
processTasks()
|
||||
|
||||
@@ -161,16 +159,6 @@ class ExpressionsOfTypeProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectClassToSearch(): PsiClass? {
|
||||
val classDescriptor = typeToSearch.type.constructor.declarationDescriptor ?: return null
|
||||
val classDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor)
|
||||
return when (classDeclaration) {
|
||||
is PsiClass -> classDeclaration
|
||||
is KtClassOrObject -> classDeclaration.toLightClass()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTask(task: Task) {
|
||||
if (taskSet.add(task)) {
|
||||
tasks.push(task)
|
||||
@@ -208,7 +196,6 @@ class ExpressionsOfTypeProcessor(
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private fun addNonKotlinClassToProcess(classToSearch: PsiClass) {
|
||||
if (!checkPsiClass(classToSearch)) {
|
||||
return
|
||||
@@ -398,7 +385,10 @@ class ExpressionsOfTypeProcessor(
|
||||
}
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
data class ProcessCallableUsagesTask(val declaration: PsiElement, val processor: ReferenceProcessor, val scope: SearchScope) : Task {
|
||||
data class ProcessCallableUsagesTask(
|
||||
val declaration: PsiElement,
|
||||
val processor: ReferenceProcessor,
|
||||
val scope: SearchScope) : Task {
|
||||
override fun perform() {
|
||||
if (scope is LocalSearchScope) {
|
||||
testLog { "Searched imported static member $declaration in ${scope.scope.toList()}" }
|
||||
|
||||
+41
-15
@@ -23,12 +23,14 @@ import com.intellij.psi.search.*
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaOrKotlinMemberDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
|
||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
||||
@@ -192,7 +194,6 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
|
||||
}
|
||||
|
||||
//TODO: check no light elements here
|
||||
private object SearchesInProgress : ThreadLocal<HashSet<PsiElement>>() {
|
||||
override fun initialValue() = HashSet<PsiElement>()
|
||||
}
|
||||
@@ -207,14 +208,27 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
}
|
||||
|
||||
fun run() {
|
||||
val receiverType = runReadAction { extractReceiverType() } ?: return
|
||||
val psiClass = runReadAction { receiverType.toPsiClass() }
|
||||
|
||||
val inProgress = SearchesInProgress.get()
|
||||
if (!inProgress.add(targetDeclaration)) return //TODO: it's not quite correct
|
||||
if (psiClass != null) {
|
||||
if (!inProgress.add(psiClass)) {
|
||||
testLog { "ExpressionOfTypeProcessor is already started for ${runReadAction { psiClass.qualifiedName }}. Exit for operator ${logPresentation(targetDeclaration)}." }
|
||||
return
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!inProgress.add(targetDeclaration)) {
|
||||
testLog { "ExpressionOfTypeProcessor is already started for operator ${logPresentation(targetDeclaration)}. Exit." }
|
||||
return //TODO: it's not quite correct
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val receiverType = runReadAction { extractReceiverType() } ?: return
|
||||
|
||||
ExpressionsOfTypeProcessor(
|
||||
receiverType,
|
||||
psiClass,
|
||||
searchScope,
|
||||
project,
|
||||
possibleMatchHandler = { expression -> processPossibleReceiverExpression(expression) },
|
||||
@@ -222,7 +236,17 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
).run()
|
||||
}
|
||||
finally {
|
||||
inProgress.remove(targetDeclaration)
|
||||
inProgress.remove(if (psiClass != null) psiClass else targetDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FuzzyType.toPsiClass(): PsiClass? {
|
||||
val classDescriptor = type.constructor.declarationDescriptor ?: return null
|
||||
val classDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor)
|
||||
return when (classDeclaration) {
|
||||
is PsiClass -> classDeclaration
|
||||
is KtClassOrObject -> classDeclaration.toLightClass()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,16 +336,18 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
is LocalSearchScope -> {
|
||||
scope
|
||||
.map { element ->
|
||||
" " + when (element) {
|
||||
is KtFunctionLiteral -> element.text
|
||||
is KtWhenEntry -> {
|
||||
if (element.isElse)
|
||||
"KtWhenEntry \"else\""
|
||||
else
|
||||
"KtWhenEntry \"" + element.conditions.joinToString(", ") { it.text } + "\""
|
||||
" " + runReadAction {
|
||||
when (element) {
|
||||
is KtFunctionLiteral -> element.text
|
||||
is KtWhenEntry -> {
|
||||
if (element.isElse)
|
||||
"KtWhenEntry \"else\""
|
||||
else
|
||||
"KtWhenEntry \"" + element.conditions.joinToString(", ") { it.text } + "\""
|
||||
}
|
||||
is KtNamedDeclaration -> element.node.elementType.toString() + ":" + element.name
|
||||
else -> element.toString()
|
||||
}
|
||||
is KtNamedDeclaration -> element.node.elementType.toString() + ":" + element.name
|
||||
else -> element.toString()
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
@@ -333,4 +359,4 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user