Add intention to evaluate compile time expression
#KT-40251 Fixed
This commit is contained in:
committed by
klunnii
parent
8cac3f654c
commit
d9cf4ee732
+1
@@ -0,0 +1 @@
|
||||
val x = 5
|
||||
+1
@@ -0,0 +1 @@
|
||||
val x = 2 + 3
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention replaces compile-time constant expression with its actual value.
|
||||
</body>
|
||||
</html>
|
||||
@@ -2224,6 +2224,7 @@ inspection.redundant.nullable.return.type.display.name=Redundant nullable return
|
||||
0.is.always.non.null.type=''{0}'' is always non-null type
|
||||
inspection.simplifiable.scope.function.display.name=Scope function with nested forEach can be simplified
|
||||
nested.1.call.in.0.could.be.simplified.to.2=Nested ''{1}'' call in ''{0}'' could be simplified to {2}
|
||||
evaluate.compile.time.expression=Evaluate compile-time expression
|
||||
|
||||
hints.title.codevision=Code Vision
|
||||
hints.title.codevision.show.hints.for=Show hints for:
|
||||
|
||||
@@ -835,6 +835,10 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.EvaluateCompileTimeExpressionIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<lang.inspectionSuppressor language="kotlin" implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinInspectionSuppressor"/>
|
||||
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class EvaluateCompileTimeExpressionIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(
|
||||
KtBinaryExpression::class.java,
|
||||
KotlinBundle.lazyMessage("evaluate.compile.time.expression")
|
||||
) {
|
||||
companion object {
|
||||
val constantNodeTypes = listOf(KtNodeTypes.FLOAT_CONSTANT, KtNodeTypes.CHARACTER_CONSTANT, KtNodeTypes.INTEGER_CONSTANT)
|
||||
}
|
||||
|
||||
override fun isApplicableTo(element: KtBinaryExpression): Boolean {
|
||||
if (element.getStrictParentOfType<KtBinaryExpression>() != null || !element.isConstantExpression()) return false
|
||||
val constantValue = element.getConstantValue() ?: return false
|
||||
setTextGetter { KotlinBundle.message("replace.with.0", constantValue) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtBinaryExpression, editor: Editor?) {
|
||||
val constantValue = element.getConstantValue() ?: return
|
||||
element.replace(KtPsiFactory(element).createExpression(constantValue))
|
||||
}
|
||||
|
||||
private fun KtExpression?.isConstantExpression(): Boolean {
|
||||
return when (val expression = KtPsiUtil.deparenthesize(this)) {
|
||||
is KtConstantExpression -> expression.elementType in constantNodeTypes
|
||||
is KtPrefixExpression -> expression.baseExpression.isConstantExpression()
|
||||
is KtBinaryExpression -> expression.left.isConstantExpression() && expression.right.isConstantExpression()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtBinaryExpression.getConstantValue(): String? {
|
||||
val context = analyze(BodyResolveMode.PARTIAL)
|
||||
val type = getType(context) ?: return null
|
||||
val constantValue = ConstantExpressionEvaluator.getConstant(this, context)?.toConstantValue(type) ?: return null
|
||||
return when (val value = constantValue.value) {
|
||||
is Char -> "'${StringUtil.escapeStringCharacters(value.toString())}'"
|
||||
is Long -> "${value}L"
|
||||
is Float -> when {
|
||||
value.isNaN() -> "Float.NaN"
|
||||
value.isInfinite() -> if (value > 0.0f) "Float.POSITIVE_INFINITY" else "Float.NEGATIVE_INFINITY"
|
||||
else -> "${value}f"
|
||||
}
|
||||
is Double -> when {
|
||||
value.isNaN() -> "Double.NaN"
|
||||
value.isInfinite() -> if (value > 0.0) "Double.POSITIVE_INFINITY" else "Double.NEGATIVE_INFINITY"
|
||||
else -> value.toString()
|
||||
}
|
||||
else -> value.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.EvaluateCompileTimeExpressionIntention
|
||||
@@ -0,0 +1,2 @@
|
||||
// IS_APPLICABLE: false
|
||||
val x = <caret>true && false
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with ''b''"
|
||||
val x = <caret>'a' + 1
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with ''b''"
|
||||
val x = 'b'
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with ''\n''"
|
||||
val x = <caret>'\t' + 1
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with ''\n''"
|
||||
val x = '\n'
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '3.0'"
|
||||
val x = <caret>1.0 + 2.0
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '3.0'"
|
||||
val x = 3.0
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Double.NaN'"
|
||||
val x = <caret>0.0 / 0.0
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Double.NaN'"
|
||||
val x = Double.NaN
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Double.NEGATIVE_INFINITY'"
|
||||
val x = <caret>-1.0 / 0.0
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Double.NEGATIVE_INFINITY'"
|
||||
val x = Double.NEGATIVE_INFINITY
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Double.POSITIVE_INFINITY'"
|
||||
val x = <caret>1.0 / 0.0
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Double.POSITIVE_INFINITY'"
|
||||
val x = Double.POSITIVE_INFINITY
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '3.0f'"
|
||||
val x = <caret>1.0f + 2.0f
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '3.0f'"
|
||||
val x = 3.0f
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Float.NaN'"
|
||||
val x = <caret>0.0f / 0.0f
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Float.NaN'"
|
||||
val x = Float.NaN
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Float.NEGATIVE_INFINITY'"
|
||||
val x = <caret>-1.0f / 0.0f
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Float.NEGATIVE_INFINITY'"
|
||||
val x = Float.NEGATIVE_INFINITY
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Float.POSITIVE_INFINITY'"
|
||||
val x = <caret>1.0f / 0.0f
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with 'Float.POSITIVE_INFINITY'"
|
||||
val x = Float.POSITIVE_INFINITY
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '5'"
|
||||
val x = <caret>2 + 3
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '5'"
|
||||
val x = 5
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '5L'"
|
||||
val x = 2L + 3L<caret>
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '5L'"
|
||||
val x = 5L
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '14'"
|
||||
val x = ((1 + 2) * (-3 + 4))<caret> + (5 + 6)
|
||||
@@ -0,0 +1,2 @@
|
||||
// INTENTION_TEXT: "Replace with '14'"
|
||||
val x = 14
|
||||
@@ -0,0 +1,2 @@
|
||||
// IS_APPLICABLE: false
|
||||
val x = <caret>"a" + "b"
|
||||
@@ -0,0 +1,3 @@
|
||||
// IS_APPLICABLE: false
|
||||
val i = 3
|
||||
val x = <caret>2 + i
|
||||
@@ -8804,6 +8804,99 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/evaluateCompileTimeExpression")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class EvaluateCompileTimeExpression extends AbstractIntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInEvaluateCompileTimeExpression() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/evaluateCompileTimeExpression"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("boolean.kt")
|
||||
public void testBoolean() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/boolean.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("char.kt")
|
||||
public void testChar() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/char.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("char2.kt")
|
||||
public void testChar2() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/char2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("double.kt")
|
||||
public void testDouble() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/double.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleNaN.kt")
|
||||
public void testDoubleNaN() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/doubleNaN.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("doubleNegativeInfinity.kt")
|
||||
public void testDoubleNegativeInfinity() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/doubleNegativeInfinity.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("doublePositiveInfinity.kt")
|
||||
public void testDoublePositiveInfinity() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/doublePositiveInfinity.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("float.kt")
|
||||
public void testFloat() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/float.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("floatNaN.kt")
|
||||
public void testFloatNaN() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/floatNaN.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("floatNegativeInfinity.kt")
|
||||
public void testFloatNegativeInfinity() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/floatNegativeInfinity.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("floatPositiveInfinity.kt")
|
||||
public void testFloatPositiveInfinity() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/floatPositiveInfinity.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("int.kt")
|
||||
public void testInt() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/int.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("long.kt")
|
||||
public void testLong() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/long.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parens.kt")
|
||||
public void testParens() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/parens.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("string.kt")
|
||||
public void testString() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/string.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("variable.kt")
|
||||
public void testVariable() throws Exception {
|
||||
runTest("idea/testData/intentions/evaluateCompileTimeExpression/variable.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/expandBooleanExpression")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user