KT-5455 Need warning about redundant type cast

#KT-5455 Fixed
This commit is contained in:
Svetlana Isakova
2014-10-03 19:33:42 +04:00
parent b20327770b
commit a47729c626
13 changed files with 139 additions and 33 deletions
@@ -252,7 +252,7 @@ public class DefaultErrorMessages {
MAP.put(ABSTRACT_SUPER_CALL, "Abstract member cannot be accessed directly");
MAP.put(NOT_A_SUPERTYPE, "Not a supertype");
MAP.put(TYPE_ARGUMENTS_REDUNDANT_IN_SUPER_QUALIFIER, "Type arguments do not need to be specified in a 'super' qualifier");
MAP.put(USELESS_CAST_STATIC_ASSERT_IS_FINE, "No cast needed, use ':' instead");
MAP.put(USELESS_CAST_STATIC_ASSERT_IS_FINE, "No cast needed. You can use ':' if you need a cast to a super type");
MAP.put(USELESS_CAST, "No cast needed");
MAP.put(CAST_NEVER_SUCCEEDS, "This cast can never succeed");
MAP.put(USELESS_NULLABLE_CHECK, "Non-null type is checked for instance of nullable type");
@@ -80,9 +80,7 @@ public class SmartCastUtils {
return collectSmartCastReceiverValues(dataFlowInfo, dataFlowValue);
}
else if (receiverToCast instanceof ExpressionReceiver) {
ExpressionReceiver receiver = (ExpressionReceiver) receiverToCast;
DataFlowValue dataFlowValue =
DataFlowValueFactory.createDataFlowValue(receiver.getExpression(), receiver.getType(), bindingContext);
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverToCast, bindingContext);
return collectSmartCastReceiverValues(dataFlowInfo, dataFlowValue);
}
return Collections.emptyList();
@@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.util.Collection;
import java.util.Collections;
@@ -42,6 +43,7 @@ public class CastDiagnosticsUtil {
@NotNull JetType rhsType,
@NotNull PlatformToKotlinClassMap platformToKotlinClassMap
) {
if (KotlinBuiltIns.getInstance().isNullableNothing(lhsType) && !TypeUtils.isNullableType(rhsType)) return false;
if (isRelated(lhsType, rhsType, platformToKotlinClassMap)) return true;
// This is an oversimplification (which does not render the method incomplete):
// we consider any type parameter capable of taking any value, which may be made more precise if we considered bounds
@@ -61,8 +63,8 @@ public class CastDiagnosticsUtil {
* (i.e. java.lang.String -> kotlin.String) and ignore mappings that go the other way.
*/
private static boolean isRelated(@NotNull JetType a, @NotNull JetType b, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) {
List<JetType> aTypes = mapToPlatformIndependentTypes(a, platformToKotlinClassMap);
List<JetType> bTypes = mapToPlatformIndependentTypes(b, platformToKotlinClassMap);
List<JetType> aTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(a), platformToKotlinClassMap);
List<JetType> bTypes = mapToPlatformIndependentTypes(TypeUtils.makeNotNullable(b), platformToKotlinClassMap);
for (JetType aType : aTypes) {
for (JetType bType : bTypes) {
@@ -27,7 +27,6 @@ import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
@@ -45,7 +44,6 @@ import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImp
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsUtil;
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValueFactory;
import org.jetbrains.jet.lang.resolve.calls.smartcasts.Nullability;
import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind;
import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionCandidate;
@@ -76,6 +74,7 @@ import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getStaticNestedClassesScope;
import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
import static org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValueFactory.createDataFlowValue;
import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
import static org.jetbrains.jet.lang.resolve.source.SourcePackage.toSourceElement;
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
@@ -174,7 +173,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, subjectType);
dataFlowInfo = typeInfo.getDataFlowInfo();
if (operationType == AS_KEYWORD) {
DataFlowValue value = DataFlowValueFactory.createDataFlowValue(left, subjectType, context.trace.getBindingContext());
DataFlowValue value = createDataFlowValue(left, subjectType, context.trace.getBindingContext());
dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType);
}
}
@@ -199,10 +198,10 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
context.trace.report(UNSUPPORTED.on(operationSign, "binary operation with type RHS"));
return;
}
checkForCastImpossibility(expression, actualType, targetType, context);
checkForCastImpossibilityOrRedundancy(expression, actualType, targetType, context);
}
private void checkForCastImpossibility(
private void checkForCastImpossibilityOrRedundancy(
JetBinaryExpressionWithTypeRHS expression,
JetType actualType,
JetType targetType,
@@ -212,24 +211,25 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (!CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMap)) {
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
return;
}
else {
JetTypeChecker typeChecker = JetTypeChecker.DEFAULT;
// Upcast?
if (typeChecker.isSubtypeOf(actualType, targetType)) {
if (!typeChecker.isSubtypeOf(targetType, actualType)) {
// proper upcast: String as Any
context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression.getOperationReference()));
}
else {
// cast to itself: String as String
context.trace.report(USELESS_CAST.on(expression.getOperationReference()));
}
}
else if (CastDiagnosticsUtil.isCastErased(actualType, targetType, typeChecker)) {
context.trace.report(Errors.UNCHECKED_CAST.on(expression, actualType, targetType));
JetTypeChecker typeChecker = JetTypeChecker.DEFAULT;
if (actualType.equals(targetType)) {
// cast to itself: String as String
context.trace.report(USELESS_CAST.on(expression.getOperationReference()));
return;
}
Collection<JetType> possibleTypes = DataFlowUtils.getAllPossibleTypes(
expression.getLeft(), context.dataFlowInfo, actualType, context.trace.getBindingContext());
for (JetType possibleType : possibleTypes) {
if (typeChecker.isSubtypeOf(possibleType, targetType)) {
context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression.getOperationReference()));
return;
}
}
if (CastDiagnosticsUtil.isCastErased(actualType, targetType, typeChecker)) {
context.trace.report(UNCHECKED_CAST.on(expression, actualType, targetType));
}
}
@Override
@@ -803,7 +803,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
context.trace.report(UNNECESSARY_NOT_NULL_ASSERTION.on(operationSign, baseType));
}
else {
DataFlowValue value = DataFlowValueFactory.createDataFlowValue(baseExpression, baseType, context.trace.getBindingContext());
DataFlowValue value = createDataFlowValue(baseExpression, baseType, context.trace.getBindingContext());
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
}
return JetTypeInfo.create(TypeUtils.makeNotNullable(baseType), dataFlowInfo);
@@ -835,7 +835,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
private static boolean isKnownToBeNotNull(JetExpression expression, JetType jetType, ExpressionTypingContext context) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, jetType, context.trace.getBindingContext());
DataFlowValue dataFlowValue = createDataFlowValue(expression, jetType, context.trace.getBindingContext());
return !context.dataFlowInfo.getNullability(dataFlowValue).canBeNull();
}
@@ -1075,7 +1075,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
DataFlowInfo dataFlowInfo = resolvedCall.getDataFlowInfoForArguments().getResultInfo();
if (leftType != null && rightType != null && KotlinBuiltIns.getInstance().isNothingOrNullableNothing(rightType) && !rightType.isNullable()) {
DataFlowValue value = DataFlowValueFactory.createDataFlowValue(left, leftType, context.trace.getBindingContext());
DataFlowValue value = createDataFlowValue(left, leftType, context.trace.getBindingContext());
dataFlowInfo = dataFlowInfo.disequate(value, DataFlowValue.NULL);
}
JetType type = resolvedCall.getResultingDescriptor().getReturnType();
@@ -1158,7 +1158,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
JetType type = facade.getTypeInfo(expr, context).getType();
if (type == null || type.isError()) return;
DataFlowValue value = DataFlowValueFactory.createDataFlowValue(expr, type, context.trace.getBindingContext());
DataFlowValue value = createDataFlowValue(expr, type, context.trace.getBindingContext());
Nullability nullability = context.dataFlowInfo.getNullability(value);
boolean expressionIsAlways;
@@ -16,6 +16,7 @@
package org.jetbrains.jet.lang.types.expressions;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.tree.IElementType;
@@ -39,6 +40,7 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collection;
import java.util.Set;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
@@ -265,4 +267,19 @@ public class DataFlowUtils {
context.trace.report(EXPRESSION_EXPECTED.on(expression, expression));
return JetTypeInfo.create(null, context.dataFlowInfo);
}
@NotNull
public static Collection<JetType> getAllPossibleTypes(
@NotNull JetExpression expression,
@NotNull DataFlowInfo dataFlowInfo,
@NotNull JetType type,
@NotNull BindingContext bindingContext
) {
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, bindingContext);
Collection<JetType> possibleTypes = Sets.newHashSet(type);
if (dataFlowValue.isStableIdentifier()) {
possibleTypes.addAll(dataFlowInfo.getPossibleTypes(dataFlowValue));
}
return possibleTypes;
}
}
@@ -1,6 +1,6 @@
class G<T>
fun foo(p: <!UNRESOLVED_REFERENCE!>P<!>) {
val v = <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>p<!> <!USELESS_CAST!>as<!> <!NO_TYPE_ARGUMENTS_ON_RHS!>G?<!>
val v = <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>p<!> <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as<!> <!NO_TYPE_ARGUMENTS_ON_RHS!>G?<!>
v!!: G<*>
}
@@ -1,6 +1,6 @@
class P
fun foo(p: P): Any {
val v = p <!USELESS_CAST!>as<!> <!UNRESOLVED_REFERENCE!>G<!>
val v = p <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as<!> <!UNRESOLVED_REFERENCE!>G<!>
return <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>v<!>
}
@@ -0,0 +1,8 @@
open class A {
fun foo() {}
}
class B : A()
fun test(b: B?) {
(b as A).foo()
}
@@ -0,0 +1,19 @@
package
internal fun test(/*0*/ b: B?): kotlin.Unit
internal open class A {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal final fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal final class B : A {
public constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -11,7 +11,7 @@ fun f2(s: Number?) {
}
fun f3(s: Number?) {
if (s is Int && s as Int == 42);
if (s is Int && s <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as<!> Int == 42);
<!TYPE_MISMATCH!>s<!> : Int
}
@@ -0,0 +1,28 @@
//KT-5455 Need warning about redundant type cast
fun foo(o: Any): Int {
if (o is String) {
return (o <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as<!> String).length
}
return -1
}
open class A {
fun foo() {}
}
class B: A()
fun test(a: Any?) {
if (a is B) {
(a <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as<!> A).foo()
}
}
fun test1(a: B) {
(a <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as<!> A?)?.foo()
}
fun test2(b: B?) {
if (b != null) {
(b <!USELESS_CAST_STATIC_ASSERT_IS_FINE!>as<!> A).foo()
}
}
@@ -0,0 +1,22 @@
package
internal fun foo(/*0*/ o: kotlin.Any): kotlin.Int
internal fun test(/*0*/ a: kotlin.Any?): kotlin.Unit
internal fun test1(/*0*/ a: B): kotlin.Unit
internal fun test2(/*0*/ b: B?): kotlin.Unit
internal open class A {
public constructor A()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal final fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
internal final class B : A {
public constructor B()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -1276,6 +1276,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/cast/neverSucceeds"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("CastToNotNullSuper.kt")
public void testCastToNotNullSuper() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/neverSucceeds/CastToNotNullSuper.kt");
doTest(fileName);
}
@TestMetadata("MappedDirect.kt")
public void testMappedDirect() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedDirect.kt");
@@ -9410,6 +9416,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("kt5455.kt")
public void testKt5455() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/kt5455.kt");
doTest(fileName);
}
@TestMetadata("noErrorCheckForPackageLevelVal.kt")
public void testNoErrorCheckForPackageLevelVal() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/noErrorCheckForPackageLevelVal.kt");