FIR checker: warn useless as and is

This commit is contained in:
Jinseong Jeon
2021-04-28 22:32:51 -07:00
committed by TeamCityServer
parent 19d939c36e
commit e2dc21da90
141 changed files with 417 additions and 543 deletions
+1 -1
View File
@@ -13,6 +13,6 @@ interface B
fun test_2(x: Any?) {
if (x is A && x is B) {
x is A
<!USELESS_IS_CHECK!>x is A<!>
}
}
@@ -27,7 +27,7 @@ fun test_1(e: A) {
val d = when (e) {
is E -> 1
is A -> 2
<!USELESS_IS_CHECK!>is A<!> -> 2
}.plus(0)
}
@@ -1,6 +1,6 @@
fun foo() = if (true) 1 else 0
fun bar(arg: Any?) = when (arg) {
is Int -> arg as Int
is Int -> arg <!USELESS_CAST!>as Int<!>
else -> 42
}
@@ -3,7 +3,7 @@ class Some
fun foo(): () -> Boolean {
val s = Some()
if (true) {
return { if (s is Some) true else false }
return { if (<!USELESS_IS_CHECK!>s is Some<!>) true else false }
} else {
return { true }
}
@@ -14,7 +14,7 @@ class Case1() {
this.yield("") //UNRESOLVED_REFERENCE
this as SequenceScope<String>
this <!USELESS_CAST!>as SequenceScope<String><!>
yield("") // resolved to SequenceScope.yield
@@ -31,10 +31,10 @@ fun case2() {
this.yield("") //UNRESOLVED_REFERENCE
this as SequenceScope<String>
this <!USELESS_CAST!>as SequenceScope<String><!>
yield("") // UNRESOLVED_REFERENCE
this.yield("") // UNRESOLVED_REFERENCE
}
}
}
@@ -38,6 +38,7 @@ fun main(args: Array<String>) {
alias<FirEqualityOperatorCall>("EqualityOperatorCallChecker")
alias<FirAnonymousFunction>("AnonymousFunctionAsExpressionChecker")
alias<FirStringConcatenationCall>("StringConcatenationCallChecker")
alias<FirTypeOperatorCall>("TypeOperatorCallChecker")
alias<FirResolvedQualifier>("ResolvedQualifierChecker")
}
@@ -52,6 +52,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
CONST_MODIFIER,
ARRAY_ACCESS,
SAFE_ACCESS,
AS_TYPE,
USELESS_ELVIS,
NAME_OF_NAMED_ARGUMENT,
VALUE_ARGUMENTS,
@@ -654,6 +654,13 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val USELESS_ELVIS_RIGHT_IS_NULL by warning<KtBinaryExpression>(PositioningStrategy.USELESS_ELVIS)
}
val CASTS_AND_IS_CHECKS by object : DiagnosticGroup("Casts and is-checks") {
val USELESS_CAST by warning<KtBinaryExpressionWithTypeRHS>(PositioningStrategy.AS_TYPE)
val USELESS_IS_CHECK by warning<KtElement> {
parameter<Boolean>("compileTimeCheckResult")
}
}
val WHEN_EXPRESSIONS by object : DiagnosticGroup("When expressions") {
val NO_ELSE_IN_WHEN by error<KtWhenExpression>(PositioningStrategy.WHEN_EXPRESSION) {
parameter<List<WhenMissingCase>>("missingWhenCases")
@@ -45,6 +45,8 @@ class ComposedExpressionCheckers : ExpressionCheckers() {
get() = _anonymousFunctionAsExpressionCheckers
override val stringConcatenationCallCheckers: Set<FirStringConcatenationCallChecker>
get() = _stringConcatenationCallCheckers
override val typeOperatorCallCheckers: Set<FirTypeOperatorCallChecker>
get() = _typeOperatorCallCheckers
override val resolvedQualifierCheckers: Set<FirResolvedQualifierChecker>
get() = _resolvedQualifierCheckers
@@ -64,6 +66,7 @@ class ComposedExpressionCheckers : ExpressionCheckers() {
private val _equalityOperatorCallCheckers: MutableSet<FirEqualityOperatorCallChecker> = mutableSetOf()
private val _anonymousFunctionAsExpressionCheckers: MutableSet<FirAnonymousFunctionAsExpressionChecker> = mutableSetOf()
private val _stringConcatenationCallCheckers: MutableSet<FirStringConcatenationCallChecker> = mutableSetOf()
private val _typeOperatorCallCheckers: MutableSet<FirTypeOperatorCallChecker> = mutableSetOf()
private val _resolvedQualifierCheckers: MutableSet<FirResolvedQualifierChecker> = mutableSetOf()
@CheckersComponentInternal
@@ -84,6 +87,7 @@ class ComposedExpressionCheckers : ExpressionCheckers() {
_equalityOperatorCallCheckers += checkers.equalityOperatorCallCheckers
_anonymousFunctionAsExpressionCheckers += checkers.anonymousFunctionAsExpressionCheckers
_stringConcatenationCallCheckers += checkers.stringConcatenationCallCheckers
_typeOperatorCallCheckers += checkers.typeOperatorCallCheckers
_resolvedQualifierCheckers += checkers.resolvedQualifierCheckers
}
}
@@ -33,6 +33,7 @@ abstract class ExpressionCheckers {
open val equalityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker> = emptySet()
open val anonymousFunctionAsExpressionCheckers: Set<FirAnonymousFunctionAsExpressionChecker> = emptySet()
open val stringConcatenationCallCheckers: Set<FirStringConcatenationCallChecker> = emptySet()
open val typeOperatorCallCheckers: Set<FirTypeOperatorCallChecker> = emptySet()
open val resolvedQualifierCheckers: Set<FirResolvedQualifierChecker> = emptySet()
@CheckersComponentInternal internal val allBasicExpressionCheckers: Set<FirBasicExpressionChecker> get() = basicExpressionCheckers
@@ -51,5 +52,6 @@ abstract class ExpressionCheckers {
@CheckersComponentInternal internal val allEqualityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker> get() = equalityOperatorCallCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allAnonymousFunctionAsExpressionCheckers: Set<FirAnonymousFunctionAsExpressionChecker> get() = anonymousFunctionAsExpressionCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allStringConcatenationCallCheckers: Set<FirStringConcatenationCallChecker> get() = stringConcatenationCallCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allTypeOperatorCallCheckers: Set<FirTypeOperatorCallChecker> get() = typeOperatorCallCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allResolvedQualifierCheckers: Set<FirResolvedQualifierChecker> get() = resolvedQualifierCheckers + basicExpressionCheckers
}
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirStringConcatenationCall
import org.jetbrains.kotlin.fir.expressions.FirTryExpression
import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
@@ -44,4 +45,5 @@ typealias FirSafeCallExpressionChecker = FirExpressionChecker<FirSafeCallExpress
typealias FirEqualityOperatorCallChecker = FirExpressionChecker<FirEqualityOperatorCall>
typealias FirAnonymousFunctionAsExpressionChecker = FirExpressionChecker<FirAnonymousFunction>
typealias FirStringConcatenationCallChecker = FirExpressionChecker<FirStringConcatenationCall>
typealias FirTypeOperatorCallChecker = FirExpressionChecker<FirTypeOperatorCall>
typealias FirResolvedQualifierChecker = FirExpressionChecker<FirResolvedQualifier>
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotation
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
@@ -390,6 +391,10 @@ object FirErrors {
val USELESS_ELVIS by warning1<KtBinaryExpression, ConeKotlinType>(SourceElementPositioningStrategies.USELESS_ELVIS)
val USELESS_ELVIS_RIGHT_IS_NULL by warning0<KtBinaryExpression>(SourceElementPositioningStrategies.USELESS_ELVIS)
// Casts and is-checks
val USELESS_CAST by warning0<KtBinaryExpressionWithTypeRHS>(SourceElementPositioningStrategies.AS_TYPE)
val USELESS_IS_CHECK by warning1<KtElement, Boolean>()
// When expressions
val NO_ELSE_IN_WHEN by error1<KtWhenExpression, List<WhenMissingCase>>(SourceElementPositioningStrategies.WHEN_EXPRESSION)
val INVALID_IF_AS_EXPRESSION by error0<KtIfExpression>(SourceElementPositioningStrategies.IF_EXPRESSION)
@@ -0,0 +1,91 @@
/*
* 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.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
// See .../types/CastDiagnosticsUtil.kt for counterparts, including isRefinementUseless, isExactTypeCast, isUpcast.
object FirUselessTypeOperationCallChecker : FirTypeOperatorCallChecker() {
override fun check(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression.operation !in FirOperation.TYPES) return
val arg = expression.argument
val candidateType = arg.typeRef.coneType.upperBoundIfFlexible().fullyExpandedType(context.session)
if (candidateType is ConeKotlinErrorType) return
val targetType = expression.conversionTypeRef.coneType.fullyExpandedType(context.session)
if (targetType is ConeKotlinErrorType) return
// x as? Type <=> x as Type?
val refinedTargetType =
if (expression.operation == FirOperation.SAFE_AS) {
targetType.withNullability(ConeNullability.NULLABLE, context.session.typeContext)
} else {
targetType
}
if (isRefinementUseless(context, candidateType, refinedTargetType, shouldCheckForExactType(expression, context))) {
when (expression.operation) {
FirOperation.IS -> reporter.reportOn(expression.source, FirErrors.USELESS_IS_CHECK, true, context)
FirOperation.NOT_IS -> reporter.reportOn(expression.source, FirErrors.USELESS_IS_CHECK, false, context)
FirOperation.AS, FirOperation.SAFE_AS -> reporter.reportOn(expression.source, FirErrors.USELESS_CAST, context)
else -> throw AssertionError("Should not be here: ${expression.operation}")
}
}
}
@Suppress("UNUSED_PARAMETER")
private fun shouldCheckForExactType(expression: FirTypeOperatorCall, context: CheckerContext): Boolean {
return when (expression.operation) {
FirOperation.IS, FirOperation.NOT_IS -> false
// TODO: differentiate if this expression defines the enclosing thing's type
// e.g.,
// val c1 get() = 1 as Number
// val c2: Number get() = 1 <!USELESS_CAST!>as Number<!>
FirOperation.AS, FirOperation.SAFE_AS -> true
else -> throw AssertionError("Should not be here: ${expression.operation}")
}
}
private fun isRefinementUseless(
context: CheckerContext,
candidateType: ConeKotlinType,
targetType: ConeKotlinType,
shouldCheckForExactType: Boolean,
): Boolean {
return if (shouldCheckForExactType) {
isExactTypeCast(context, candidateType, targetType)
} else {
isUpcast(context, candidateType, targetType)
}
}
private fun isExactTypeCast(context: CheckerContext, candidateType: ConeKotlinType, targetType: ConeKotlinType): Boolean {
if (!AbstractTypeChecker.equalTypes(context.session.typeContext, candidateType, targetType, stubTypesEqualToAnything = false))
return false
// See comments at [isUpcast] why we need to check the existence of @ExtensionFunctionType
return candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType
}
private fun isUpcast(context: CheckerContext, candidateType: ConeKotlinType, targetType: ConeKotlinType): Boolean {
if (!AbstractTypeChecker.isSubtypeOf(context.session.typeContext, candidateType, targetType, stubTypesEqualToAnything = false))
return false
// E.g., foo(p1: (X) -> Y), where p1 has a functional type whose receiver type is X and return type is Y.
// For bar(p2: X.() -> Y), p2 has the same functional type (with same receiver and return types).
// The only difference is the existence of type annotation, @ExtensionFunctionType,
// which indicates that the annotated type represents an extension function.
// If one casts p1 to p2 (or vice versa), it is _not_ up cast, i.e., not redundant, yet meaningful.
return candidateType.isExtensionFunctionType == targetType.isExtensionFunctionType
}
}
@@ -28,7 +28,7 @@ class ExpressionCheckersDiagnosticComponent(
}
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: CheckerContext) {
checkers.allBasicExpressionCheckers.check(typeOperatorCall, data, reporter)
checkers.allTypeOperatorCallCheckers.check(typeOperatorCall, data, reporter)
}
override fun <T> visitConstExpression(constExpression: FirConstExpression<T>, data: CheckerContext) {
@@ -282,8 +282,10 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USAGE_IS_NOT_INLINABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_CAST
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS_RIGHT_IS_NULL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_IS_CHECK
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_CLASS_CANNOT_BE_CLONEABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION
@@ -871,6 +873,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(USELESS_ELVIS, "Elvis operator (?:) always returns the left operand of non-nullable type {0}", RENDER_TYPE)
map.put(USELESS_ELVIS_RIGHT_IS_NULL, "Right operand of elvis operator (?:) is useless if it is null")
// Casts and is-checks
map.put(USELESS_CAST, "No cast needed")
map.put(USELESS_IS_CHECK, "Check for instance is always ''{0}''", TO_STRING)
// When expressions
map.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", WHEN_MISSING_CASES)
map.put(INVALID_IF_AS_EXPRESSION, "'if' must have both main and 'else' branches if used as an expression")
@@ -594,7 +594,7 @@ object LightTreePositioningStrategies {
}
}
val USELESS_ELVIS = object : LightTreePositioningStrategy() {
private val OPERATION_TO_END = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
startOffset: Int,
@@ -605,6 +605,10 @@ object LightTreePositioningStrategies {
}
}
val AS_TYPE = OPERATION_TO_END
val USELESS_ELVIS = OPERATION_TO_END
val RETURN_WITH_LABEL = object : LightTreePositioningStrategy() {
override fun mark(
node: LighterASTNode,
@@ -118,7 +118,6 @@ object SourceElementPositioningStrategies {
PositioningStrategies.SUSPEND_MODIFIER
)
val OPERATOR = SourceElementPositioningStrategy(
LightTreePositioningStrategies.OPERATOR,
PositioningStrategies.OPERATOR
@@ -194,6 +193,11 @@ object SourceElementPositioningStrategies {
PositioningStrategies.SAFE_ACCESS
)
val AS_TYPE = SourceElementPositioningStrategy(
LightTreePositioningStrategies.AS_TYPE,
PositioningStrategies.AS_TYPE
)
val USELESS_ELVIS = SourceElementPositioningStrategy(
LightTreePositioningStrategies.USELESS_ELVIS,
PositioningStrategies.USELESS_ELVIS
@@ -94,6 +94,11 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirAnonymousFunctionChecker,
)
override val typeOperatorCallCheckers: Set<FirTypeOperatorCallChecker>
get() = setOf(
FirUselessTypeOperationCallChecker,
)
override val resolvedQualifierCheckers: Set<FirResolvedQualifierChecker>
get() = setOf(
FirStandaloneQualifierChecker,
+1 -1
View File
@@ -7,7 +7,7 @@ fun test() : Unit {
checkSubtype<Int?>(x)
checkSubtype<Int>(y)
checkSubtype<Int>(x as Int)
checkSubtype<Int>(y as Int)
checkSubtype<Int>(y <!USELESS_CAST!>as Int<!>)
checkSubtype<Int?>(x as Int?)
checkSubtype<Int?>(y as Int?)
checkSubtype<Int?>(x as? Int)
+1 -1
View File
@@ -33,7 +33,7 @@ fun test() {
checkSubtype<Float>(<!ARGUMENT_TYPE_MISMATCH!>1<!>)
1 as Byte
1 as Int
1 <!USELESS_CAST!>as Int<!>
0xff as Long
1.1 as Int
+4 -4
View File
@@ -1,21 +1,21 @@
// FILE: KotlinFile.kt
fun test() {
if (1 is Int) {
if (<!USELESS_IS_CHECK!>1 is Int<!>) {
if (1 is Boolean) {
}
}
A.create() is A
A.create() is A?
<!USELESS_IS_CHECK!>A.create() is A?<!>
<!UNRESOLVED_REFERENCE!>unresolved<!> is A
<!UNRESOLVED_REFERENCE!>unresolved<!> is A?
val x = foo()
x as String
x is String
<!USELESS_IS_CHECK!>x is String<!>
}
fun foo(): Any = ""
@@ -23,4 +23,4 @@ fun foo(): Any = ""
// FILE: A.java
class A {
static A create() { return null; }
}
}
@@ -45,10 +45,10 @@ fun test(a: Any) {
.0fin a
.0<!UNRESOLVED_REFERENCE!>din<!> a
1is Any
<!USELESS_IS_CHECK!>1is Any<!>
1as Any
1as? Any
1!is Any
<!USELESS_IS_CHECK!>1!is Any<!>
1!in a
}
}
@@ -31,8 +31,8 @@ fun test(a: Any) {
a !in's'
a !in<!EMPTY_CHARACTER_LITERAL!>''<!>
if("s"is Any) {}
if("s"is Any) {}
if(<!USELESS_IS_CHECK!>"s"is Any<!>) {}
if(<!USELESS_IS_CHECK!>"s"is Any<!>) {}
test("s"as Any)
a foo""<!SYNTAX!>1<!>
@@ -26,7 +26,7 @@ fun foo() {
}
fun bar(a: Ann = Ann()) {
if (a is Ann) {}
if (<!USELESS_IS_CHECK!>a is Ann<!>) {}
}
operator fun String.invoke() {}
@@ -25,8 +25,8 @@ fun testRun() {
}
run<Int?> {
1 as Int
1 as Int
1 <!USELESS_CAST!>as Int<!>
1 <!USELESS_CAST!>as Int<!>
}
runWithoutReturn {
@@ -4,7 +4,7 @@ fun test() {
val a = 1 as Any?
val b: Number = 1 as Number
val c = null as String?
val d: Number = 1 as Int
val d: Number = 1 <!USELESS_CAST!>as Int<!>
}
val c1 get() = 1 as Number
@@ -14,4 +14,4 @@ val d: Number
get() {
1 as Number
return 1 as Number
}
}
@@ -18,9 +18,9 @@ public class Foo {
fun test() {
Foo.create() as Foo
Foo.createN() as Foo
Foo.createNN() as Foo
Foo.createNN() <!USELESS_CAST!>as Foo<!>
Foo.create() as Foo?
Foo.createN() as Foo?
Foo.create() <!USELESS_CAST!>as Foo?<!>
Foo.createN() <!USELESS_CAST!>as Foo?<!>
Foo.createNN() as Foo?
}
}
@@ -1,5 +0,0 @@
open class Base<A>
class Some: Base<Int>()
// a is Some => a is Base<Int>
fun f(a: Some) = a is Base<Int>
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
open class Base<A>
class Some: Base<Int>()
@@ -1,8 +0,0 @@
open class A
open class B: A()
open class Base<in T>
class SubBase: Base<A>()
// f is SubBase => f is Base<A> => (Base<Contravariant T>, B <: A) f is Base<B>
fun test(f: SubBase) = f is Base<B>
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
open class A
open class B: A()
@@ -1,8 +0,0 @@
open class A
open class B: A()
open class Base<out T>
class SubBase: Base<B>()
// f is SubBase => (SubBase <: Base<B>) f is Base<B> => (B <: A, Base<Covariant T> => SubBase <: Base<A>) f is Base<A>
fun test(f: SubBase) = f is Base<A>
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
open class A
open class B: A()
@@ -1 +0,0 @@
fun f(a: MutableList<out Number>) = a is MutableList<out Any>
@@ -1 +1,2 @@
// FIR_IDENTICAL
fun f(a: MutableList<out Number>) = <!USELESS_IS_CHECK!>a is MutableList<out Any><!>
@@ -1 +0,0 @@
fun f(a: MutableList<String>) = a is MutableList<out CharSequence>
@@ -1 +1,2 @@
// FIR_IDENTICAL
fun f(a: MutableList<String>) = <!USELESS_IS_CHECK!>a is MutableList<out CharSequence><!>
@@ -1 +0,0 @@
fun f(a: List<Number>) = a is List<Any>
@@ -1 +1,2 @@
// FIR_IDENTICAL
fun f(a: List<Number>) = <!USELESS_IS_CHECK!>a is List<Any><!>
@@ -1 +0,0 @@
fun <T> testing(a: T) = a is T
@@ -1 +1,2 @@
// FIR_IDENTICAL
fun <T> testing(a: T) = <!USELESS_IS_CHECK!>a is T<!>
@@ -1,14 +1,14 @@
fun <T, S : T> test(x: T?, y: S, z: T) {
x is T
x is T?
<!USELESS_IS_CHECK!>x is T?<!>
y is T
y is S
y is T?
y is S?
<!USELESS_IS_CHECK!>y is T<!>
<!USELESS_IS_CHECK!>y is S<!>
<!USELESS_IS_CHECK!>y is T?<!>
<!USELESS_IS_CHECK!>y is S?<!>
z is T
z is T?
<!USELESS_IS_CHECK!>z is T<!>
<!USELESS_IS_CHECK!>z is T?<!>
null as T
null as T?
@@ -22,6 +22,6 @@ inline fun <reified T> test(x: T?) {
}
fun <T> foo(x: List<T>, y: List<T>?) {
x is List<T>
<!USELESS_IS_CHECK!>x is List<T><!>
y is List<T>
}
}
@@ -2,15 +2,15 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER, -UNUSED_VARIABLE
fun test(x: Int?) {
val a1 = x as? Int
val a2 = x as? Int?
val a1 = x <!USELESS_CAST!>as? Int<!>
val a2 = x <!USELESS_CAST!>as? Int?<!>
val a3 = x as? Number
val a4 = x as? Number?
val a5: Int? = x as? Int
val a6: Number? = x as? Int
val a5: Int? = x <!USELESS_CAST!>as? Int<!>
val a6: Number? = x <!USELESS_CAST!>as? Int<!>
val a7: Number? = 1 as? Number
run { x as? Int }
run { x <!USELESS_CAST!>as? Int<!> }
run { x as? Number }
foo(x as? Number)
@@ -22,17 +22,17 @@ public class JavaClass {
// FILE: test.kt
fun <T, S: Any> test(x1: T, x2: T?, y1: S, y2: S?) {
x1 is T?
x2 is T?
y1 is S?
y2 is S?
<!USELESS_IS_CHECK!>x1 is T?<!>
<!USELESS_IS_CHECK!>x2 is T?<!>
<!USELESS_IS_CHECK!>y1 is S?<!>
<!USELESS_IS_CHECK!>y2 is S?<!>
val f1 = JavaClass.foo()
f1 is Int?
<!USELESS_IS_CHECK!>f1 is Int?<!>
val f2 = JavaClass.fooN()
f2 is Int?
<!USELESS_IS_CHECK!>f2 is Int?<!>
val f3 = JavaClass.fooNN()
f3 is Int?
<!USELESS_IS_CHECK!>f3 is Int?<!>
}
+4 -4
View File
@@ -1,5 +1,5 @@
fun asCall() {
1 as Int
1 <!USELESS_CAST!>as Int<!>
1 as Byte
1 as Short
1 as Long
@@ -12,7 +12,7 @@ fun asCall() {
1.0 as Short
1.0 as Long
1.0 as Char
1.0 as Double
1.0 <!USELESS_CAST!>as Double<!>
1.0 as Float
1f as Int
@@ -21,7 +21,7 @@ fun asCall() {
1f as Long
1f as Char
1f as Double
1f as Float
1f <!USELESS_CAST!>as Float<!>
}
fun asSafe() {
@@ -48,4 +48,4 @@ fun asSafe() {
1f as? Char
1f as? Double
1f as? Float
}
}
@@ -1,20 +0,0 @@
open class C {
}
fun C.foo() {}
open class X {
companion object : C() {}
}
open class Y {
companion object : C() {}
}
fun bar() {
val x = X
x.foo()
X.foo()
(X as C).foo()
((if (1<2) X else Y) as C).foo()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
open class C {
}
@@ -33,8 +33,18 @@ inline fun <reified T> case_5() {
val z = x.test()
if (z is T) {
if (<!USELESS_IS_CHECK!>z is T<!>) {
// z is {T!! & T!!} (smart cast from T)
<!UNRESOLVED_REFERENCE!>println<!>(z)
}
val a = object<A> {
fun test() = 42 as <!UNRESOLVED_REFERENCE!>A<!>
}
val b = a.test()
if (a is T) {
<!UNRESOLVED_REFERENCE!>println<!>(a)
}
}
@@ -37,4 +37,14 @@ inline fun <reified T> case_5() {
// z is {T!! & T!!} (smart cast from T)
<!UNRESOLVED_REFERENCE!>println<!>(z)
}
val a = object<!TYPE_PARAMETERS_IN_ANONYMOUS_OBJECT!><A><!> {
fun test() = 42 <!UNCHECKED_CAST!>as A<!>
}
val b = a.test()
if (a is T) {
<!UNRESOLVED_REFERENCE!>println<!>(a)
}
}
@@ -33,8 +33,18 @@ inline fun <reified T> case_5() {
val z = x.test()
if (z is T) {
if (<!USELESS_IS_CHECK!>z is T<!>) {
// z is {T!! & T!!} (smart cast from T)
<!UNRESOLVED_REFERENCE!>println<!>(z)
}
val a = object<A> {
fun test() = 42 as <!UNRESOLVED_REFERENCE!>A<!>
}
val b = a.test()
if (a is T) {
<!UNRESOLVED_REFERENCE!>println<!>(a)
}
}
@@ -37,4 +37,14 @@ inline fun <reified T> case_5() {
// z is {T!! & T!!} (smart cast from T)
<!UNRESOLVED_REFERENCE!>println<!>(z)
}
val a = object<!TYPE_PARAMETERS_IN_OBJECT!><A><!> {
fun test() = 42 <!UNCHECKED_CAST!>as A<!>
}
val b = a.test()
if (a is T) {
<!UNRESOLVED_REFERENCE!>println<!>(a)
}
}
@@ -39,7 +39,7 @@ fun t1(b : Boolean) {
return;
}
doSmth(i)
if (i is Int) {
if (<!USELESS_IS_CHECK!>i is Int<!>) {
return;
}
}
@@ -26,7 +26,7 @@ fun t1(x: Int) = when(x) {
}
fun t5(x: Int) = <!NO_ELSE_IN_WHEN!>when<!> (x) {
is Int -> 1
<!USELESS_IS_CHECK!>is Int<!> -> 1
2 -> 2
}
@@ -13,7 +13,7 @@ fun f2(s: Number?) {
}
fun f3(s: Number?) {
if (s is Int && s as Int == 42);
if (s is Int && s <!USELESS_CAST!>as Int<!> == 42);
checkSubtype<Int>(<!ARGUMENT_TYPE_MISMATCH!>s<!>)
}
@@ -2,7 +2,7 @@
fun foo(o: Any) {
if (o is String) {
val s = o as String
val s = o <!USELESS_CAST!>as String<!>
s.length
}
}
@@ -13,4 +13,4 @@ fun foo1(o: Any) {
val s = o
s.length
}
}
}
@@ -1,7 +1,7 @@
// !CHECK_TYPE
fun foo(x: Number) {
if ((x as Int) is Int) {
if (<!USELESS_IS_CHECK!>(x as Int) is Int<!>) {
checkSubtype<Int>(x)
}
checkSubtype<Int>(x)
@@ -1,11 +1,11 @@
// !CHECK_TYPE
fun noUselessDataFlowInfoCreation(x: Number) {
if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) {
if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) {
if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) {
if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) {
if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) { if (x is Int) {
if (x is Int) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) {
if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) {
if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) {
if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) {
if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) { if (<!USELESS_IS_CHECK!>x is Int<!>) {
} } } } } } } } } } } } } } } } } } } } } } } } }
}
@@ -5,7 +5,7 @@ enum class En { A, B, С }
fun foo() {
// nullable variable
val en2: Any? = En.A
if (en2 is En) {
if (<!USELESS_IS_CHECK!>en2 is En<!>) {
when (en2) {
En.A -> {}
En.B -> {}
@@ -15,7 +15,7 @@ fun foo() {
// not nullable variable
val en1: Any = En.A
if (en1 is En) {
if (<!USELESS_IS_CHECK!>en1 is En<!>) {
when (en1) {
En.A -> {}
En.B -> {}
@@ -37,4 +37,4 @@ fun bar(x: Any) {
else -> {}
}
}
}
}
@@ -7,7 +7,7 @@ fun <T> foo() = foo() as T
fun <T> foo2(): T = TODO()
val test = foo2().plus("") as String
val test = foo2().plus("") <!USELESS_CAST!>as String<!>
fun <T> T.bar() = this
val barTest = "".bar() as Number
@@ -17,5 +17,5 @@ val asStarList = foo() as List<*>
val safeAs = foo() as? String
val fromIs = foo() is String
val fromNoIs = foo() !is String
val fromIs = <!USELESS_IS_CHECK!>foo() is String<!>
val fromNoIs = <!USELESS_IS_CHECK!>foo() !is String<!>
@@ -1,59 +0,0 @@
// !WITH_NEW_INFERENCE
// !LANGUAGE: +ExpectedTypeFromCast
// !DIAGNOSTICS: -UNUSED_VARIABLE -DEBUG_INFO_LEAKING_THIS
// FILE: a/View.java
package a;
public class View {
}
// FILE: a/Test.java
package a;
public class Test {
public <T extends View> T findViewById(int id);
}
// FILE: 1.kt
package a
class X : View()
class Y<T> : View()
val xExplicit: X = Test().findViewById(0)
val xCast = Test().findViewById(0) as X
val xCastExplicitType = Test().findViewById<X>(0) as X
val xSafeCastExplicitType = Test().findViewById<X>(0) as? X
val yExplicit: Y<String> = Test().findViewById(0)
val yCast = Test().findViewById(0) as Y<String>
class TestChild : Test() {
val xExplicit: X = findViewById(0)
val xCast = findViewById(0) as X
val yExplicit: Y<String> = findViewById(0)
val yCast = findViewById(0) as Y<String>
}
fun test(t: Test) {
val xExplicit: X = t.findViewById(0)
val xCast = t.findViewById(0) as X
val yExplicit: Y<String> = t.findViewById(0)
val yCast = t.findViewById(0) as Y<String>
}
fun test2(t: Test?) {
val xSafeCallSafeCast = t?.findViewById(0) as? X
val xSafeCallSafeCastExplicitType = t?.findViewById<X>(0) as? X
val xSafeCallCast = t?.findViewById(0) as X
val xSafeCallCastExplicitType = t<!UNNECESSARY_SAFE_CALL!>?.<!>findViewById<X>(0) as X
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !WITH_NEW_INFERENCE
// !LANGUAGE: +ExpectedTypeFromCast
// !DIAGNOSTICS: -UNUSED_VARIABLE -DEBUG_INFO_LEAKING_THIS
@@ -36,7 +36,7 @@ fun f10(init : A?) {
if (!(a is B)) {
return;
}
if (!(a is B)) {
if (!(<!USELESS_IS_CHECK!>a is B<!>)) {
return;
}
}
@@ -58,7 +58,7 @@ fun f11(a : A?) {
is B -> a.bar()
is A -> a.foo()
is Any -> a.foo()
is Any? -> a.<!UNRESOLVED_REFERENCE!>bar<!>()
<!USELESS_IS_CHECK!>is Any?<!> -> a.<!UNRESOLVED_REFERENCE!>bar<!>()
else -> a?.foo()
}
}
@@ -68,12 +68,12 @@ fun f12(a : A?) {
is B -> a.bar()
is A -> a.foo()
is Any -> a.foo();
is Any? -> a.<!UNRESOLVED_REFERENCE!>bar<!>()
<!USELESS_IS_CHECK!>is Any?<!> -> a.<!UNRESOLVED_REFERENCE!>bar<!>()
is C -> a.bar()
else -> a?.foo()
}
if (a is Any?) {
if (<!USELESS_IS_CHECK!>a is Any?<!>) {
a?.<!UNRESOLVED_REFERENCE!>bar<!>()
}
if (a is B) {
@@ -198,7 +198,7 @@ fun mergeSmartCasts(a: Any?) {
when (a) {
is String, is Any -> a.<!UNRESOLVED_REFERENCE!>compareTo<!>("")
}
if (a is String && a is Any) {
if (a is String && <!USELESS_IS_CHECK!>a is Any<!>) {
val i: Int = a.compareTo("")
}
if (a is String && a.compareTo("") == 0) {}
@@ -1,10 +0,0 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
inline public fun reg(converter: (Any) -> Any, flag: Boolean) {
flag
converter("")
}
public inline fun register(converter: (Any) -> Any) {
<!USAGE_IS_NOT_INLINABLE!>converter<!> is (Any) -> Any
reg(converter, <!USAGE_IS_NOT_INLINABLE!>converter<!> is (Any) -> Any)
}
+1
View File
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_EXPRESSION
inline public fun reg(converter: (Any) -> Any, flag: Boolean) {
flag
-11
View File
@@ -1,11 +0,0 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
inline public fun reg(converter: (Any) -> Any) {
converter("")
}
public inline fun register(converter: (Any) -> Any) {
reg(when(<!USAGE_IS_NOT_INLINABLE!>converter<!>) {
is (Any) -> Any -> <!USAGE_IS_NOT_INLINABLE!>converter<!>
else -> <!USAGE_IS_NOT_INLINABLE!>converter<!>
})
}
+1
View File
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_EXPRESSION
inline public fun reg(converter: (Any) -> Any) {
converter("")
@@ -6,7 +6,7 @@ class A
fun <T> test(v: T): T {
val a = if (v !is A) {
foo(v) as T
foo(v) <!USELESS_CAST!>as T<!>
}
else {
v
@@ -18,7 +18,7 @@ fun <T> test(v: T): T {
fun <T> test2(v: T): T {
val a = if (v !is A) {
foo(v) as T
foo(v) <!USELESS_CAST!>as T<!>
}
else {
v as T
@@ -42,7 +42,7 @@ fun <T> test3(v: T): T {
fun <T> test4(v: T): T {
val a: T = if (v !is A) {
foo(v) as T
foo(v) <!USELESS_CAST!>as T<!>
}
else {
v
@@ -1,23 +0,0 @@
sealed class Stmt
class ForStmt : Stmt()
sealed class Expr : Stmt() {
object BinExpr : Expr()
}
fun test(x: Stmt): String =
when (x) {
is Expr -> "expr"
is Stmt -> "stmt"
}
fun test2(x: Stmt): String =
<!NO_ELSE_IN_WHEN!>when<!> (x) {
is Expr -> "expr"
}
fun test3(x: Expr): String =
when (x) {
is Stmt -> "stmt"
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
sealed class Stmt
class ForStmt : Stmt()
@@ -1,15 +0,0 @@
sealed class Sealed {
object First: Sealed()
open class NonFirst: Sealed() {
object Second: NonFirst()
object Third: NonFirst()
}
}
fun foo(s: Sealed): Int {
return <!NO_ELSE_IN_WHEN!>when<!>(s) {
is Sealed.First -> 1
!is Any -> 0
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
sealed class Sealed {
object First: Sealed()
open class NonFirst: Sealed() {
@@ -17,7 +17,7 @@ fun String?.gav() {}
fun bar(s: String?) {
if (s != null) return
s.gav()
s as? String
s as String?
s <!USELESS_CAST!>as? String<!>
s <!USELESS_CAST!>as String?<!>
s as String
}
@@ -14,7 +14,7 @@ fun g(a: SomeClass?) {
a.hashCode()
a.foo
(a as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
(a as SomeSubClass).foo
(a <!USELESS_CAST!>as SomeSubClass<!>).foo
}
val b = (a as? SomeSubClass)?.foo
if (b != null) {
@@ -22,7 +22,7 @@ fun g(a: SomeClass?) {
a.hashCode()
a.foo
(a as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
(a as SomeSubClass).foo
(a <!USELESS_CAST!>as SomeSubClass<!>).foo
}
val c = a as? SomeSubClass
if (c != null) {
@@ -33,4 +33,4 @@ fun g(a: SomeClass?) {
c.hashCode()
c.foo
}
}
}
@@ -14,7 +14,7 @@ fun g(a: SomeClass?) {
a.hashCode()
a.foo
(a as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
(a as SomeSubClass).foo
(a <!USELESS_CAST!>as SomeSubClass<!>).foo
}
val b = (a as? SomeSubClass)?.foo
if (b != null) {
@@ -22,7 +22,7 @@ fun g(a: SomeClass?) {
a.hashCode()
a.foo
(a as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
(a as SomeSubClass).foo
(a <!USELESS_CAST!>as SomeSubClass<!>).foo
}
val c = a as? SomeSubClass
if (c != null) {
@@ -33,4 +33,4 @@ fun g(a: SomeClass?) {
c.hashCode()
c.foo
}
}
}
@@ -1,25 +0,0 @@
// !LANGUAGE: +SafeCastCheckBoundSmartCasts
// See KT-20752
class Unstable {
val first: String? get() = null
}
class StringList {
fun remove(s: String) = s
}
fun StringList.remove(s: String?) = s ?: ""
fun foo(list: StringList, arg: Unstable) {
list.remove(arg.first)
if (arg.first as? String != null) {
// Should be still resolved to extension, without smart cast or smart cast impossible
list.remove(arg.first)
}
val s = arg.first as? String
if (s != null) {
// Should be still resolved to extension, without smart cast or smart cast impossible
list.remove(arg.first)
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +SafeCastCheckBoundSmartCasts
// See KT-20752
@@ -7,7 +7,7 @@ fun <T> T?.let(f: (T) -> Unit) {
}
fun test(your: Your?) {
(your?.foo() as? Any)?.let {}
(your?.foo() <!USELESS_CAST!>as? Any<!>)?.let {}
// strange smart cast to 'Your' at this point
your<!UNSAFE_CALL!>.<!>hashCode()
}
}
@@ -1,15 +0,0 @@
// Type inference failed after smart cast
interface A<T>
interface B<T> : A<T>
fun <T> foo(b: A<T>) = b
fun <T> test(a: A<T>) {
if (a is Any) {
// Error:(9, 9) Kotlin: Type inference failed: fun <T> foo(b: A<T>): kotlin.Unit
// cannot be applied to (A<T>)
foo(a)
}
foo(a) // ok
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// Type inference failed after smart cast
interface A<T>
@@ -19,10 +19,10 @@ fun foo(a: A) {
fun foo2(a: A) {
if (a is C) {
if (a is B) {
if (<!USELESS_IS_CHECK!>a is B<!>) {
val t = when (a) {
is CC -> "CC"
}
}
}
}
}
@@ -12,7 +12,7 @@ object DD : D()
fun foo1(a: A) {
if (a is B) {
if (a is D) {
if (a is C) {
if (<!USELESS_IS_CHECK!>a is C<!>) {
val t =
when (a) {
is DD -> "DD"
@@ -25,7 +25,7 @@ fun foo1(a: A) {
fun foo2(a: A) {
if (a is B) {
if (a is D) {
if (a is C) {
if (<!USELESS_IS_CHECK!>a is C<!>) {
val t =
when (a) {
is DD -> "DD"
@@ -33,4 +33,4 @@ fun foo2(a: A) {
}
}
}
}
}
@@ -1,28 +0,0 @@
//KT-5455 Need warning about redundant type cast
fun foo(o: Any): Int {
if (o is String) {
return (o as String).length
}
return -1
}
open class A {
fun foo() {}
}
class B: A()
fun test(a: Any?) {
if (a is B) {
(a as A).foo()
}
}
fun test1(a: B) {
(a as A?)?.foo()
}
fun test2(b: B?) {
if (b != null) {
(b as A).foo()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
//KT-5455 Need warning about redundant type cast
fun foo(o: Any): Int {
if (o is String) {
@@ -1,13 +1,13 @@
// See also KT-10992: we should have no errors for all unsafe hashCode() calls
fun foo(arg: Any?) {
val x = arg as? Any ?: return
val x = arg <!USELESS_CAST!>as? Any<!> ?: return
arg.hashCode()
x.hashCode()
}
fun bar(arg: Any?) {
arg as? Any ?: return
arg <!USELESS_CAST!>as? Any<!> ?: return
arg.hashCode()
}
@@ -1,4 +0,0 @@
@Suppress("REDUNDANT_NULLABLE")
class C {
fun foo(): String?? = null as Nothing??
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
@Suppress("REDUNDANT_NULLABLE")
class C {
fun foo(): String?? = null <!USELESS_CAST!>as Nothing??<!>
@@ -1,6 +0,0 @@
class C {
@Suppress("REDUNDANT_NULLABLE")
companion object {
val foo: String?? = null as Nothing??
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class C {
@Suppress("REDUNDANT_NULLABLE")
companion object {
@@ -1,4 +0,0 @@
fun foo(): Any? {
@Suppress("REDUNDANT_NULLABLE")
return null as Nothing??
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun foo(): Any? {
@Suppress("REDUNDANT_NULLABLE")
return null <!USELESS_CAST!>as Nothing??<!>
@@ -1,4 +0,0 @@
class C {
@Suppress("REDUNDANT_NULLABLE")
fun foo(): String?? = null as Nothing??
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class C {
@Suppress("REDUNDANT_NULLABLE")
fun foo(): String?? = null <!USELESS_CAST!>as Nothing??<!>
@@ -1,7 +0,0 @@
class C {
fun foo(): Any? {
@Suppress("REDUNDANT_NULLABLE")
val v: String?? = null as Nothing??
return v
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class C {
fun foo(): Any? {
@Suppress("REDUNDANT_NULLABLE")
@@ -1,4 +0,0 @@
@Suppress("REDUNDANT_NULLABLE")
object C {
fun foo(): String?? = null as Nothing??
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
@Suppress("REDUNDANT_NULLABLE")
object C {
fun foo(): String?? = null <!USELESS_CAST!>as Nothing??<!>
@@ -1,3 +0,0 @@
class C {
fun foo(@Suppress("REDUNDANT_NULLABLE") p: String?? = null as Nothing??) = p
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class C {
fun foo(@Suppress("REDUNDANT_NULLABLE") p: String?? = null <!USELESS_CAST!>as Nothing??<!>) = p
}
@@ -1,4 +0,0 @@
class C {
@Suppress("REDUNDANT_NULLABLE")
val foo: String?? = null as Nothing?
}

Some files were not shown because too many files have changed in this diff Show More