Another pattern for min/max supported
This commit is contained in:
+57
-12
@@ -45,41 +45,86 @@ class MaxOrMinTransformation(
|
||||
* variable = <input variable>
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* or
|
||||
*
|
||||
* val variable = <initial>
|
||||
* for (...) {
|
||||
* ...
|
||||
* // or '<', '<=', '>=' or operands swapped
|
||||
* variable = if (variable > <input variable>) <input variable> else variable
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
object Matcher : TransformationMatcher {
|
||||
override val indexVariableAllowed: Boolean
|
||||
get() = false
|
||||
|
||||
override fun match(state: MatchingState): TransformationMatch.Result? {
|
||||
return matchIfAssign(state) ?: matchAssignIf(state)
|
||||
}
|
||||
|
||||
private fun matchIfAssign(state: MatchingState): TransformationMatch.Result? {
|
||||
val ifExpression = state.statements.singleOrNull() as? KtIfExpression ?: return null
|
||||
if (ifExpression.`else` != null) return null
|
||||
|
||||
val condition = ifExpression.condition as? KtBinaryExpression ?: return null
|
||||
val then = ifExpression.then ?: return null
|
||||
val statement = then.blockExpressionsOrSingle().singleOrNull() as? KtBinaryExpression ?: return null
|
||||
if (statement.operationToken != KtTokens.EQ) return null
|
||||
|
||||
return match(ifExpression.condition, statement.left, statement.right, null, state.inputVariable, state.outerLoop)
|
||||
}
|
||||
|
||||
private fun matchAssignIf(state: MatchingState): TransformationMatch.Result? {
|
||||
val assignment = state.statements.singleOrNull() as? KtBinaryExpression ?: return null
|
||||
if (assignment.operationToken != KtTokens.EQ) return null
|
||||
|
||||
val ifExpression = assignment.right as? KtIfExpression ?: return null
|
||||
|
||||
return match(ifExpression.condition, assignment.left, ifExpression.then, ifExpression.`else`, state.inputVariable, state.outerLoop)
|
||||
}
|
||||
|
||||
private fun match(
|
||||
condition: KtExpression?,
|
||||
assignmentTarget: KtExpression?,
|
||||
valueAssignedIfTrue: KtExpression?,
|
||||
valueAssignedIfFalse: KtExpression?,
|
||||
inputVariable: KtCallableDeclaration,
|
||||
loop: KtForExpression
|
||||
): TransformationMatch.Result? {
|
||||
if (condition !is KtBinaryExpression) return null
|
||||
val comparison = condition.operationToken
|
||||
if (comparison !in setOf(KtTokens.GT, KtTokens.LT)) return null
|
||||
if (comparison !in setOf(KtTokens.GT, KtTokens.LT, KtTokens.GTEQ, KtTokens.LTEQ)) return null
|
||||
val left = condition.left as? KtNameReferenceExpression ?: return null
|
||||
val right = condition.right as? KtNameReferenceExpression ?: return null
|
||||
val otherHand = if (left.isVariableReference(state.inputVariable)) {
|
||||
val otherHand = if (left.isVariableReference(inputVariable)) {
|
||||
right
|
||||
}
|
||||
else if (right.isVariableReference(state.inputVariable)) {
|
||||
else if (right.isVariableReference(inputVariable)) {
|
||||
left
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
|
||||
val variableInitialization = (otherHand.isVariableInitializedBeforeLoop(state.outerLoop, checkNoOtherUsagesInLoop = false)
|
||||
val variableInitialization = (otherHand.isVariableInitializedBeforeLoop(loop, checkNoOtherUsagesInLoop = false)
|
||||
?: return null)
|
||||
|
||||
val then = ifExpression.then ?: return null
|
||||
val statement = then.blockExpressionsOrSingle().singleOrNull() as? KtBinaryExpression ?: return null
|
||||
if (statement.operationToken != KtTokens.EQ) return null
|
||||
if (!statement.left.isVariableReference(variableInitialization.variable)) return null
|
||||
if (!statement.right.isVariableReference(state.inputVariable)) return null
|
||||
if (!assignmentTarget.isVariableReference(variableInitialization.variable)) return null
|
||||
|
||||
val isMax = (comparison == KtTokens.GT) xor (otherHand == left)
|
||||
val transformation = MaxOrMinTransformation(state.outerLoop, state.inputVariable, variableInitialization, isMax)
|
||||
val valueToBeVariable = if (valueAssignedIfTrue.isVariableReference(inputVariable)) {
|
||||
valueAssignedIfFalse
|
||||
}
|
||||
else if (valueAssignedIfFalse.isVariableReference(inputVariable)) {
|
||||
valueAssignedIfTrue
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
if (valueToBeVariable != null && !valueToBeVariable.isVariableReference(variableInitialization.variable)) return null
|
||||
|
||||
val isMax = (comparison == KtTokens.GT || comparison == KtTokens.GTEQ) xor (otherHand == left)
|
||||
val transformation = MaxOrMinTransformation(loop, inputVariable, variableInitialization, isMax)
|
||||
return TransformationMatch.Result(transformation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.max()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.max()'"
|
||||
fun getMaxLineWidth(lineCount: Int): Float {
|
||||
var max_width = 0.0f
|
||||
<caret>for (i in 0..lineCount - 1) {
|
||||
val width = getLineWidth(i)
|
||||
max_width = if (width > max_width) width else max_width
|
||||
}
|
||||
return max_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Float = TODO()
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.max()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.max()'"
|
||||
fun getMaxLineWidth(lineCount: Int): Float {
|
||||
val <caret>max_width = (0..lineCount - 1)
|
||||
.map { getLineWidth(it) }
|
||||
.max()
|
||||
?: 0.0f
|
||||
return max_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Float = TODO()
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.max()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.max()'"
|
||||
fun getMaxLineWidth(lineCount: Int): Float {
|
||||
val <caret>max_width = (0..lineCount - 1)
|
||||
.asSequence()
|
||||
.map { getLineWidth(it) }
|
||||
.max()
|
||||
?: 0.0f
|
||||
return max_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Float = TODO()
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.min()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.min()'"
|
||||
fun getMinLineWidth(lineCount: Int): Double {
|
||||
var min_width = Double.MAX_VALUE
|
||||
<caret>for (i in 0..lineCount - 1) {
|
||||
val width = getLineWidth(i)
|
||||
min_width = if (min_width > width) min_width else width
|
||||
}
|
||||
return min_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Double = TODO()
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.min()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.min()'"
|
||||
fun getMinLineWidth(lineCount: Int): Double {
|
||||
val <caret>min_width = (0..lineCount - 1)
|
||||
.map { getLineWidth(it) }
|
||||
.min()
|
||||
?: Double.MAX_VALUE
|
||||
return min_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Double = TODO()
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.min()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.min()'"
|
||||
fun getMinLineWidth(lineCount: Int): Double {
|
||||
val <caret>min_width = (0..lineCount - 1)
|
||||
.asSequence()
|
||||
.map { getLineWidth(it) }
|
||||
.min()
|
||||
?: Double.MAX_VALUE
|
||||
return min_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Double = TODO()
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.min()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.min()'"
|
||||
fun getMinLineWidth(lineCount: Int): Double {
|
||||
var min_width = Double.MAX_VALUE
|
||||
<caret>for (i in 0..lineCount - 1) {
|
||||
val width = getLineWidth(i)
|
||||
min_width = if (min_width >= width) width else min_width
|
||||
}
|
||||
return min_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Double = TODO()
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.min()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.min()'"
|
||||
fun getMinLineWidth(lineCount: Int): Double {
|
||||
val <caret>min_width = (0..lineCount - 1)
|
||||
.map { getLineWidth(it) }
|
||||
.min()
|
||||
?: Double.MAX_VALUE
|
||||
return min_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Double = TODO()
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'map{}.min()'"
|
||||
// INTENTION_TEXT_2: "Replace with 'asSequence().map{}.min()'"
|
||||
fun getMinLineWidth(lineCount: Int): Double {
|
||||
val <caret>min_width = (0..lineCount - 1)
|
||||
.asSequence()
|
||||
.map { getLineWidth(it) }
|
||||
.min()
|
||||
?: Double.MAX_VALUE
|
||||
return min_width
|
||||
}
|
||||
|
||||
fun getLineWidth(i: Int): Double = TODO()
|
||||
@@ -959,6 +959,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("max3.kt")
|
||||
public void testMax3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/max3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("min1.kt")
|
||||
public void testMin1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/min1.kt");
|
||||
@@ -971,6 +977,18 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("min3.kt")
|
||||
public void testMin3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/min3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("min4.kt")
|
||||
public void testMin4() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/min4.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("moveInitializationsCloserToLoop.kt")
|
||||
public void testMoveInitializationsCloserToLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/moveInitializationsCloserToLoop.kt");
|
||||
|
||||
@@ -8119,6 +8119,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("max3.kt")
|
||||
public void testMax3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/max3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("min1.kt")
|
||||
public void testMin1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/min1.kt");
|
||||
@@ -8131,6 +8137,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("min3.kt")
|
||||
public void testMin3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/min3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("min4.kt")
|
||||
public void testMin4() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/min4.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("moveInitializationsCloserToLoop.kt")
|
||||
public void testMoveInitializationsCloserToLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/moveInitializationsCloserToLoop.kt");
|
||||
|
||||
Reference in New Issue
Block a user