Implement search from expect element in KotlinReferenceSearcher

So mostly #KT-17512 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-06-20 19:58:33 +03:00
parent 97d158d833
commit e885e54233
18 changed files with 105 additions and 29 deletions
@@ -24,6 +24,7 @@ import com.intellij.psi.search.SearchRequestCollector
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.containers.nullize
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMember
@@ -42,8 +43,11 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.getClassNameForCompanionObj
import org.jetbrains.kotlin.idea.search.usagesSearch.operators.OperatorReferenceSearcher
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.expectedDeclarationIfAny
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.psi.psiUtil.parents
import java.util.*
@@ -54,7 +58,8 @@ data class KotlinReferencesSearchOptions(
val acceptCompanionObjectMembers: Boolean = false,
val searchForComponentConventions: Boolean = true,
val searchForOperatorConventions: Boolean = true,
val searchNamedArguments: Boolean = true
val searchNamedArguments: Boolean = true,
val searchForExpectedUsages: Boolean = true
) {
fun anyEnabled(): Boolean = acceptCallableOverrides || acceptOverloads || acceptExtensionsOfDeclarationClass
@@ -94,47 +99,55 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
val unwrappedElement = element.namedUnwrappedElement ?: return
val effectiveSearchScope = run {
val elements = if (unwrappedElement is KtDeclaration && !isOnlyKotlinSearch(queryParameters.scopeDeterminedByUser)) {
unwrappedElement.toLightElements()
val elementToSearch =
if (kotlinOptions.searchForExpectedUsages && unwrappedElement is KtDeclaration && unwrappedElement.hasActualModifier()) {
unwrappedElement.expectedDeclarationIfAny() as? PsiNamedElement
} else {
listOf(unwrappedElement)
}
null
} ?: unwrappedElement
val effectiveSearchScope = run {
val elements = if (elementToSearch is KtDeclaration && !isOnlyKotlinSearch(queryParameters.scopeDeterminedByUser)) {
elementToSearch.toLightElements().nullize()
} else {
null
} ?: listOf(elementToSearch)
elements.fold(queryParameters.effectiveSearchScope) { scope, e ->
scope.unionSafe(queryParameters.effectiveSearchScope(e))
}
}
val refFilter: (PsiReference) -> Boolean = when (unwrappedElement) {
val refFilter: (PsiReference) -> Boolean = when (elementToSearch) {
is KtParameter -> ({ ref: PsiReference -> !ref.isNamedArgumentReference()/* they are processed later*/ })
else -> ({ true })
}
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement, filter = refFilter, options = kotlinOptions)
val resultProcessor = KotlinRequestResultProcessor(elementToSearch, filter = refFilter, options = kotlinOptions)
val name = unwrappedElement.name
if (kotlinOptions.anyEnabled()) {
val name = elementToSearch.name
if (kotlinOptions.anyEnabled() || elementToSearch is KtNamedDeclaration && elementToSearch.isExpectDeclaration()) {
if (name != null) {
// Check difference with default scope
queryParameters.optimizer.searchWord(
name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement, resultProcessor
name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, elementToSearch, resultProcessor
)
}
}
val classNameForCompanionObject = unwrappedElement.getClassNameForCompanionObject()
val classNameForCompanionObject = elementToSearch.getClassNameForCompanionObject()
if (classNameForCompanionObject != null) {
queryParameters.optimizer.searchWord(
classNameForCompanionObject, effectiveSearchScope, UsageSearchContext.ANY, true, unwrappedElement, resultProcessor
classNameForCompanionObject, effectiveSearchScope, UsageSearchContext.ANY, true, elementToSearch, resultProcessor
)
}
if (unwrappedElement is KtParameter && kotlinOptions.searchNamedArguments) {
searchNamedArguments(unwrappedElement)
if (elementToSearch is KtParameter && kotlinOptions.searchNamedArguments) {
searchNamedArguments(elementToSearch)
}
if (!(unwrappedElement is KtElement && isOnlyKotlinSearch(effectiveSearchScope))) {
if (!(elementToSearch is KtElement && isOnlyKotlinSearch(effectiveSearchScope))) {
searchLightElements(element)
}
@@ -135,9 +135,14 @@ class KotlinFindClassUsagesHandler(
}
private fun processClassReferencesLater(classOrObject: KtClassOrObject) {
val searchParameters = KotlinReferencesSearchParameters(classOrObject,
scope = options.searchScope,
kotlinOptions = KotlinReferencesSearchOptions(acceptCompanionObjectMembers = true))
val searchParameters = KotlinReferencesSearchParameters(
classOrObject,
scope = options.searchScope,
kotlinOptions = KotlinReferencesSearchOptions(
acceptCompanionObjectMembers = true,
searchForExpectedUsages = kotlinOptions.searchExpected
)
)
var usagesQuery = ReferencesSearch.search(searchParameters)
if (kotlinOptions.isSkipImportStatements) {
@@ -146,8 +151,7 @@ class KotlinFindClassUsagesHandler(
if (!kotlinOptions.searchConstructorUsages) {
usagesQuery = FilteredQuery(usagesQuery) { !it.isConstructorUsage(classOrObject) }
}
else if (!options.isUsages) {
} else if (!options.isUsages) {
usagesQuery = FilteredQuery(usagesQuery) { it.isConstructorUsage(classOrObject) }
}
addTask { usagesQuery.forEach(referenceProcessor) }
@@ -83,7 +83,12 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration> protected c
override fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions {
val kotlinOptions = options as KotlinFunctionFindUsagesOptions
return KotlinReferencesSearchOptions(true, kotlinOptions.isIncludeOverloadUsages, kotlinOptions.isIncludeOverloadUsages)
return KotlinReferencesSearchOptions(
acceptCallableOverrides = true,
acceptOverloads = kotlinOptions.isIncludeOverloadUsages,
acceptExtensionsOfDeclarationClass = kotlinOptions.isIncludeOverloadUsages,
searchForExpectedUsages = kotlinOptions.searchExpected
)
}
override fun applyQueryFilters(element: PsiElement, options: FindUsagesOptions, query: Query<PsiReference>): Query<PsiReference> {
@@ -142,7 +147,13 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration> protected c
}
override fun createKotlinReferencesSearchOptions(options: FindUsagesOptions): KotlinReferencesSearchOptions {
return KotlinReferencesSearchOptions(true, false, false)
val kotlinOptions = options as KotlinPropertyFindUsagesOptions
return KotlinReferencesSearchOptions(
acceptCallableOverrides = true,
acceptOverloads = false,
acceptExtensionsOfDeclarationClass = false,
searchForExpectedUsages = kotlinOptions.searchExpected
)
}
}
@@ -0,0 +1,8 @@
fun test() {
My("a").boo()
My("b").boo()
}
expect class My(val s: String) {
fun boo()
}
@@ -0,0 +1,6 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtClass
// OPTIONS: usages, expected
actual class <caret>My(val s: String) {
actual fun boo() {}
}
@@ -0,0 +1,2 @@
[common.kt] New instance creation 2 My("a").boo()
[common.kt] New instance creation 3 My("b").boo()
@@ -1,4 +1,4 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtNamedFunction
// OPTIONS: usages
// OPTIONS: usages, expected
actual fun <caret>boo(s: String) {}
@@ -0,0 +1,2 @@
[common.kt] Function call 2 boo("a")
[common.kt] Function call 3 boo("b")
@@ -0,0 +1,8 @@
fun test() {
use(boo)
use(boo)
}
fun use(arg: Int) {}
expect val boo: Int
@@ -0,0 +1,4 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtProperty
// OPTIONS: usages, expected
actual val <caret>boo: Int get() = 42
@@ -0,0 +1,2 @@
[common.kt] Value read 2 use(boo)
[common.kt] Value read 3 use(boo)
@@ -1 +1 @@
function Foo.foo(Int) has 1 usage that is not safe to delete.
function Foo.foo(Int) has 2 usages that are not safe to delete.
@@ -1 +1 @@
property Foo.foo has 1 usage that is not safe to delete.
property Foo.foo has 2 usages that are not safe to delete.
@@ -1 +1 @@
constructor Foo(Int) has 1 usage that is not safe to delete.
constructor Foo(Int) has 2 usages that are not safe to delete.
@@ -1 +1 @@
function test.foo(Int) has 1 usage that is not safe to delete.
function test.foo(Int) has 2 usages that are not safe to delete.
@@ -1 +1 @@
property test.foo has 1 usage that is not safe to delete.
property test.foo has 2 usages that are not safe to delete.
@@ -22,11 +22,21 @@ import java.io.File
class FindUsagesMultiModuleTest : AbstractFindUsagesMultiModuleTest() {
@Test
fun testFindCommonClassFromActual() {
doTest()
}
@Test
fun testFindCommonFromActual() {
doTest()
}
@Test
fun testFindCommonPropertyFromActual() {
doTest()
}
@Test
fun testFindImplFromHeader() {
doTest()
@@ -41,6 +41,7 @@ internal enum class OptionsParser {
"derivedClasses" -> isDerivedClasses = true
"functionUsages" -> isMethodsUsages = true
"propertyUsages" -> isFieldsUsages = true
"expected" -> searchExpected = true
else -> throw IllegalStateException("Invalid option: " + s)
}
}
@@ -63,6 +64,10 @@ internal enum class OptionsParser {
isIncludeOverloadUsages = true
isUsages = true
}
"expected" -> {
searchExpected = true
isUsages = true
}
else -> throw IllegalStateException("Invalid option: " + s)
}
}
@@ -80,6 +85,7 @@ internal enum class OptionsParser {
"overrides" -> searchOverrides = true
"skipRead" -> isReadAccess = false
"skipWrite" -> isWriteAccess = false
"expected" -> searchExpected = true
else -> throw IllegalStateException("Invalid option: " + s)
}
}