Checking that input variable is really used

This commit is contained in:
Valentin Kipyatkov
2016-04-20 17:22:34 +03:00
parent 345f2b8a52
commit e0a7e1dbed
8 changed files with 60 additions and 5 deletions
@@ -51,9 +51,11 @@ interface Transformation {
fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression
val chainCallCount: Int
get() = 1
val shouldUseInputVariable: Boolean
get() = true
}
/**
@@ -71,9 +71,17 @@ fun match(loop: KtForExpression): ResultTransformationMatch? {
state = state.copy(statements = block.statements)
}
val inputVariableUsed = state.inputVariable.hasUsages(state.statements)
for (matcher in MatcherRegistrar.resultMatchers) {
val match = matcher.match(state)
if (match != null) {
if (!inputVariableUsed
&& (match.sequenceTransformations.any { it.shouldUseInputVariable }
|| match.resultTransformation.shouldUseInputVariable)) {
return null
}
sequenceTransformations.addAll(match.sequenceTransformations)
return ResultTransformationMatch(match.resultTransformation, sequenceTransformations)
.let { mergeTransformations(it) }
@@ -84,6 +92,10 @@ fun match(loop: KtForExpression): ResultTransformationMatch? {
for (matcher in MatcherRegistrar.sequenceMatchers) {
val match = matcher.match(state)
if (match != null) {
if (!inputVariableUsed && match.transformations.any { it.shouldUseInputVariable }) {
return null
}
val newState = match.newState
// check that old input variable is not needed anymore
if (state.inputVariable != newState.inputVariable && state.inputVariable.hasUsages(newState.statements)) return null
@@ -42,6 +42,9 @@ class CountTransformation(
override val presentation: String
get() = "count" + (if (filter != null) "{}" else "()")
override val shouldUseInputVariable: Boolean
get() = false
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
val call = if (filter != null) {
val lambda = generateLambda(inputVariable, filter)
@@ -46,6 +46,9 @@ class FindAndAssignTransformation(
override val chainCallCount: Int
get() = generator.chainCallCount
override val shouldUseInputVariable: Boolean
get() = generator.shouldUseInputVariable
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
return generator.generate(chainedCallGenerator, filter)
}
@@ -50,6 +50,9 @@ class FindAndReturnTransformation(
override val chainCallCount: Int
get() = generator.chainCallCount
override val shouldUseInputVariable: Boolean
get() = generator.shouldUseInputVariable
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
return generator.generate(chainedCallGenerator, filter)
}
@@ -115,6 +115,8 @@ fun KtProperty.hasWriteUsages(): Boolean {
interface FindOperatorGenerator {
val functionName: String
val shouldUseInputVariable: Boolean
fun generate(chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression
val chainCallCount: Int
@@ -140,7 +142,7 @@ fun buildFindOperationGenerator(
}
}
class SimpleGenerator(override val functionName: String) : FindOperatorGenerator {
class SimpleGenerator(override val functionName: String, override val shouldUseInputVariable: Boolean) : FindOperatorGenerator {
override fun generate(chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression {
return generateChainedCall(functionName, chainedCallGenerator, filter)
}
@@ -164,13 +166,13 @@ fun buildFindOperationGenerator(
when {
valueIfFound.isVariableReference(inputVariable) -> {
val generator = SimpleGenerator(if (findFirst) "firstOrNull" else "lastOrNull")
val generator = SimpleGenerator(if (findFirst) "firstOrNull" else "lastOrNull", shouldUseInputVariable = true)
return generator.useElvisOperatorIfNeeded()
}
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return SimpleGenerator("any")
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return SimpleGenerator("any", shouldUseInputVariable = false)
valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return SimpleGenerator("none")
valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return SimpleGenerator("none", shouldUseInputVariable = false)
inputVariable.hasUsages(valueIfFound) -> {
if (!findFirst) return null // too dangerous because of side effects
@@ -185,6 +187,9 @@ fun buildFindOperationGenerator(
override val functionName: String
get() = "firstOrNull"
override val shouldUseInputVariable: Boolean
get() = true
override val chainCallCount: Int
get() = 2
@@ -203,6 +208,9 @@ fun buildFindOperationGenerator(
override val functionName: String
get() = "firstOrNull"
override val shouldUseInputVariable: Boolean
get() = true
override val chainCallCount: Int
get() = 2 // also includes "let"
@@ -219,6 +227,9 @@ fun buildFindOperationGenerator(
override val functionName: String
get() = "any"
override val shouldUseInputVariable: Boolean
get() = false
override fun generate(chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression {
val chainedCall = generateChainedCall(functionName, chainedCallGenerator, filter)
return KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound)
@@ -0,0 +1,13 @@
// WITH_RUNTIME
// IS_APPLICABLE: false
import java.util.*
fun foo(list: List<String>): String? {
val random = Random()
for (s in list) {
if (random.nextBoolean()) {
return s
}
}
return null
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
// IS_APPLICABLE: false
fun foo(list: List<String>, target: MutableList<Int>) {
<caret>for (s in list) {
if (s.length > 0)
target.add(0)
}
}