Compiler warning on identical enum constant / is type entries #KT-4829 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-04-19 16:25:02 +03:00
parent d7ecd12737
commit 771fb0ace2
10 changed files with 155 additions and 3 deletions
@@ -1202,6 +1202,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
builder.bindLabel(doneLabel)
mergeValues(branches, expression)
WhenChecker.checkDuplicatedLabels(expression, trace)
}
override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) {
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass
import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import java.util.*
interface WhenMissingCase {
@@ -333,6 +335,46 @@ object WhenChecker {
fun containsNullCase(expression: KtWhenExpression, context: BindingContext) =
WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, true).isEmpty()
fun checkDuplicatedLabels(expression: KtWhenExpression, trace: BindingTrace) {
if (expression.subjectExpression == null) return
val checkedTypes = HashSet<Pair<KotlinType, Boolean>>()
val checkedConstants = HashSet<CompileTimeConstant<*>>()
for (entry in expression.entries) {
if (entry.isElse) continue
conditions@ for (condition in entry.conditions) {
when (condition) {
is KtWhenConditionWithExpression -> {
val constantExpression = condition.expression ?: continue@conditions
val constant = ConstantExpressionEvaluator.getConstant(
constantExpression, trace.bindingContext) ?: continue@conditions
if (checkedConstants.contains(constant)) {
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(constantExpression))
}
else {
checkedConstants.add(constant)
}
}
is KtWhenConditionIsPattern -> {
val typeReference = condition.typeReference ?: continue@conditions
val type = trace.get(BindingContext.TYPE, typeReference) ?: continue@conditions
val typeWithIsNegation = type to condition.isNegated
if (checkedTypes.contains(typeWithIsNegation)) {
trace.report(Errors.DUPLICATE_LABEL_IN_WHEN.on(typeReference))
}
else {
checkedTypes.add(typeWithIsNegation)
}
}
else -> {}
}
}
}
}
fun checkDeprecatedWhenSyntax(trace: BindingTrace, expression: KtWhenExpression) {
if (expression.subjectExpression != null) return
@@ -728,6 +728,7 @@ public interface Errors {
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>> NO_ELSE_IN_WHEN = DiagnosticFactory1.create(ERROR, WHEN_EXPRESSION);
DiagnosticFactory1<KtWhenExpression, List<WhenMissingCase>> NON_EXHAUSTIVE_WHEN = DiagnosticFactory1.create(WARNING, WHEN_EXPRESSION);
DiagnosticFactory0<PsiElement> COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> DUPLICATE_LABEL_IN_WHEN = DiagnosticFactory0.create(WARNING);
// Type mismatch
@@ -430,6 +430,7 @@ public class DefaultErrorMessages {
MAP.put(ELSE_MISPLACED_IN_WHEN, "'else' entry must be the last one in a when-expression");
MAP.put(COMMA_IN_WHEN_CONDITION_WITHOUT_ARGUMENT, "Deprecated syntax. Use '||' instead of commas in when-condition for 'when' without argument");
MAP.put(DUPLICATE_LABEL_IN_WHEN, "Duplicate label in when");
MAP.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", RENDER_WHEN_MISSING_CASES);
MAP.put(NON_EXHAUSTIVE_WHEN, "''when'' expression on enum is recommended to be exhaustive, add {0}", RENDER_WHEN_MISSING_CASES);
@@ -3,7 +3,7 @@ public fun foo(a: Any, <!UNUSED_PARAMETER!>b<!>: <!WRONG_NUMBER_OF_TYPE_ARGUMENT
is Map<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!> -> {}
is <!NO_TYPE_ARGUMENTS_ON_RHS!>Map<!> -> {}
is Map<out Any?, Any?> -> {}
is Map<*, *> -> {}
is <!DUPLICATE_LABEL_IN_WHEN!>Map<*, *><!> -> {}
is Map<<!SYNTAX!><!>> -> {}
is List<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>> -> {}
is <!NO_TYPE_ARGUMENTS_ON_RHS!>List<!> -> {}
@@ -0,0 +1,51 @@
package test
const val four = 4
fun first(arg: Int) = when (arg) {
1 -> 2
2 -> 3
<!DUPLICATE_LABEL_IN_WHEN!>1<!> -> 4
4 -> 5
<!DUPLICATE_LABEL_IN_WHEN!>1<!> -> 6
<!DUPLICATE_LABEL_IN_WHEN!>2<!> -> 7
// Error should be here: see KT-11971
four -> 8
else -> 0
}
fun second(arg: String): Int {
when (arg) {
"ABC" -> return 0
"DEF" -> return 1
<!DUPLICATE_LABEL_IN_WHEN!>"ABC"<!> -> return -1
<!DUPLICATE_LABEL_IN_WHEN!>"DEF"<!> -> return -2
}
return 42
}
fun third(arg: Any?): Int {
when (arg) {
null -> return -1
is String -> return 0
is Double -> return 1
is <!DUPLICATE_LABEL_IN_WHEN!>Double<!> -> return 2
<!DUPLICATE_LABEL_IN_WHEN!>null<!> -> return 3
else -> return 5
}
}
enum class Color { RED, GREEN, BLUE }
fun fourth(arg: Color) = when (arg) {
Color.RED -> "RED"
Color.GREEN -> "GREEN"
<!DUPLICATE_LABEL_IN_WHEN!>Color.RED<!> -> "BLUE"
Color.BLUE -> "BLUE"
}
fun fifth(arg: Any?) = when (arg) {
is Any -> "Any"
<!ELSE_MISPLACED_IN_WHEN!>else<!> -> ""
<!UNREACHABLE_CODE!>else -> null<!>
}
@@ -0,0 +1,31 @@
package
package test {
public const val four: kotlin.Int = 4
public fun fifth(/*0*/ arg: kotlin.Any?): kotlin.String?
public fun first(/*0*/ arg: kotlin.Int): kotlin.Int
public fun fourth(/*0*/ arg: test.Color): kotlin.String
public fun second(/*0*/ arg: kotlin.String): kotlin.Int
public fun third(/*0*/ arg: kotlin.Any?): kotlin.Int
public final enum class Color : kotlin.Enum<test.Color> {
enum entry RED
enum entry GREEN
enum entry BLUE
private constructor Color()
public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Color): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): test.Color
public final /*synthesized*/ fun values(): kotlin.Array<test.Color>
}
}
@@ -15,9 +15,9 @@ fun test(d: Any, dl: Collection<dynamic>) {
when (d) {
is <!DYNAMIC_NOT_ALLOWED!>dynamic<!> -> {}
is <!DYNAMIC_NOT_ALLOWED!>dynamic?<!> -> {}
is <!DYNAMIC_NOT_ALLOWED, DUPLICATE_LABEL_IN_WHEN!>dynamic?<!> -> {}
!is <!DYNAMIC_NOT_ALLOWED!>dynamic<!> -> {}
!is <!DYNAMIC_NOT_ALLOWED!>dynamic?<!> -> {}
!is <!DYNAMIC_NOT_ALLOWED, DUPLICATE_LABEL_IN_WHEN!>dynamic?<!> -> {}
}
dl as List<dynamic>
@@ -19260,6 +19260,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("DuplicatedLabels.kt")
public void testDuplicatedLabels() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/DuplicatedLabels.kt");
doTest(fileName);
}
@TestMetadata("ElseOnNullableEnum.kt")
public void testElseOnNullableEnum() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/ElseOnNullableEnum.kt");
@@ -56,6 +56,21 @@ class TypedCompileTimeConstant<out T>(
val type: KotlinType = constantValue.type
override fun toConstantValue(expectedType: KotlinType): ConstantValue<T> = constantValue
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TypedCompileTimeConstant<*>) return false
if (isError) return other.isError
if (other.isError) return false
return constantValue.value == other.constantValue.value && type == other.type
}
override fun hashCode(): Int {
if (isError) return 13
var result = constantValue.value?.hashCode() ?: 0
result = 31 * result + type.hashCode()
return result
}
}
class IntegerValueTypeConstant(
@@ -92,4 +107,8 @@ class IntegerValueTypeConstant(
fun getType(expectedType: KotlinType): KotlinType = TypeUtils.getPrimitiveNumberType(typeConstructor, expectedType)
override fun toString() = typeConstructor.toString()
override fun equals(other: Any?) = other is IntegerValueTypeConstant && value == other.value
override fun hashCode() = value.hashCode()
}