Data Inflow: Show composite assignments
#KT-19112 Fixed
This commit is contained in:
@@ -220,7 +220,9 @@ enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) {
|
|||||||
READ(true, false), WRITE(false, true), READ_WRITE(true, true)
|
READ(true, false), WRITE(false, true), READ_WRITE(true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean): ReferenceAccess {
|
fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean) = readWriteAccessWithFullExpression(useResolveForReadWrite).first
|
||||||
|
|
||||||
|
fun KtExpression.readWriteAccessWithFullExpression(useResolveForReadWrite: Boolean): Pair<ReferenceAccess, KtExpression> {
|
||||||
var expression = getQualifiedExpressionForSelectorOrThis()
|
var expression = getQualifiedExpressionForSelectorOrThis()
|
||||||
loop@ while (true) {
|
loop@ while (true) {
|
||||||
val parent = expression.parent
|
val parent = expression.parent
|
||||||
@@ -233,26 +235,27 @@ fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean): ReferenceAcce
|
|||||||
val assignment = expression.getAssignmentByLHS()
|
val assignment = expression.getAssignmentByLHS()
|
||||||
if (assignment != null) {
|
if (assignment != null) {
|
||||||
when (assignment.operationToken) {
|
when (assignment.operationToken) {
|
||||||
KtTokens.EQ -> return ReferenceAccess.WRITE
|
KtTokens.EQ -> return ReferenceAccess.WRITE to assignment
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
if (!useResolveForReadWrite) return ReferenceAccess.READ_WRITE
|
if (!useResolveForReadWrite) return ReferenceAccess.READ_WRITE to assignment
|
||||||
|
|
||||||
val bindingContext = assignment.analyze(BodyResolveMode.PARTIAL)
|
val bindingContext = assignment.analyze(BodyResolveMode.PARTIAL)
|
||||||
val resolvedCall = assignment.getResolvedCall(bindingContext) ?: return ReferenceAccess.READ_WRITE
|
val resolvedCall = assignment.getResolvedCall(bindingContext) ?: return ReferenceAccess.READ_WRITE to assignment
|
||||||
if (!resolvedCall.isReallySuccess()) return ReferenceAccess.READ_WRITE
|
if (!resolvedCall.isReallySuccess()) return ReferenceAccess.READ_WRITE to assignment
|
||||||
return if (resolvedCall.resultingDescriptor.name in OperatorConventions.ASSIGNMENT_OPERATIONS.values)
|
return if (resolvedCall.resultingDescriptor.name in OperatorConventions.ASSIGNMENT_OPERATIONS.values)
|
||||||
ReferenceAccess.READ
|
ReferenceAccess.READ to assignment
|
||||||
else
|
else
|
||||||
ReferenceAccess.READ_WRITE
|
ReferenceAccess.READ_WRITE to assignment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if ((expression.parent as? KtUnaryExpression)?.operationToken in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) })
|
val unaryExpression = expression.parent as? KtUnaryExpression
|
||||||
ReferenceAccess.READ_WRITE
|
return if (unaryExpression != null && unaryExpression.operationToken in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) })
|
||||||
|
ReferenceAccess.READ_WRITE to unaryExpression
|
||||||
else
|
else
|
||||||
ReferenceAccess.READ
|
ReferenceAccess.READ to expression
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtReference.canBeResolvedViaImport(target: DeclarationDescriptor, bindingContext: BindingContext): Boolean {
|
fun KtReference.canBeResolvedViaImport(target: DeclarationDescriptor, bindingContext: BindingContext): Boolean {
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
|
|||||||
import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages
|
import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
|
||||||
|
import org.jetbrains.kotlin.idea.references.ReferenceAccess
|
||||||
|
import org.jetbrains.kotlin.idea.references.readWriteAccessWithFullExpression
|
||||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
|
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
@@ -93,17 +95,21 @@ private fun KtFunction.processCalls(scope: SearchScope, processor: (UsageInfo) -
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum class AccessKind {
|
||||||
|
READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE
|
||||||
|
}
|
||||||
|
|
||||||
private fun KtDeclaration.processVariableAccesses(
|
private fun KtDeclaration.processVariableAccesses(
|
||||||
scope: SearchScope,
|
scope: SearchScope,
|
||||||
kind: Access,
|
kind: AccessKind,
|
||||||
processor: (UsageInfo) -> Unit
|
processor: (UsageInfo) -> Unit
|
||||||
) {
|
) {
|
||||||
processAllExactUsages(
|
processAllExactUsages(
|
||||||
{
|
{
|
||||||
KotlinPropertyFindUsagesOptions(project).apply {
|
KotlinPropertyFindUsagesOptions(project).apply {
|
||||||
isReadAccess = kind == Access.Read || kind == Access.ReadWrite
|
isReadAccess = kind == AccessKind.READ_ONLY || kind == AccessKind.READ_OR_WRITE
|
||||||
isWriteAccess = kind == Access.Write || kind == Access.ReadWrite
|
isWriteAccess = kind == AccessKind.WRITE_ONLY || kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
|
||||||
isReadWriteAccess = kind == Access.ReadWrite
|
isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
|
||||||
isSearchForTextOccurrences = false
|
isSearchForTextOccurrences = false
|
||||||
isSkipImportStatements = true
|
isSkipImportStatements = true
|
||||||
searchScope = scope.intersectWith(useScope)
|
searchScope = scope.intersectWith(useScope)
|
||||||
@@ -155,11 +161,15 @@ class InflowSlicer(
|
|||||||
private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
|
private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
|
||||||
|
|
||||||
private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) {
|
private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) {
|
||||||
processVariableAccesses(accessSearchScope, Access.Write) body@ {
|
processVariableAccesses(accessSearchScope, AccessKind.WRITE_WITH_OPTIONAL_READ) body@ {
|
||||||
val refExpression = it.element as? KtExpression ?: return@body
|
val refExpression = it.element as? KtExpression ?: return@body
|
||||||
val rhs = KtPsiUtil.safeDeparenthesize(refExpression).getQualifiedExpressionForSelectorOrThis().getAssignmentByLHS()?.right
|
val (accessKind, accessExpression) = refExpression.readWriteAccessWithFullExpression(true)
|
||||||
?: return@body
|
if (accessKind == ReferenceAccess.WRITE && accessExpression is KtBinaryExpression && accessExpression.operationToken == KtTokens.EQ) {
|
||||||
rhs.passToProcessorAsValue()
|
accessExpression.right?.passToProcessorAsValue()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
accessExpression.passToProcessorAsValue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +362,8 @@ class OutflowSlicer(
|
|||||||
if (this is KtParameter && !canProcess()) return@processHierarchyUpward
|
if (this is KtParameter && !canProcess()) return@processHierarchyUpward
|
||||||
|
|
||||||
val withDereferences = parentUsage.params.showInstanceDereferences
|
val withDereferences = parentUsage.params.showInstanceDereferences
|
||||||
processVariableAccesses(parentUsage.scope.toSearchScope(), if (withDereferences) Access.ReadWrite else Access.Read) body@ {
|
val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY
|
||||||
|
processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@ {
|
||||||
val refExpression = (it.element as? KtExpression)?.let { KtPsiUtil.safeDeparenthesize(it) } ?: return@body
|
val refExpression = (it.element as? KtExpression)?.let { KtPsiUtil.safeDeparenthesize(it) } ?: return@body
|
||||||
if (withDereferences) {
|
if (withDereferences) {
|
||||||
refExpression.processDereferences()
|
refExpression.processDereferences()
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
fun <caret>assignmentWithSum(n: Int): Int {
|
||||||
|
var result = 0
|
||||||
|
result += n
|
||||||
|
result++
|
||||||
|
--result
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
3 fun <bold>assignmentWithSum(n: Int): Int {</bold>
|
||||||
|
8 return <bold>result</bold>
|
||||||
|
4 var <bold>result = 0</bold>
|
||||||
|
4 var result = <bold>0</bold>
|
||||||
|
5 <bold>result += n</bold>
|
||||||
|
6 <bold>result++</bold>
|
||||||
|
4 DUPLICATE: var <bold>result = 0</bold>
|
||||||
|
7 <bold>--result</bold>
|
||||||
@@ -54,6 +54,12 @@ public class SlicerTestGenerated extends AbstractSlicerTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inflow/compositeAssignments.kt")
|
||||||
|
public void testInflow_CompositeAssignments() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/compositeAssignments.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inflow/defaultGetterFieldInSetter.kt")
|
@TestMetadata("inflow/defaultGetterFieldInSetter.kt")
|
||||||
public void testInflow_DefaultGetterFieldInSetter() throws Exception {
|
public void testInflow_DefaultGetterFieldInSetter() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user