Merge pull request #392 from wutalman/simplify_booleans

KT-4569: Intention to simplify boolean expressions with constants #KT-4569 Fixed
This commit is contained in:
Natalia Ukhorskaya
2014-04-10 11:00:16 +04:00
79 changed files with 684 additions and 131 deletions
@@ -38,6 +38,7 @@ public class JavaPropertyInitializerEvaluatorImpl implements JavaPropertyInitial
evaluatedExpression,
DescriptorUtils.isPropertyCompileTimeConstant(descriptor),
false,
true,
descriptor.getType());
}
return null;
@@ -54,7 +54,6 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, compileTimeConstant)
return compileTimeConstant
}
return null
}
@@ -70,9 +69,9 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
return createStringConstant(this@ConstantExpressionEvaluator.evaluate(expression, KotlinBuiltIns.getInstance().getStringType()))
}
override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = StringValue(entry.getText(), true)
override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = StringValue(entry.getText(), true, false)
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = StringValue(entry.getUnescapedValue(), true)
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = StringValue(entry.getUnescapedValue(), true, false)
}
override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -92,8 +91,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
if (result == null) return null
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createCompileTimeConstant(result, expectedType, !isLongWithSuffix())
return createCompileTimeConstant(result, expectedType, !isLongWithSuffix(), true, false)
}
override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -118,6 +116,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val sb = StringBuilder()
var interupted = false
var canBeUsedInAnnotation = true
var usesVariableAsConstant = false
for (entry in expression.getEntries()) {
val constant = stringExpressionEvaluator.evaluate(entry)
if (constant == null) {
@@ -126,12 +125,14 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
}
else {
if (!constant.canBeUsedInAnnotations()) canBeUsedInAnnotation = false
if (constant.usesVariableAsConstant()) usesVariableAsConstant = true
sb.append(constant.getValue())
}
}
return if (!interupted)
createCompileTimeConstant(sb.toString(), expectedType,
isPure = true, canBeUsedInAnnotation = canBeUsedInAnnotation)
isPure = true, canBeUsedInAnnotation = canBeUsedInAnnotation,
usesVariableAsConstant = usesVariableAsConstant)
else null
}
@@ -160,7 +161,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
JetTokens.OROR -> leftValue as Boolean || rightValue as Boolean
else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}")
}
return createCompileTimeConstant(result, expectedType)
val usesVariableAsConstant = leftConstant.usesVariableAsConstant() || rightConstant.usesVariableAsConstant()
return createCompileTimeConstant(result, expectedType, true, true, usesVariableAsConstant)
}
else {
return evaluateCall(expression.getOperationReference(), leftExpression, expectedType)
@@ -182,12 +184,16 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
if (result == null) return null
val isArgumentPure = isPureConstant(argumentForReceiver.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression)
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression)
val isNumberConversionMethod = resultingDescriptorName in OperatorConventions.NUMBER_CONVERSIONS
return createCompileTimeConstant(result, expectedType, !isNumberConversionMethod && isArgumentPure, canBeUsedInAnnotation)
return createCompileTimeConstant(result,
expectedType,
!isNumberConversionMethod && isArgumentPure,
canBeUsedInAnnotation,
usesVariableAsConstant)
}
else if (argumentsEntrySet.size() == 1) {
val (parameter, argument) = argumentsEntrySet.first()
val argumentForParameter = createOperationArgumentForFirstParameter(argument, parameter)
if (argumentForParameter == null) return null
@@ -200,12 +206,13 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression)
val c = EvaluatorContext(canBeUsedInAnnotation, areArgumentsPure)
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression) || usesVariableAsConstant(argumentForParameter.expression)
val c = EvaluatorContext(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant)
return when(resultingDescriptorName) {
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, c)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, c)
else -> {
createCompileTimeConstant(result, expectedType, areArgumentsPure, canBeUsedInAnnotation)
createCompileTimeConstant(result, expectedType, areArgumentsPure, canBeUsedInAnnotation, usesVariableAsConstant)
}
}
}
@@ -213,7 +220,10 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
return null
}
private fun usesVariableAsConstant(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.usesVariableAsConstant() ?: false
private fun canBeUsedInAnnotation(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.canBeUsedInAnnotations() ?: false
private fun isPureConstant(expression: JetExpression): Boolean {
val compileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)
if (compileTimeConstant is IntegerValueConstant) {
@@ -287,7 +297,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val enumDescriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, expression);
if (enumDescriptor != null && DescriptorUtils.isEnumEntry(enumDescriptor)) {
return EnumValue(enumDescriptor as ClassDescriptor);
return EnumValue(enumDescriptor as ClassDescriptor, false);
}
val resolvedCall = trace.getBindingContext().get(BindingContext.RESOLVED_CALL, expression)
@@ -303,7 +313,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
else
compileTimeConstant.getValue()
return createCompileTimeConstant(value, expectedType, isPure = false,
canBeUsedInAnnotation = DescriptorUtils.isPropertyCompileTimeConstant(callableDescriptor))
canBeUsedInAnnotation = DescriptorUtils.isPropertyCompileTimeConstant(callableDescriptor),
usesVariableAsConstant = true)
}
}
return null
@@ -341,7 +352,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val varargType = resultingDescriptor.getValueParameters().first?.getVarargElementType()!!
val arguments = call.getValueArguments().values().flatMap { resolveArguments(it.getArguments(), varargType) }
return ArrayValue(arguments, resultingDescriptor.getReturnType()!!, true)
return ArrayValue(arguments, resultingDescriptor.getReturnType()!!, true, arguments.any() { it.usesVariableAsConstant() })
}
// Ann()
@@ -402,7 +413,6 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val argumentExpression = arguments.first().getArgumentExpression()
if (argumentExpression == null) return null
return createOperationArgument(argumentExpression, parameter.getType(), argumentCompileTimeType)
}
@@ -421,8 +431,12 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
return OperationArgument(evaluationResult, compileTimeType, expression)
}
fun createCompileTimeConstant(value: Any?, expectedType: JetType?, isPure: Boolean = true, canBeUsedInAnnotation: Boolean = true): CompileTimeConstant<*>? {
val c = EvaluatorContext(canBeUsedInAnnotation, isPure)
fun createCompileTimeConstant(value: Any?,
expectedType: JetType?,
isPure: Boolean = true,
canBeUsedInAnnotation: Boolean = true,
usesVariableAsConstant: Boolean = false): CompileTimeConstant<*>? {
val c = EvaluatorContext(canBeUsedInAnnotation, isPure, usesVariableAsConstant)
return createCompileTimeConstant(value, c, if (isPure) expectedType ?: TypeUtils.NO_EXPECTED_TYPE else null)
}
}
@@ -490,11 +504,11 @@ private fun createCompileTimeConstantForEquals(result: Any?, operationReference:
assert(operationReference is JetSimpleNameExpression, "This method should be called only for equals operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
return when (operationToken) {
JetTokens.EQEQ -> BooleanValue(result, c.canBeUsedInAnnotation)
JetTokens.EXCLEQ -> BooleanValue(!result, c.canBeUsedInAnnotation)
JetTokens.EQEQ -> BooleanValue(result, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.EXCLEQ -> BooleanValue(!result, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.IDENTIFIER -> {
assert ((operationReference as JetSimpleNameExpression).getReferencedNameAsName() == OperatorConventions.EQUALS, "This method should be called only for equals operations")
return BooleanValue(result, c.canBeUsedInAnnotation)
return BooleanValue(result, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
}
else -> throw IllegalStateException("Unknown equals operation token: $operationToken ${operationReference.getText()}")
}
@@ -507,13 +521,13 @@ private fun createCompileTimeConstantForCompareTo(result: Any?, operationReferen
assert(operationReference is JetSimpleNameExpression, "This method should be called only for compareTo operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
return when (operationToken) {
JetTokens.LT -> BooleanValue(result < 0, c.canBeUsedInAnnotation)
JetTokens.LTEQ -> BooleanValue(result <= 0, c.canBeUsedInAnnotation)
JetTokens.GT -> BooleanValue(result > 0, c.canBeUsedInAnnotation)
JetTokens.GTEQ -> BooleanValue(result >= 0, c.canBeUsedInAnnotation)
JetTokens.LT -> BooleanValue(result < 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.LTEQ -> BooleanValue(result <= 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.GT -> BooleanValue(result > 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.GTEQ -> BooleanValue(result >= 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.IDENTIFIER -> {
assert ((operationReference as JetSimpleNameExpression).getReferencedNameAsName() == OperatorConventions.COMPARE_TO, "This method should be called only for compareTo operations")
return IntValue(result, c.canBeUsedInAnnotation, c.isPure)
return IntValue(result, c.canBeUsedInAnnotation, c.isPure, c.usesVariableAsConstant)
}
else -> throw IllegalStateException("Unknown compareTo operation token: $operationToken")
}
@@ -523,19 +537,19 @@ private fun createCompileTimeConstantForCompareTo(result: Any?, operationReferen
private fun createStringConstant(value: CompileTimeConstant<*>?): StringValue? {
return when (value) {
is IntegerValueTypeConstant -> StringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString(), value.canBeUsedInAnnotations())
is IntegerValueTypeConstant -> StringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString(), value.canBeUsedInAnnotations(), value.usesVariableAsConstant())
is StringValue -> value
is IntValue, is ByteValue, is ShortValue, is LongValue,
is CharValue,
is DoubleValue, is FloatValue,
is BooleanValue,
is NullValue -> StringValue(value.getValue().toString(), value.canBeUsedInAnnotations())
is NullValue -> StringValue(value.getValue().toString(), value.canBeUsedInAnnotations(), value.usesVariableAsConstant())
else -> null
}
}
private fun createCompileTimeConstant(value: Any?, c: EvaluatorContext, expectedType: JetType? = null): CompileTimeConstant<*>? {
return createCompileTimeConstant(value, c.canBeUsedInAnnotation, c.isPure, expectedType)
return createCompileTimeConstant(value, c.canBeUsedInAnnotation, c.isPure, c.usesVariableAsConstant, expectedType)
}
fun isIntegerType(value: Any?) = value is Byte || value is Short || value is Int || value is Long
@@ -567,7 +581,7 @@ private fun getCompileTimeType(c: JetType): CompileTimeType<out Any>? {
}
}
private class EvaluatorContext(val canBeUsedInAnnotation: Boolean, val isPure: Boolean)
private class EvaluatorContext(val canBeUsedInAnnotation: Boolean, val isPure: Boolean, val usesVariableAsConstant: Boolean = false)
private class CompileTimeType<T>
@@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -282,11 +283,20 @@ public class AnnotationResolver {
trace);
if (argumentsAsVararg) {
boolean usesVariableAsConstant = KotlinPackage.any(constants, new Function1<CompileTimeConstant<?>, Boolean>() {
@Override
public Boolean invoke(CompileTimeConstant<?> constant) {
return constant.usesVariableAsConstant();
}
});
JetType arrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(varargElementType);
if (arrayType == null) {
arrayType = KotlinBuiltIns.getInstance().getArrayType(varargElementType);
}
return new ArrayValue(constants, arrayType, true);
return new ArrayValue(constants, arrayType, true, usesVariableAsConstant);
}
else {
// we should actually get only one element, but just in case of getting many, we take the last one
@@ -18,13 +18,11 @@ package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.jet.lang.psi.JetConstantExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.psi.JetTypeReference;
@@ -129,12 +127,10 @@ public class CompileTimeConstantUtils {
@NotNull BindingTrace trace,
@Nullable Boolean expectedValue
) {
if (!(expression instanceof JetConstantExpression) || expression.getNode().getElementType() != JetNodeTypes.BOOLEAN_CONSTANT) {
return false;
}
if (expression == null) return false;
CompileTimeConstant<?> compileTimeConstant =
ConstantExpressionEvaluator.object$.evaluate(expression, trace, KotlinBuiltIns.getInstance().getBooleanType());
if (!(compileTimeConstant instanceof BooleanValue)) return false;
if (!(compileTimeConstant instanceof BooleanValue) || compileTimeConstant.usesVariableAsConstant()) return false;
Boolean value = ((BooleanValue) compileTimeConstant).getValue();
return expectedValue == null || expectedValue.equals(value);
@@ -1,25 +1,25 @@
== main ==
fun main() {
while(1 > 0) {
while(0 > 1) {
2
}
}
---------------------
L0:
1 <START>
2 mark({ while(1 > 0) { 2 } })
mark(while(1 > 0) { 2 })
2 mark({ while(0 > 1) { 2 } })
mark(while(0 > 1) { 2 })
L2 [loop entry point]:
L5 [condition entry point]:
mark(1 > 0) PREV:[mark(while(1 > 0) { 2 }), jmp(L2 [loop entry point])]
r(1)
mark(0 > 1) PREV:[mark(while(0 > 1) { 2 }), jmp(L2 [loop entry point])]
r(0)
r(1)
call(>, compareTo)
jf(L3 [loop exit point]) NEXT:[read (Unit), mark({ 2 })]
L4 [body entry point]:
3 mark({ 2 })
r(2)
2 jmp(L2 [loop entry point]) NEXT:[mark(1 > 0)]
2 jmp(L2 [loop entry point]) NEXT:[mark(0 > 1)]
L3 [loop exit point]:
read (Unit) PREV:[jf(L3 [loop exit point])]
L1:
@@ -32,21 +32,21 @@ sink:
== dowhile ==
fun dowhile() {
do {return}
while(1 > 0)
while(0 > 1)
}
---------------------
L0:
1 <START>
2 mark({ do {return} while(1 > 0) })
3 mark(do {return} while(1 > 0))
2 mark({ do {return} while(0 > 1) })
3 mark(do {return} while(0 > 1))
L2 [loop entry point]:
L4 [body entry point]:
mark({return})
ret L1 NEXT:[<END>]
L5 [condition entry point]:
- mark(1 > 0) PREV:[]
- r(1) PREV:[]
- mark(0 > 1) PREV:[]
- r(0) PREV:[]
- r(1) PREV:[]
- call(>, compareTo) PREV:[]
- jt(L2 [loop entry point]) NEXT:[read (Unit), mark({return})] PREV:[]
L3 [loop exit point]:
@@ -1,10 +1,10 @@
fun main() {
while(1 > 0) {
while(0 > 1) {
2
}
}
fun dowhile() {
do {return}
while(1 > 0)
while(0 > 1)
}
@@ -1,9 +1,9 @@
fun test() {
[ann]
while (1 < 2) {}
while (2 < 1) {}
[ann]
do {} while (1 < 2)
do {} while (2 < 1)
[ann]
for (i in 1..2) {}
@@ -0,0 +1,12 @@
fun test() {
[ann]
while (2 > 1) {}
[ann]
<!UNREACHABLE_CODE!>do {} while (2 > 1)<!>
[ann]
<!UNREACHABLE_CODE!>for (i in 1..2) {}<!>
}
annotation class ann
@@ -0,0 +1,11 @@
package test
val x = 1
val y = "a"
// val prop1: true
val prop1 = x
// val prop2: true
val prop2 = y
@@ -0,0 +1,16 @@
package test
enum class MyEnum { A;B }
fun foo(): Boolean = true
val x = 1
// val prop1: null
val prop1 = MyEnum.A
// val prop2: null
val prop2 = foo()
// val prop3: true
val prop3 = "$x"
@@ -0,0 +1,29 @@
package test
val x = 1
val y = true
// val prop1: false
val prop1 = 1 > 2
// val prop2: false
val prop2 = 2 + 3
// val prop3: true
val prop3 = 2 + x
// val prop4: true
val prop4 = x < 2
// val prop5: true
val prop5 = y && true
// val prop6: false
val prop6 = true && false || 2 > 1
// val prop7: true
val prop7 = x == 1
// val prop8: true
val prop8 = 1 / x
@@ -0,0 +1,23 @@
package test
// val prop1: false
val prop1 = 1
// val prop2: false
val prop2 = "hello"
// val prop3: false
val prop3 = true
// val prop4: false
val prop4 = false
// val prop5: false
val prop5 = -7453
// val prop6: false
val prop6 = 3.56
// val prop7: false
val prop7 = 5464564L
@@ -616,6 +616,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/annotations/onLoops.kt");
}
@TestMetadata("onLoopsUnreachable.kt")
public void testOnLoopsUnreachable() throws Exception {
doTest("compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.kt");
}
@TestMetadata("onMultiDeclaration.kt")
public void testOnMultiDeclaration() throws Exception {
doTest("compiler/testData/diagnostics/tests/annotations/onMultiDeclaration.kt");
@@ -20,20 +20,18 @@ import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.resolve.annotation.AbstractAnnotationDescriptorResolveTest
import java.io.File
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.lang.psi.JetProperty
import org.jetbrains.jet.InTextDirectivesUtils
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import java.util.regex.Pattern
import org.intellij.lang.annotations.RegExp
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.jet.JetTestUtils
import org.jetbrains.jet.util.slicedmap.WritableSlice
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant
import org.jetbrains.jet.lang.resolve.constants.StringValue
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
import org.jetbrains.jet.lang.resolve.constants.IntegerValueConstant
import java.util.HashMap
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator
import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace
import org.jetbrains.jet.lang.types.TypeUtils
abstract class AbstractEvaluateExpressionTest : AbstractAnnotationDescriptorResolveTest() {
@@ -63,6 +61,19 @@ abstract class AbstractEvaluateExpressionTest : AbstractAnnotationDescriptorReso
}
}
// Test directives should look like [// val testedPropertyName: expectedValue]
fun doUsesVariableAsConstantTest(path: String) {
doTest(path) {
property, context ->
val compileTimeConstant = property.getCompileTimeInitializer()
if (compileTimeConstant == null) {
"null"
} else {
compileTimeConstant.usesVariableAsConstant().toString()
}
}
}
private fun doTest(path: String, getValueToTest: (VariableDescriptor, BindingContext) -> String) {
val myFile = File(path)
val fileText = FileUtil.loadFile(myFile, true)
@@ -30,7 +30,7 @@ import org.jetbrains.jet.evaluate.AbstractEvaluateExpressionTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@InnerTestClasses({EvaluateExpressionTestGenerated.Constant.class, EvaluateExpressionTestGenerated.IsPure.class})
@InnerTestClasses({EvaluateExpressionTestGenerated.Constant.class, EvaluateExpressionTestGenerated.IsPure.class, EvaluateExpressionTestGenerated.UsesVariableAsConstant.class})
public class EvaluateExpressionTestGenerated extends AbstractEvaluateExpressionTest {
@TestMetadata("compiler/testData/evaluate/constant")
public static class Constant extends AbstractEvaluateExpressionTest {
@@ -168,10 +168,39 @@ public class EvaluateExpressionTestGenerated extends AbstractEvaluateExpressionT
}
@TestMetadata("compiler/testData/evaluate/usesVariableAsConstant")
public static class UsesVariableAsConstant extends AbstractEvaluateExpressionTest {
public void testAllFilesPresentInUsesVariableAsConstant() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/evaluate/usesVariableAsConstant"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("binaryTypes.kt")
public void testBinaryTypes() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/binaryTypes.kt");
}
@TestMetadata("NamedConstants.kt")
public void testNamedConstants() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/NamedConstants.kt");
}
@TestMetadata("OtherTypes.kt")
public void testOtherTypes() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/OtherTypes.kt");
}
@TestMetadata("simpleTypes.kt")
public void testSimpleTypes() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/simpleTypes.kt");
}
}
public static Test suite() {
TestSuite suite = new TestSuite("EvaluateExpressionTestGenerated");
suite.addTestSuite(Constant.class);
suite.addTestSuite(IsPure.class);
suite.addTestSuite(UsesVariableAsConstant.class);
return suite;
}
}
@@ -108,7 +108,7 @@ class LazyJavaAnnotationDescriptor(
private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): CompileTimeConstant<*>? {
return when (argument) {
is JavaLiteralAnnotationArgument -> createCompileTimeConstant(argument.getValue(), true, false, null)
is JavaLiteralAnnotationArgument -> createCompileTimeConstant(argument.getValue(), true, false, false, null)
is JavaReferenceAnnotationArgument -> resolveFromReference(argument.resolve())
is JavaArrayAnnotationArgument -> resolveFromArray(argument.getName() ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements())
is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation())
@@ -137,7 +137,7 @@ class LazyJavaAnnotationDescriptor(
val values = elements.map {
argument -> resolveAnnotationArgument(argument) ?: NullValue.NULL
}
return ArrayValue(values, valueParameter.getType(), true)
return ArrayValue(values, valueParameter.getType(), true, values.any { it.usesVariableAsConstant() })
}
private fun resolveFromReference(element: JavaElement?): CompileTimeConstant<*>? {
@@ -154,7 +154,7 @@ class LazyJavaAnnotationDescriptor(
val classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(element.getName())
if (classifier !is ClassDescriptor) return null
return EnumValue(classifier)
return EnumValue(classifier, false)
}
private fun resolveFromJavaClassObjectType(javaType: JavaType): CompileTimeConstant<*>? {
@@ -126,7 +126,7 @@ public class AnnotationDescriptorDeserializer extends BaseDescriptorDeserializer
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
if (name != null) {
CompileTimeConstant<?> argument = ConstantsPackage.createCompileTimeConstant(value, true, false, null);
CompileTimeConstant<?> argument = ConstantsPackage.createCompileTimeConstant(value, true, false, false, null);
setArgumentValueByName(name, argument != null ? argument : ErrorValue.create("Unsupported annotation argument: " + name));
}
}
@@ -149,7 +149,7 @@ public class AnnotationDescriptorDeserializer extends BaseDescriptorDeserializer
if (enumClass.getKind() == ClassKind.ENUM_CLASS) {
ClassifierDescriptor classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(name);
if (classifier instanceof ClassDescriptor) {
return new EnumValue((ClassDescriptor) classifier);
return new EnumValue((ClassDescriptor) classifier, false);
}
}
return ErrorValue.create("Unresolved enum entry: " + enumClassName.getInternalName() + "." + name);
@@ -89,7 +89,7 @@ public class DescriptorDeserializersStorage {
MemberSignature signature = MemberSignature.fromFieldNameAndDesc(name, desc);
if (initializer != null) {
propertyConstants.put(signature, ConstantsPackage.createCompileTimeConstant(
initializer, /* canBeUsedInAnnotation */ true, /* isPureIntConstant */ true, /* expectedType */ null));
initializer, /* canBeUsedInAnnotation */ true, /* isPureIntConstant */ true, /* usesVariableAsConstant */ true, /* expectedType */ null));
}
return new MemberAnnotationVisitor(signature);
}
@@ -25,7 +25,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class AnnotationValue extends CompileTimeConstant<AnnotationDescriptor> {
public AnnotationValue(@NotNull AnnotationDescriptor value) {
super(value, true);
super(value, true, false, false);
}
@NotNull
@@ -27,8 +27,11 @@ public class ArrayValue extends CompileTimeConstant<List<CompileTimeConstant<?>>
private final JetType type;
public ArrayValue(@NotNull List<CompileTimeConstant<?>> value, @NotNull JetType type, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public ArrayValue(@NotNull List<CompileTimeConstant<?>> value,
@NotNull JetType type,
boolean canBeUsedInAnnotations,
boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
this.type = type;
}
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class BooleanValue extends CompileTimeConstant<Boolean> {
public BooleanValue(boolean value, boolean canBeUseInAnnotation) {
super(value, canBeUseInAnnotation);
public BooleanValue(boolean value, boolean canBeUseInAnnotation, boolean usesVariableAsConstant) {
super(value, canBeUseInAnnotation, false, usesVariableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class ByteValue extends IntegerValueConstant<Byte> {
public ByteValue(byte value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public ByteValue(byte value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVaraiableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVaraiableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class CharValue extends IntegerValueConstant<Character> {
public CharValue(char value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public CharValue(char value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -24,15 +24,38 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public abstract class CompileTimeConstant<T> {
protected final T value;
private final boolean canBeUsedInAnnotations;
private final int flags;
protected CompileTimeConstant(T value, boolean canBeUsedInAnnotations) {
/*
* if is pure is false then constant type cannot be changed
* ex1. val a: Long = 1.toInt() (TYPE_MISMATCH error, 1.toInt() isn't pure)
* ex2. val b: Int = a (TYPE_MISMATCH error, a isn't pure)
*
*/
private static final int IS_PURE_MASK = 1;
private static final int CAN_BE_USED_IN_ANNOTATIONS_MASK = 1 << 1;
private static final int USES_VARIABLE_AS_CONSTANT_MASK = 1 << 2;
protected CompileTimeConstant(T value,
boolean canBeUsedInAnnotations,
boolean isPure,
boolean usesVariableAsConstant) {
this.value = value;
this.canBeUsedInAnnotations = canBeUsedInAnnotations;
flags = (isPure ? IS_PURE_MASK : 0) |
(canBeUsedInAnnotations ? CAN_BE_USED_IN_ANNOTATIONS_MASK : 0) |
(usesVariableAsConstant ? USES_VARIABLE_AS_CONSTANT_MASK : 0);
}
public boolean canBeUsedInAnnotations() {
return canBeUsedInAnnotations;
return (flags & CAN_BE_USED_IN_ANNOTATIONS_MASK) != 0;
}
public boolean isPure() {
return (flags & IS_PURE_MASK) != 0;
}
public boolean usesVariableAsConstant() {
return (flags & USES_VARIABLE_AS_CONSTANT_MASK) != 0;
}
@Nullable
@@ -24,23 +24,24 @@ public fun createCompileTimeConstant(
value: Any?,
canBeUsedInAnnotation: Boolean,
isPureIntConstant: Boolean,
usesVariableAsConstant: Boolean = false,
expectedType: JetType? = null
): CompileTimeConstant<*>? {
if (expectedType == null) {
when(value) {
is Byte -> return ByteValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Short -> return ShortValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Int -> return IntValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Long -> return LongValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Byte -> return ByteValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Short -> return ShortValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Int -> return IntValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Long -> return LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
}
}
return when(value) {
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), canBeUsedInAnnotation, isPureIntConstant, expectedType)
is Char -> CharValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Float -> FloatValue(value, canBeUsedInAnnotation)
is Double -> DoubleValue(value, canBeUsedInAnnotation)
is Boolean -> BooleanValue(value, canBeUsedInAnnotation)
is String -> StringValue(value, canBeUsedInAnnotation)
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant, expectedType)
is Char -> CharValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Float -> FloatValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
is Double -> DoubleValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
is Boolean -> BooleanValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
is String -> StringValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
null -> NullValue.NULL
else -> null
}
@@ -50,30 +51,31 @@ private fun getIntegerValue(
value: Long,
canBeUsedInAnnotation: Boolean,
isPureIntConstant: Boolean,
usesVariableAsConstant: Boolean,
expectedType: JetType
): CompileTimeConstant<*>? {
fun defaultIntegerValue(value: Long) = when (value) {
value.toInt().toLong() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant)
else -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant)
value.toInt().toLong() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
}
if (TypeUtils.noExpectedType(expectedType) || expectedType.isError()) {
return IntegerValueTypeConstant(value, canBeUsedInAnnotation)
return IntegerValueTypeConstant(value, canBeUsedInAnnotation, usesVariableAsConstant)
}
val builtIns = KotlinBuiltIns.getInstance()
return when (TypeUtils.makeNotNullable(expectedType)) {
builtIns.getLongType() -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant)
builtIns.getLongType() -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
builtIns.getShortType() -> when (value) {
value.toShort().toLong() -> ShortValue(value.toShort(), canBeUsedInAnnotation, isPureIntConstant)
value.toShort().toLong() -> ShortValue(value.toShort(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> defaultIntegerValue(value)
}
builtIns.getByteType() -> when (value) {
value.toByte().toLong() -> ByteValue(value.toByte(), canBeUsedInAnnotation, isPureIntConstant)
value.toByte().toLong() -> ByteValue(value.toByte(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> defaultIntegerValue(value)
}
builtIns.getCharType() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant)
builtIns.getCharType() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> defaultIntegerValue(value)
}
}
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class DoubleValue extends CompileTimeConstant<Double> {
public DoubleValue(double value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public DoubleValue(double value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
}
@NotNull
@@ -24,8 +24,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class EnumValue extends CompileTimeConstant<ClassDescriptor> {
public EnumValue(@NotNull ClassDescriptor value) {
super(value, true);
public EnumValue(@NotNull ClassDescriptor value, boolean usesVariableAsConstant) {
super(value, true, false, usesVariableAsConstant);
}
@NotNull
@@ -25,7 +25,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public abstract class ErrorValue extends CompileTimeConstant<Void> {
public ErrorValue() {
super(null, true);
super(null, true, false, false);
}
@Override
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class FloatValue extends CompileTimeConstant<Float> {
public FloatValue(float value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public FloatValue(float value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class IntValue extends IntegerValueConstant<Integer> {
public IntValue(int value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public IntValue(int value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -18,19 +18,7 @@ package org.jetbrains.jet.lang.resolve.constants;
public abstract class IntegerValueConstant<T> extends CompileTimeConstant<T> {
/*
* if false then constant type cannot be changed
* ex1. val a: Long = 1.toInt() (TYPE_MISMATCH error, 1.toInt() isn't pure)
* ex2. val b: Int = a (TYPE_MISMATCH error, a isn't pure)
* */
private final boolean isPure;
protected IntegerValueConstant(T value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations);
isPure = pure;
}
public boolean isPure() {
return isPure;
protected IntegerValueConstant(T value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVaraiableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVaraiableAsConstant);
}
}
@@ -19,10 +19,9 @@ package org.jetbrains.jet.lang.resolve.constants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationArgumentVisitor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import java.util.Collections;
@@ -30,8 +29,8 @@ public class IntegerValueTypeConstant extends IntegerValueConstant<Number> {
private final IntegerValueTypeConstructor typeConstructor;
public IntegerValueTypeConstant(@NotNull Number value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations, true);
public IntegerValueTypeConstant(@NotNull Number value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, true, usesVariableAsConstant);
this.typeConstructor = new IntegerValueTypeConstructor(value.longValue());
}
@@ -24,7 +24,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class JavaClassValue extends CompileTimeConstant<JetType> {
public JavaClassValue(@NotNull JetType value) {
super(value, true);
super(value, true, false, false);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class LongValue extends IntegerValueConstant<Long> {
public LongValue(long value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public LongValue(long value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -26,7 +26,7 @@ public class NullValue extends CompileTimeConstant<Void> {
public static final NullValue NULL = new NullValue();
private NullValue() {
super(null, false);
super(null, false, false, false);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class ShortValue extends IntegerValueConstant<Short> {
public ShortValue(short value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public ShortValue(short value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class StringValue extends CompileTimeConstant<String> {
public StringValue(String value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public StringValue(String value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
}
@NotNull
@@ -242,6 +242,7 @@ fun main(args: Array<String>) {
testClass(javaClass<AbstractEvaluateExpressionTest>()) {
model("evaluate/constant", testMethod = "doConstantTest")
model("evaluate/isPure", testMethod = "doIsPureTest")
model("evaluate/usesVariableAsConstant", testMethod = "doUsesVariableAsConstantTest")
}
}
@@ -388,6 +389,7 @@ fun main(args: Array<String>) {
model("intentions/splitIf", testMethod = "doTestSplitIf")
model("intentions/replaceWithOperatorAssign", testMethod = "doTestReplaceWithOperatorAssign")
model("intentions/replaceWithTraditionalAssignment", testMethod = "doTestReplaceWithTraditionalAssignment")
model("intentions/simplifyBooleanWithConstants", testMethod = "doTestSimplifyBooleanWithConstants")
}
testClass(javaClass<AbstractJetInspectionTest>()) {
@@ -0,0 +1,3 @@
fun foo() {
val x = <spot>y</spot>
}
@@ -0,0 +1,3 @@
fun foo() {
val x = <spot>3 > 2 && y && true</spot>
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention simplifies a boolean expression that has parts which can be reduced to constants
</body>
</html>
+5
View File
@@ -627,6 +627,11 @@
<className>org.jetbrains.jet.plugin.intentions.ConvertNegatedExpressionWithDemorgansLawIntention</className>
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.jet.plugin.intentions.SimplifyBooleanWithConstantsIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.jet.plugin.inspections.ExplicitGetInspection"
displayName="Explicit 'get'"
@@ -291,6 +291,8 @@ replace.with.operator.assign.intention=Replace with an Operator-Assign Expressio
replace.with.operator.assign.intention.family=Replace with an Operator-Assign Expression
replace.with.traditional.assignment.intention=Replace with Traditional Assignment
replace.with.traditional.assignment.intention.family=Replace with Traditional Assignment
simplify.boolean.with.constants=Simplify boolean expression
simplify.boolean.with.constants.family=Simplify boolean expression
property.is.implemented.too.many=Has implementations
property.is.overridden.too.many=Is overridden in subclasses
@@ -0,0 +1,137 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.intentions
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.jet.lang.psi.JetPsiUtil
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.psi.JetParenthesizedExpression
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.lang.resolve.CompileTimeConstantUtils
import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import com.intellij.psi.tree.IElementType
public class SimplifyBooleanWithConstantsIntention : JetSelfTargetingIntention<JetBinaryExpression>(
"simplify.boolean.with.constants", javaClass()) {
private var topParent : JetBinaryExpression? = null
override fun isApplicableTo(element: JetBinaryExpression): Boolean {
topParent = JetPsiUtil.getTopmostParentOfTypes(element, element.javaClass) as JetBinaryExpression? ?: element
return areThereExpressionsToBeSimplified(topParent)
}
private fun areThereExpressionsToBeSimplified(element: JetExpression?) : Boolean {
if (element == null) return false
when (element) {
is JetParenthesizedExpression -> return areThereExpressionsToBeSimplified(element.getExpression())
is JetBinaryExpression -> {
val op = element.getOperationToken()
if ((op == JetTokens.ANDAND || op == JetTokens.OROR) &&
(areThereExpressionsToBeSimplified(element.getLeft()) ||
areThereExpressionsToBeSimplified(element.getRight()))) return true
}
}
return element.canBeReducedToBooleanConstant(null)
}
override fun applyTo(element: JetBinaryExpression, editor: Editor) {
// we know from isApplicableTo that topParent is not null
val simplified = simplifyBoolean(topParent!!)
if (simplified is JetParenthesizedExpression) {
val expr = simplified.getExpression()
if (expr != null) {
// this extra check is for the case where there are empty parentheses ()
topParent!!.replace(expr)
return
}
}
topParent!!.replace(simplified)
}
private fun simplifyBoolean(element: JetExpression) : JetExpression {
if (element.canBeReducedToTrue())
return JetPsiFactory.createExpression(element.getProject(), "true")
if (element.canBeReducedToFalse())
return JetPsiFactory.createExpression(element.getProject(), "false")
when (element) {
is JetParenthesizedExpression -> {
val expr = element.getExpression()
if (expr == null) return element
val simplified = simplifyBoolean(expr)
if (expr == simplified) return element
if (simplified is JetBinaryExpression) {
val simpText = simplified.getText()
if (simpText == null) return element
// wrap in new parentheses to keep the user's original format
return JetPsiFactory.createExpression(element.getProject(), "($simpText)")
}
// if we now have a simpleName, constant, or parenthesized we don't need parentheses
return simplified
}
is JetBinaryExpression -> {
val left = element.getLeft()
val right = element.getRight()
val op = element.getOperationToken()
if (left == null || right == null || op == null || (op != JetTokens.ANDAND && op != JetTokens.OROR))
return element
val simpleLeft = simplifyBoolean(left)
val simpleRight = simplifyBoolean(right)
if (simpleLeft.canBeReducedToTrue() || simpleLeft.canBeReducedToFalse())
return simplifyBooleanBinaryExpressionWithConstantOperand(simpleLeft, simpleRight, op)
if (simpleRight.canBeReducedToTrue() || simpleRight.canBeReducedToFalse())
return simplifyBooleanBinaryExpressionWithConstantOperand(simpleRight, simpleLeft, op)
val opText = element.getOperationReference().getText()
if (opText == null) return element
return JetPsiFactory.createBinaryExpression(element.getProject(), simpleLeft, opText, simpleRight)
}
else -> return element
}
}
private fun simplifyBooleanBinaryExpressionWithConstantOperand(
booleanConstantOperand: JetExpression,
otherOperand: JetExpression,
operation: IElementType
): JetExpression {
assert(booleanConstantOperand.canBeReducedToBooleanConstant(null), "should only be called when we know it can be reduced")
if (booleanConstantOperand.canBeReducedToTrue() && operation == JetTokens.OROR)
return JetPsiFactory.createExpression(otherOperand.getProject(), "true")
if (booleanConstantOperand.canBeReducedToFalse() && operation == JetTokens.ANDAND)
return JetPsiFactory.createExpression(otherOperand.getProject(), "false")
return simplifyBoolean(otherOperand)
}
private fun JetExpression.canBeReducedToBooleanConstant(constant: Boolean?): Boolean {
val bindingContext = AnalyzerFacadeWithCache.getContextForElement(this)
val trace = DelegatingBindingTrace(bindingContext, "trace for constant check")
return CompileTimeConstantUtils.canBeReducedToBooleanConstant(this, trace, constant)
}
private fun JetExpression.canBeReducedToTrue(): Boolean {
return this.canBeReducedToBooleanConstant(true)
}
private fun JetExpression.canBeReducedToFalse(): Boolean {
return this.canBeReducedToBooleanConstant(false)
}
}
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
fun foo(y: Boolean) {
y && <caret>y || y
}
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
fun foo(y: Boolean) {
<caret>true
}
@@ -0,0 +1,6 @@
// IS_APPLICABLE: false
fun foo(y: Boolean) {
val x = 4
val z = 5
<caret>x < z
}
@@ -0,0 +1,6 @@
// IS_APPLICABLE: false
fun foo(y: Boolean) {
val x = 4
val z = 5
<caret>x == z || x != z
}
@@ -0,0 +1,6 @@
// IS_APPLICABLE: false
fun foo(y: Boolean) {
val x = true
val z = false
<caret>x && z
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
<caret>2 > 1
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
true
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
<caret>2 > 1 && y || y || (3 + 3 > 10)
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y || y
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
<caret>3 != 3 && 2 > 1 || y
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y
}
@@ -0,0 +1,4 @@
fun foo(y: Boolean) {
val x = 3
<caret>x != x && (2 > 1 || y)
}
@@ -0,0 +1,4 @@
fun foo(y: Boolean) {
val x = 3
x != x
}
@@ -0,0 +1,3 @@
fun foo() {
val x = <caret>true && false || true
}
@@ -0,0 +1,3 @@
fun foo() {
val x = true
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
(y && false) || (y && y && true && (y && true))<caret> && false && true
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
false
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
false || false || y || y ||<caret> false || y && y || y
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y || y || y && y || y
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y || false && true || <caret>false || false || false || y && true
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y || y
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
false || false || y || y || <caret>false && (y && y || true)
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y || y
}
@@ -0,0 +1,5 @@
fun foo(y: Boolean) {
bar() && y || <caret>(y && true && bar()) || false
}
fun bar(): Boolean = false
@@ -0,0 +1,5 @@
fun foo(y: Boolean) {
bar() && y || (y && bar())
}
fun bar(): Boolean = false
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
((y && true) || false) <caret>&& (true && (y && (y && (y ||false))))
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y && (y && (y && y))
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
<caret>true && () && y
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
() && y
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
(false)<caret> && true
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
false
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y || <caret>(y && true)
}
@@ -0,0 +1,3 @@
fun foo(y: Boolean) {
y || y
}
@@ -227,6 +227,10 @@ public abstract class AbstractCodeTransformationTest extends LightCodeInsightTes
doTestIntention(path, new ReplaceWithTraditionalAssignmentIntention());
}
public void doTestSimplifyBooleanWithConstants(@NotNull String path) throws Exception {
doTestIntention(path, new SimplifyBooleanWithConstantsIntention());
}
private void doTestIntention(@NotNull String path, @NotNull IntentionAction intentionAction) throws Exception {
configureByFile(path);
@@ -30,7 +30,7 @@ import org.jetbrains.jet.plugin.intentions.AbstractCodeTransformationTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@InnerTestClasses({CodeTransformationTestGenerated.ElvisToIfThen.class, CodeTransformationTestGenerated.IfThenToElvis.class, CodeTransformationTestGenerated.SafeAccessToIfThen.class, CodeTransformationTestGenerated.IfThenToSafeAccess.class, CodeTransformationTestGenerated.IfToAssignment.class, CodeTransformationTestGenerated.IfToReturn.class, CodeTransformationTestGenerated.IfToReturnAsymmetrically.class, CodeTransformationTestGenerated.WhenToAssignment.class, CodeTransformationTestGenerated.WhenToReturn.class, CodeTransformationTestGenerated.AssignmentToIf.class, CodeTransformationTestGenerated.AssignmentToWhen.class, CodeTransformationTestGenerated.PropertyToIf.class, CodeTransformationTestGenerated.PropertyToWhen.class, CodeTransformationTestGenerated.ReturnToIf.class, CodeTransformationTestGenerated.ReturnToWhen.class, CodeTransformationTestGenerated.IfToWhen.class, CodeTransformationTestGenerated.WhenToIf.class, CodeTransformationTestGenerated.Flatten.class, CodeTransformationTestGenerated.Merge.class, CodeTransformationTestGenerated.IntroduceSubject.class, CodeTransformationTestGenerated.EliminateSubject.class, CodeTransformationTestGenerated.Split.class, CodeTransformationTestGenerated.Join.class, CodeTransformationTestGenerated.ConvertMemberToExtension.class, CodeTransformationTestGenerated.ReconstructedType.class, CodeTransformationTestGenerated.RemoveUnnecessaryParentheses.class, CodeTransformationTestGenerated.ReplaceWithDotQualifiedMethodCall.class, CodeTransformationTestGenerated.ReplaceWithInfixFunctionCall.class, CodeTransformationTestGenerated.RemoveCurlyBracesFromTemplate.class, CodeTransformationTestGenerated.MoveLambdaInsideParentheses.class, CodeTransformationTestGenerated.MoveLambdaOutsideParentheses.class, CodeTransformationTestGenerated.ReplaceExplicitFunctionLiteralParamWithIt.class, CodeTransformationTestGenerated.ReplaceItWithExplicitFunctionLiteralParam.class, CodeTransformationTestGenerated.RemoveBraces.class, CodeTransformationTestGenerated.AddBraces.class, CodeTransformationTestGenerated.ReplaceGetIntention.class, CodeTransformationTestGenerated.ReplaceContainsIntention.class, CodeTransformationTestGenerated.ReplaceBinaryInfixIntention.class, CodeTransformationTestGenerated.ReplaceUnaryPrefixIntention.class, CodeTransformationTestGenerated.ReplaceInvokeIntention.class, CodeTransformationTestGenerated.SimplifyNegatedBinaryExpressionIntention.class, CodeTransformationTestGenerated.ConvertNegatedBooleanSequence.class, CodeTransformationTestGenerated.ConvertNegatedExpressionWithDemorgansLaw.class, CodeTransformationTestGenerated.SwapBinaryExpression.class, CodeTransformationTestGenerated.SplitIf.class, CodeTransformationTestGenerated.ReplaceWithOperatorAssign.class, CodeTransformationTestGenerated.ReplaceWithTraditionalAssignment.class})
@InnerTestClasses({CodeTransformationTestGenerated.ElvisToIfThen.class, CodeTransformationTestGenerated.IfThenToElvis.class, CodeTransformationTestGenerated.SafeAccessToIfThen.class, CodeTransformationTestGenerated.IfThenToSafeAccess.class, CodeTransformationTestGenerated.IfToAssignment.class, CodeTransformationTestGenerated.IfToReturn.class, CodeTransformationTestGenerated.IfToReturnAsymmetrically.class, CodeTransformationTestGenerated.WhenToAssignment.class, CodeTransformationTestGenerated.WhenToReturn.class, CodeTransformationTestGenerated.AssignmentToIf.class, CodeTransformationTestGenerated.AssignmentToWhen.class, CodeTransformationTestGenerated.PropertyToIf.class, CodeTransformationTestGenerated.PropertyToWhen.class, CodeTransformationTestGenerated.ReturnToIf.class, CodeTransformationTestGenerated.ReturnToWhen.class, CodeTransformationTestGenerated.IfToWhen.class, CodeTransformationTestGenerated.WhenToIf.class, CodeTransformationTestGenerated.Flatten.class, CodeTransformationTestGenerated.Merge.class, CodeTransformationTestGenerated.IntroduceSubject.class, CodeTransformationTestGenerated.EliminateSubject.class, CodeTransformationTestGenerated.Split.class, CodeTransformationTestGenerated.Join.class, CodeTransformationTestGenerated.ConvertMemberToExtension.class, CodeTransformationTestGenerated.ReconstructedType.class, CodeTransformationTestGenerated.RemoveUnnecessaryParentheses.class, CodeTransformationTestGenerated.ReplaceWithDotQualifiedMethodCall.class, CodeTransformationTestGenerated.ReplaceWithInfixFunctionCall.class, CodeTransformationTestGenerated.RemoveCurlyBracesFromTemplate.class, CodeTransformationTestGenerated.MoveLambdaInsideParentheses.class, CodeTransformationTestGenerated.MoveLambdaOutsideParentheses.class, CodeTransformationTestGenerated.ReplaceExplicitFunctionLiteralParamWithIt.class, CodeTransformationTestGenerated.ReplaceItWithExplicitFunctionLiteralParam.class, CodeTransformationTestGenerated.RemoveBraces.class, CodeTransformationTestGenerated.AddBraces.class, CodeTransformationTestGenerated.ReplaceGetIntention.class, CodeTransformationTestGenerated.ReplaceContainsIntention.class, CodeTransformationTestGenerated.ReplaceBinaryInfixIntention.class, CodeTransformationTestGenerated.ReplaceUnaryPrefixIntention.class, CodeTransformationTestGenerated.ReplaceInvokeIntention.class, CodeTransformationTestGenerated.SimplifyNegatedBinaryExpressionIntention.class, CodeTransformationTestGenerated.ConvertNegatedBooleanSequence.class, CodeTransformationTestGenerated.ConvertNegatedExpressionWithDemorgansLaw.class, CodeTransformationTestGenerated.SwapBinaryExpression.class, CodeTransformationTestGenerated.SplitIf.class, CodeTransformationTestGenerated.ReplaceWithOperatorAssign.class, CodeTransformationTestGenerated.ReplaceWithTraditionalAssignment.class, CodeTransformationTestGenerated.SimplifyBooleanWithConstants.class})
public class CodeTransformationTestGenerated extends AbstractCodeTransformationTest {
@TestMetadata("idea/testData/intentions/branched/elvisToIfThen")
public static class ElvisToIfThen extends AbstractCodeTransformationTest {
@@ -3224,6 +3224,109 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
}
@TestMetadata("idea/testData/intentions/simplifyBooleanWithConstants")
public static class SimplifyBooleanWithConstants extends AbstractCodeTransformationTest {
public void testAllFilesPresentInSimplifyBooleanWithConstants() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/intentions/simplifyBooleanWithConstants"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("inapplicableNoConstants.kt")
public void testInapplicableNoConstants() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/inapplicableNoConstants.kt");
}
@TestMetadata("inapplicableNotBinary.kt")
public void testInapplicableNotBinary() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/inapplicableNotBinary.kt");
}
@TestMetadata("inapplicableUsesVals.kt")
public void testInapplicableUsesVals() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/inapplicableUsesVals.kt");
}
@TestMetadata("inapplicableUsesVals2.kt")
public void testInapplicableUsesVals2() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/inapplicableUsesVals2.kt");
}
@TestMetadata("inapplicableUsesVals3.kt")
public void testInapplicableUsesVals3() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/inapplicableUsesVals3.kt");
}
@TestMetadata("reduceableBinary.kt")
public void testReduceableBinary() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/reduceableBinary.kt");
}
@TestMetadata("reduceableBinary2.kt")
public void testReduceableBinary2() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/reduceableBinary2.kt");
}
@TestMetadata("reduceableBinary3.kt")
public void testReduceableBinary3() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/reduceableBinary3.kt");
}
@TestMetadata("reduceableBinaryWithParenthese.kt")
public void testReduceableBinaryWithParenthese() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/reduceableBinaryWithParenthese.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simple.kt");
}
@TestMetadata("simpleWithMoreBinaries.kt")
public void testSimpleWithMoreBinaries() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithMoreBinaries.kt");
}
@TestMetadata("simpleWithMoreBinaries2.kt")
public void testSimpleWithMoreBinaries2() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithMoreBinaries2.kt");
}
@TestMetadata("simpleWithMoreBinaries3.kt")
public void testSimpleWithMoreBinaries3() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithMoreBinaries3.kt");
}
@TestMetadata("simpleWithMoreBinariesAndParentheses.kt")
public void testSimpleWithMoreBinariesAndParentheses() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithMoreBinariesAndParentheses.kt");
}
@TestMetadata("simpleWithMoreBinariesAndParentheses2.kt")
public void testSimpleWithMoreBinariesAndParentheses2() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithMoreBinariesAndParentheses2.kt");
}
@TestMetadata("simpleWithMoreBinariesAndParentheses3.kt")
public void testSimpleWithMoreBinariesAndParentheses3() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithMoreBinariesAndParentheses3.kt");
}
@TestMetadata("simpleWithNonsensical2.kt")
public void testSimpleWithNonsensical2() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithNonsensical2.kt");
}
@TestMetadata("simpleWithParentheses.kt")
public void testSimpleWithParentheses() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithParentheses.kt");
}
@TestMetadata("simpleWithParentheses2.kt")
public void testSimpleWithParentheses2() throws Exception {
doTestSimplifyBooleanWithConstants("idea/testData/intentions/simplifyBooleanWithConstants/simpleWithParentheses2.kt");
}
}
public static Test suite() {
TestSuite suite = new TestSuite("CodeTransformationTestGenerated");
suite.addTestSuite(ElvisToIfThen.class);
@@ -3273,6 +3376,7 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
suite.addTestSuite(SplitIf.class);
suite.addTestSuite(ReplaceWithOperatorAssign.class);
suite.addTestSuite(ReplaceWithTraditionalAssignment.class);
suite.addTestSuite(SimplifyBooleanWithConstants.class);
return suite;
}
}