NI: introduce new data flow analysis for try/catch (#KT-28370)
This commit is contained in:
+88
@@ -0,0 +1,88 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
// !LANGUAGE: +NewDataFlowForTryExpressions
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_VALUE -VARIABLE_WITH_REDUNDANT_INITIALIZER
|
||||
// Related issue: KT-28370
|
||||
|
||||
class ExcA : Exception()
|
||||
class ExcB : Exception()
|
||||
|
||||
fun test1(s: String?) {
|
||||
var t2: Boolean? = true
|
||||
if (t2 != null) {
|
||||
try {
|
||||
t2 = null
|
||||
}
|
||||
catch (e: Exception) {
|
||||
requireNotNull(s)
|
||||
}
|
||||
t2<!UNSAFE_CALL!>.<!>not()
|
||||
s<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
}
|
||||
|
||||
fun test2(s: String?) {
|
||||
var t2: Boolean? = true
|
||||
if (t2 != null) {
|
||||
try {
|
||||
t2 = null
|
||||
}
|
||||
finally {
|
||||
requireNotNull(s)
|
||||
t2 = true
|
||||
}
|
||||
<!DEBUG_INFO_SMARTCAST!>t2<!>.not()
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length
|
||||
}
|
||||
}
|
||||
|
||||
fun test3() {
|
||||
var s: String? = null
|
||||
s = ""
|
||||
try {
|
||||
|
||||
}
|
||||
catch (e: Exception) {
|
||||
s = null
|
||||
return
|
||||
}
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length
|
||||
}
|
||||
|
||||
fun test4() {
|
||||
var s: String? = null
|
||||
s = ""
|
||||
try {
|
||||
|
||||
}
|
||||
catch (e: ExcA) {
|
||||
s = null
|
||||
return
|
||||
}
|
||||
catch (e: ExcB) {
|
||||
|
||||
}
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length
|
||||
}
|
||||
|
||||
fun test5(s: String?) {
|
||||
try {
|
||||
requireNotNull(s)
|
||||
}
|
||||
catch (e: ExcA) {
|
||||
return
|
||||
}
|
||||
catch (e: ExcB) {
|
||||
|
||||
}
|
||||
s<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
fun test6(s: String?) {
|
||||
try {
|
||||
requireNotNull(s)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package
|
||||
|
||||
public fun test1(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
public fun test2(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
public fun test3(): kotlin.Unit
|
||||
public fun test4(): kotlin.Unit
|
||||
public fun test5(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
public fun test6(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
|
||||
public final class ExcA : kotlin.Exception /* = java.lang.Exception */ {
|
||||
public constructor ExcA()
|
||||
public open override /*1*/ /*fake_override*/ val cause: kotlin.Throwable?
|
||||
public open override /*1*/ /*fake_override*/ val message: kotlin.String?
|
||||
public final override /*1*/ /*fake_override*/ fun addSuppressed(/*0*/ exception: kotlin.Throwable!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun fillInStackTrace(): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun getLocalizedMessage(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun getStackTrace(): kotlin.Array<(out) java.lang.StackTraceElement!>!
|
||||
public final override /*1*/ /*fake_override*/ fun getSuppressed(): kotlin.Array<(out) kotlin.Throwable!>!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun initCause(/*0*/ cause: kotlin.Throwable!): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintStream!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintWriter!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun setStackTrace(/*0*/ stackTrace: kotlin.Array<(out) java.lang.StackTraceElement!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class ExcB : kotlin.Exception /* = java.lang.Exception */ {
|
||||
public constructor ExcB()
|
||||
public open override /*1*/ /*fake_override*/ val cause: kotlin.Throwable?
|
||||
public open override /*1*/ /*fake_override*/ val message: kotlin.String?
|
||||
public final override /*1*/ /*fake_override*/ fun addSuppressed(/*0*/ exception: kotlin.Throwable!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun fillInStackTrace(): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun getLocalizedMessage(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun getStackTrace(): kotlin.Array<(out) java.lang.StackTraceElement!>!
|
||||
public final override /*1*/ /*fake_override*/ fun getSuppressed(): kotlin.Array<(out) kotlin.Throwable!>!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun initCause(/*0*/ cause: kotlin.Throwable!): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintStream!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintWriter!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun setStackTrace(/*0*/ stackTrace: kotlin.Array<(out) java.lang.StackTraceElement!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
// !LANGUAGE: +NewDataFlowForTryExpressions
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_VALUE -VARIABLE_WITH_REDUNDANT_INITIALIZER
|
||||
// Related issue: KT-28370
|
||||
|
||||
fun test1(s1: String?) {
|
||||
var s: String? = null
|
||||
s = ""
|
||||
try {
|
||||
s = ""
|
||||
requireNotNull(s1)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
finally {
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>s<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun test1(/*0*/ s1: kotlin.String?): kotlin.Unit
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
// !LANGUAGE: +NewDataFlowForTryExpressions
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_VALUE -VARIABLE_WITH_REDUNDANT_INITIALIZER
|
||||
// Related issue: KT-28370
|
||||
|
||||
class ExcA : Exception()
|
||||
class ExcB : Exception()
|
||||
|
||||
fun test1() {
|
||||
var x: String? = null
|
||||
x = ""
|
||||
|
||||
try {
|
||||
x = null
|
||||
} catch (e: Exception) {
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>x<!><!NI;UNSAFE_CALL!>.<!>length // smartcast shouldn't be allowed (OOME could happen after `x = null`)
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
// smartcast shouldn't be allowed, `x = null` could've happened
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>x<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
// smartcast shouldn't be allowed, `x = null` could've happened
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>x<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
// With old DFA of try/catch info about unsound smartcasts after try
|
||||
// removes only if there is at least one catch branch that not returns Nothing
|
||||
fun test2() {
|
||||
var x: String? = null
|
||||
x = ""
|
||||
|
||||
try {
|
||||
x = null
|
||||
} catch (e: Exception) {
|
||||
// BAD
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>x<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
finally {
|
||||
x<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
x<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
fun test3() {
|
||||
var t2: Boolean? = true
|
||||
if (t2 != null) { // or `t2 is Boolean`
|
||||
try {
|
||||
throw Exception()
|
||||
} catch (e: Exception) {
|
||||
t2 = null
|
||||
}
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>t2<!><!NI;UNSAFE_CALL!>.<!>not() // wrong smartcast, NPE
|
||||
}
|
||||
}
|
||||
|
||||
fun test4() {
|
||||
var t2: Boolean? = true
|
||||
if (t2 != null) { // or `t2 is Boolean`
|
||||
try {
|
||||
t2 = null
|
||||
} finally { }
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>t2<!><!NI;UNSAFE_CALL!>.<!>not() // wrong smartcast, NPE
|
||||
}
|
||||
}
|
||||
|
||||
fun test5() {
|
||||
var s1: String? = null
|
||||
var s2: String? = null
|
||||
s1 = ""
|
||||
s2 = ""
|
||||
try {
|
||||
TODO()
|
||||
}
|
||||
catch (e: ExcA) {
|
||||
s1 = ""
|
||||
}
|
||||
catch (e: ExcB) {
|
||||
s2 = null
|
||||
return
|
||||
}
|
||||
finally {
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>s1<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>s2<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>s1<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
<!DEBUG_INFO_SMARTCAST!>s2<!>.length
|
||||
}
|
||||
|
||||
fun test6(s1: String?, s2: String?) {
|
||||
var s: String? = null
|
||||
s = ""
|
||||
try {
|
||||
s = null
|
||||
requireNotNull(s1)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
finally {
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>s<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
requireNotNull(s2)
|
||||
}
|
||||
<!OI;DEBUG_INFO_SMARTCAST!>s<!><!NI;UNSAFE_CALL!>.<!>length
|
||||
<!NI;DEBUG_INFO_SMARTCAST!>s1<!><!OI;UNSAFE_CALL!>.<!>length
|
||||
<!DEBUG_INFO_SMARTCAST!>s2<!>.length
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package
|
||||
|
||||
public fun test1(): kotlin.Unit
|
||||
public fun test2(): kotlin.Unit
|
||||
public fun test3(): kotlin.Unit
|
||||
public fun test4(): kotlin.Unit
|
||||
public fun test5(): kotlin.Unit
|
||||
public fun test6(/*0*/ s1: kotlin.String?, /*1*/ s2: kotlin.String?): kotlin.Unit
|
||||
|
||||
public final class ExcA : kotlin.Exception /* = java.lang.Exception */ {
|
||||
public constructor ExcA()
|
||||
public open override /*1*/ /*fake_override*/ val cause: kotlin.Throwable?
|
||||
public open override /*1*/ /*fake_override*/ val message: kotlin.String?
|
||||
public final override /*1*/ /*fake_override*/ fun addSuppressed(/*0*/ exception: kotlin.Throwable!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun fillInStackTrace(): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun getLocalizedMessage(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun getStackTrace(): kotlin.Array<(out) java.lang.StackTraceElement!>!
|
||||
public final override /*1*/ /*fake_override*/ fun getSuppressed(): kotlin.Array<(out) kotlin.Throwable!>!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun initCause(/*0*/ cause: kotlin.Throwable!): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintStream!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintWriter!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun setStackTrace(/*0*/ stackTrace: kotlin.Array<(out) java.lang.StackTraceElement!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class ExcB : kotlin.Exception /* = java.lang.Exception */ {
|
||||
public constructor ExcB()
|
||||
public open override /*1*/ /*fake_override*/ val cause: kotlin.Throwable?
|
||||
public open override /*1*/ /*fake_override*/ val message: kotlin.String?
|
||||
public final override /*1*/ /*fake_override*/ fun addSuppressed(/*0*/ exception: kotlin.Throwable!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun fillInStackTrace(): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun getLocalizedMessage(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun getStackTrace(): kotlin.Array<(out) java.lang.StackTraceElement!>!
|
||||
public final override /*1*/ /*fake_override*/ fun getSuppressed(): kotlin.Array<(out) kotlin.Throwable!>!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun initCause(/*0*/ cause: kotlin.Throwable!): kotlin.Throwable!
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintStream!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun printStackTrace(/*0*/ s: java.io.PrintWriter!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun setStackTrace(/*0*/ stackTrace: kotlin.Array<(out) java.lang.StackTraceElement!>!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
Reference in New Issue
Block a user