"Convert to range check" inspection: apply ReplaceRangeToWithUntilInspection after convertion

#KT-33880 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-09-19 13:08:55 +09:00
committed by Dmitry Gridin
parent 54df0aad16
commit 82f75c707e
7 changed files with 41 additions and 11 deletions
@@ -27,8 +27,7 @@ import org.jetbrains.kotlin.psi.*
class ReplaceRangeToWithUntilInspection : AbstractPrimitiveRangeToInspection() { class ReplaceRangeToWithUntilInspection : AbstractPrimitiveRangeToInspection() {
override fun visitRangeToExpression(expression: KtExpression, holder: ProblemsHolder) { override fun visitRangeToExpression(expression: KtExpression, holder: ProblemsHolder) {
if (expression.getArguments()?.second?.deparenthesize()?.isMinusOne() != true) return if (!isApplicable(expression)) return
holder.registerProblem( holder.registerProblem(
expression, expression,
"'rangeTo' or the '..' call should be replaced with 'until'", "'rangeTo' or the '..' call should be replaced with 'until'",
@@ -44,6 +43,20 @@ class ReplaceRangeToWithUntilInspection : AbstractPrimitiveRangeToInspection() {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) { override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement as KtExpression val element = descriptor.psiElement as KtExpression
applyFix(element)
}
}
companion object {
fun applyFixIfApplicable(expression: KtExpression) {
if (isApplicable(expression)) applyFix(expression)
}
private fun isApplicable(expression: KtExpression): Boolean {
return expression.getArguments()?.second?.deparenthesize()?.isMinusOne() == true
}
private fun applyFix(element: KtExpression) {
val args = element.getArguments() ?: return val args = element.getArguments() ?: return
element.replace( element.replace(
KtPsiFactory(element).createExpressionByPattern( KtPsiFactory(element).createExpressionByPattern(
@@ -53,15 +66,15 @@ class ReplaceRangeToWithUntilInspection : AbstractPrimitiveRangeToInspection() {
) )
) )
} }
}
private fun KtExpression.isMinusOne(): Boolean { private fun KtExpression.isMinusOne(): Boolean {
if (this !is KtBinaryExpression) return false if (this !is KtBinaryExpression) return false
if (operationToken != KtTokens.MINUS) return false if (operationToken != KtTokens.MINUS) return false
val constantValue = right?.constantValueOrNull() val constantValue = right?.constantValueOrNull()
val rightValue = (constantValue?.value as? Number)?.toInt() ?: return false val rightValue = (constantValue?.value as? Number)?.toInt() ?: return false
return rightValue == 1 return rightValue == 1
}
} }
} }
@@ -21,6 +21,7 @@ import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.inspections.ReplaceRangeToWithUntilInspection
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -49,13 +50,16 @@ class ConvertTwoComparisonsToRangeCheckIntention : SelfTargetingOffsetIndependen
override fun applyTo(element: KtBinaryExpression, editor: Editor?) { override fun applyTo(element: KtBinaryExpression, editor: Editor?) {
val rangeData = generateRangeExpressionData(element) ?: return val rangeData = generateRangeExpressionData(element) ?: return
val factory = KtPsiFactory(element) val factory = KtPsiFactory(element)
element.replace( val replaced = element.replace(
factory.createExpressionByPattern( factory.createExpressionByPattern(
"$0 in $1..$2", rangeData.value, "$0 in $1..$2", rangeData.value,
factory.createExpression(rangeData.min), factory.createExpression(rangeData.min),
factory.createExpression(rangeData.max) factory.createExpression(rangeData.max)
) )
) )
(replaced as? KtBinaryExpression)?.right?.let {
ReplaceRangeToWithUntilInspection.applyFixIfApplicable(it)
}
} }
private fun generateRangeExpressionData(condition: KtBinaryExpression): RangeExpressionData? { private fun generateRangeExpressionData(condition: KtBinaryExpression): RangeExpressionData? {
@@ -1,3 +1,4 @@
// WITH_RUNTIME
fun foo(bar: Int, min: Int, max: Int) { fun foo(bar: Int, min: Int, max: Int) {
min < bar && bar < max<caret> min < bar && bar < max<caret>
} }
@@ -1,3 +1,4 @@
// WITH_RUNTIME
fun foo(bar: Int, min: Int, max: Int) { fun foo(bar: Int, min: Int, max: Int) {
bar in (min + 1)..(max - 1) bar in (min + 1) until max
} }
@@ -0,0 +1,3 @@
// WITH_RUNTIME
val N = 42
fun test(a: Int) = a >= 0 && a < N<caret>
@@ -0,0 +1,3 @@
// WITH_RUNTIME
val N = 42
fun test(a: Int) = a in 0 until N
@@ -7809,6 +7809,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
public void testNonConstants() throws Exception { public void testNonConstants() throws Exception {
runTest("idea/testData/intentions/convertTwoComparisonsToRangeCheck/nonConstants.kt"); runTest("idea/testData/intentions/convertTwoComparisonsToRangeCheck/nonConstants.kt");
} }
@TestMetadata("until.kt")
public void testUntil() throws Exception {
runTest("idea/testData/intentions/convertTwoComparisonsToRangeCheck/until.kt");
}
} }
@TestMetadata("idea/testData/intentions/convertUnsafeCastCallToUnsafeCast") @TestMetadata("idea/testData/intentions/convertUnsafeCastCallToUnsafeCast")