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