Refactored to avoid code duplication

This commit is contained in:
Valentin Kipyatkov
2016-09-01 01:29:27 +03:00
parent e3b37f9219
commit f2c0d131e4
22 changed files with 365 additions and 335 deletions
@@ -123,7 +123,7 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
when (element) {
is KtFunction -> {
if (name != null && isComponentLike(name)) {
findDestructuringDeclarationUsages(element, effectiveSearchScope, consumer, queryParameters.optimizer)
DestructuringDeclarationReferenceSearcher(element, effectiveSearchScope, consumer, queryParameters.optimizer).run()
}
}
@@ -146,7 +146,7 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
//TODO: Java invoke's
if (kotlinOptions.searchInvokeOperator && element is KtFunction && name == OperatorNameConventions.INVOKE.asString()) {
findInvokeOperatorUsages(element, effectiveSearchScope, consumer)
InvokeOperatorReferenceSearcher(element, effectiveSearchScope, consumer, queryParameters.optimizer).run()
}
}
@@ -249,7 +249,7 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
}
if (componentFunction != null) {
searchNamedElement(queryParameters, componentFunction)
findDestructuringDeclarationUsages(componentFunction, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer)
DestructuringDeclarationReferenceSearcher.runForPsiMethod(componentFunction, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer)
}
}
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.search.usagesSearch
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchRequestCollector
import com.intellij.psi.search.SearchScope
import com.intellij.util.Processor
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.logPresentation
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.testLog
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
class DestructuringDeclarationReferenceSearcher(
targetDeclaration: KtDeclaration,
searchScope: SearchScope,
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector
) : OperatorReferenceSearcher<KtDestructuringDeclaration>(targetDeclaration, searchScope, consumer, optimizer, wordToSearch = "(") {
companion object {
fun runForPsiMethod(
componentFunction: PsiMethod,
scope: SearchScope,
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector
) {
if (componentFunction !is KtLightMethod) return //TODO
val ktDeclarationTarget = componentFunction.kotlinOrigin as? KtDeclaration ?: return //TODO?
DestructuringDeclarationReferenceSearcher(ktDeclarationTarget, scope, consumer, optimizer).run()
}
}
private val componentIndex = when (targetDeclaration) {
is KtParameter -> targetDeclaration.dataClassComponentFunction()?.name?.asString()?.let { getComponentIndex(it) }
is KtFunction -> targetDeclaration.name?.let { getComponentIndex(it) }
//TODO: java component functions (see KT-13605)
else -> null
}
override fun run() {
if (componentIndex == null) return
super.run()
}
override fun extractReference(element: PsiElement): PsiReference? {
val destructuringDeclaration = element as? KtDestructuringDeclaration ?: return null
val entries = destructuringDeclaration.entries
if (entries.size < componentIndex!!) return null
return entries[componentIndex - 1].references.firstIsInstance<KtDestructuringDeclarationReference>()
}
override fun isReferenceToCheck(ref: PsiReference) = ref is KtDestructuringDeclarationReference
override fun processSuspiciousExpression(expression: KtExpression) {
val parent = expression.parent
val destructuringDeclaration = when (parent) {
is KtDestructuringDeclaration -> parent
is KtContainerNode -> {
if (parent.node.elementType == KtNodeTypes.LOOP_RANGE) {
(parent.parent as KtForExpression).destructuringParameter
}
else {
null
}
}
else -> null
}
if (destructuringDeclaration != null) {
processReferenceElement(destructuringDeclaration)
}
}
}
@@ -74,16 +74,17 @@ class ExpressionsOfTypeProcessor(
var mode = if (ApplicationManager.getApplication().isUnitTestMode) Mode.ALWAYS_SMART else Mode.PLAIN_WHEN_NEEDED
var testLog: MutableList<String>? = null
fun logPresentation(declaration: PsiElement): String? {
val fqName = declaration.getKotlinFqName()?.asString()
?: (declaration as? KtNamedDeclaration)?.name
return when (declaration) {
fun logPresentation(element: PsiElement): String? {
if (element !is KtDeclaration && element !is PsiMember) return element.text
val fqName = element.getKotlinFqName()?.asString()
?: (element as? KtNamedDeclaration)?.name
return when (element) {
is PsiMethod, is KtFunction -> fqName + "()"
is KtParameter -> {
val owner = declaration.ownerFunction?.let { logPresentation(it) } ?: declaration.parent.toString()
"parameter ${declaration.name} in $owner"
val owner = element.ownerFunction?.let { logPresentation(it) } ?: element.parent.toString()
"parameter ${element.name} in $owner"
}
is KtDestructuringDeclaration -> declaration.entries.joinToString(", ", prefix = "(", postfix = ")") { it.text }
is KtDestructuringDeclaration -> element.entries.joinToString(", ", prefix = "(", postfix = ")") { it.text }
else -> fqName
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.search.usagesSearch
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchRequestCollector
import com.intellij.psi.search.SearchScope
import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
class InvokeOperatorReferenceSearcher(
targetDeclaration: KtDeclaration,
searchScope: SearchScope,
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector
) : OperatorReferenceSearcher<KtCallExpression>(targetDeclaration, searchScope, consumer, optimizer, wordToSearch = null) {
companion object {
fun runForPsiMethod(
invokeFunction: PsiMethod,
scope: SearchScope,
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector
) {
if (invokeFunction !is KtLightMethod) return //TODO
val ktDeclarationTarget = invokeFunction.kotlinOrigin as? KtDeclaration ?: return //TODO?
InvokeOperatorReferenceSearcher(ktDeclarationTarget, scope, consumer, optimizer).run()
}
}
override fun processSuspiciousExpression(expression: KtExpression) {
val callExpression = expression.parent as? KtCallExpression ?: return
processReferenceElement(callExpression)
}
override fun isReferenceToCheck(ref: PsiReference) = ref is KtInvokeFunctionReference
override fun extractReference(element: PsiElement): PsiReference? {
return (element as? KtCallExpression)?.references?.firstIsInstance<KtInvokeFunctionReference>()
}
}
@@ -0,0 +1,154 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.search.usagesSearch
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
import com.intellij.psi.PsiReference
import com.intellij.psi.search.*
import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.logPresentation
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.testLog
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.util.isValidOperator
import java.util.*
abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
private val targetDeclaration: KtDeclaration,
private val searchScope: SearchScope,
private val consumer: Processor<PsiReference>,
private val optimizer: SearchRequestCollector,
private val wordToSearch: String?
) {
private val project = targetDeclaration.project
protected abstract fun processSuspiciousExpression(expression: KtExpression)
protected abstract fun extractReference(element: PsiElement): PsiReference?
protected abstract fun isReferenceToCheck(ref: PsiReference): Boolean
protected fun processReferenceElement(element: TReferenceElement): Boolean {
val reference = extractReference(element) ?: return true
testLog?.add("Resolved ${logPresentation(element)}")
if (reference.isReferenceTo(targetDeclaration)) {
return consumer.process(reference)
}
else {
return true
}
}
companion object {
private object SearchesInProgress : ThreadLocal<HashSet<KtDeclaration>>() {
override fun initialValue() = HashSet<KtDeclaration>()
}
}
open fun run() {
val inProgress = SearchesInProgress.get()
try {
if (!inProgress.add(targetDeclaration)) return //TODO: it's not quite correct
val usePlainSearch = when (ExpressionsOfTypeProcessor.mode) {
ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART -> false
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) {
doPlainSearch(searchScope)
return
}
val descriptor = targetDeclaration.resolveToDescriptor() as? CallableDescriptor ?: return
if (descriptor is FunctionDescriptor && !descriptor.isValidOperator()) return
val dataType = if (descriptor.isExtension) {
descriptor.fuzzyExtensionReceiverType()!!
}
else {
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters)
}
ExpressionsOfTypeProcessor(
dataType,
searchScope,
suspiciousExpressionHandler = { expression -> processSuspiciousExpression(expression) },
suspiciousScopeHandler = { searchScope -> doPlainSearch(searchScope) },
resolutionFacade = targetDeclaration.getResolutionFacade()
).run()
}
finally {
inProgress.remove(targetDeclaration)
}
}
private fun doPlainSearch(scope: SearchScope) {
if (scope is LocalSearchScope) {
for (element in scope.scope) {
element.accept(object : PsiRecursiveElementWalkingVisitor() {
override fun visitElement(element: PsiElement) {
val reference = extractReference(element)
if (reference != null && reference.isReferenceTo(targetDeclaration)) {
consumer.process(reference)
}
super.visitElement(element)
}
})
}
}
else {
scope as GlobalSearchScope
if (wordToSearch != null) {
val unwrappedElement = targetDeclaration.namedUnwrappedElement ?: return
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement,
filter = { ref -> isReferenceToCheck(ref) })
optimizer.searchWord(wordToSearch, scope.restrictToKotlinSources(), UsageSearchContext.IN_CODE, true, unwrappedElement, resultProcessor)
}
else {
val psiManager = PsiManager.getInstance(project)
ProjectRootManager.getInstance(project).fileIndex.iterateContent { file ->
if (file in scope) {
val ktFile = psiManager.findFile(file) as? KtFile
if (ktFile != null) {
doPlainSearch(LocalSearchScope(ktFile))
}
}
true
}
}
}
}
}
@@ -1,147 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.search.usagesSearch
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchRequestCollector
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.UsageSearchContext
import com.intellij.util.Processor
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.logPresentation
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.testLog
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.util.isValidOperator
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import java.util.*
private object DestructuringDeclarationSearchesInProgress : ThreadLocal<HashSet<KtDeclaration>>() {
override fun initialValue() = HashSet<KtDeclaration>()
}
fun findDestructuringDeclarationUsages(
componentFunction: PsiMethod,
scope: SearchScope,
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector
) {
if (componentFunction !is KtLightMethod) return //TODO
val ktDeclarationTarget = componentFunction.kotlinOrigin as? KtDeclaration ?: return //TODO?
findDestructuringDeclarationUsages(ktDeclarationTarget, scope, consumer, optimizer)
}
fun findDestructuringDeclarationUsages(
targetDeclaration: KtDeclaration,
searchScope: SearchScope,
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector
) {
val inProgress = DestructuringDeclarationSearchesInProgress.get()
try {
if (!inProgress.add(targetDeclaration)) return
val usePlainSearch = when (ExpressionsOfTypeProcessor.mode) {
ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART -> false
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) {
doPlainSearch(targetDeclaration, searchScope, optimizer)
return
}
val descriptor = targetDeclaration.resolveToDescriptor() as? CallableDescriptor ?: return
if (descriptor is FunctionDescriptor && !descriptor.isValidOperator()) return
val dataType = if (descriptor.isExtension) {
descriptor.fuzzyExtensionReceiverType()!!
}
else {
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters)
}
val componentIndex = when (targetDeclaration) {
is KtParameter -> targetDeclaration.dataClassComponentFunction()?.name?.asString()?.let { getComponentIndex(it) }
is KtFunction -> targetDeclaration.name?.let { getComponentIndex(it) }
//TODO: java component functions (see KT-13605)
else -> null
} ?: return
ExpressionsOfTypeProcessor(
dataType,
searchScope,
suspiciousExpressionHandler = { expression -> processSuspiciousExpression(expression, targetDeclaration, componentIndex, consumer) },
suspiciousScopeHandler = { searchScope -> doPlainSearch(targetDeclaration, searchScope, optimizer) },
resolutionFacade = targetDeclaration.getResolutionFacade()
).run()
}
finally {
inProgress.remove(targetDeclaration)
}
}
private fun processSuspiciousExpression(expression: KtExpression, targetDeclaration: KtDeclaration, componentIndex: Int, consumer: Processor<PsiReference>) {
val parent = expression.parent
val destructuringDeclaration = when (parent) {
is KtDestructuringDeclaration -> parent
is KtContainerNode -> {
if (parent.node.elementType == KtNodeTypes.LOOP_RANGE) {
(parent.parent as KtForExpression).destructuringParameter
}
else {
null
}
}
else -> null
}
if (destructuringDeclaration != null && componentIndex <= destructuringDeclaration.entries.size) {
testLog?.add("Checked type of ${logPresentation(destructuringDeclaration)}")
val declarationReference = destructuringDeclaration.entries[componentIndex - 1].references.firstIsInstance<KtDestructuringDeclarationReference>()
if (declarationReference.isReferenceTo(targetDeclaration)) {
consumer.process(declarationReference)
}
}
}
private fun doPlainSearch(ktDeclaration: KtDeclaration, scope: SearchScope, optimizer: SearchRequestCollector) {
val unwrappedElement = ktDeclaration.namedUnwrappedElement ?: return
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement,
filter = { ref -> ref is KtDestructuringDeclarationReference })
optimizer.searchWord("(", scope.restrictToKotlinSources(), UsageSearchContext.IN_CODE, true, unwrappedElement, resultProcessor)
}
@@ -1,136 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.search.usagesSearch
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor.Companion.testLog
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.util.isValidOperator
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
//TODO: problem with infinite recursion not solved
//TODO: code duplication
fun findInvokeOperatorUsages(
invokeFunction: PsiMethod,
scope: SearchScope,
consumer: Processor<PsiReference>
) {
if (invokeFunction !is KtLightMethod) return //TODO
val ktDeclarationTarget = invokeFunction.kotlinOrigin as? KtDeclaration ?: return //TODO?
findInvokeOperatorUsages(ktDeclarationTarget, scope, consumer)
}
fun findInvokeOperatorUsages(
targetDeclaration: KtDeclaration,
searchScope: SearchScope,
consumer: Processor<PsiReference>
) {
val usePlainSearch = when (ExpressionsOfTypeProcessor.mode) {
ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART -> false
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) {
doPlainSearch(targetDeclaration, searchScope, consumer)
return
}
val descriptor = targetDeclaration.resolveToDescriptor() as? CallableDescriptor ?: return
if (descriptor is FunctionDescriptor && !descriptor.isValidOperator()) return
val dataType = if (descriptor.isExtension) {
descriptor.fuzzyExtensionReceiverType()!!
}
else {
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters)
}
ExpressionsOfTypeProcessor(
dataType,
searchScope,
suspiciousExpressionHandler = { expression -> processSuspiciousExpression(expression, targetDeclaration, consumer) },
suspiciousScopeHandler = { searchScope -> doPlainSearch(targetDeclaration, searchScope, consumer) },
resolutionFacade = targetDeclaration.getResolutionFacade()
).run()
}
private fun processSuspiciousExpression(expression: KtExpression, targetDeclaration: KtDeclaration, consumer: Processor<PsiReference>) {
val callExpression = expression.parent as? KtCallExpression ?: return
testLog?.add("Resolving call ${callExpression.text}")
processCallExpression(callExpression, targetDeclaration, consumer)
}
private fun doPlainSearch(ktDeclaration: KtDeclaration, scope: SearchScope, consumer: Processor<PsiReference>) {
if (scope is LocalSearchScope) {
for (element in scope.scope) {
val stop = element.anyDescendantOfType<KtCallExpression> { !processCallExpression(it, ktDeclaration, consumer) }
if (stop) break
}
}
else {
scope as GlobalSearchScope
val project = ktDeclaration.project
val psiManager = PsiManager.getInstance(project)
ProjectRootManager.getInstance(project).fileIndex.iterateContent { file ->
if (file in scope) {
val ktFile = psiManager.findFile(file) as? KtFile
if (ktFile != null) {
val stop = ktFile.anyDescendantOfType<KtCallExpression> {
!processCallExpression(it, ktDeclaration, consumer)
}
if (stop) return@iterateContent false
}
}
true
}
}
}
private fun processCallExpression(callExpression: KtCallExpression, targetDeclaration: KtDeclaration, consumer: Processor<PsiReference>): Boolean {
val reference = callExpression.references.firstIsInstance<KtInvokeFunctionReference>()
if (reference.isReferenceTo(targetDeclaration)) {
return consumer.process(reference)
}
else {
return true
}
}