Refactored reference searches to take read-actions when necessary

This commit is contained in:
Valentin Kipyatkov
2016-09-04 14:17:43 +03:00
parent 637da2e693
commit 4e0c3fcee7
13 changed files with 509 additions and 420 deletions
@@ -23,15 +23,15 @@ import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.util.EmptyQuery
import com.intellij.util.Query
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.KtClassOrObject
fun HierarchySearchRequest<*>.searchInheritors(): Query<PsiClass> {
val psiClass: PsiClass? = when (originalElement) {
is KtClassOrObject -> originalElement.toLightClass()
is PsiClass -> originalElement
else -> null
}
if (psiClass == null) return EmptyQuery.getEmptyQuery()
val psiClass: PsiClass = when (originalElement) {
is KtClassOrObject -> runReadAction { originalElement.toLightClass() }
is PsiClass -> originalElement
else -> null
} ?: return EmptyQuery.getEmptyQuery()
return ClassInheritorsSearch.search(
psiClass,
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.toLightElements
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT
import org.jetbrains.kotlin.idea.search.allScope
@@ -46,6 +45,8 @@ import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.utils.singletonOrEmptyList
import java.util.*
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = false,
val acceptOverloads: Boolean = false,
@@ -70,255 +71,245 @@ class KotlinReferencesSearchParameters(elementToSearch: PsiElement,
}
class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters>() {
override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
val element = queryParameters.elementToSearch
val processor = QueryProcessor(queryParameters, consumer)
runReadAction { processor.processInReadAction() }
processor.executeLongRunningTasks()
}
val unwrappedElement = element.namedUnwrappedElement ?: return
private class QueryProcessor(val queryParameters: ReferencesSearch.SearchParameters, val consumer: Processor<PsiReference>) {
val kotlinOptions = (queryParameters as? KotlinReferencesSearchParameters)?.kotlinOptions
?: KotlinReferencesSearchOptions.Empty
private val kotlinOptions = (queryParameters as? KotlinReferencesSearchParameters)?.kotlinOptions
?: KotlinReferencesSearchOptions.Empty
val specialSymbols = runReadAction { unwrappedElement.getSpecialNamesToSearch(kotlinOptions) }
val words = runReadAction {
val classNameForCompanionObject = unwrappedElement.getClassNameForCompanionObject()
(specialSymbols?.first ?: emptyList()) +
(if (classNameForCompanionObject != null) listOf(classNameForCompanionObject) else emptyList())
private val longTasks = ArrayList<() -> Unit>()
fun executeLongRunningTasks() {
longTasks.forEach { it() }
}
val effectiveSearchScope = runReadAction {
fun processInReadAction() {
val element = queryParameters.elementToSearch
if (!element.isValid) return
val unwrappedElement = element.namedUnwrappedElement ?: return
val specialSymbols = unwrappedElement.getSpecialNamesToSearch(kotlinOptions)
val words = (specialSymbols?.first ?: emptyList()) + unwrappedElement.getClassNameForCompanionObject().singletonOrEmptyList()
val elements = if (unwrappedElement is KtDeclaration) unwrappedElement.toLightElements() else listOf(unwrappedElement)
elements.fold(queryParameters.effectiveSearchScope) { scope, e -> scope.unionSafe(queryParameters.effectiveSearchScope(e)) }
}
val refFilter: (PsiReference) -> Boolean = when {
unwrappedElement is KtParameter -> ({ ref: PsiReference -> !ref.isNamedArgumentReference()/* they are processed later*/ })
specialSymbols != null -> { ref -> ref.javaClass == specialSymbols.second }
else -> ({true})
}
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement, filter = refFilter, options = kotlinOptions)
val name = runReadAction { unwrappedElement.name }
if (kotlinOptions.anyEnabled()) {
if (name != null) {
queryParameters.optimizer.searchWord(name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement,
resultProcessor)
val effectiveSearchScope = elements.fold(queryParameters.effectiveSearchScope) { scope, e ->
scope.unionSafe(queryParameters.effectiveSearchScope(e))
}
}
words.forEach { word ->
queryParameters.optimizer.searchWord(word, effectiveSearchScope,
UsageSearchContext.ANY, true, unwrappedElement,
resultProcessor)
}
if (unwrappedElement is KtParameter && kotlinOptions.searchNamedArguments) {
runReadAction { searchNamedArguments(unwrappedElement, queryParameters) }
}
val refFilter: (PsiReference) -> Boolean = when {
unwrappedElement is KtParameter -> ({ ref: PsiReference -> !ref.isNamedArgumentReference()/* they are processed later*/ })
specialSymbols != null -> { ref -> ref.javaClass == specialSymbols.second }
else -> ({true})
}
if (!(unwrappedElement is KtElement && isOnlyKotlinSearch(effectiveSearchScope))) {
searchLightElements(queryParameters, element, consumer)
}
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement, filter = refFilter, options = kotlinOptions)
if (element is KtFunction || element is PsiMethod) {
val referenceSearcher = OperatorReferenceSearcher.create(
element, effectiveSearchScope, consumer, queryParameters.optimizer, kotlinOptions)
referenceSearcher?.run()
}
if (kotlinOptions.searchForComponentConventions) {
when (element) {
is KtParameter -> {
val componentFunctionDescriptor = runReadAction { element.dataClassComponentFunction() }
if (componentFunctionDescriptor != null) {
val containingClass = element.getStrictParentOfType<KtClassOrObject>()?.toLightClass()
searchDataClassComponentUsages(queryParameters, containingClass, componentFunctionDescriptor, consumer, kotlinOptions)
}
}
is KtLightParameter -> {
val componentFunctionDescriptor = runReadAction { element.kotlinOrigin?.dataClassComponentFunction() }
if (componentFunctionDescriptor != null) {
searchDataClassComponentUsages(queryParameters, element.method.containingClass, componentFunctionDescriptor, consumer, kotlinOptions)
}
val name = unwrappedElement.name
if (kotlinOptions.anyEnabled()) {
if (name != null) {
queryParameters.optimizer.searchWord(
name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement, resultProcessor)
}
}
}
}
words.forEach { word ->
queryParameters.optimizer.searchWord(
word, effectiveSearchScope, UsageSearchContext.ANY, true, unwrappedElement, resultProcessor)
}
private fun searchNamedArguments(parameter: KtParameter, queryParameters: ReferencesSearch.SearchParameters) {
val parameterName = parameter.name ?: return
val function = parameter.ownerFunction ?: return
if (function.nameAsName?.isSpecial ?: true) return
val project = function.project
var namedArgsScope = function.useScope.intersectWith(queryParameters.scopeDeterminedByUser)
if (unwrappedElement is KtParameter && kotlinOptions.searchNamedArguments) {
searchNamedArguments(unwrappedElement)
}
if (namedArgsScope is GlobalSearchScope) {
namedArgsScope = KotlinSourceFilterScope.sourcesAndLibraries(namedArgsScope, project)
if (!(unwrappedElement is KtElement && isOnlyKotlinSearch(effectiveSearchScope))) {
searchLightElements(element)
}
val filesWithFunctionName = CacheManager.SERVICE.getInstance(project).getVirtualFilesWithWord(
function.name!!, UsageSearchContext.IN_CODE, namedArgsScope, true)
namedArgsScope = GlobalSearchScope.filesScope(project, filesWithFunctionName.asList())
}
if (element is KtFunction || element is PsiMethod) {
val referenceSearcher = OperatorReferenceSearcher.create(
element, effectiveSearchScope, consumer, queryParameters.optimizer, kotlinOptions)
if (referenceSearcher != null) {
longTasks.add { referenceSearcher.run() }
}
val processor = KotlinRequestResultProcessor(parameter, filter = { it.isNamedArgumentReference() })
queryParameters.optimizer.searchWord(parameterName,
namedArgsScope,
KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT,
true,
parameter,
processor)
}
}
private fun PsiReference.isNamedArgumentReference(): Boolean {
return this is KtSimpleNameReference && expression.parent is KtValueArgumentName
}
companion object {
fun processKtClassOrObject(element: KtClassOrObject, queryParameters: ReferencesSearch.SearchParameters) {
val className = runReadAction { element.name }
if (className != null) {
val lightClass = runReadAction { element.toLightClass() }
if (lightClass != null) {
searchNamedElement(queryParameters, lightClass, className)
if (element is KtObjectDeclaration && element.isCompanion()) {
val fieldForCompanionObject = runReadAction { LightClassUtil.getLightFieldForCompanionObject(element) }
if (fieldForCompanionObject != null) {
searchNamedElement(queryParameters, fieldForCompanionObject)
if (kotlinOptions.searchForComponentConventions) {
when (element) {
is KtParameter -> {
val componentFunctionDescriptor = element.dataClassComponentFunction()
if (componentFunctionDescriptor != null) {
val containingClass = element.getStrictParentOfType<KtClassOrObject>()?.toLightClass()
searchDataClassComponentUsages(containingClass, componentFunctionDescriptor, kotlinOptions)
}
}
val kotlinReferencesSearchOptions = (queryParameters as? KotlinReferencesSearchParameters)?.kotlinOptions
if (kotlinReferencesSearchOptions?.acceptCompanionObjectMembers == true) {
runReadAction {
val originClass = element.getStrictParentOfType<KtClass>()
val originLightClass = originClass?.toLightClass()
if (originLightClass != null) {
val lightDeclarations: List<KtLightElement<*, *>?> =
originLightClass.methods.map { it as? KtLightMethod } +
originLightClass.fields.map { it as? KtLightField }
for (declaration in element.declarations) {
val lightDeclaration = lightDeclarations.find { it?.kotlinOrigin == declaration }
if (lightDeclaration != null) {
searchNamedElement(queryParameters, lightDeclaration)
}
}
}
}
is KtLightParameter -> {
val componentFunctionDescriptor = element.kotlinOrigin?.dataClassComponentFunction()
if (componentFunctionDescriptor != null) {
searchDataClassComponentUsages(element.method.containingClass, componentFunctionDescriptor, kotlinOptions)
}
}
}
}
}
private fun findStaticMethodsFromCompanionObject(declaration: KtDeclaration): List<PsiMethod> {
val originObject = declaration.parents
.dropWhile { it is KtClassBody }
.firstOrNull() as? KtObjectDeclaration ?: return emptyList()
if (originObject.isCompanion()) {
val originClass = originObject.getStrictParentOfType<KtClass>()
val originLightClass = originClass?.toLightClass() ?: return emptyList()
val allMethods = originLightClass.allMethods
return allMethods.filter { it is KtLightMethod && it.kotlinOrigin == declaration }
private fun searchNamedArguments(parameter: KtParameter) {
val parameterName = parameter.name ?: return
val function = parameter.ownerFunction ?: return
if (function.nameAsName?.isSpecial ?: true) return
val project = function.project
var namedArgsScope = function.useScope.intersectWith(queryParameters.scopeDeterminedByUser)
if (namedArgsScope is GlobalSearchScope) {
namedArgsScope = KotlinSourceFilterScope.sourcesAndLibraries(namedArgsScope, project)
val filesWithFunctionName = CacheManager.SERVICE.getInstance(project).getVirtualFilesWithWord(
function.name!!, UsageSearchContext.IN_CODE, namedArgsScope, true)
namedArgsScope = GlobalSearchScope.filesScope(project, filesWithFunctionName.asList())
}
return emptyList()
val processor = KotlinRequestResultProcessor(parameter, filter = { it.isNamedArgumentReference() })
queryParameters.optimizer.searchWord(parameterName,
namedArgsScope,
KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT,
true,
parameter,
processor)
}
private fun processStaticsFromCompanionObject(element: KtDeclaration, queryParameters: ReferencesSearch.SearchParameters) {
val staticsFromCompanionObject = runReadAction { findStaticMethodsFromCompanionObject(element) }
staticsFromCompanionObject.forEach { searchNamedElement(queryParameters, it) }
}
private fun searchPropertyMethods(queryParameters: ReferencesSearch.SearchParameters, parameter: KtParameter) {
val lightElements = runReadAction { parameter.toLightElements() }
lightElements.forEach { searchNamedElement(queryParameters, it) }
}
private fun searchDataClassComponentUsages(queryParameters: ReferencesSearch.SearchParameters,
containingClass: PsiClass?,
componentFunctionDescriptor: FunctionDescriptor,
consumer: Processor<PsiReference>,
kotlinOptions: KotlinReferencesSearchOptions
) {
val componentFunction = containingClass?.methods?.find {
it.name == componentFunctionDescriptor.name.asString() && it.parameterList.parametersCount == 0
}
if (componentFunction != null) {
searchNamedElement(queryParameters, componentFunction)
val searcher = OperatorReferenceSearcher.create(
componentFunction, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer, kotlinOptions)
searcher!!.run()
}
}
private fun searchLightElements(queryParameters: ReferencesSearch.SearchParameters, element: PsiElement, consumer: Processor<PsiReference>) {
private fun searchLightElements(element: PsiElement) {
when (element) {
is KtClassOrObject -> processKtClassOrObject(element, queryParameters)
is KtClassOrObject -> {
processKtClassOrObject(element)
}
is KtNamedFunction, is KtSecondaryConstructor -> {
val function = element as KtFunction
val name = runReadAction { function.name }
val name = (element as KtFunction).name
if (name != null) {
val methods = runReadAction { LightClassUtil.getLightClassMethods(function) }
val methods = LightClassUtil.getLightClassMethods(element)
for (method in methods) {
searchNamedElement(queryParameters, method)
searchNamedElement(method)
}
}
processStaticsFromCompanionObject(element, queryParameters)
processStaticsFromCompanionObject(element)
}
is KtProperty -> {
val propertyMethods = runReadAction { LightClassUtil.getLightClassPropertyMethods(element) }
propertyMethods.allDeclarations.forEach { searchNamedElement(queryParameters, it) }
processStaticsFromCompanionObject(element, queryParameters)
val propertyMethods = LightClassUtil.getLightClassPropertyMethods(element)
propertyMethods.allDeclarations.forEach { searchNamedElement(it) }
processStaticsFromCompanionObject(element)
}
is KtParameter -> {
searchPropertyMethods(queryParameters, element)
searchPropertyAccessorMethods(element)
}
is KtLightMethod -> {
val declaration = element.kotlinOrigin
if (declaration is KtProperty || (declaration is KtParameter && declaration.hasValOrVar())) {
searchNamedElement(queryParameters, declaration as PsiNamedElement)
processStaticsFromCompanionObject(declaration, queryParameters)
searchNamedElement(declaration as PsiNamedElement)
processStaticsFromCompanionObject(declaration)
}
else if (declaration is KtPropertyAccessor) {
val property = declaration.getStrictParentOfType<KtProperty>()
searchNamedElement(queryParameters, property)
searchNamedElement(property)
}
else if (declaration is KtFunction) {
processStaticsFromCompanionObject(declaration, queryParameters)
processStaticsFromCompanionObject(declaration)
}
}
is KtLightParameter -> {
val origin = element.kotlinOrigin ?: return
searchPropertyMethods(queryParameters, origin)
searchPropertyAccessorMethods(origin)
}
}
}
private fun isOnlyKotlinSearch(searchScope: SearchScope) =
searchScope is LocalSearchScope && runReadAction {
searchScope.scope.all { it.containingFile.fileType == KotlinFileType.INSTANCE }
}
private fun searchPropertyAccessorMethods(origin: KtParameter) {
origin.toLightElements().forEach { searchNamedElement(it) }
}
private fun searchNamedElement(queryParameters: ReferencesSearch.SearchParameters,
element: PsiNamedElement?,
name: String? = element?.name) {
private fun processKtClassOrObject(element: KtClassOrObject) {
val className = element.name ?: return
val lightClass = element.toLightClass() ?: return
searchNamedElement(lightClass, className)
if (element is KtObjectDeclaration && element.isCompanion()) {
LightClassUtil.getLightFieldForCompanionObject(element)?.let { searchNamedElement(it) }
if (kotlinOptions.acceptCompanionObjectMembers) {
val originLightClass = element.getStrictParentOfType<KtClass>()?.toLightClass()
if (originLightClass != null) {
val lightDeclarations: List<KtLightElement<*, *>?> =
originLightClass.methods.map { it as? KtLightMethod } +
originLightClass.fields.map { it as? KtLightField }
for (declaration in element.declarations) {
lightDeclarations
.firstOrNull { it?.kotlinOrigin == declaration }
?.let { searchNamedElement(it) }
}
}
}
}
}
private fun searchDataClassComponentUsages(containingClass: PsiClass?,
componentFunctionDescriptor: FunctionDescriptor,
kotlinOptions: KotlinReferencesSearchOptions
) {
val componentFunction = containingClass?.methods?.firstOrNull {
it.name == componentFunctionDescriptor.name.asString() && it.parameterList.parametersCount == 0
}
if (componentFunction != null) {
searchNamedElement(componentFunction)
val searcher = OperatorReferenceSearcher.create(
componentFunction, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer, kotlinOptions)
longTasks.add { searcher!!.run() }
}
}
private fun isOnlyKotlinSearch(searchScope: SearchScope): Boolean {
return searchScope is LocalSearchScope && searchScope.scope.all { it.containingFile is KtFile }
}
private fun processStaticsFromCompanionObject(element: KtDeclaration) {
findStaticMethodsFromCompanionObject(element).forEach { searchNamedElement(it) }
}
private fun findStaticMethodsFromCompanionObject(declaration: KtDeclaration): List<PsiMethod> {
val originObject = declaration.parents
.dropWhile { it is KtClassBody }
.firstOrNull() as? KtObjectDeclaration ?: return emptyList()
if (!originObject.isCompanion()) return emptyList()
val originClass = originObject.getStrictParentOfType<KtClass>()
val originLightClass = originClass?.toLightClass() ?: return emptyList()
val allMethods = originLightClass.allMethods
return allMethods.filter { it is KtLightMethod && it.kotlinOrigin == declaration }
}
private fun searchNamedElement(element: PsiNamedElement?, name: String? = element?.name) {
if (name != null && element != null) {
val scope = runReadAction { queryParameters.effectiveSearchScope(element) }
val scope = queryParameters.effectiveSearchScope(element)
val context = UsageSearchContext.IN_CODE + UsageSearchContext.IN_FOREIGN_LANGUAGES + UsageSearchContext.IN_COMMENTS
val kotlinOptions = (queryParameters as? KotlinReferencesSearchParameters)?.kotlinOptions
?: KotlinReferencesSearchOptions.Empty
val resultProcessor = KotlinRequestResultProcessor(element,
queryParameters.elementToSearch.namedUnwrappedElement ?: element,
options = kotlinOptions)
queryParameters.optimizer.searchWord(name, scope, context.toShort(), true, element,
resultProcessor)
queryParameters.optimizer.searchWord(name, scope, context.toShort(), true, element, resultProcessor)
}
}
private fun PsiReference.isNamedArgumentReference(): Boolean {
return this is KtSimpleNameReference && expression.parent is KtValueArgumentName
}
}
}
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchPar
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
@@ -78,18 +79,20 @@ class ExpressionsOfTypeProcessor(
var testLog: MutableList<String>? = null
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 -> fqName + element.parameterList.text
is KtFunction -> fqName + element.valueParameterList!!.text
is KtParameter -> {
val owner = element.ownerFunction?.let { logPresentation(it) } ?: element.parent.toString()
"parameter ${element.name} of $owner"
return runReadAction {
if (element !is KtDeclaration && element !is PsiMember) return@runReadAction element.text
val fqName = element.getKotlinFqName()?.asString()
?: (element as? KtNamedDeclaration)?.name
when (element) {
is PsiMethod -> fqName + element.parameterList.text
is KtFunction -> fqName + element.valueParameterList!!.text
is KtParameter -> {
val owner = element.ownerFunction?.let { logPresentation(it) } ?: element.parent.toString()
"parameter ${element.name} of $owner"
}
is KtDestructuringDeclaration -> element.entries.joinToString(", ", prefix = "(", postfix = ")") { it.text }
else -> fqName
}
is KtDestructuringDeclaration -> element.entries.joinToString(", ", prefix = "(", postfix = ")") { it.text }
else -> fqName
}
}
@@ -118,18 +121,10 @@ class ExpressionsOfTypeProcessor(
private val scopesToUsePlainSearch = LinkedHashMap<KtFile, ArrayList<PsiElement>>()
fun run() {
if (searchScope.restrictToKotlinSources().isEmpty()) return // optimization
val classDescriptor = typeToSearch.type.constructor.declarationDescriptor ?: return
val classDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor)
val psiClass = when (classDeclaration) {
is PsiClass -> classDeclaration
is KtClassOrObject -> classDeclaration.toLightClass() ?: return
else -> return
}
val psiClass = runReadAction { detectClassToSearch() } ?: return
// for class from library always use plain search because we cannot search usages in compiled code (we could though)
if (!ProjectRootsUtil.isInProjectSource(psiClass)) {
if (!runReadAction { psiClass.isValid && ProjectRootsUtil.isInProjectSource (psiClass) }) {
suspiciousScopeHandler(searchScope)
return
}
@@ -138,9 +133,26 @@ class ExpressionsOfTypeProcessor(
processTasks()
val scopeElements = scopesToUsePlainSearch.values.flatMap { it }.toTypedArray()
if (scopeElements.isNotEmpty()) {
suspiciousScopeHandler(LocalSearchScope(scopeElements))
runReadAction {
val scopeElements = scopesToUsePlainSearch.values
.flatMap { it }
.filter { it.isValid }
.toTypedArray()
if (scopeElements.isNotEmpty()) {
suspiciousScopeHandler(LocalSearchScope(scopeElements))
}
}
}
private fun detectClassToSearch(): PsiClass? {
if (searchScope.restrictToKotlinSources().isEmpty()) return null // optimization
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
}
}
@@ -167,19 +179,19 @@ class ExpressionsOfTypeProcessor(
data class ProcessClassUsagesTask(val classToSearch: PsiClass) : Task {
override fun perform() {
testLog?.add("Searched references to ${logPresentation(classToSearch)}")
ReferencesSearch.search(classToSearch).forEach(Processor processor@ { reference -> //TODO: see KT-13607
if (processClassUsage(reference)) return@processor true
searchReferences(classToSearch, GlobalSearchScope.allScope(project)) { reference ->
if (processClassUsage(reference)) return@searchReferences true
if (mode != Mode.ALWAYS_SMART) {
downShiftToPlainSearch()
return@processor false
return@searchReferences false
}
val element = reference.element
val document = PsiDocumentManager.getInstance(project).getDocument(element.containingFile)
val lineAndCol = DiagnosticUtils.offsetToLineAndColumn(document, element.startOffset)
error("Unsupported reference: '${element.text}' in ${element.containingFile.name} line ${lineAndCol.line} column ${lineAndCol.column}")
})
}
// we must use plain search inside our class (and inheritors) because implicit 'this' can happen anywhere
(classToSearch as? KtLightClass)?.kotlinOrigin?.let { usePlainSearch(it) }
@@ -211,7 +223,7 @@ class ExpressionsOfTypeProcessor(
testLog?.add("Searched references to ${logPresentation(declaration)} in Kotlin files")
val searchParameters = KotlinReferencesSearchParameters(
declaration, scope, kotlinOptions = KotlinReferencesSearchOptions(searchNamedArguments = false))
ReferencesSearch.search(searchParameters).forEach { reference ->
searchReferences(searchParameters) { reference ->
when (kind) {
CallableToProcessKind.HAS_OUR_CLASS_TYPE -> {
if (reference is KtDestructuringDeclarationReference) {
@@ -227,6 +239,7 @@ class ExpressionsOfTypeProcessor(
(reference.element as? KtReferenceExpression)?.let { processLambdasForCallableReference(it) }
}
}
true
}
}
}
@@ -239,13 +252,14 @@ class ExpressionsOfTypeProcessor(
//TODO: what about other JVM languages?
val scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(project), JavaFileType.INSTANCE)
testLog?.add("Searched references to ${logPresentation(psiClass)} in java files")
ReferencesSearch.search(psiClass, scope).forEach { reference ->
searchReferences(psiClass, scope) { reference ->
// check if the reference is method parameter type
val parameter = ((reference as? PsiJavaCodeReferenceElement)?.parent as? PsiTypeElement)?.parent as? PsiParameter
val method = parameter?.declarationScope as? PsiMethod
if (method != null) {
addCallableDeclarationToProcess(method, CallableToProcessKind.PROCESS_LAMBDAS)
}
true
}
}
}
@@ -559,24 +573,30 @@ class ExpressionsOfTypeProcessor(
}
private fun usePlainSearch(scope: KtElement) {
val file = scope.getContainingKtFile()
val restricted = LocalSearchScope(scope).intersectWith(searchScope)
if (restricted is LocalSearchScope) {
ScopeLoop@
for (element in restricted.scope) {
val prevElements = scopesToUsePlainSearch.getOrPut(file) { ArrayList() }
for ((index, prevElement) in prevElements.withIndex()) {
if (prevElement.isAncestor(element, strict = false)) continue@ScopeLoop
if (element.isAncestor(prevElement)) {
prevElements[index] = element
continue@ScopeLoop
runReadAction {
if (!scope.isValid) return@runReadAction
val file = scope.getContainingKtFile()
val restricted = LocalSearchScope(scope).intersectWith(searchScope)
if (restricted is LocalSearchScope) {
ScopeLoop@
for (element in restricted.scope) {
val prevElements = scopesToUsePlainSearch.getOrPut(file) { ArrayList() }
for ((index, prevElement) in prevElements.withIndex()) {
if (!prevElement.isValid) continue@ScopeLoop
if (prevElement.isAncestor(element, strict = false)) continue@ScopeLoop
if (element.isAncestor(prevElement)) {
prevElements[index] = element
continue@ScopeLoop
}
}
prevElements.add(element)
}
prevElements.add(element)
}
}
else {
assert(restricted == GlobalSearchScope.EMPTY_SCOPE)
else {
assert(restricted == GlobalSearchScope.EMPTY_SCOPE)
}
}
}
@@ -642,4 +662,22 @@ class ExpressionsOfTypeProcessor(
else -> false
}
}
private fun searchReferences(element: PsiElement,scope: SearchScope, processor: (PsiReference) -> Boolean) {
val parameters = ReferencesSearch.SearchParameters(element, scope, false)
searchReferences(parameters, processor)
}
private fun searchReferences(parameters: ReferencesSearch.SearchParameters, processor: (PsiReference) -> Boolean) {
ReferencesSearch.search(parameters).forEach(Processor { ref ->
runReadAction {
if (ref.element.isValid) {
processor(ref)
}
else {
true
}
}
})
}
}
@@ -79,6 +79,21 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector,
options: KotlinReferencesSearchOptions
): OperatorReferenceSearcher<*>? {
return runReadAction {
if (declaration.isValid)
_create(declaration, searchScope, consumer, optimizer, options)
else
null
}
}
private fun _create(
declaration: PsiElement,
searchScope: SearchScope,
consumer: Processor<PsiReference>,
optimizer: SearchRequestCollector,
options: KotlinReferencesSearchOptions
): OperatorReferenceSearcher<*>? {
val functionName = when (declaration) {
is KtNamedFunction -> declaration.name
@@ -212,16 +227,20 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
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)
}
runReadAction {
if (element.isValid) {
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)
super.visitElement(element)
}
})
}
})
}
}
}
else {
@@ -238,7 +257,7 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
val psiManager = PsiManager.getInstance(project)
ProjectRootManager.getInstance(project).fileIndex.iterateContent { file ->
if (file in scope) {
val ktFile = psiManager.findFile(file) as? KtFile
val ktFile = runReadAction { psiManager.findFile(file) as? KtFile }
if (ktFile != null) {
doPlainSearch(LocalSearchScope(ktFile))
}
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
import org.jetbrains.kotlin.idea.references.unwrappedTargets
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.contains
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
@@ -39,6 +40,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.utils.addToStdlib.check
val KtDeclaration.descriptor: DeclarationDescriptor?
get() = this.analyze().get(BindingContext.DECLARATION_TO_DESCRIPTOR, this)
@@ -109,36 +111,40 @@ private fun KtElement.getConstructorCallDescriptor(): DeclarationDescriptor? {
return null
}
fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (KtCallElement) -> Boolean): Boolean {
if (!processDelegationCallKotlinConstructorUsages(scope, process)) return false
return processDelegationCallJavaConstructorUsages(scope, process)
// should be executed under read-action, returns long-running part to be executed outside read-action
fun PsiElement.buildProcessDelegationCallConstructorUsagesTask(scope: SearchScope, process: (KtCallElement) -> Boolean): () -> Boolean {
val task1 = buildProcessDelegationCallKotlinConstructorUsagesTask(scope, process)
val task2 = buildProcessDelegationCallJavaConstructorUsagesTask(scope, process)
return { task1() && task2() }
}
private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: SearchScope, process: (KtCallElement) -> Boolean): Boolean {
private fun PsiElement.buildProcessDelegationCallKotlinConstructorUsagesTask(scope: SearchScope, process: (KtCallElement) -> Boolean): () -> Boolean {
val element = unwrapped
if (element != null && element !in scope) return true
if (element != null && element !in scope) return { true }
val klass = when (element) {
is KtConstructor<*> -> element.getContainingClassOrObject()
is KtClass -> element
else -> return true
else -> return { true }
}
if (klass !is KtClass || element !is KtDeclaration) return true
val descriptor = element.constructor ?: return true
if (klass !is KtClass || element !is KtDeclaration) return { true }
val descriptor = element.constructor ?: return { true }
if (!processClassDelegationCallsToSpecifiedConstructor(klass, descriptor, process)) return false
return processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process)
if (!processClassDelegationCallsToSpecifiedConstructor(klass, descriptor, process)) return { false }
// long-running task, return it to execute outside read-action
return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) }
}
private fun PsiElement.processDelegationCallJavaConstructorUsages(scope: SearchScope, process: (KtCallElement) -> Boolean): Boolean {
if (this is KtLightElement<*, *>) return true
private fun PsiElement.buildProcessDelegationCallJavaConstructorUsagesTask(scope: SearchScope, process: (KtCallElement) -> Boolean): () -> Boolean {
if (this is KtLightElement<*, *>) return { true }
// TODO: Temporary hack to avoid NPE while KotlinNoOriginLightMethod is around
if (this is KtLightMethod && this.kotlinOrigin == null) return true
if (!(this is PsiMethod && isConstructor)) return true
val klass = containingClass ?: return true
val descriptor = getJavaMethodDescriptor() as? ConstructorDescriptor ?: return true
return processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process)
if (this is KtLightMethod && this.kotlinOrigin == null) return { true }
if (!(this is PsiMethod && isConstructor)) return { true }
val klass = containingClass ?: return { true }
val descriptor = getJavaMethodDescriptor() as? ConstructorDescriptor ?: return { true }
return { processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) }
}
@@ -149,11 +155,13 @@ private fun processInheritorsDelegatingCallToSpecifiedConstructor(
process: (KtCallElement) -> Boolean
): Boolean {
return HierarchySearchRequest(klass, scope, false).searchInheritors().all {
val unwrapped = it.unwrapped
if (unwrapped is KtClass) {
processClassDelegationCallsToSpecifiedConstructor(unwrapped, descriptor, process)
} else
true
runReadAction {
val unwrapped = it.check { it.isValid }?.unwrapped
if (unwrapped is KtClass)
processClassDelegationCallsToSpecifiedConstructor(unwrapped, descriptor, process)
else
true
}
}
}
@@ -22,8 +22,6 @@ import com.intellij.find.findUsages.JavaFindUsagesHelper
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.PsiElementProcessor
import com.intellij.psi.search.PsiElementProcessorAdapter
import com.intellij.psi.search.searches.ReferencesSearch
@@ -44,9 +42,10 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchPar
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.search.usagesSearch.isConstructorUsage
import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.idea.search.usagesSearch.buildProcessDelegationCallConstructorUsagesTask
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.contains
import org.jetbrains.kotlin.psi.psiUtil.effectiveDeclarations
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -71,19 +70,60 @@ class KotlinFindClassUsagesHandler(
this)
}
override fun searchReferences(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Boolean {
val kotlinOptions = options as KotlinClassFindUsagesOptions
override fun createSearcher(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Searcher {
return MySearcher(element, processor, options)
}
fun processInheritors(): Boolean {
val request = HierarchySearchRequest(element, options.searchScope, options.isCheckDeepInheritance)
return runReadAction {
private class MySearcher(
element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions
) : Searcher(element, processor, options) {
private val kotlinOptions = options as KotlinClassFindUsagesOptions
private val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor)
override fun buildTaskList(): Boolean {
val classOrObject = element as KtClassOrObject
if (kotlinOptions.isUsages || kotlinOptions.searchConstructorUsages) {
processClassReferencesLater(classOrObject)
}
if (kotlinOptions.isFieldsUsages || kotlinOptions.isMethodsUsages) {
processMemberReferencesLater(classOrObject)
}
if (kotlinOptions.isUsages && classOrObject is KtObjectDeclaration && classOrObject.isCompanion() && classOrObject in options.searchScope ) {
if (!processCompanionObjectInternalReferences(classOrObject)) return false
}
if (kotlinOptions.searchConstructorUsages) {
classOrObject.toLightClass()?.constructors?.filterIsInstance<KtLightMethod>()?.forEach { constructor ->
val scope = constructor.useScope.intersectWith(options.searchScope)
val task = constructor.buildProcessDelegationCallConstructorUsagesTask(scope) {
it.calleeExpression?.mainReference?.let { referenceProcessor.process(it) } ?: false
}
addTask(task)
}
}
if (kotlinOptions.isDerivedClasses || kotlinOptions.isDerivedInterfaces) {
processInheritorsLater()
}
return true
}
private fun processInheritorsLater() {
val request = HierarchySearchRequest(element, options.searchScope, kotlinOptions.isCheckDeepInheritance)
addTask {
request.searchInheritors().forEach(
PsiElementProcessorAdapter(
object : PsiElementProcessor<PsiClass> {
override fun execute(element: PsiClass): Boolean {
PsiElementProcessor<PsiClass> { element ->
runReadAction {
if (!element.isValid) return@runReadAction false
val isInterface = element.isInterface
return when {
isInterface && options.isDerivedInterfaces || !isInterface && options.isDerivedClasses ->
when {
isInterface && kotlinOptions.isDerivedInterfaces || !isInterface && kotlinOptions.isDerivedClasses ->
KotlinFindUsagesHandler.processUsage(processor, element.navigationElement)
else -> true
}
@@ -94,101 +134,49 @@ class KotlinFindClassUsagesHandler(
}
}
val classOrObject = element as KtClassOrObject
val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor)
private fun processClassReferencesLater(classOrObject: KtClassOrObject) {
val searchParameters = KotlinReferencesSearchParameters(classOrObject,
scope = options.searchScope,
kotlinOptions = KotlinReferencesSearchOptions(acceptCompanionObjectMembers = true))
var usagesQuery = ReferencesSearch.search(searchParameters)
if (kotlinOptions.isUsages || kotlinOptions.searchConstructorUsages) {
if (!processClassReferences(classOrObject, kotlinOptions, referenceProcessor)) return false
}
if (kotlinOptions.isFieldsUsages || kotlinOptions.isMethodsUsages) {
if (!processMemberReferences(classOrObject, kotlinOptions, referenceProcessor)) return false
}
if (kotlinOptions.isUsages && classOrObject is KtObjectDeclaration && classOrObject.isCompanion() && classOrObject in options.searchScope) {
if (!processCompanionObjectInternalReferences(classOrObject, referenceProcessor)) {
return false
if (kotlinOptions.isSkipImportStatements) {
usagesQuery = FilteredQuery(usagesQuery) { !it.isImportUsage() }
}
}
if (kotlinOptions.searchConstructorUsages) {
val result = runReadAction {
val constructors = classOrObject.toLightClass()?.constructors ?: PsiMethod.EMPTY_ARRAY
constructors.filterIsInstance<KtLightMethod>().all { constructor ->
constructor.processDelegationCallConstructorUsages(constructor.useScope.intersectWith(options.searchScope)) {
it.calleeExpression?.mainReference?.let { referenceProcessor.process(it) } ?: false
}
}
if (!kotlinOptions.searchConstructorUsages) {
usagesQuery = FilteredQuery(usagesQuery) { !it.isConstructorUsage(classOrObject) }
}
if (!result) return false
else if (!options.isUsages) {
usagesQuery = FilteredQuery(usagesQuery) { it.isConstructorUsage(classOrObject) }
}
addTask { usagesQuery.forEach(referenceProcessor) }
}
if (options.isDerivedClasses || options.isDerivedInterfaces) {
if (!processInheritors()) return false
}
return true
}
private fun processClassReferences(classOrObject: KtClassOrObject,
options: KotlinClassFindUsagesOptions,
processor: Processor<PsiReference>): Boolean {
val searchParameters = KotlinReferencesSearchParameters(classOrObject,
scope = options.searchScope,
kotlinOptions = KotlinReferencesSearchOptions(acceptCompanionObjectMembers = true))
var usagesQuery = ReferencesSearch.search(searchParameters)
if (options.isSkipImportStatements) {
usagesQuery = FilteredQuery(usagesQuery) { !it.isImportUsage() }
}
if (!options.searchConstructorUsages) {
usagesQuery = FilteredQuery(usagesQuery) { !it.isConstructorUsage(classOrObject) }
}
else if (!options.isUsages) {
usagesQuery = FilteredQuery(usagesQuery) { it.isConstructorUsage(classOrObject) }
}
return usagesQuery.forEach(processor)
}
private fun processCompanionObjectInternalReferences(companionObject: KtObjectDeclaration,
processor: Processor<PsiReference>): Boolean {
var stop: Boolean = false
runReadAction {
val klass = companionObject.getStrictParentOfType<KtClass>() ?: return@runReadAction
private fun processCompanionObjectInternalReferences(companionObject: KtObjectDeclaration): Boolean {
val klass = companionObject.getStrictParentOfType<KtClass>() ?: return true
val companionObjectDescriptor = companionObject.descriptor
klass.acceptChildren(object : KtVisitorVoid() {
override fun visitKtElement(element: KtElement) {
if (element == companionObject) return // skip companion object itself
if (stop) return
element.acceptChildren(this)
return !klass.anyDescendantOfType<KtElement>(fun (element: KtElement): Boolean {
if (element == companionObject) return false // skip companion object itself
val bindingContext = element.analyze()
val resolvedCall = bindingContext[BindingContext.CALL, element]?.getResolvedCall(bindingContext) ?: return
if ((resolvedCall.dispatchReceiver as? ImplicitClassReceiver)?.declarationDescriptor == companionObjectDescriptor
|| (resolvedCall.extensionReceiver as? ImplicitClassReceiver)?.declarationDescriptor == companionObjectDescriptor) {
element.references.forEach {
if (!stop && !processor.process(it)) {
stop = true
}
}
}
val bindingContext = element.analyze()
val resolvedCall = bindingContext[BindingContext.CALL, element]?.getResolvedCall(bindingContext) ?: return false
if ((resolvedCall.dispatchReceiver as? ImplicitClassReceiver)?.declarationDescriptor == companionObjectDescriptor
|| (resolvedCall.extensionReceiver as? ImplicitClassReceiver)?.declarationDescriptor == companionObjectDescriptor) {
return element.references.any { !referenceProcessor.process(it) }
}
return false
})
}
return !stop
}
private fun processMemberReferences(classOrObject: KtClassOrObject,
options: KotlinClassFindUsagesOptions,
processor: Processor<PsiReference>): Boolean {
for (decl in classOrObject.effectiveDeclarations()) {
if ((decl is KtNamedFunction && options.isMethodsUsages) ||
((decl is KtProperty || decl is KtParameter) && options.isFieldsUsages)) {
if (!ReferencesSearch.search(decl, options.searchScope).forEach(processor)) return false
private fun processMemberReferencesLater(classOrObject: KtClassOrObject) {
for (declaration in classOrObject.effectiveDeclarations()) {
if ((declaration is KtNamedFunction && kotlinOptions.isMethodsUsages) ||
((declaration is KtProperty || declaration is KtParameter) && kotlinOptions.isFieldsUsages)) {
addTask { ReferencesSearch.search(declaration, options.searchScope).forEach(referenceProcessor) }
}
}
}
return true
}
override fun getStringsToSearch(element: PsiElement): Collection<String> {
@@ -54,6 +54,7 @@ import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.utils.addToStdlib.check
abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
protected constructor(declaration: T, elementsToSearch: Collection<PsiElement>, factory: KotlinFindUsagesHandlerFactory)
@@ -98,7 +99,7 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
}
override fun applyQueryFilters(element: PsiElement, options: FindUsagesOptions, query: Query<PsiReference>): Query<PsiReference> {
var kotlinOptions = options as KotlinPropertyFindUsagesOptions
val kotlinOptions = options as KotlinPropertyFindUsagesOptions
if (!kotlinOptions.isReadAccess && !kotlinOptions.isWriteAccess) {
return EmptyQuery()
@@ -127,53 +128,67 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
}
}
override fun searchReferences(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Boolean {
val kotlinOptions = options as KotlinCallableFindUsagesOptions
val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor)
val uniqueProcessor = CommonProcessors.UniqueProcessor(processor)
if (options.isUsages) {
val kotlinSearchOptions = createKotlinReferencesSearchOptions(options)
val searchParameters = KotlinReferencesSearchParameters(element, options.searchScope, kotlinOptions = kotlinSearchOptions)
with(applyQueryFilters(element, options, ReferencesSearch.search(searchParameters))) {
if (!forEach(referenceProcessor)) return false
}
for (psiMethod in runReadAction { element.toLightMethods() }) {
var searchScope = options.searchScope
// TODO: very bad code!! ReferencesSearch does not work correctly for constructors and annotation parameters
if (element is KtNamedFunction || (element is KtParameter && element.dataClassComponentFunction() != null)) {
searchScope = searchScope.excludeKotlinSources()
}
with(applyQueryFilters(element, options, MethodReferencesSearch.search(psiMethod, searchScope, true))) {
if (!forEach(referenceProcessor)) return false
}
}
}
if (kotlinOptions.searchOverrides) {
for (method in HierarchySearchRequest(element, options.searchScope, true).searchOverriders()) {
if (!KotlinFindUsagesHandler.processUsage(uniqueProcessor, method.navigationElement)) break
}
}
return true
override fun createSearcher(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Searcher {
return MySearcher(element, processor, options)
}
private fun SearchScope.excludeKotlinSources(): SearchScope {
if (this is GlobalSearchScope) {
val fileTypes = FileTypeManager.getInstance().registeredFileTypes.filter { it != KotlinFileType.INSTANCE }.toTypedArray()
return GlobalSearchScope.getScopeRestrictedByFileTypes(this, *fileTypes)
private inner class MySearcher(
element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions
) : Searcher(element, processor, options) {
private val kotlinOptions = options as KotlinCallableFindUsagesOptions
override fun buildTaskList(): Boolean {
val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor)
val uniqueProcessor = CommonProcessors.UniqueProcessor(processor)
if (options.isUsages) {
val kotlinSearchOptions = createKotlinReferencesSearchOptions(options)
val searchParameters = KotlinReferencesSearchParameters(element, options.searchScope, kotlinOptions = kotlinSearchOptions)
applyQueryFilters(element, options, ReferencesSearch.search(searchParameters)).let { query ->
addTask { query.forEach(referenceProcessor) }
}
for (psiMethod in element.toLightMethods()) {
var searchScope = options.searchScope
// TODO: very bad code!! ReferencesSearch does not work correctly for constructors and annotation parameters
if (element is KtNamedFunction || (element is KtParameter && element.dataClassComponentFunction() != null)) {
searchScope = searchScope.excludeKotlinSources()
}
applyQueryFilters(element, options, MethodReferencesSearch.search(psiMethod, searchScope, true)).let { query ->
addTask { query.forEach(referenceProcessor) }
}
}
}
if (kotlinOptions.searchOverrides) {
addTask {
val overriders = HierarchySearchRequest(element, options.searchScope, true).searchOverriders()
overriders.all {
val element = runReadAction { it.check { it.isValid }?.navigationElement } ?: return@all true
KotlinFindUsagesHandler.processUsage(uniqueProcessor, element)
}
}
}
return true
}
else {
this as LocalSearchScope
val filteredElements = scope.filter { it.containingFile !is KtFile }
return if (filteredElements.isNotEmpty())
LocalSearchScope(filteredElements.toTypedArray())
else
GlobalSearchScope.EMPTY_SCOPE
private fun SearchScope.excludeKotlinSources(): SearchScope {
if (this is GlobalSearchScope) {
val fileTypes = FileTypeManager.getInstance().registeredFileTypes.filter { it != KotlinFileType.INSTANCE }.toTypedArray()
return GlobalSearchScope.getScopeRestrictedByFileTypes(this, *fileTypes)
}
else {
this as LocalSearchScope
val filteredElements = scope.filter { it.containingFile !is KtFile }
return if (filteredElements.isNotEmpty())
LocalSearchScope(filteredElements.toTypedArray())
else
GlobalSearchScope.EMPTY_SCOPE
}
}
}
@@ -68,7 +68,13 @@ abstract class KotlinFindUsagesHandler<T : PsiElement>(psiElement: T,
return searchReferences(element, processor, options) && searchTextOccurrences(element, processor, options)
}
protected abstract fun searchReferences(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Boolean
protected fun searchReferences(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Boolean {
val searcher = createSearcher(element, processor, options)
if (!runReadAction { searcher.buildTaskList() }) return false
return searcher.executeTasks()
}
protected abstract fun createSearcher(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Searcher
override fun findReferencesToHighlight(target: PsiElement, searchScope: SearchScope): Collection<PsiReference> {
val results = Collections.synchronizedList(arrayListOf<PsiReference>())
@@ -86,6 +92,20 @@ abstract class KotlinFindUsagesHandler<T : PsiElement>(psiElement: T,
return results
}
protected abstract class Searcher(val element: PsiElement, val processor: Processor<UsageInfo>, val options: FindUsagesOptions) {
private val tasks = ArrayList<() -> Boolean>()
protected fun addTask(task: () -> Boolean) {
tasks.add(task)
}
fun executeTasks(): Boolean {
return tasks.all { it() }
}
abstract fun buildTaskList(): Boolean
}
companion object {
val LOG = Logger.getInstance(KotlinFindUsagesHandler::class.java)
@@ -39,8 +39,15 @@ class KotlinTypeParameterFindUsagesHandler(
)
}
override fun searchReferences(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Boolean {
return ReferencesSearch.search(element, options.searchScope).all { KotlinFindUsagesHandler.processUsage(processor, it ) }
override fun createSearcher(element: PsiElement, processor: Processor<UsageInfo>, options: FindUsagesOptions): Searcher {
return object: Searcher(element, processor, options) {
override fun buildTaskList(): Boolean {
addTask {
ReferencesSearch.search(element, options.searchScope).all { KotlinFindUsagesHandler.processUsage(processor, it ) }
}
return true
}
}
}
override fun getFindUsagesOptions(dataContext: DataContext?): FindUsagesOptions {
@@ -52,7 +52,7 @@ import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.idea.search.usagesSearch.buildProcessDelegationCallConstructorUsagesTask
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
@@ -311,13 +311,13 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
}
}
functionPsi.processDelegationCallConstructorUsages(functionPsi.useScope) {
functionPsi.buildProcessDelegationCallConstructorUsagesTask(functionPsi.useScope) {
when (it) {
is KtConstructorDelegationCall -> result.add(KotlinConstructorDelegationCallUsage(it, changeInfo))
is KtSuperTypeCallEntry -> result.add(KotlinFunctionCallUsage(it, functionUsageInfo))
}
true
}
}.invoke()
}
private fun processInternalReferences(functionUsageInfo: KotlinCallableDefinitionUsage<*>, visitor: KtTreeVisitor<BindingContext>) {
@@ -43,7 +43,7 @@ import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
import org.jetbrains.kotlin.idea.refactoring.formatClass
import org.jetbrains.kotlin.idea.refactoring.formatFunction
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.idea.search.usagesSearch.buildProcessDelegationCallConstructorUsagesTask
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
@@ -179,12 +179,12 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
else -> return
}
for (constructor in constructors) {
constructor.processDelegationCallConstructorUsages(constructor.useScope) {
constructor.buildProcessDelegationCallConstructorUsagesTask(constructor.useScope) {
if (!getIgnoranceCondition().value(it)) {
usages.add(SafeDeleteReferenceSimpleDeleteUsageInfo(it, element, false))
}
true
}
}.invoke()
}
}
@@ -20,15 +20,15 @@ import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.psi.PsiReference
import com.intellij.psi.search.searches.MethodReferencesSearch.SearchParameters
import com.intellij.util.Processor
import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages
import org.jetbrains.kotlin.idea.search.usagesSearch.buildProcessDelegationCallConstructorUsagesTask
class KotlinConstructorDelegationCallReferenceSearcher() : QueryExecutorBase<PsiReference, SearchParameters>(true) {
override fun processQuery(queryParameters: SearchParameters, consumer: Processor<PsiReference>) {
val method = queryParameters.method
if (!method.isConstructor) return
method.processDelegationCallConstructorUsages(method.useScope.intersectWith(queryParameters.effectiveSearchScope)) {
method.buildProcessDelegationCallConstructorUsagesTask(method.useScope.intersectWith(queryParameters.effectiveSearchScope)) {
it.calleeExpression?.reference?.let { consumer.process(it) } ?: true
}
}.invoke()
}
}
@@ -388,8 +388,6 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
options: FindUsagesOptions?,
highlightingMode: Boolean
): Collection<UsageInfo> {
@Suppress("NAME_SHADOWING")
var options = options
val project = project
val handler: FindUsagesHandler = (if (targetElement is PsiMember) {
@@ -402,21 +400,26 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
(FindManager.getInstance(project) as FindManagerImpl).findUsagesManager.getFindUsagesHandler(targetElement, false)
}) ?: error("Cannot find handler for: $targetElement")
if (options == null) {
options = handler.getFindUsagesOptions(null)
}
@Suppress("NAME_SHADOWING")
val options = options ?: handler.getFindUsagesOptions(null)
options.searchScope = GlobalSearchScope.allScope(project)
val processor = CommonProcessors.CollectProcessor<UsageInfo>()
for (psiElement in handler.primaryElements + handler.secondaryElements) {
if (highlightingMode) {
//TODO: should findReferencesToHighlight work outside read-action or it makes no sense?
for (reference in handler.findReferencesToHighlight(psiElement, options.searchScope)) {
processor.process(UsageInfo(reference))
}
}
else {
handler.processElementUsages(psiElement, processor, options)
// run in another thread to test read-action assertions
val thread = Thread {
handler.processElementUsages(psiElement, processor, options)
}
thread.start()
thread.join()
}
}