[FIR] Add CAST_NEVER_SUCCEEDS
This commit is contained in:
committed by
TeamCityServer
parent
4ca757446a
commit
2b5524b18f
+6
@@ -3104,6 +3104,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.CAST_NEVER_SUCCEEDS) { firDiagnostic ->
|
||||
CastNeverSucceedsImpl(
|
||||
firDiagnostic as FirPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.USELESS_CAST) { firDiagnostic ->
|
||||
UselessCastImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
|
||||
+4
@@ -2173,6 +2173,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
abstract val type: KtType
|
||||
}
|
||||
|
||||
abstract class CastNeverSucceeds : KtFirDiagnostic<KtBinaryExpressionWithTypeRHS>() {
|
||||
override val diagnosticClass get() = CastNeverSucceeds::class
|
||||
}
|
||||
|
||||
abstract class UselessCast : KtFirDiagnostic<KtBinaryExpressionWithTypeRHS>() {
|
||||
override val diagnosticClass get() = UselessCast::class
|
||||
}
|
||||
|
||||
+7
@@ -2618,6 +2618,13 @@ internal class CannotCheckForErasedImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class CastNeverSucceedsImpl(
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.CastNeverSucceeds(), KtAbstractFirDiagnostic<KtBinaryExpressionWithTypeRHS> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class UselessCastImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: ValidityToken,
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ fun test_1(b: B<String, Number>) {
|
||||
}
|
||||
|
||||
fun test_2(s: String) {
|
||||
val func = { s.length } as B<Int, Int>
|
||||
val func = { s.length } <!CAST_NEVER_SUCCEEDS!>as<!> B<Int, Int>
|
||||
}
|
||||
|
||||
class B<out K, V>(val k: K, val v: V)
|
||||
|
||||
+1
@@ -1105,6 +1105,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val CANNOT_CHECK_FOR_ERASED by error<PsiElement> {
|
||||
parameter<ConeKotlinType>("type")
|
||||
}
|
||||
val CAST_NEVER_SUCCEEDS by warning<KtBinaryExpressionWithTypeRHS>(PositioningStrategy.OPERATOR)
|
||||
val USELESS_CAST by warning<KtBinaryExpressionWithTypeRHS>(PositioningStrategy.AS_TYPE)
|
||||
val USELESS_IS_CHECK by warning<KtElement> {
|
||||
parameter<Boolean>("compileTimeCheckResult")
|
||||
|
||||
@@ -591,6 +591,7 @@ object FirErrors {
|
||||
|
||||
// Casts and is-checks
|
||||
val CANNOT_CHECK_FOR_ERASED by error1<PsiElement, ConeKotlinType>()
|
||||
val CAST_NEVER_SUCCEEDS by warning0<KtBinaryExpressionWithTypeRHS>(SourceElementPositioningStrategies.OPERATOR)
|
||||
val USELESS_CAST by warning0<KtBinaryExpressionWithTypeRHS>(SourceElementPositioningStrategies.AS_TYPE)
|
||||
val USELESS_IS_CHECK by warning1<KtElement, Boolean>()
|
||||
val IS_ENUM_ENTRY by error0<KtTypeReference>()
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
override val typeOperatorCallCheckers: Set<FirTypeOperatorCallChecker>
|
||||
get() = setOf(
|
||||
FirUselessTypeOperationCallChecker,
|
||||
FirCannotCheckForErasedChecker
|
||||
FirCastOperatorsChecker
|
||||
)
|
||||
|
||||
override val resolvedQualifierCheckers: Set<FirResolvedQualifierChecker>
|
||||
|
||||
+87
-2
@@ -5,19 +5,104 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.platformClassMapper
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
||||
import org.jetbrains.kotlin.fir.scopes.platformClassMapper
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker.findCorrespondingSupertypes
|
||||
import org.jetbrains.kotlin.types.model.typeConstructor
|
||||
|
||||
fun isCastPossible(
|
||||
lhsType: ConeKotlinType,
|
||||
rhsType: ConeKotlinType,
|
||||
isSafeCase: Boolean,
|
||||
context: CheckerContext
|
||||
): Boolean {
|
||||
val lhsLowerType = lhsType.lowerBoundIfFlexible()
|
||||
val rhsLowerType = rhsType.lowerBoundIfFlexible()
|
||||
val session = context.session
|
||||
|
||||
if (lhsLowerType is ConeIntersectionType) {
|
||||
var result = false
|
||||
for (intersectedType in lhsLowerType.intersectedTypes) {
|
||||
val isIntersectedCastPossible = isCastPossible(intersectedType, rhsLowerType, isSafeCase, context)
|
||||
val intersectedTypeSymbol = intersectedType.toRegularClassSymbol(context.session)
|
||||
if (intersectedTypeSymbol?.isInterface == false && !isIntersectedCastPossible) {
|
||||
return false // Any class type in intersection type should be subtype of RHS
|
||||
}
|
||||
result = result or isIntersectedCastPossible
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
val lhsNullable = lhsLowerType.canBeNull
|
||||
val rhsNullable = rhsLowerType.canBeNull
|
||||
if (lhsLowerType.isNothing) return true
|
||||
if (lhsLowerType.isNullableNothing && !rhsNullable) {
|
||||
return isSafeCase
|
||||
}
|
||||
if (rhsLowerType.isNothing) return false
|
||||
if (rhsLowerType.isNullableNothing) return lhsNullable
|
||||
if (lhsNullable && rhsNullable) return true
|
||||
val lhsClassSymbol = lhsLowerType.toRegularClassSymbol(context.session)
|
||||
val rhsClassSymbol = rhsLowerType.toRegularClassSymbol(context.session)
|
||||
if (isRelated(lhsLowerType, rhsLowerType, lhsClassSymbol, rhsClassSymbol, context)) 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 (lhsLowerType is ConeTypeParameterType || rhsLowerType is ConeTypeParameterType) return true
|
||||
|
||||
if (isFinal(lhsLowerType, session) || isFinal(rhsLowerType, session)) return false
|
||||
if (lhsClassSymbol?.isInterface == true || rhsClassSymbol?.isInterface == true) return true
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Two types are related, roughly, when one of them is a subtype of the other constructing class
|
||||
*
|
||||
* Note that some types have platform-specific counterparts, i.e. kotlin.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 -> kotlin.String) and ignore mappings that go the other way.
|
||||
*/
|
||||
private fun isRelated(
|
||||
aType: ConeKotlinType,
|
||||
bType: ConeKotlinType,
|
||||
aClassSymbol: FirRegularClassSymbol?,
|
||||
bClassSymbol: FirRegularClassSymbol?,
|
||||
context: CheckerContext
|
||||
): Boolean {
|
||||
val typeContext = context.session.typeContext
|
||||
|
||||
if (AbstractTypeChecker.isSubtypeOf(typeContext, aType, bType) ||
|
||||
AbstractTypeChecker.isSubtypeOf(typeContext, bType, aType)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
fun getCorrespondingKotlinClass(type: ConeKotlinType): ConeKotlinType {
|
||||
return context.session.platformClassMapper.getCorrespondingKotlinClass(type.classId)?.defaultType(listOf()) ?: type
|
||||
}
|
||||
|
||||
val aNormalizedType = getCorrespondingKotlinClass(aClassSymbol?.defaultType() ?: aType)
|
||||
val bNormalizedType = getCorrespondingKotlinClass(bClassSymbol?.defaultType() ?: bType)
|
||||
|
||||
return AbstractTypeChecker.isSubtypeOf(typeContext, aNormalizedType, bNormalizedType) ||
|
||||
AbstractTypeChecker.isSubtypeOf(typeContext, bNormalizedType, aNormalizedType)
|
||||
}
|
||||
|
||||
private fun isFinal(type: ConeKotlinType, session: FirSession): Boolean {
|
||||
return !type.canHaveSubtypes(session)
|
||||
}
|
||||
|
||||
fun isCastErased(supertype: ConeKotlinType, subtype: ConeKotlinType, context: CheckerContext): Boolean {
|
||||
val typeContext = context.session.typeContext
|
||||
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isCastErased
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.expressions.FirOperation
|
||||
import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
|
||||
object FirCannotCheckForErasedChecker : FirTypeOperatorCallChecker() {
|
||||
override fun check(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (expression.operation != FirOperation.IS) return
|
||||
|
||||
val session = context.session
|
||||
val subjectType = expression.argumentList.arguments[0].typeRef.coneType.fullyExpandedType(session)
|
||||
val conversionTypeRef = expression.conversionTypeRef
|
||||
val targetType = conversionTypeRef.coneType.fullyExpandedType(session)
|
||||
|
||||
if (isCastErased(subjectType, targetType, context)) {
|
||||
reporter.reportOn(conversionTypeRef.source, FirErrors.CANNOT_CHECK_FOR_ERASED, targetType, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -5,13 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.CastingType
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isCastErased
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.checkCasting
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.expressions.FirOperation
|
||||
import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall
|
||||
|
||||
+1
@@ -725,6 +725,7 @@ object PositioningStrategies {
|
||||
override fun mark(element: KtExpression): List<TextRange> {
|
||||
return when (element) {
|
||||
is KtBinaryExpression -> mark(element.operationReference)
|
||||
is KtBinaryExpressionWithTypeRHS -> mark(element.operationReference)
|
||||
is KtUnaryExpression -> mark(element.operationReference)
|
||||
else -> super.mark(element)
|
||||
}
|
||||
|
||||
+3
-3
@@ -32,11 +32,11 @@ fun test() {
|
||||
checkSubtype<Double>(<!ARGUMENT_TYPE_MISMATCH!>1<!>)
|
||||
checkSubtype<Float>(<!ARGUMENT_TYPE_MISMATCH!>1<!>)
|
||||
|
||||
1 as Byte
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as<!> Byte
|
||||
1 <!USELESS_CAST!>as Int<!>
|
||||
0xff as Long
|
||||
0xff <!CAST_NEVER_SUCCEEDS!>as<!> Long
|
||||
|
||||
1.1 as Int
|
||||
1.1 <!CAST_NEVER_SUCCEEDS!>as<!> Int
|
||||
checkSubtype<Int>(<!ARGUMENT_TYPE_MISMATCH!>1.1<!>)
|
||||
|
||||
varargByte(0x77, 1, 3, <!ARGUMENT_TYPE_MISMATCH!>200<!>, 0b111)
|
||||
|
||||
@@ -33,4 +33,4 @@ fun bar(a: Ann = <!ANNOTATION_CLASS_CONSTRUCTOR_CALL!>Ann()<!>) {
|
||||
operator fun String.invoke() {}
|
||||
|
||||
// from stdlib
|
||||
fun <T> javaClass() : Class<T> = null as Class<T>
|
||||
fun <T> javaClass() : Class<T> = null <!CAST_NEVER_SUCCEEDS!>as<!> Class<T>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Nothing can be cast to Nothing
|
||||
fun foo(x: String) {
|
||||
x as Nothing
|
||||
}
|
||||
|
||||
fun gav(y: String?) {
|
||||
y as Nothing
|
||||
}
|
||||
|
||||
// Only nullable can be cast to Nothing?
|
||||
fun bar(x: String, y: String?) {
|
||||
x as Nothing?
|
||||
y as Nothing?
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// Nothing can be cast to Nothing
|
||||
fun foo(x: String) {
|
||||
x <!CAST_NEVER_SUCCEEDS!>as<!> Nothing
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
typealias MyString = String
|
||||
|
||||
val x: MyString = ""
|
||||
val y = x as Any
|
||||
|
||||
interface Base
|
||||
class Derived : Base
|
||||
interface Other : Base
|
||||
typealias IBase = Base
|
||||
typealias IOther = Other
|
||||
|
||||
val ib: IBase = Derived()
|
||||
val d = ib as Derived
|
||||
val o = ib as Other
|
||||
val io = ib as IOther
|
||||
val s = d as String
|
||||
val ms = d as MyString
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
typealias MyString = String
|
||||
|
||||
val x: MyString = ""
|
||||
|
||||
+36
-36
@@ -1,51 +1,51 @@
|
||||
fun asCall() {
|
||||
1 <!USELESS_CAST!>as Int<!>
|
||||
1 as Byte
|
||||
1 as Short
|
||||
1 as Long
|
||||
1 as Char
|
||||
1 as Double
|
||||
1 as Float
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as<!> Byte
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as<!> Short
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as<!> Long
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as<!> Char
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as<!> Double
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as<!> Float
|
||||
|
||||
1.0 as Int
|
||||
1.0 as Byte
|
||||
1.0 as Short
|
||||
1.0 as Long
|
||||
1.0 as Char
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as<!> Int
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as<!> Byte
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as<!> Short
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as<!> Long
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as<!> Char
|
||||
1.0 <!USELESS_CAST!>as Double<!>
|
||||
1.0 as Float
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as<!> Float
|
||||
|
||||
1f as Int
|
||||
1f as Byte
|
||||
1f as Short
|
||||
1f as Long
|
||||
1f as Char
|
||||
1f as Double
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as<!> Int
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as<!> Byte
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as<!> Short
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as<!> Long
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as<!> Char
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as<!> Double
|
||||
1f <!USELESS_CAST!>as Float<!>
|
||||
}
|
||||
|
||||
fun asSafe() {
|
||||
1 as? Int
|
||||
1 as? Byte
|
||||
1 as? Short
|
||||
1 as? Long
|
||||
1 as? Char
|
||||
1 as? Double
|
||||
1 as? Float
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as?<!> Byte
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as?<!> Short
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as?<!> Long
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as?<!> Char
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as?<!> Double
|
||||
1 <!CAST_NEVER_SUCCEEDS!>as?<!> Float
|
||||
|
||||
1.0 as? Int
|
||||
1.0 as? Byte
|
||||
1.0 as? Short
|
||||
1.0 as? Long
|
||||
1.0 as? Char
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as?<!> Int
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as?<!> Byte
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as?<!> Short
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as?<!> Long
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as?<!> Char
|
||||
1.0 as? Double
|
||||
1.0 as? Float
|
||||
1.0 <!CAST_NEVER_SUCCEEDS!>as?<!> Float
|
||||
|
||||
1f as? Int
|
||||
1f as? Byte
|
||||
1f as? Short
|
||||
1f as? Long
|
||||
1f as? Char
|
||||
1f as? Double
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as?<!> Int
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as?<!> Byte
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as?<!> Short
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as?<!> Long
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as?<!> Char
|
||||
1f <!CAST_NEVER_SUCCEEDS!>as?<!> Double
|
||||
1f as? Float
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -USELESS_CAST
|
||||
import java.lang.String as JString
|
||||
import java.lang.CharSequence as JCS
|
||||
|
||||
fun test(
|
||||
s: String,
|
||||
js: JString,
|
||||
cs: CharSequence,
|
||||
jcs: JCS
|
||||
) {
|
||||
s as JString
|
||||
s as JCS
|
||||
s as CharSequence
|
||||
s as String
|
||||
|
||||
js as JString
|
||||
js as JCS
|
||||
js as CharSequence
|
||||
js as String
|
||||
|
||||
cs as JString
|
||||
cs as JCS
|
||||
cs as CharSequence
|
||||
cs as String
|
||||
|
||||
jcs as JString
|
||||
jcs as JCS
|
||||
jcs as CharSequence
|
||||
jcs as String
|
||||
|
||||
jcs as Int
|
||||
s as java.lang.Integer
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -USELESS_CAST
|
||||
import java.lang.String as JString
|
||||
import java.lang.CharSequence as JCS
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -UNUSED_PARAMETER -ABSTRACT_MEMBER_NOT_IMPLEMENTED -USELESS_CAST
|
||||
import java.lang.CharSequence as JCS
|
||||
|
||||
class JSub: JCS
|
||||
class Sub: CharSequence
|
||||
|
||||
fun test(
|
||||
s: Sub,
|
||||
js: JSub,
|
||||
cs: CharSequence,
|
||||
jcs: JCS
|
||||
) {
|
||||
// js as CharSequence // - this case is not supported due to limitation in PlatformToKotlinClassMap
|
||||
js as JCS
|
||||
|
||||
s as CharSequence
|
||||
s as JCS
|
||||
|
||||
js as Sub
|
||||
s as JSub
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -UNUSED_PARAMETER -ABSTRACT_MEMBER_NOT_IMPLEMENTED -USELESS_CAST
|
||||
import java.lang.CharSequence as JCS
|
||||
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
// !DIAGNOSTICS: -UNCHECKED_CAST
|
||||
interface Trait1
|
||||
interface Trait2
|
||||
open class OClass1
|
||||
open class OClass2
|
||||
class FClass1
|
||||
class FClass2
|
||||
|
||||
fun <TP1: OClass1, TP2: OClass2> test(
|
||||
t1: Trait1,
|
||||
oc1: OClass1,
|
||||
fc1: FClass1,
|
||||
tp1: TP1
|
||||
) {
|
||||
t1 as Trait2
|
||||
t1 as OClass2
|
||||
t1 as FClass2
|
||||
t1 as TP2
|
||||
|
||||
oc1 as Trait2
|
||||
oc1 as OClass2
|
||||
oc1 as FClass2
|
||||
oc1 as TP2
|
||||
|
||||
fc1 as Trait2
|
||||
fc1 as OClass2
|
||||
fc1 as FClass2
|
||||
fc1 as TP2
|
||||
|
||||
tp1 as Trait2
|
||||
tp1 as OClass2
|
||||
tp1 as FClass2
|
||||
tp1 as TP2
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNCHECKED_CAST
|
||||
interface Trait1
|
||||
interface Trait2
|
||||
|
||||
+17
-17
@@ -19,35 +19,35 @@ class Inv<T>() {
|
||||
|
||||
fun testInOut() {
|
||||
In<String>().f("1");
|
||||
(null as In<in String>).f("1")
|
||||
(null as In<*>).<!NONE_APPLICABLE!>f<!>("1") // Wrong Arg
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<in String>).f("1")
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<*>).<!NONE_APPLICABLE!>f<!>("1") // Wrong Arg
|
||||
|
||||
In<String>().f(1);
|
||||
(null as In<in String>).f(1)
|
||||
(null as In<*>).f(1);
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<in String>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> In<*>).f(1);
|
||||
|
||||
Out<Int>().f(1)
|
||||
(null as Out<out Int>).f(1)
|
||||
(null as Out<*>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<out Int>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<*>).f(1)
|
||||
|
||||
Out<Int>().f()
|
||||
(null as Out<out Int>).f()
|
||||
(null as Out<*>).f()
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<out Int>).f()
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Out<*>).f()
|
||||
|
||||
Inv<Int>().f(1)
|
||||
(null as Inv<in Int>).f(1)
|
||||
(null as Inv<out Int>).f(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
(null as Inv<*>).f(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<in Int>).f(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<out Int>).f(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<*>).f(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
|
||||
Inv<Int>().inf(1)
|
||||
(null as Inv<in Int>).inf(1)
|
||||
(null as Inv<out Int>).inf(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
(null as Inv<*>).inf(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<in Int>).inf(1)
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<out Int>).inf(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<*>).inf(<!ARGUMENT_TYPE_MISMATCH!>1<!>) // !!
|
||||
|
||||
Inv<Int>().outf()
|
||||
checkSubtype<Int>(<!ARGUMENT_TYPE_MISMATCH!>(null as Inv<in Int>).outf()<!>) // Type mismatch
|
||||
(null as Inv<out Int>).outf()
|
||||
(null as Inv<*>).outf()
|
||||
checkSubtype<Int>(<!ARGUMENT_TYPE_MISMATCH!>(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<in Int>).outf()<!>) // Type mismatch
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<out Int>).outf()
|
||||
(null <!CAST_NEVER_SUCCEEDS!>as<!> Inv<*>).outf()
|
||||
|
||||
Inv<Int>().outf(<!TOO_MANY_ARGUMENTS!>1<!>) // Wrong Arg
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,4 +9,4 @@ fun <T> foo2(): T = TODO()
|
||||
val test = <!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>foo2<!>().plus("") <!USELESS_CAST!>as String<!>
|
||||
|
||||
fun <T> T.bar() = this
|
||||
val barTest = "".bar() as Number
|
||||
val barTest = "".bar() <!CAST_NEVER_SUCCEEDS!>as<!> Number
|
||||
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
fun <R> runCatching(block: () -> R) = null as Result<R>
|
||||
|
||||
class Result<out T> {
|
||||
fun getOrNull(): T? = null
|
||||
}
|
||||
|
||||
fun main() {
|
||||
runCatching {
|
||||
null
|
||||
}.getOrNull() // don't report IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
fun <R> runCatching(block: () -> R) = null <!CAST_NEVER_SUCCEEDS!>as<!> Result<R>
|
||||
|
||||
class Result<out T> {
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ fun test() {
|
||||
if (<!USELESS_IS_CHECK!>x11 is Foo<<!UNRESOLVED_REFERENCE!>_<!>><!>) { }
|
||||
|
||||
x10 as <!UNRESOLVED_REFERENCE!>_<!>
|
||||
x10 as Foo<<!UNRESOLVED_REFERENCE!>_<!>>
|
||||
x10 <!CAST_NEVER_SUCCEEDS!>as<!> Foo<<!UNRESOLVED_REFERENCE!>_<!>>
|
||||
|
||||
val x12: Foo<@<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!> Int>? = null
|
||||
val x13: Foo<@<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>() Int>? = null
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// KT-702 Type inference failed
|
||||
fun <T> getJavaClass() : java.lang.Class<T> { return "" as Class<T> }
|
||||
|
||||
public class Throwables() {
|
||||
companion object {
|
||||
public fun <X : Throwable?> propagateIfInstanceOf(throwable : Throwable?, declaredType : Class<X?>?) {
|
||||
if (((throwable != null) && declaredType?.isInstance(throwable)!!))
|
||||
{
|
||||
throw <!SAFE_CALL_WILL_CHANGE_NULLABILITY!>declaredType<!UNNECESSARY_SAFE_CALL!>?.<!>cast(throwable)<!>!!
|
||||
}
|
||||
}
|
||||
public fun propagateIfPossible(throwable : Throwable?) {
|
||||
propagateIfInstanceOf(throwable, getJavaClass<Error?>()) //; Type inference failed: Mismatch while expanding constraints
|
||||
propagateIfInstanceOf(throwable, getJavaClass<RuntimeException?>()) // Type inference failed: Mismatch while expanding constraints
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// KT-702 Type inference failed
|
||||
fun <T> getJavaClass() : java.lang.Class<T> { return "" <!CAST_NEVER_SUCCEEDS!>as<!> Class<T> }
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class TypeInfo<T>
|
||||
|
||||
fun <T> typeinfo() : TypeInfo<T> = null as TypeInfo<T>
|
||||
fun <T> typeinfo() : TypeInfo<T> = null <!CAST_NEVER_SUCCEEDS!>as<!> TypeInfo<T>
|
||||
|
||||
fun <T> TypeInfo<T>.getJavaClass() : java.lang.Class<T> {
|
||||
val t : java.lang.Object = this as java.lang.Object
|
||||
@@ -13,4 +13,4 @@ fun <T> getJavaClass() = typeinfo<T>().getJavaClass()
|
||||
|
||||
fun main() {
|
||||
System.out.println(getJavaClass<String>())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ fun f(a: SomeClass?) {
|
||||
aa<!UNSAFE_CALL!>.<!>hashCode()
|
||||
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
||||
(aa as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
|
||||
(aa as SomeSubClass).foo
|
||||
(aa <!CAST_NEVER_SUCCEEDS!>as<!> SomeSubClass).foo
|
||||
}
|
||||
val b = (aa as? SomeSubClass)?.foo
|
||||
aa = null
|
||||
@@ -52,7 +52,7 @@ fun f(a: SomeClass?) {
|
||||
aa<!UNSAFE_CALL!>.<!>hashCode()
|
||||
aa.<!UNRESOLVED_REFERENCE!>foo<!>
|
||||
(aa as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
|
||||
(aa as SomeSubClass).foo
|
||||
(aa <!CAST_NEVER_SUCCEEDS!>as<!> SomeSubClass).foo
|
||||
}
|
||||
aa = a
|
||||
val c = aa as? SomeSubClass
|
||||
|
||||
+11
-11
@@ -4,7 +4,7 @@ fun <T : CharSequence> foo(x: Array<Any>, y: IntArray, block: (T, Int) -> Int) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
// comment
|
||||
/* comment */
|
||||
r = block(x[0] as T, "" as Int)
|
||||
r = block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int)
|
||||
|
||||
// to prevent unused assignment diagnostic for the above statement
|
||||
r.hashCode()
|
||||
@@ -13,42 +13,42 @@ fun <T : CharSequence> foo(x: Array<Any>, y: IntArray, block: (T, Int) -> Int) {
|
||||
|
||||
if (i != 1) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
}
|
||||
|
||||
if (i != 1)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
|
||||
if (i != 2)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
else
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[1] as T, "" as Int).toInt()
|
||||
i += block(x[1] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
|
||||
while (i != 1)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
|
||||
do
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
while (i != 1)
|
||||
|
||||
for (j in 1..100)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
|
||||
when (i) {
|
||||
1 ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
}
|
||||
|
||||
val l: () -> Unit = {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
}
|
||||
l()
|
||||
|
||||
@@ -56,5 +56,5 @@ fun <T : CharSequence> foo(x: Array<Any>, y: IntArray, block: (T, Int) -> Int) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
||||
|
||||
y[i] += block(x[0] as T, "" as Int).toInt()
|
||||
y[i] += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
fun <T : CharSequence> foo(x: Array<Any>, block: (T, Int) -> Int) {
|
||||
var r: Any?
|
||||
|
||||
@Suppress("UNCHECKED_CAST") r = block(x[0] as T, "" as Int)
|
||||
@Suppress("UNCHECKED_CAST") r = block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int)
|
||||
|
||||
// to prevent unused assignment diagnostic for the above statement
|
||||
r.hashCode()
|
||||
@@ -9,11 +9,11 @@ fun <T : CharSequence> foo(x: Array<Any>, block: (T, Int) -> Int) {
|
||||
var i = 1
|
||||
|
||||
if (i != 1) {
|
||||
@Suppress("UNCHECKED_CAST") i += block(x[0] as T, "" as Int).toInt()
|
||||
@Suppress("UNCHECKED_CAST") i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
}
|
||||
|
||||
if (i != 1) @Suppress("UNCHECKED_CAST")
|
||||
i += block(x[0] as T, "" as Int).toInt()
|
||||
i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
|
||||
if (i != 1) @Suppress("UNCHECKED_CAST") i += block(x[0] as T, "" as Int).toInt()
|
||||
if (i != 1) @Suppress("UNCHECKED_CAST") i += block(x[0] as T, "" <!CAST_NEVER_SUCCEEDS!>as<!> Int).toInt()
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ fun interface ReadOnlyProperty<in T, out V> {
|
||||
|
||||
class Problem {
|
||||
val variable: Int by <!DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH, NEW_INFERENCE_ERROR, NEW_INFERENCE_ERROR!>delegate()<!> // delegate returns `ReadOnlyProperty<Problem, {CharSequence & Int}>`
|
||||
fun <T : CharSequence> delegate() = null as ReadOnlyProperty<Problem, T>
|
||||
fun <T : CharSequence> delegate() = null <!CAST_NEVER_SUCCEEDS!>as<!> ReadOnlyProperty<Problem, T>
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ fun test1(s: SubInt, sWrong: SubIntWrong) {
|
||||
val a = "foo"
|
||||
foo(<!ARGUMENT_TYPE_MISMATCH!>a<!>)
|
||||
|
||||
a as (Int, String) -> String
|
||||
a <!CAST_NEVER_SUCCEEDS!>as<!> (Int, String) -> String
|
||||
foo(<!ARGUMENT_TYPE_MISMATCH!>a<!>)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -37,6 +37,6 @@ fun test() {
|
||||
|
||||
// TODO: should we allow somehow to call with passing continuation explicitly?
|
||||
severalParams("", 89, <!TOO_MANY_ARGUMENTS!>6.9<!>) checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><Unit>() }
|
||||
severalParams("", 89, <!TOO_MANY_ARGUMENTS!>this as Continuation<Double><!>) checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><Unit>() }
|
||||
severalParams("", 89, <!TOO_MANY_ARGUMENTS!>this <!CAST_NEVER_SUCCEEDS!>as<!> Continuation<Double><!>) checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><Unit>() }
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ fun box() {
|
||||
val b = Array<<!REIFIED_TYPE_FORBIDDEN_SUBSTITUTION!>Nothing?<!>>(5) { null!! }
|
||||
val c = <!REIFIED_TYPE_FORBIDDEN_SUBSTITUTION!>foo<!>() { null!! }
|
||||
val d = foo<Any> { null!! }
|
||||
val e = <!REIFIED_TYPE_FORBIDDEN_SUBSTITUTION!>foo<!> { "1" as Nothing }
|
||||
val e1 = <!REIFIED_TYPE_FORBIDDEN_SUBSTITUTION!>foo<!> { "1" as Nothing? }
|
||||
val e = <!REIFIED_TYPE_FORBIDDEN_SUBSTITUTION!>foo<!> { "1" <!CAST_NEVER_SUCCEEDS!>as<!> Nothing }
|
||||
val e1 = <!REIFIED_TYPE_FORBIDDEN_SUBSTITUTION!>foo<!> { "1" <!CAST_NEVER_SUCCEEDS!>as<!> Nothing? }
|
||||
|
||||
val f = javaClass<<!REIFIED_TYPE_FORBIDDEN_SUBSTITUTION!>Nothing<!>>()
|
||||
}
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ fun case_5(value_1: Int?) {
|
||||
println("!")
|
||||
contract {
|
||||
returns(true) implies (value_1 != null)
|
||||
} as ContractBuilder
|
||||
} <!CAST_NEVER_SUCCEEDS!>as<!> ContractBuilder
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -93,6 +93,6 @@ fun case_9(number: Int?): Boolean {
|
||||
val value_1 = number != null
|
||||
contract {
|
||||
returns(false) implies (value_1)
|
||||
} as ContractBuilder
|
||||
} <!CAST_NEVER_SUCCEEDS!>as<!> ContractBuilder
|
||||
return number == null
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ fun case_1(value_1: Int?) {
|
||||
println("!")
|
||||
contract {
|
||||
returns(true) implies (value_1 != null)
|
||||
} as ContractBuilder
|
||||
} <!CAST_NEVER_SUCCEEDS!>as<!> ContractBuilder
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 2
|
||||
@@ -42,6 +42,6 @@ fun case_5(number: Int?): Boolean {
|
||||
val value_1 = number != null
|
||||
contract {
|
||||
returns(false) implies (value_1)
|
||||
} as ContractBuilder
|
||||
} <!CAST_NEVER_SUCCEEDS!>as<!> ContractBuilder
|
||||
return number == null
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
fun case_1(x: Interface1) = x
|
||||
fun case_1(x: Interface2) = x
|
||||
fun case_1() {
|
||||
val x: Interface1 = null as Interface1
|
||||
val x: Interface1 = null <!CAST_NEVER_SUCCEEDS!>as<!> Interface1
|
||||
x as Interface2
|
||||
<!OVERLOAD_RESOLUTION_AMBIGUITY!>case_1<!>(<!DEBUG_INFO_EXPRESSION_TYPE("Interface1 & Interface2 & Interface1")!>x<!>)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user