Fix false "useless cast" when target type is flexible
#KT-17820 Fixed
This commit is contained in:
@@ -213,13 +213,14 @@ object CastDiagnosticsUtil {
|
|||||||
return isRefinementUseless(possibleTypes, refinedTargetType, typeChecker, shouldCheckForExactType(expression, context.expectedType))
|
return isRefinementUseless(possibleTypes, refinedTargetType, typeChecker, shouldCheckForExactType(expression, context.expectedType))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It is a warning "useless cast" for `as` and a warning "redundant is" for `is`
|
||||||
fun isRefinementUseless(
|
fun isRefinementUseless(
|
||||||
possibleTypes: Collection<KotlinType>,
|
possibleTypes: Collection<KotlinType>,
|
||||||
targetType: KotlinType,
|
targetType: KotlinType,
|
||||||
typeChecker: KotlinTypeChecker,
|
typeChecker: KotlinTypeChecker,
|
||||||
shouldCheckForExactType: Boolean
|
shouldCheckForExactType: Boolean
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val intersectedType = TypeIntersector.intersectTypes(typeChecker, possibleTypes) ?: return false
|
val intersectedType = TypeIntersector.intersectTypes(typeChecker, possibleTypes.map { it.upperIfFlexible() }) ?: return false
|
||||||
|
|
||||||
return if (shouldCheckForExactType)
|
return if (shouldCheckForExactType)
|
||||||
isExactTypeCast(intersectedType, targetType)
|
isExactTypeCast(intersectedType, targetType)
|
||||||
|
|||||||
+1
-3
@@ -40,7 +40,6 @@ import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.*
|
|||||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
||||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
|
||||||
import org.jetbrains.kotlin.types.typeUtil.containsError
|
import org.jetbrains.kotlin.types.typeUtil.containsError
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
||||||
@@ -464,8 +463,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
if (subjectType.containsError() || targetType.containsError()) return
|
if (subjectType.containsError() || targetType.containsError()) return
|
||||||
|
|
||||||
val possibleTypes = DataFlowAnalyzer.getAllPossibleTypes(subjectType, context, subjectDataFlowValue)
|
val possibleTypes = DataFlowAnalyzer.getAllPossibleTypes(subjectType, context, subjectDataFlowValue)
|
||||||
val intersection = TypeIntersector.intersectTypes(KotlinTypeChecker.DEFAULT, possibleTypes.map { it.upperIfFlexible() })
|
if (CastDiagnosticsUtil.isRefinementUseless(possibleTypes, targetType, KotlinTypeChecker.DEFAULT, false)) {
|
||||||
if (intersection?.isSubtypeOf(targetType) ?: false) {
|
|
||||||
context.trace.report(Errors.USELESS_IS_CHECK.on(isCheck, !negated))
|
context.trace.report(Errors.USELESS_IS_CHECK.on(isCheck, !negated))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// FILE: Foo.java
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class Foo {
|
||||||
|
static Foo create() { return null; }
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
static Foo createN() { return null; }
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
static Foo createNN() { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: sample.kt
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
Foo.create() as Foo
|
||||||
|
Foo.createN() as Foo
|
||||||
|
Foo.createNN() <!USELESS_CAST!>as Foo<!>
|
||||||
|
|
||||||
|
Foo.create() <!USELESS_CAST!>as Foo?<!>
|
||||||
|
Foo.createN() <!USELESS_CAST!>as Foo?<!>
|
||||||
|
Foo.createNN() <!USELESS_CAST!>as Foo?<!>
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun test(): kotlin.Unit
|
||||||
|
|
||||||
|
public open class Foo {
|
||||||
|
public constructor Foo()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|
||||||
|
// Static members
|
||||||
|
public/*package*/ open fun create(): Foo!
|
||||||
|
@org.jetbrains.annotations.Nullable public/*package*/ open fun createN(): Foo?
|
||||||
|
@org.jetbrains.annotations.NotNull public/*package*/ open fun createNN(): Foo
|
||||||
|
}
|
||||||
@@ -2746,6 +2746,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FlexibleTargetType.kt")
|
||||||
|
public void testFlexibleTargetType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/FlexibleTargetType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric.kt")
|
@TestMetadata("IsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric.kt")
|
||||||
public void testIsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric() throws Exception {
|
public void testIsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/IsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/cast/IsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user