KT-5455 Need warning about redundant type cast
#KT-5455 Fixed
This commit is contained in:
+1
-1
@@ -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");
|
||||
|
||||
+1
-3
@@ -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) {
|
||||
|
||||
+24
-24
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user