Refactored reference searches to take read-actions when necessary
This commit is contained in:
+6
-6
@@ -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,
|
||||
|
||||
+173
-182
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+83
-45
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+28
-9
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user