From 9d482bbbb192fe8cc113760b5f04a88fd1898364 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 9 Nov 2017 16:41:31 +0300 Subject: [PATCH] Data Inflow: Show composite assignments #KT-19112 Fixed --- .../kotlin/idea/references/referenceUtil.kt | 23 ++++++++------- .../jetbrains/kotlin/idea/slicer/Slicer.kt | 29 +++++++++++++------ .../slicer/inflow/compositeAssignments.kt | 9 ++++++ .../inflow/compositeAssignments.results.txt | 8 +++++ .../idea/slicer/SlicerTestGenerated.java | 6 ++++ 5 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 idea/testData/slicer/inflow/compositeAssignments.kt create mode 100644 idea/testData/slicer/inflow/compositeAssignments.results.txt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/referenceUtil.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/referenceUtil.kt index 4bd5d3961d9..3f730509950 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/referenceUtil.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/referenceUtil.kt @@ -220,7 +220,9 @@ enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) { 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 { var expression = getQualifiedExpressionForSelectorOrThis() loop@ while (true) { val parent = expression.parent @@ -233,26 +235,27 @@ fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean): ReferenceAcce val assignment = expression.getAssignmentByLHS() if (assignment != null) { when (assignment.operationToken) { - KtTokens.EQ -> return ReferenceAccess.WRITE + KtTokens.EQ -> return ReferenceAccess.WRITE to assignment else -> { - if (!useResolveForReadWrite) return ReferenceAccess.READ_WRITE + if (!useResolveForReadWrite) return ReferenceAccess.READ_WRITE to assignment val bindingContext = assignment.analyze(BodyResolveMode.PARTIAL) - val resolvedCall = assignment.getResolvedCall(bindingContext) ?: return ReferenceAccess.READ_WRITE - if (!resolvedCall.isReallySuccess()) return ReferenceAccess.READ_WRITE + val resolvedCall = assignment.getResolvedCall(bindingContext) ?: return ReferenceAccess.READ_WRITE to assignment + if (!resolvedCall.isReallySuccess()) return ReferenceAccess.READ_WRITE to assignment return if (resolvedCall.resultingDescriptor.name in OperatorConventions.ASSIGNMENT_OPERATIONS.values) - ReferenceAccess.READ + ReferenceAccess.READ to assignment else - ReferenceAccess.READ_WRITE + ReferenceAccess.READ_WRITE to assignment } } } - return if ((expression.parent as? KtUnaryExpression)?.operationToken in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) }) - ReferenceAccess.READ_WRITE + val unaryExpression = expression.parent as? KtUnaryExpression + return if (unaryExpression != null && unaryExpression.operationToken in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) }) + ReferenceAccess.READ_WRITE to unaryExpression else - ReferenceAccess.READ + ReferenceAccess.READ to expression } fun KtReference.canBeResolvedViaImport(target: DeclarationDescriptor, bindingContext: BindingContext): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index 2cf8419f0b1..a1690991e71 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -48,6 +48,8 @@ import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar 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.searchOverriders 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( scope: SearchScope, - kind: Access, + kind: AccessKind, processor: (UsageInfo) -> Unit ) { processAllExactUsages( { KotlinPropertyFindUsagesOptions(project).apply { - isReadAccess = kind == Access.Read || kind == Access.ReadWrite - isWriteAccess = kind == Access.Write || kind == Access.ReadWrite - isReadWriteAccess = kind == Access.ReadWrite + isReadAccess = kind == AccessKind.READ_ONLY || kind == AccessKind.READ_OR_WRITE + isWriteAccess = kind == AccessKind.WRITE_ONLY || kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE + isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE isSearchForTextOccurrences = false isSkipImportStatements = true searchScope = scope.intersectWith(useScope) @@ -155,11 +161,15 @@ class InflowSlicer( private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true) 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 rhs = KtPsiUtil.safeDeparenthesize(refExpression).getQualifiedExpressionForSelectorOrThis().getAssignmentByLHS()?.right - ?: return@body - rhs.passToProcessorAsValue() + val (accessKind, accessExpression) = refExpression.readWriteAccessWithFullExpression(true) + if (accessKind == ReferenceAccess.WRITE && accessExpression is KtBinaryExpression && accessExpression.operationToken == KtTokens.EQ) { + accessExpression.right?.passToProcessorAsValue() + } + else { + accessExpression.passToProcessorAsValue() + } } } @@ -352,7 +362,8 @@ class OutflowSlicer( if (this is KtParameter && !canProcess()) return@processHierarchyUpward 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 if (withDereferences) { refExpression.processDereferences() diff --git a/idea/testData/slicer/inflow/compositeAssignments.kt b/idea/testData/slicer/inflow/compositeAssignments.kt new file mode 100644 index 00000000000..38ca51cf0de --- /dev/null +++ b/idea/testData/slicer/inflow/compositeAssignments.kt @@ -0,0 +1,9 @@ +// FLOW: IN + +fun assignmentWithSum(n: Int): Int { + var result = 0 + result += n + result++ + --result + return result +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/compositeAssignments.results.txt b/idea/testData/slicer/inflow/compositeAssignments.results.txt new file mode 100644 index 00000000000..3869cb9728b --- /dev/null +++ b/idea/testData/slicer/inflow/compositeAssignments.results.txt @@ -0,0 +1,8 @@ +3 fun assignmentWithSum(n: Int): Int { +8 return result +4 var result = 0 +4 var result = 0 +5 result += n +6 result++ +4 DUPLICATE: var result = 0 +7 --result diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java index 4c5310e87d8..de0237155b3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java @@ -54,6 +54,12 @@ public class SlicerTestGenerated extends AbstractSlicerTest { 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") public void testInflow_DefaultGetterFieldInSetter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");