Refactoring: move utils related to casts to CastDiagnosticUtil

This commit is contained in:
Mikhail Zarechenskiy
2017-05-10 16:35:54 +03:00
parent b323d2b24a
commit a8f1e32dec
2 changed files with 87 additions and 74 deletions
@@ -17,13 +17,19 @@
package org.jetbrains.kotlin.types
import com.google.common.collect.Maps
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure
import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
object CastDiagnosticsUtil {
@@ -193,4 +199,84 @@ object CastDiagnosticsUtil {
}
private fun allParametersReified(subtype: KotlinType) = subtype.constructor.parameters.all { it.isReified }
fun castIsUseless(
expression: KtBinaryExpressionWithTypeRHS,
context: ExpressionTypingContext,
targetType: KotlinType,
actualType: KotlinType,
typeChecker: KotlinTypeChecker
): Boolean {
// Here: x as? Type <=> x as Type?
val refinedTargetType = if (KtPsiUtil.isSafeCast(expression)) TypeUtils.makeNullable(targetType) else targetType
val possibleTypes = DataFlowAnalyzer.getAllPossibleTypes(expression.left, actualType, context)
return isRefinementUseless(possibleTypes, refinedTargetType, typeChecker, shouldCheckForExactType(expression, context.expectedType))
}
fun isRefinementUseless(
possibleTypes: Collection<KotlinType>,
targetType: KotlinType,
typeChecker: KotlinTypeChecker,
shouldCheckForExactType: Boolean
): Boolean {
val intersectedType = TypeIntersector.intersectTypes(typeChecker, possibleTypes) ?: return false
return if (shouldCheckForExactType)
isExactTypeCast(intersectedType, targetType)
else
isUpcast(intersectedType, targetType, typeChecker)
}
private fun shouldCheckForExactType(expression: KtBinaryExpressionWithTypeRHS, expectedType: KotlinType): Boolean {
if (TypeUtils.noExpectedType(expectedType)) {
return checkExactTypeForUselessCast(expression)
}
// If expected type is parameterized, then cast has an effect on inference, therefore it isn't a useless cast
// Otherwise, we are interested in situation like: `a: Any? = 1 as Int?`
return TypeUtils.isDontCarePlaceholder(expectedType)
}
private fun isExactTypeCast(candidateType: KotlinType, targetType: KotlinType): Boolean {
return candidateType == targetType && candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType
}
private fun isUpcast(candidateType: KotlinType, targetType: KotlinType, typeChecker: KotlinTypeChecker): Boolean {
if (!typeChecker.isSubtypeOf(candidateType, targetType)) return false
if (candidateType.isFunctionType && targetType.isFunctionType) {
return candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType
}
return true
}
// Casting an argument or a receiver to a supertype may be useful to select an exact overload of a method.
// Casting to a supertype in other contexts is unlikely to be useful.
private fun checkExactTypeForUselessCast(expression: KtBinaryExpressionWithTypeRHS): Boolean {
var parent = expression.parent
while (parent is KtParenthesizedExpression ||
parent is KtLabeledExpression ||
parent is KtAnnotatedExpression) {
parent = parent.parent
}
return when (parent) {
is KtValueArgument -> true
is KtQualifiedExpression -> {
val receiver = parent.receiverExpression
PsiTreeUtil.isAncestor(receiver, expression, false)
}
// in binary expression, left argument can be a receiver and right an argument
// in unary expression, left argument can be a receiver
is KtBinaryExpression, is KtUnaryExpression -> true
// Previously we've checked that there is no expected type, therefore cast in property has an effect on inference
is KtProperty, is KtPropertyAccessor -> true
else -> false
}
}
}
@@ -79,8 +79,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.jetbrains.kotlin.builtins.FunctionTypesKt.isExtensionFunctionType;
import static org.jetbrains.kotlin.builtins.FunctionTypesKt.isFunctionType;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.lexer.KtTokens.*;
import static org.jetbrains.kotlin.resolve.BindingContext.*;
@@ -377,7 +375,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
KotlinTypeChecker typeChecker = KotlinTypeChecker.DEFAULT;
if (castIsUseless(expression, context, targetType, actualType, typeChecker)) {
if (CastDiagnosticsUtil.INSTANCE.castIsUseless(expression, context, targetType, actualType, typeChecker)) {
context.trace.report(USELESS_CAST.on(expression));
return;
}
@@ -387,77 +385,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
}
private static boolean castIsUseless(
@NotNull KtBinaryExpressionWithTypeRHS expression,
@NotNull ExpressionTypingContext context,
@NotNull KotlinType targetType,
@NotNull KotlinType actualType,
@NotNull KotlinTypeChecker typeChecker
) {
// Here: x as? Type <=> x as Type?
KotlinType refinedTargetType = KtPsiUtil.isSafeCast(expression) ? TypeUtils.makeNullable(targetType) : targetType;
Collection<KotlinType> possibleTypes = DataFlowAnalyzer.getAllPossibleTypes(expression.getLeft(), actualType, context);
KotlinType intersectedType = TypeIntersector.intersectTypes(typeChecker, possibleTypes);
if (intersectedType == null) return false;
return shouldCheckForExactType(expression, context.expectedType)
? isExactTypeCast(intersectedType, refinedTargetType)
: isUpcast(intersectedType, refinedTargetType, typeChecker);
}
private static boolean shouldCheckForExactType(KtBinaryExpressionWithTypeRHS expression, KotlinType expectedType) {
if (TypeUtils.noExpectedType(expectedType)) {
return checkExactTypeForUselessCast(expression);
}
// If expected type is parameterized, then cast has an effect on inference, therefore it isn't a useless cast
// Otherwise, we are interested in situation like: `a: Any? = 1 as Int?`
return TypeUtils.isDontCarePlaceholder(expectedType);
}
private static boolean isExactTypeCast(KotlinType candidateType, KotlinType targetType) {
return candidateType.equals(targetType) && isExtensionFunctionType(candidateType) == isExtensionFunctionType(targetType);
}
private static boolean isUpcast(KotlinType candidateType, KotlinType targetType, KotlinTypeChecker typeChecker) {
if (!typeChecker.isSubtypeOf(candidateType, targetType)) return false;
if (isFunctionType(candidateType) && isFunctionType(targetType)) {
return isExtensionFunctionType(candidateType) == isExtensionFunctionType(targetType);
}
return true;
}
// Casting an argument or a receiver to a supertype may be useful to select an exact overload of a method.
// Casting to a supertype in other contexts is unlikely to be useful.
private static boolean checkExactTypeForUselessCast(KtBinaryExpressionWithTypeRHS expression) {
PsiElement parent = expression.getParent();
while (parent instanceof KtParenthesizedExpression ||
parent instanceof KtLabeledExpression ||
parent instanceof KtAnnotatedExpression) {
parent = parent.getParent();
}
if (parent instanceof KtValueArgument) {
return true;
}
if (parent instanceof KtQualifiedExpression) {
KtExpression receiver = ((KtQualifiedExpression) parent).getReceiverExpression();
return PsiTreeUtil.isAncestor(receiver, expression, false);
}
// in binary expression, left argument can be a receiver and right an argument
// in unary expression, left argument can be a receiver
if (parent instanceof KtBinaryExpression || parent instanceof KtUnaryExpression) {
return true;
}
// Previously we've checked that there is no expected type, therefore cast in property has an effect on inference
if (parent instanceof KtProperty || parent instanceof KtPropertyAccessor) {
return true;
}
return false;
}
@Override
public KotlinTypeInfo visitThisExpression(@NotNull KtThisExpression expression, ExpressionTypingContext context) {
KotlinType result = null;