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)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user