Data Inflow: Show composite assignments

#KT-19112 Fixed
This commit is contained in:
Alexey Sedunov
2017-11-09 16:41:31 +03:00
parent a333eb917b
commit 9d482bbbb1
5 changed files with 56 additions and 19 deletions
@@ -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()
+9
View File
@@ -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");