Report warning on usages of non-const vals in places where constants expected
This commit is contained in:
@@ -141,6 +141,8 @@ public interface Errors {
|
||||
DiagnosticFactory0<PsiElement> CONST_VAL_WITHOUT_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetExpression> CONST_VAL_WITH_NON_CONST_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<JetExpression> NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> INAPPLICABLE_TARGET_ON_PROPERTY = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
+1
@@ -602,6 +602,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(TYPE_CANT_BE_USED_FOR_CONST_VAL, "Const 'val' has type ''{0}''. Only primitives and String are allowed", RENDER_TYPE);
|
||||
MAP.put(CONST_VAL_WITHOUT_INITIALIZER, "Const 'val' should have an initializer");
|
||||
MAP.put(CONST_VAL_WITH_NON_CONST_INITIALIZER, "Const 'val' initializer should be a constant value");
|
||||
MAP.put(NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION, "Only 'const val' can be used in constant expressions");
|
||||
|
||||
MAP.put(DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE, "An overriding function is not allowed to specify default values for its parameters");
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsPackage;
|
||||
@@ -880,7 +881,7 @@ public class DescriptorResolver {
|
||||
}
|
||||
|
||||
private void setConstantForVariableIfNeeded(
|
||||
@NotNull VariableDescriptorWithInitializerImpl variableDescriptor,
|
||||
@NotNull final VariableDescriptorWithInitializerImpl variableDescriptor,
|
||||
@NotNull final LexicalScope scope,
|
||||
@NotNull final JetVariableDeclaration variable,
|
||||
@NotNull final DataFlowInfo dataFlowInfo,
|
||||
@@ -898,7 +899,15 @@ public class DescriptorResolver {
|
||||
public ConstantValue<?> invoke() {
|
||||
JetExpression initializer = variable.getInitializer();
|
||||
JetType initializerType = expressionTypingServices.safeGetType(scope, initializer, variableType, dataFlowInfo, trace);
|
||||
return constantExpressionEvaluator.evaluateToConstantValue(initializer, trace, initializerType);
|
||||
CompileTimeConstant<?> constant = constantExpressionEvaluator.evaluateExpression(initializer, trace, initializerType);
|
||||
|
||||
if (constant == null) return null;
|
||||
|
||||
if (constant.getUsesNonConstValAsConstant() && variableDescriptor.isConst()) {
|
||||
trace.report(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.on(initializer));
|
||||
}
|
||||
|
||||
return constant.toConstantValue(initializerType);
|
||||
}
|
||||
}, null)
|
||||
);
|
||||
|
||||
+25
-10
@@ -124,6 +124,9 @@ public class ConstantExpressionEvaluator(
|
||||
|
||||
val constant = ConstantExpressionEvaluator.getConstant(argumentExpression, trace.getBindingContext())
|
||||
if (constant != null && constant.canBeUsedInAnnotations) {
|
||||
if (constant.usesNonConstValAsConstant) {
|
||||
trace.report(Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.on(argumentExpression))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -284,7 +287,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
} ?: return null
|
||||
|
||||
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
|
||||
return createConstant(result, expectedType, CompileTimeConstant.Parameters(true, !isLongWithSuffix(), false))
|
||||
return createConstant(result, expectedType, CompileTimeConstant.Parameters(true, !isLongWithSuffix(), false, usesNonConstValAsConstant = false))
|
||||
}
|
||||
|
||||
override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? {
|
||||
@@ -308,6 +311,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
var interupted = false
|
||||
var canBeUsedInAnnotation = true
|
||||
var usesVariableAsConstant = false
|
||||
var usesNonConstantVariableAsConstant = false
|
||||
for (entry in expression.getEntries()) {
|
||||
val constant = stringExpressionEvaluator.evaluate(entry)
|
||||
if (constant == null) {
|
||||
@@ -317,6 +321,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
else {
|
||||
if (!constant.canBeUsedInAnnotations) canBeUsedInAnnotation = false
|
||||
if (constant.usesVariableAsConstant) usesVariableAsConstant = true
|
||||
if (constant.usesNonConstValAsConstant) usesNonConstantVariableAsConstant = true
|
||||
sb.append(constant.constantValue.value)
|
||||
}
|
||||
}
|
||||
@@ -327,7 +332,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
CompileTimeConstant.Parameters(
|
||||
isPure = false,
|
||||
canBeUsedInAnnotation = canBeUsedInAnnotation,
|
||||
usesVariableAsConstant = usesVariableAsConstant
|
||||
usesVariableAsConstant = usesVariableAsConstant,
|
||||
usesNonConstValAsConstant = usesNonConstantVariableAsConstant
|
||||
)
|
||||
)
|
||||
else null
|
||||
@@ -363,7 +369,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
CompileTimeConstant.Parameters(
|
||||
canBeUsedInAnnotation = true,
|
||||
isPure = false,
|
||||
usesVariableAsConstant = leftConstant.usesVariableAsConstant || rightConstant.usesVariableAsConstant
|
||||
usesVariableAsConstant = leftConstant.usesVariableAsConstant || rightConstant.usesVariableAsConstant,
|
||||
usesNonConstValAsConstant = leftConstant.usesNonConstValAsConstant || rightConstant.usesNonConstValAsConstant
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -388,6 +395,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
val isArgumentPure = isPureConstant(argumentForReceiver.expression)
|
||||
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression)
|
||||
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression)
|
||||
val usesNonConstValAsConstant = usesNonConstValAsConstant(argumentForReceiver.expression)
|
||||
val isNumberConversionMethod = resultingDescriptorName in OperatorConventions.NUMBER_CONVERSIONS
|
||||
return createConstant(
|
||||
result,
|
||||
@@ -395,7 +403,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
CompileTimeConstant.Parameters(
|
||||
canBeUsedInAnnotation,
|
||||
!isNumberConversionMethod && isArgumentPure,
|
||||
usesVariableAsConstant)
|
||||
usesVariableAsConstant, usesNonConstValAsConstant)
|
||||
)
|
||||
}
|
||||
else if (argumentsEntrySet.size() == 1) {
|
||||
@@ -415,7 +423,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression)
|
||||
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression)
|
||||
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression) || usesVariableAsConstant(argumentForParameter.expression)
|
||||
val parameters = CompileTimeConstant.Parameters(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant)
|
||||
val usesNonConstValAsConstant = usesNonConstValAsConstant(argumentForReceiver.expression) || usesNonConstValAsConstant(argumentForParameter.expression)
|
||||
val parameters = CompileTimeConstant.Parameters(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant, usesNonConstValAsConstant)
|
||||
return when (resultingDescriptorName) {
|
||||
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, factory)?.wrap(parameters)
|
||||
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, factory)?.wrap(parameters)
|
||||
@@ -429,6 +438,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
}
|
||||
|
||||
private fun usesVariableAsConstant(expression: JetExpression) = ConstantExpressionEvaluator.getConstant(expression, trace.getBindingContext())?.usesVariableAsConstant ?: false
|
||||
private fun usesNonConstValAsConstant(expression: JetExpression)
|
||||
= ConstantExpressionEvaluator.getConstant(expression, trace.bindingContext)?.usesNonConstValAsConstant ?: false
|
||||
|
||||
private fun canBeUsedInAnnotation(expression: JetExpression) = ConstantExpressionEvaluator.getConstant(expression, trace.getBindingContext())?.canBeUsedInAnnotations ?: false
|
||||
|
||||
@@ -515,7 +526,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
CompileTimeConstant.Parameters(
|
||||
canBeUsedInAnnotation = isPropertyCompileTimeConstant(callableDescriptor),
|
||||
isPure = false,
|
||||
usesVariableAsConstant = true
|
||||
usesVariableAsConstant = true,
|
||||
usesNonConstValAsConstant = !callableDescriptor.isConst
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -523,6 +535,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
return null
|
||||
}
|
||||
|
||||
// TODO: Should be replaced with descriptor.isConst
|
||||
private fun isPropertyCompileTimeConstant(descriptor: VariableDescriptor): Boolean {
|
||||
if (descriptor.isVar()) {
|
||||
return false
|
||||
@@ -566,7 +579,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
|
||||
val resultingDescriptor = call.getResultingDescriptor()
|
||||
|
||||
// array()
|
||||
// arrayOf()
|
||||
if (CompileTimeConstantUtils.isArrayMethodCall(call)) {
|
||||
val varargType = resultingDescriptor.getValueParameters().first().getVarargElementType()!!
|
||||
|
||||
@@ -574,7 +587,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
|
||||
return factory.createArrayValue(arguments.map { it.toConstantValue(varargType) }, resultingDescriptor.getReturnType()!!).
|
||||
wrap(
|
||||
usesVariableAsConstant = arguments.any { it.usesVariableAsConstant }
|
||||
usesVariableAsConstant = arguments.any { it.usesVariableAsConstant },
|
||||
usesNonConstValAsConstant = arguments.any { it.usesNonConstValAsConstant }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -696,9 +710,10 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
private fun <T> ConstantValue<T>.wrap(
|
||||
canBeUsedInAnnotation: Boolean = this !is NullValue,
|
||||
isPure: Boolean = false,
|
||||
usesVariableAsConstant: Boolean = false
|
||||
usesVariableAsConstant: Boolean = false,
|
||||
usesNonConstValAsConstant: Boolean = false
|
||||
): TypedCompileTimeConstant<T>
|
||||
= wrap(CompileTimeConstant.Parameters(canBeUsedInAnnotation, isPure, usesVariableAsConstant))
|
||||
= wrap(CompileTimeConstant.Parameters(canBeUsedInAnnotation, isPure, usesVariableAsConstant, usesNonConstValAsConstant))
|
||||
}
|
||||
|
||||
private fun hasLongSuffix(text: String) = text.endsWith('l') || text.endsWith('L')
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// Properties can be recursively annotated
|
||||
annotation class ann(val x: Int)
|
||||
@ann(x) val x: Int = 1
|
||||
@ann(x) const val x: Int = 1
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package
|
||||
|
||||
@ann(x = 1) public val x: kotlin.Int = 1
|
||||
@ann(x = 1) public const val x: kotlin.Int = 1
|
||||
|
||||
@kotlin.annotation.annotation() public final class ann : kotlin.Annotation {
|
||||
public constructor ann(/*0*/ x: kotlin.Int)
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@ annotation class Ann(vararg val i: Int)
|
||||
class Test
|
||||
|
||||
var i1 = 1 // var
|
||||
val i2 = 1 // val
|
||||
const val i2 = 1 // val
|
||||
val i3 = i1 // val with var in initializer
|
||||
val i4 = i2 // val with val in initializer
|
||||
const val i4 = i2 // val with val in initializer
|
||||
var i5 = i1 // var with var in initializer
|
||||
var i6 = i2 // var with val in initializer
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
package
|
||||
|
||||
public var i1: kotlin.Int
|
||||
public val i2: kotlin.Int = 1
|
||||
public const val i2: kotlin.Int = 1
|
||||
public val i3: kotlin.Int
|
||||
public val i4: kotlin.Int = 1
|
||||
public const val i4: kotlin.Int = 1
|
||||
public var i5: kotlin.Int
|
||||
public var i6: kotlin.Int
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
annotation class Ann(vararg val i: String)
|
||||
|
||||
val topLevel = "topLevel"
|
||||
const val topLevel = "topLevel"
|
||||
|
||||
fun foo() {
|
||||
val a1 = "a"
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
package
|
||||
|
||||
public val topLevel: kotlin.String = "topLevel"
|
||||
public const val topLevel: kotlin.String = "topLevel"
|
||||
public fun foo(): kotlin.Unit
|
||||
|
||||
@kotlin.annotation.annotation() public final class Ann : kotlin.Annotation {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
annotation class ann(val name: String)
|
||||
val ok = "OK"
|
||||
const val ok = "OK"
|
||||
|
||||
class A
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package
|
||||
|
||||
public val extensionWithoutName: A.() -> kotlin.Unit
|
||||
public val funfun: () -> () -> kotlin.Int
|
||||
public val ok: kotlin.String = "OK"
|
||||
public const val ok: kotlin.String = "OK"
|
||||
public val parentesized: () -> kotlin.Unit
|
||||
public val parentesizedWithType: () -> kotlin.Unit
|
||||
public val withExpression: () -> kotlin.Int
|
||||
|
||||
compiler/testData/diagnostics/testsWithStdLib/annotations/annotationParameterMustBeConstant/array.kt
Vendored
+1
-1
@@ -10,7 +10,7 @@ annotation class Ann(val i: IntArray)
|
||||
class Test
|
||||
|
||||
var i = 1
|
||||
val i2 = 1
|
||||
const val i2 = 1
|
||||
val i3 = foo()
|
||||
|
||||
fun foo(): Int = 1
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
package
|
||||
|
||||
public var i: kotlin.Int
|
||||
public val i2: kotlin.Int = 1
|
||||
public const val i2: kotlin.Int = 1
|
||||
public val i3: kotlin.Int
|
||||
public val iAnn: Ann
|
||||
public fun foo(): kotlin.Int
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
val nonConst = 1
|
||||
|
||||
const val constConst = <!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>nonConst * nonConst + 2<!>
|
||||
|
||||
annotation class Ann(val x: Int, val y: String)
|
||||
|
||||
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>nonConst<!>, <!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>"${nonConst}"<!>)
|
||||
fun foo1() {}
|
||||
|
||||
@Ann(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>nonConst + constConst<!>, "${constConst}")
|
||||
fun foo2() {}
|
||||
|
||||
annotation class ArrayAnn(val x: IntArray)
|
||||
|
||||
@ArrayAnn(<!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>intArrayOf(1, constConst, <!NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION!>nonConst<!>)<!>)
|
||||
fun foo3() {}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package
|
||||
|
||||
public const val constConst: kotlin.Int = 3
|
||||
public val nonConst: kotlin.Int = 1
|
||||
@Ann(x = 1, y = "1") public fun foo1(): kotlin.Unit
|
||||
@Ann(x = 4, y = "3") public fun foo2(): kotlin.Unit
|
||||
@ArrayAnn(x = {1, 3, 1}) public fun foo3(): kotlin.Unit
|
||||
|
||||
@kotlin.annotation.annotation() public final class Ann : kotlin.Annotation {
|
||||
public constructor Ann(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.String)
|
||||
public final val x: kotlin.Int
|
||||
public final val y: kotlin.String
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@kotlin.annotation.annotation() public final class ArrayAnn : kotlin.Annotation {
|
||||
public constructor ArrayAnn(/*0*/ x: kotlin.IntArray)
|
||||
public final val x: kotlin.IntArray
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@ annotation class Ann(vararg val i: Int)
|
||||
class Test
|
||||
|
||||
var i = 1
|
||||
val i2 = 1
|
||||
const val i2 = 1
|
||||
val i3 = foo()
|
||||
|
||||
fun foo(): Int = 1
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
package
|
||||
|
||||
public var i: kotlin.Int
|
||||
public val i2: kotlin.Int = 1
|
||||
public const val i2: kotlin.Int = 1
|
||||
public val i3: kotlin.Int
|
||||
public val iAnn: Ann
|
||||
public fun foo(): kotlin.Int
|
||||
|
||||
+6
@@ -162,6 +162,12 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("useOfNonConstVal.kt")
|
||||
public void testUseOfNonConstVal() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/annotationParameterMustBeConstant/useOfNonConstVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("vararg.kt")
|
||||
public void testVararg() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/annotationParameterMustBeConstant/vararg.kt");
|
||||
|
||||
@@ -34,12 +34,15 @@ public interface CompileTimeConstant<out T> {
|
||||
|
||||
public val usesVariableAsConstant: Boolean get() = parameters.usesVariableAsConstant
|
||||
|
||||
public val usesNonConstValAsConstant: Boolean get() = parameters.usesNonConstValAsConstant
|
||||
|
||||
public val isPure: Boolean get() = parameters.isPure
|
||||
|
||||
public class Parameters(
|
||||
public val canBeUsedInAnnotation: Boolean,
|
||||
public val isPure: Boolean,
|
||||
public val usesVariableAsConstant: Boolean
|
||||
public val usesVariableAsConstant: Boolean,
|
||||
public val usesNonConstValAsConstant: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@
|
||||
|
||||
package boo
|
||||
|
||||
val BAZ = "baz"
|
||||
val N = 0
|
||||
const val BAZ = "baz"
|
||||
const val N = 0
|
||||
|
||||
@Target(AnnotationTarget.FILE)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
|
||||
Reference in New Issue
Block a user