'Cast can never succeed' diagnostics improved
This commit is contained in:
+80
-8
@@ -35,7 +35,9 @@ import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability;
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.CheckValueArgumentsMode;
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.ExpressionPosition;
|
||||
import org.jetbrains.jet.lang.resolve.calls.context.TemporaryTraceAndCache;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
|
||||
@@ -71,7 +73,8 @@ import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.IND
|
||||
import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue.NO_RECEIVER;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.*;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.createCallForSpecialConstruction;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.resolveSpecialConstructionAsCall;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*;
|
||||
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
@@ -190,7 +193,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
return DataFlowUtils.checkType(result, expression, context, dataFlowInfo);
|
||||
}
|
||||
|
||||
private static void checkBinaryWithTypeRHS(
|
||||
private void checkBinaryWithTypeRHS(
|
||||
@NotNull JetBinaryExpressionWithTypeRHS expression,
|
||||
@NotNull ExpressionTypingContext context,
|
||||
@NotNull JetType targetType,
|
||||
@@ -209,7 +212,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
checkForCastImpossibility(expression, actualType, targetType, context);
|
||||
}
|
||||
|
||||
private static void checkForCastImpossibility(
|
||||
private void checkForCastImpossibility(
|
||||
JetBinaryExpressionWithTypeRHS expression,
|
||||
JetType actualType,
|
||||
JetType targetType,
|
||||
@@ -217,15 +220,15 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
) {
|
||||
if (actualType == null || noExpectedType(targetType)) return;
|
||||
|
||||
if (!isCastPossible(actualType, targetType)) {
|
||||
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
|
||||
}
|
||||
|
||||
JetTypeChecker typeChecker = JetTypeChecker.INSTANCE;
|
||||
if (!typeChecker.isSubtypeOf(targetType, actualType)) {
|
||||
if (typeChecker.isSubtypeOf(actualType, targetType)) {
|
||||
context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression.getOperationReference()));
|
||||
}
|
||||
else {
|
||||
// See JET-58 Make 'as never succeeds' a warning, or even never check for Java (external) types
|
||||
context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (typeChecker.isSubtypeOf(actualType, targetType)) {
|
||||
@@ -239,6 +242,75 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
// As this method produces a warning, it must be _complete_ (not sound), i.e. every time it says "cast impossible",
|
||||
// it must be really impossible
|
||||
public boolean isCastPossible(@NotNull JetType lhsType, @NotNull JetType rhsType) {
|
||||
if (isRelated(lhsType, rhsType)) 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
|
||||
if (isTypeParameter(lhsType) || isTypeParameter(rhsType)) return true;
|
||||
if (isFinal(lhsType) || isFinal(rhsType)) return false;
|
||||
if (isTrait(lhsType) || isTrait(rhsType)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Two types are related, roughly, when one is a subtype or supertype of the other.
|
||||
*
|
||||
* Note that some types have platform-specific counterparts, i.e. jet.String is mapped to java.lang.String,
|
||||
* such types (and all their sub- and supertypes) are related too.
|
||||
*
|
||||
* Due to limitations in PlatformToKotlinClassMap, we only consider mapping of platform classes to Kotlin classed
|
||||
* (i.e. java.lang.String -> jet.String) and ignore mappings that go the other way.
|
||||
*/
|
||||
private boolean isRelated(@NotNull JetType a, @NotNull JetType b) {
|
||||
List<JetType> aTypes = mapToPlatformIndependentTypes(a);
|
||||
List<JetType> bTypes = mapToPlatformIndependentTypes(b);
|
||||
|
||||
for (int i = 0; i < aTypes.size(); i++) {
|
||||
JetType aType = aTypes.get(i);
|
||||
for (int j = 0; j < bTypes.size(); j++) {
|
||||
JetType bType = bTypes.get(j);
|
||||
|
||||
if (JetTypeChecker.INSTANCE.isSubtypeOf(aType, bType)) return true;
|
||||
if (JetTypeChecker.INSTANCE.isSubtypeOf(bType, aType)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<JetType> mapToPlatformIndependentTypes(@NotNull JetType type) {
|
||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
if (!(descriptor instanceof ClassDescriptor)) return Collections.singletonList(type);
|
||||
|
||||
ClassDescriptor originalClass = (ClassDescriptor) descriptor;
|
||||
Collection<ClassDescriptor> kotlinClasses = platformToKotlinClassMap.mapPlatformClass(originalClass);
|
||||
if (kotlinClasses.isEmpty()) return Collections.singletonList(type);
|
||||
|
||||
List<JetType> result = Lists.newArrayListWithCapacity(2);
|
||||
result.add(type);
|
||||
for (ClassDescriptor classDescriptor : kotlinClasses) {
|
||||
JetType kotlinType = TypeUtils.substituteProjectionsForParameters(classDescriptor, type.getArguments());
|
||||
result.add(kotlinType);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isTypeParameter(@NotNull JetType type) {
|
||||
return type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor;
|
||||
}
|
||||
|
||||
private static boolean isFinal(@NotNull JetType type) {
|
||||
return !TypeUtils.canHaveSubtypes(JetTypeChecker.INSTANCE, type);
|
||||
}
|
||||
|
||||
private static boolean isTrait(@NotNull JetType type) {
|
||||
ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
|
||||
return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.TRAIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if assignment from supertype to subtype is erased.
|
||||
* It is an error in "is" statement and warning in "as".
|
||||
|
||||
Reference in New Issue
Block a user