diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt index 8e0fe1577c5..1cdcb035f11 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinReferencesSearcher.kt @@ -22,7 +22,6 @@ import com.intellij.psi.* import com.intellij.psi.impl.cache.CacheManager import com.intellij.psi.search.* import com.intellij.psi.search.searches.ReferencesSearch -import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.Processor import org.jetbrains.kotlin.asJava.KotlinLightMethod import org.jetbrains.kotlin.asJava.KotlinLightParameter @@ -31,24 +30,37 @@ import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.idea.references.JetSimpleNameReference import org.jetbrains.kotlin.idea.search.KOTLIN_NAMED_ARGUMENT_SEARCH_CONTEXT -import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchLocation -import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction -import org.jetbrains.kotlin.idea.search.usagesSearch.getClassNameForCompanionObject -import org.jetbrains.kotlin.idea.search.usagesSearch.getSpecialNamesToSearch +import org.jetbrains.kotlin.idea.search.usagesSearch.* import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope -import org.jetbrains.kotlin.idea.util.ProjectRootsUtil 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 +data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean, + val acceptOverloads: Boolean, + val acceptExtensionsOfDeclarationClass: Boolean) { + fun anyEnabled(): Boolean = acceptCallableOverrides || acceptOverloads || acceptExtensionsOfDeclarationClass + + companion object { + val Empty = KotlinReferencesSearchOptions(false, false, false) + } +} + +public class KotlinReferencesSearchParameters(elementToSearch: PsiElement, + scope: SearchScope, + ignoreAccessScope: Boolean, + optimizer: SearchRequestCollector?, + val kotlinOptions: KotlinReferencesSearchOptions) + : ReferencesSearch.SearchParameters(elementToSearch, scope, ignoreAccessScope, optimizer) { +} + public class KotlinReferencesSearcher : QueryExecutorBase() { override fun processQuery(queryParameters: ReferencesSearch.SearchParameters, consumer: Processor) { val element = queryParameters.getElementToSearch() - val unwrappedElement = element.namedUnwrappedElement - if (unwrappedElement == null || !ProjectRootsUtil.isInProjectOrLibSource(unwrappedElement)) return + val unwrappedElement = element.namedUnwrappedElement ?: return val classNameForCompanionObject = unwrappedElement.getClassNameForCompanionObject() val words = unwrappedElement.getSpecialNamesToSearch() + @@ -61,10 +73,22 @@ public class KotlinReferencesSearcher : QueryExecutorBase queryParameters.optimizer.searchWord(word, effectiveSearchScope, UsagesSearchLocation.EVERYWHERE.searchContext, true, unwrappedElement, - MyRequestResultProcessor(unwrappedElement, refFilter)) + resultProcessor) } if (unwrappedElement is JetParameter) { @@ -91,12 +115,13 @@ public class KotlinReferencesSearcher : QueryExecutorBase Boolean + private val filter: (PsiReference) -> Boolean, + private val options: KotlinReferencesSearchOptions ) : RequestResultProcessor() { private val referenceService = PsiReferenceService.getService() @@ -116,11 +142,29 @@ public class KotlinReferencesSearcher : QueryExecutorBase true !ReferenceRange.containsOffsetInElement(ref, offsetInElement) -> true - !ref.isReferenceTo(unwrappedElement) -> true + !ref.isReferenceToTarget(unwrappedElement) -> true else -> consumer.process(ref) } } } + + private fun PsiReference.isReferenceToTarget(element: PsiElement): Boolean { + if (isReferenceTo(element)) { + return true + } + if (element is JetNamedDeclaration) { + if (options.acceptCallableOverrides && isCallableOverrideUsage(element)) { + return true + } + if (options.acceptOverloads && isUsageInContainingDeclaration(element)) { + return true + } + if (options.acceptExtensionsOfDeclarationClass && isExtensionOfDeclarationClassUsage(element)) { + return true + } + } + return false + } } companion object { @@ -194,7 +238,8 @@ public class KotlinReferencesSearcher : QueryExecutorBase(declaration, javaClass())) + val property = declaration.getStrictParentOfType() + searchNamedElement(queryParameters, property) } else if (declaration is JetFunction) { val staticFromCompanionObject = findStaticMethodFromCompanionObject(declaration) @@ -225,11 +270,16 @@ public class KotlinReferencesSearcher : QueryExecutorBase { public override fun execute(element: PsiClass): Boolean { - val traitOrInterface = element.isInterface() + val isInterface = element.isInterface() return when { - traitOrInterface && options.isDerivedInterfaces, !traitOrInterface && options.isDerivedClasses -> + isInterface && options.isDerivedInterfaces, !isInterface && options.isDerivedClasses -> KotlinFindUsagesHandler.processUsage(processor, element.getNavigationElement()) else -> true } @@ -86,26 +90,94 @@ public class KotlinFindClassUsagesHandler( } val classOrObject = element as JetClassOrObject + val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor) - val uniqueProcessor = CommonProcessors.UniqueProcessor(processor) + if (kotlinOptions.isUsages || kotlinOptions.searchConstructorUsages) { + if (!processClassReferences(classOrObject, kotlinOptions, referenceProcessor)) return false + } - return runReadAction { - val target = kotlinOptions.toSearchTarget(classOrObject, true) - val classUsages = kotlinOptions.toClassHelper().newRequest(target).search() - val declarationUsages = kotlinOptions.toClassDeclarationsHelper().newRequest(target).search() + if (kotlinOptions.isFieldsUsages || kotlinOptions.isMethodsUsages) { + if (!processMemberReferences(classOrObject, kotlinOptions, referenceProcessor)) return false + } - if (kotlinOptions.searchConstructorUsages) { - val constructors = classOrObject.toLightClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY - for (constructor in constructors) { - if (constructor !is KotlinLightMethod) continue - constructor.processDelegationCallConstructorUsages(constructor.getUseScope()) { - it.getCalleeExpression()?.mainReference?.let { KotlinFindUsagesHandler.processUsage(uniqueProcessor, it) } + if (kotlinOptions.isUsages && classOrObject is JetObjectDeclaration && classOrObject.isCompanion()) { + if (!processClassObjectInternalReferences(classOrObject, referenceProcessor)) { + return false + } + } + + if (kotlinOptions.searchConstructorUsages) { + val constructors = classOrObject.toLightClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY + for (constructor in constructors) { + if (constructor !is KotlinLightMethod) continue + constructor.processDelegationCallConstructorUsages(constructor.getUseScope()) { + it.getCalleeExpression()?.mainReference?.let { referenceProcessor.process(it) } + } + } + } + + if (options.isDerivedClasses || options.isDerivedInterfaces) { + if (!processInheritors()) return false + } + + return true + } + + private fun processClassReferences(classOrObject: JetClassOrObject, + options: KotlinClassFindUsagesOptions, + processor: Processor): Boolean { + var usagesQuery = ReferencesSearch.search(classOrObject) + + if (options.isSkipImportStatements) { + usagesQuery = FilteredQuery(usagesQuery) { !it.isImportUsage() } + } + + if (!options.searchConstructorUsages) { + usagesQuery = FilteredQuery(usagesQuery) { !it.isConstructorUsage(classOrObject) } + } + else if (!options.isUsages) { + usagesQuery = FilteredQuery(usagesQuery) { it.isConstructorUsage(classOrObject) } + } + return usagesQuery.forEach(processor) + } + + private fun processClassObjectInternalReferences(classObject: JetObjectDeclaration, + processor: Processor): Boolean { + val klass = classObject.getStrictParentOfType() ?: return true + val companionObjectDescriptor = classObject.descriptor + var stop: Boolean = false + klass.acceptChildren(object : JetVisitorVoid() { + override fun visitJetElement(element: JetElement) { + if (element == classObject) return // skip companion object itself + if (stop) return + element.acceptChildren(this) + + val bindingContext = element.analyze() + val call = bindingContext[BindingContext.CALL, element] ?: return + val resolvedCall = bindingContext[BindingContext.RESOLVED_CALL, call] ?: return + if ((resolvedCall.getDispatchReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor + || (resolvedCall.getExtensionReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor) { + element.getReferences().forEach { + if (!stop && !processor.process(it)) { + stop = true + } } } } + }) + return !stop + } - (classUsages + declarationUsages).all { KotlinFindUsagesHandler.processUsage(uniqueProcessor, it)} && processInheritors() + private fun processMemberReferences(classOrObject: JetClassOrObject, + options: KotlinClassFindUsagesOptions, + processor: Processor): Boolean { + for (decl in classOrObject.effectiveDeclarations()) { + if ((decl is JetNamedFunction && options.isMethodsUsages) || + ((decl is JetProperty || decl is JetParameter) && options.isFieldsUsages)) { + if (!ReferencesSearch.search(decl).forEach(processor)) return false + } } + return true } protected override fun getStringsToSearch(element: PsiElement): Collection { diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt index 3506afcdb26..e80595feeae 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindMemberUsagesHandler.kt @@ -16,29 +16,35 @@ package org.jetbrains.kotlin.idea.findUsages.handlers +import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector import com.intellij.find.findUsages.AbstractFindUsagesDialog import com.intellij.find.findUsages.FindUsagesOptions import com.intellij.openapi.actionSystem.DataContext import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiReference import com.intellij.psi.search.searches.MethodReferencesSearch +import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.usageView.UsageInfo -import com.intellij.util.CommonProcessors -import com.intellij.util.Processor +import com.intellij.util.* import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.toLightMethods -import org.jetbrains.kotlin.idea.findUsages.* +import org.jetbrains.kotlin.idea.findUsages.KotlinCallableFindUsagesOptions +import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory +import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions +import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindFunctionUsagesDialog import org.jetbrains.kotlin.idea.findUsages.dialogs.KotlinFindPropertyUsagesDialog +import org.jetbrains.kotlin.idea.search.allScope import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders -import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearch -import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchHelper +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters +import org.jetbrains.kotlin.idea.search.usagesSearch.isImportUsage import org.jetbrains.kotlin.idea.util.application.runReadAction -import org.jetbrains.kotlin.psi.JetConstructor import org.jetbrains.kotlin.psi.JetFunction import org.jetbrains.kotlin.psi.JetNamedDeclaration -import org.jetbrains.kotlin.psi.JetParameter public abstract class KotlinFindMemberUsagesHandler protected constructor(declaration: T, elementsToSearch: Collection, factory: KotlinFindUsagesHandlerFactory) @@ -48,10 +54,6 @@ public abstract class KotlinFindMemberUsagesHandler elementsToSearch: Collection, factory: KotlinFindUsagesHandlerFactory) : KotlinFindMemberUsagesHandler(declaration, elementsToSearch, factory) { - override fun getSearchHelper(options: KotlinCallableFindUsagesOptions): UsagesSearchHelper { - return (options as KotlinFunctionFindUsagesOptions).toHelper() - } - override fun getFindUsagesOptions(dataContext: DataContext?): FindUsagesOptions = factory.findFunctionOptions override fun getFindUsagesDialog(isSingleFile: Boolean, toShowInNewTab: Boolean, mustOpenInNewTab: Boolean): AbstractFindUsagesDialog { @@ -63,50 +65,83 @@ public abstract class KotlinFindMemberUsagesHandler return super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab) } + + override fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions { + val kotlinOptions = options as KotlinFunctionFindUsagesOptions + return KotlinReferencesSearchOptions(true, + kotlinOptions.isIncludeOverloadUsages, + kotlinOptions.isIncludeOverloadUsages) + } + + override fun applyQueryFilters(element: PsiElement, options: FindUsagesOptions, query: Query): Query { + val kotlinOptions = options as KotlinFunctionFindUsagesOptions + return query + .applyFilter(kotlinOptions.isSkipImportStatements) { !it.isImportUsage() } + } } private class Property(declaration: JetNamedDeclaration, elementsToSearch: Collection, factory: KotlinFindUsagesHandlerFactory) : KotlinFindMemberUsagesHandler(declaration, elementsToSearch, factory) { - override fun getSearchHelper(options: KotlinCallableFindUsagesOptions): UsagesSearchHelper { - return (options as KotlinPropertyFindUsagesOptions).toHelper() - } - override fun getFindUsagesOptions(dataContext: DataContext?): FindUsagesOptions = factory.findPropertyOptions override fun getFindUsagesDialog(isSingleFile: Boolean, toShowInNewTab: Boolean, mustOpenInNewTab: Boolean): AbstractFindUsagesDialog { return KotlinFindPropertyUsagesDialog(getElement(), getProject(), factory.findPropertyOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this) } - } - protected abstract fun getSearchHelper(options: KotlinCallableFindUsagesOptions): UsagesSearchHelper + override fun applyQueryFilters(element: PsiElement, options: FindUsagesOptions, query: Query): Query { + var kotlinOptions = options as KotlinPropertyFindUsagesOptions + + if (!kotlinOptions.isReadAccess && !kotlinOptions.isWriteAccess) { + return EmptyQuery() + } + + val result = query + .applyFilter(kotlinOptions.isSkipImportStatements) { !it.isImportUsage() } + + if (!kotlinOptions.isReadAccess || !kotlinOptions.isWriteAccess) { + val detector = KotlinReadWriteAccessDetector() + + return FilteredQuery(result) { + val access = detector.getReferenceAccess(element, it) + when (access) { + ReadWriteAccessDetector.Access.Read -> kotlinOptions.isReadAccess + ReadWriteAccessDetector.Access.Write -> kotlinOptions.isWriteAccess + ReadWriteAccessDetector.Access.ReadWrite -> true + } + } + } + return result + } + + override fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions { + return KotlinReferencesSearchOptions(true, false, false) + } + } override fun searchReferences(element: PsiElement, processor: Processor, options: FindUsagesOptions): Boolean { val kotlinOptions = options as KotlinCallableFindUsagesOptions - val request = runReadAction { - @suppress("UNCHECKED_CAST") - val searchTarget = if (element is JetParameter) - options.toSearchTarget(element as T, restrictByTarget = false) // named argument usages are outside getUseScope for JetParameter - else - options.toSearchTarget(element as T, restrictByTarget = true) - - getSearchHelper(kotlinOptions).newRequest(searchTarget) - } - + val referenceProcessor = KotlinFindUsagesHandler.createReferenceProcessor(processor) val uniqueProcessor = CommonProcessors.UniqueProcessor(processor) - for (ref in UsagesSearch.search(request)) { - KotlinFindUsagesHandler.processUsage(uniqueProcessor, ref) - } + if (options.isUsages) { + val searchParameters = KotlinReferencesSearchParameters(element, + element.project.allScope(), + ignoreAccessScope = false, + optimizer = null, + kotlinOptions = createKotlinReferencesSearchOptions(options)) - val psiMethod: PsiMethod? = when (element) { - is PsiMethod -> element - is JetConstructor<*> -> runReadAction { LightClassUtil.getLightClassMethod(element as JetFunction) } - else -> null - } - if (psiMethod != null) { - for (ref in MethodReferencesSearch.search(psiMethod, options.searchScope, true)) { - KotlinFindUsagesHandler.processUsage(uniqueProcessor, ref) + val query = applyQueryFilters(element, options, ReferencesSearch.search(searchParameters)) + if (!query.forEach(referenceProcessor)) return false + + val psiMethod: PsiMethod? = when (element) { + is PsiMethod -> element + is JetFunction -> runReadAction { LightClassUtil.getLightClassMethod(element) } + else -> null + } + if (psiMethod != null) { + val query = applyQueryFilters(element, options, MethodReferencesSearch.search(psiMethod, options.searchScope, true)) + if (!query.forEach(referenceProcessor)) return false } } @@ -119,6 +154,12 @@ public abstract class KotlinFindMemberUsagesHandler return true } + protected abstract fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions + + protected abstract fun applyQueryFilters(element: PsiElement, + options: FindUsagesOptions, + query: Query): Query + override fun isSearchForTextOccurencesAvailable(psiElement: PsiElement, isSingleFile: Boolean): Boolean = !isSingleFile companion object { @@ -133,3 +174,8 @@ public abstract class KotlinFindMemberUsagesHandler } } } + + +fun Query.applyFilter(flag: Boolean, condition: (PsiReference) -> Boolean): Query { + return if (flag) FilteredQuery(this, condition) else this +} diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindUsagesHandler.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindUsagesHandler.kt index 92ddf99f1cb..d577ae8b993 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindUsagesHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/handlers/KotlinFindUsagesHandler.kt @@ -18,19 +18,17 @@ package org.jetbrains.kotlin.idea.findUsages.handlers import com.intellij.find.findUsages.FindUsagesHandler import com.intellij.find.findUsages.FindUsagesOptions -import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiReference import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.SearchScope import com.intellij.usageView.UsageInfo +import com.intellij.util.CommonProcessors import com.intellij.util.Processor import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.KotlinReferenceUsageInfo import org.jetbrains.kotlin.idea.util.application.runReadAction - -import java.util.ArrayList -import java.util.Collections +import java.util.* public abstract class KotlinFindUsagesHandler(psiElement: T, private val elementsToSearch: Collection, @@ -100,5 +98,15 @@ public abstract class KotlinFindUsagesHandler(psiElement: T, val usageInfo = runReadAction(callback) return if (usageInfo != null) process(usageInfo) else true } + + protected fun createReferenceProcessor(usageInfoProcessor: Processor): Processor { + val uniqueProcessor = CommonProcessors.UniqueProcessor(usageInfoProcessor) + + return object: Processor { + override fun process(t: PsiReference): Boolean { + return KotlinFindUsagesHandler.processUsage(uniqueProcessor, t) + } + } + } } }