Refactoring: move utils related to casts to CastDiagnosticUtil
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-74
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user