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)
}
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()
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 {
@@ -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()
+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);
}
@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");