mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-05 08:31:31 +00:00
Quick search of equals operator usages
This commit is contained in:
@@ -192,14 +192,14 @@ object OperatorChecks : AbstractModifierChecks() {
|
||||
Checks(RANGE_TO, MemberOrExtension, SingleValueParameter, NoDefaultAndVarargsCheck),
|
||||
Checks(EQUALS, Member) {
|
||||
fun DeclarationDescriptor.isAny() = this is ClassDescriptor && KotlinBuiltIns.isAny(this)
|
||||
ensure(overriddenDescriptors.any { it.containingDeclaration.isAny() }) { "must override ''equals()'' in Any" }
|
||||
ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() }) { "must override ''equals()'' in Any" }
|
||||
},
|
||||
Checks(COMPARE_TO, MemberOrExtension, ReturnsInt, SingleValueParameter, NoDefaultAndVarargsCheck),
|
||||
Checks(BINARY_OPERATION_NAMES, MemberOrExtension, SingleValueParameter, NoDefaultAndVarargsCheck),
|
||||
Checks(SIMPLE_UNARY_OPERATION_NAMES, MemberOrExtension, NoValueParameters),
|
||||
Checks(listOf(INC, DEC), MemberOrExtension) {
|
||||
val receiver = dispatchReceiverParameter ?: extensionReceiverParameter
|
||||
ensure(receiver != null && (returnType?.let { it.isSubtypeOf(receiver.type) } ?: false)) {
|
||||
ensure(receiver != null && (returnType?.isSubtypeOf(receiver.type) ?: false)) {
|
||||
"receiver must be a supertype of the return type"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
@@ -33,8 +34,9 @@ class BinaryOperatorReferenceSearcher(
|
||||
private val operationTokens: List<KtSingleValueToken>,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector
|
||||
) : OperatorReferenceSearcher<KtBinaryExpression>(targetFunction, searchScope, consumer, optimizer, wordsToSearch = operationTokens.map { it.value }) {
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtBinaryExpression>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = operationTokens.map { it.value }) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val binaryExpression = expression.parent as? KtBinaryExpression ?: return
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
@@ -30,8 +31,9 @@ class ContainsOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector
|
||||
) : OperatorReferenceSearcher<KtOperationReferenceExpression>(targetFunction, searchScope, consumer, optimizer, wordsToSearch = listOf("in")) {
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtOperationReferenceExpression>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = listOf("in")) {
|
||||
|
||||
private val OPERATION_TOKENS = setOf(KtTokens.IN_KEYWORD, KtTokens.NOT_IN)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
@@ -32,8 +33,9 @@ class DestructuringDeclarationReferenceSearcher(
|
||||
private val componentIndex: Int,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector
|
||||
) : OperatorReferenceSearcher<KtDestructuringDeclaration>(targetDeclaration, searchScope, consumer, optimizer, wordsToSearch = listOf("(")) {
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtDestructuringDeclaration>(targetDeclaration, searchScope, consumer, optimizer, options, wordsToSearch = listOf("(")) {
|
||||
|
||||
override fun resolveTargetToDescriptor(): FunctionDescriptor? {
|
||||
if (targetDeclaration is KtParameter) {
|
||||
|
||||
@@ -125,10 +125,13 @@ class ExpressionsOfTypeProcessor(
|
||||
return
|
||||
}
|
||||
|
||||
val psiClass = runReadAction { detectClassToSearch() } ?: return
|
||||
// optimization
|
||||
if (runReadAction { searchScope is GlobalSearchScope && !FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, searchScope) }) return
|
||||
|
||||
val psiClass = runReadAction { detectClassToSearch() }
|
||||
|
||||
// for class from library always use plain search because we cannot search usages in compiled code (we could though)
|
||||
if (!runReadAction { psiClass.isValid && ProjectRootsUtil.isInProjectSource (psiClass) }) {
|
||||
if (psiClass == null || !runReadAction { psiClass.isValid && ProjectRootsUtil.isInProjectSource(psiClass) }) {
|
||||
possibleMatchesInScopeHandler(searchScope)
|
||||
return
|
||||
}
|
||||
@@ -149,8 +152,6 @@ class ExpressionsOfTypeProcessor(
|
||||
}
|
||||
|
||||
private fun detectClassToSearch(): PsiClass? {
|
||||
if (searchScope is GlobalSearchScope && !FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, searchScope)) return null // optimization
|
||||
|
||||
val classDescriptor = typeToSearch.type.constructor.declarationDescriptor ?: return null
|
||||
val classDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor)
|
||||
return when (classDeclaration) {
|
||||
|
||||
@@ -23,10 +23,10 @@ import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtArrayAccessReference
|
||||
import org.jetbrains.kotlin.idea.references.readWriteAccess
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
class IndexingOperatorReferenceSearcher(
|
||||
@@ -34,8 +34,9 @@ class IndexingOperatorReferenceSearcher(
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions,
|
||||
private val isSet: Boolean
|
||||
) : OperatorReferenceSearcher<KtArrayAccessExpression>(targetFunction, searchScope, consumer, optimizer, wordsToSearch = listOf("[")) {
|
||||
) : OperatorReferenceSearcher<KtArrayAccessExpression>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = listOf("[")) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val accessExpression = expression.parent as? KtArrayAccessExpression ?: return
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
@@ -31,8 +32,9 @@ class InvokeOperatorReferenceSearcher(
|
||||
targetFunction: PsiElement,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector
|
||||
) : OperatorReferenceSearcher<KtCallExpression>(targetFunction, searchScope, consumer, optimizer, wordsToSearch = emptyList()) {
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtCallExpression>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = emptyList()) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val callExpression = expression.parent as? KtCallExpression ?: return
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
||||
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
@@ -55,6 +56,7 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
private val searchScope: SearchScope,
|
||||
private val consumer: Processor<PsiReference>,
|
||||
private val optimizer: SearchRequestCollector,
|
||||
private val options: KotlinReferencesSearchOptions,
|
||||
private val wordsToSearch: List<String>
|
||||
) {
|
||||
private val project = targetDeclaration.project
|
||||
@@ -138,7 +140,7 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
if (DataClassDescriptorResolver.isComponentLike(name)) {
|
||||
if (!options.searchForComponentConventions) return null
|
||||
val componentIndex = DataClassDescriptorResolver.getComponentIndex(name.asString())
|
||||
return DestructuringDeclarationReferenceSearcher(declaration, componentIndex, searchScope, consumer, optimizer)
|
||||
return DestructuringDeclarationReferenceSearcher(declaration, componentIndex, searchScope, consumer, optimizer, options)
|
||||
}
|
||||
|
||||
if (!options.searchForOperatorConventions) return null
|
||||
@@ -151,22 +153,32 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
binaryOp != null -> {
|
||||
val counterpartAssignmentOp = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.inverse()[binaryOp]
|
||||
val operationTokens = listOf(binaryOp, counterpartAssignmentOp).filterNotNull()
|
||||
return BinaryOperatorReferenceSearcher(declaration, operationTokens, searchScope, consumer, optimizer)
|
||||
return BinaryOperatorReferenceSearcher(declaration, operationTokens, searchScope, consumer, optimizer, options)
|
||||
}
|
||||
|
||||
assignmentOp != null -> return BinaryOperatorReferenceSearcher(declaration, listOf(assignmentOp), searchScope, consumer, optimizer)
|
||||
assignmentOp != null ->
|
||||
return BinaryOperatorReferenceSearcher(declaration, listOf(assignmentOp), searchScope, consumer, optimizer, options)
|
||||
|
||||
unaryOp != null -> return UnaryOperatorReferenceSearcher(declaration, unaryOp, searchScope, consumer, optimizer)
|
||||
unaryOp != null ->
|
||||
return UnaryOperatorReferenceSearcher(declaration, unaryOp, searchScope, consumer, optimizer, options)
|
||||
|
||||
name == OperatorNameConventions.INVOKE -> return InvokeOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer)
|
||||
name == OperatorNameConventions.INVOKE ->
|
||||
return InvokeOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
|
||||
|
||||
name == OperatorNameConventions.GET -> return IndexingOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, isSet = false)
|
||||
name == OperatorNameConventions.GET ->
|
||||
return IndexingOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options, isSet = false)
|
||||
|
||||
name == OperatorNameConventions.SET -> return IndexingOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, isSet = true)
|
||||
name == OperatorNameConventions.SET ->
|
||||
return IndexingOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options, isSet = true)
|
||||
|
||||
name == OperatorNameConventions.CONTAINS -> return ContainsOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer)
|
||||
name == OperatorNameConventions.CONTAINS ->
|
||||
return ContainsOperatorReferenceSearcher(declaration, searchScope, consumer, optimizer, options)
|
||||
|
||||
else -> return null
|
||||
name == OperatorNameConventions.EQUALS ->
|
||||
return BinaryOperatorReferenceSearcher(declaration, listOf(KtTokens.EQEQ, KtTokens.EXCLEQ), searchScope, consumer, optimizer, options)
|
||||
|
||||
else ->
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -246,7 +258,8 @@ abstract class OperatorReferenceSearcher<TReferenceElement : KtElement>(
|
||||
if (wordsToSearch.isNotEmpty()) {
|
||||
val unwrappedElement = targetDeclaration.namedUnwrappedElement ?: return
|
||||
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement,
|
||||
filter = { ref -> isReferenceToCheck(ref) })
|
||||
filter = { ref -> isReferenceToCheck(ref) },
|
||||
options = options)
|
||||
wordsToSearch.forEach {
|
||||
optimizer.searchWord(it, scope.restrictToKotlinSources(), UsageSearchContext.IN_CODE, true, unwrappedElement, resultProcessor)
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ import com.intellij.psi.search.SearchRequestCollector
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
@@ -34,8 +34,9 @@ class UnaryOperatorReferenceSearcher(
|
||||
private val operationToken: KtSingleValueToken,
|
||||
searchScope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector
|
||||
) : OperatorReferenceSearcher<KtUnaryExpression>(targetFunction, searchScope, consumer, optimizer, wordsToSearch = listOf(operationToken.value)) {
|
||||
optimizer: SearchRequestCollector,
|
||||
options: KotlinReferencesSearchOptions
|
||||
) : OperatorReferenceSearcher<KtUnaryExpression>(targetFunction, searchScope, consumer, optimizer, options, wordsToSearch = listOf(operationToken.value)) {
|
||||
|
||||
override fun processPossibleReceiverExpression(expression: KtExpression) {
|
||||
val unaryExpression = expression.parent as? KtUnaryExpression ?: return
|
||||
|
||||
@@ -48,7 +48,6 @@ val COMPARISON_OPERATIONS_TO_SEARCH = setOf(KtTokens.LT, KtTokens.GT)
|
||||
fun Name.getOperationSymbolsToSearch(): Pair<Set<KtToken>, Class<*>>? {
|
||||
when (this) {
|
||||
OperatorNameConventions.COMPARE_TO -> return COMPARISON_OPERATIONS_TO_SEARCH to KtSimpleNameReference::class.java
|
||||
OperatorNameConventions.EQUALS -> return EQUALS_OPERATIONS to KtSimpleNameReference::class.java
|
||||
OperatorNameConventions.ITERATOR -> return IN_OPERATIONS_TO_SEARCH to KtForLoopInReference::class.java
|
||||
in DELEGATE_ACCESSOR_NAMES -> return setOf(KtTokens.BY_KEYWORD) to KtPropertyDelegationMethodsReference::class.java
|
||||
DelegatedPropertyResolver.PROPERTY_DELEGATED_FUNCTION_NAME -> return setOf(KtTokens.BY_KEYWORD) to KtPropertyDelegationMethodsReference::class.java
|
||||
|
||||
@@ -36,7 +36,7 @@ class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReferenc
|
||||
val identifier = Name.identifier(name)
|
||||
|
||||
val operatorSearcher = OperatorReferenceSearcher.create(
|
||||
method, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer, KotlinReferencesSearchOptions.Empty)
|
||||
method, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer, KotlinReferencesSearchOptions(acceptCallableOverrides = true))
|
||||
if (operatorSearcher != null) {
|
||||
operatorSearcher.run()
|
||||
}
|
||||
|
||||
1
idea/testData/findUsages/kotlin/conventions/equals.log
vendored
Normal file
1
idea/testData/findUsages/kotlin/conventions/equals.log
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Used plain search of kotlin.Any.equals(other: kotlin.Any?) in whole search scope
|
||||
18
idea/testData/findUsages/kotlin/conventions/equalsNotAny.0.kt
vendored
Normal file
18
idea/testData/findUsages/kotlin/conventions/equalsNotAny.0.kt
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
// OPTIONS: usages
|
||||
// FIND_BY_REF
|
||||
|
||||
class A(val n: Int) {
|
||||
fun foo(a: A) = a <caret>== this
|
||||
|
||||
override override fun equals(other: Any?): Boolean = TODO()
|
||||
}
|
||||
|
||||
fun test() {
|
||||
1 == 2
|
||||
A(0) == A(1)
|
||||
A(0) != A(1)
|
||||
A(0) equals A(1)
|
||||
A(0) === A(1)
|
||||
A(0) !== A(1)
|
||||
}
|
||||
8
idea/testData/findUsages/kotlin/conventions/equalsNotAny.log
vendored
Normal file
8
idea/testData/findUsages/kotlin/conventions/equalsNotAny.log
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
Checked type of A.foo(a: A)
|
||||
Resolved A(0) != A(1)
|
||||
Resolved A(0) == A(1)
|
||||
Resolved a == this
|
||||
Searched references to A
|
||||
Searched references to parameter a of A.foo(a: A) in non-Java files
|
||||
Used plain search of A.equals(other: Any?) in LocalSearchScope:
|
||||
CLASS:A
|
||||
4
idea/testData/findUsages/kotlin/conventions/equalsNotAny.results.txt
vendored
Normal file
4
idea/testData/findUsages/kotlin/conventions/equalsNotAny.results.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
Function call 13 A(0) == A(1)
|
||||
Function call 14 A(0) != A(1)
|
||||
Function call 15 A(0) equals A(1)
|
||||
Function call 6 fun foo(a: A) = a == this
|
||||
@@ -96,6 +96,12 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNotAny.0.kt")
|
||||
public void testEqualsNotAny() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/equalsNotAny.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("forIteration.0.kt")
|
||||
public void testForIteration() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/forIteration.0.kt");
|
||||
|
||||
Reference in New Issue
Block a user