Implement search from expect element in KotlinReferenceSearcher
So mostly #KT-17512 Fixed
This commit is contained in:
+29
-16
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -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) }
|
||||
|
||||
+13
-2
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -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() {}
|
||||
}
|
||||
+2
@@ -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")
|
||||
+8
@@ -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
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
[common.kt] Value read 2 use(boo)
|
||||
[common.kt] Value read 3 use(boo)
|
||||
+1
-1
@@ -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
@@ -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.
|
||||
Vendored
+1
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user