Preliminary declaration visitor for estimating local variable's predictability for smart casts
Predictability estimation algorithm is completely new, but backward compatibility should present. A large set of tests. Some updated tests. Smart casts allowed for captured variables if they are not modified in closure #KT-9051 Fixed Also #KT-8643 Fixed Also #KT-7976 Fixed Correct handling of lambda arguments in functions #KT-9143 Fixed
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
// !DIAGNOSTICS: -NOTHING_TO_INLINE
|
||||
|
||||
inline fun <T> foo(t1: T, t2: T) = t1 ?: t2
|
||||
|
||||
inline fun <T> bar(<!UNUSED_PARAMETER!>l<!>: (T) -> Unit): T = null!!
|
||||
|
||||
fun use() {
|
||||
var x: Int?
|
||||
x = 5
|
||||
// Write is AFTER
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
// x is nullable at the second argument
|
||||
foo(bar { x = null }, x!!)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
@kotlin.inline() public fun </*0*/ T> bar(/*0*/ l: (T) -> kotlin.Unit): T
|
||||
@kotlin.inline() public fun </*0*/ T> foo(/*0*/ t1: T, /*1*/ t2: T): T
|
||||
public fun use(): kotlin.Unit
|
||||
@@ -0,0 +1,14 @@
|
||||
// !DIAGNOSTICS: -NOTHING_TO_INLINE
|
||||
// See KT-9143: smart cast on a variable nulled inside a lambda argument
|
||||
inline fun <T> foo(t1: T, t2: T) = t1 ?: t2
|
||||
|
||||
inline fun <T> bar(<!UNUSED_PARAMETER!>l<!>: (T) -> Unit): T = null!!
|
||||
|
||||
fun use() {
|
||||
var x: Int?
|
||||
x = 5
|
||||
// Write to x is AFTER
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
// No smart cast should be here!
|
||||
foo(bar { x = null }, <!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode())
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package
|
||||
|
||||
@kotlin.inline() public fun </*0*/ T> bar(/*0*/ l: (T) -> kotlin.Unit): T
|
||||
@kotlin.inline() public fun </*0*/ T> foo(/*0*/ t1: T, /*1*/ t2: T): T
|
||||
public fun use(): kotlin.Unit
|
||||
@@ -0,0 +1,10 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
fun run(f: () -> Unit) = 0
|
||||
|
||||
fun foo(arg: Int?) {
|
||||
run {
|
||||
var x = arg
|
||||
if (x == null) return@run
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
|
||||
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
fun run(f: () -> Unit) = 0
|
||||
|
||||
fun foo(arg: Int?) {
|
||||
run {
|
||||
var x = arg
|
||||
while (x != null) {
|
||||
x = <!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
if (x == 0) x = null
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
|
||||
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
fun run(f: () -> Unit) = 0
|
||||
|
||||
class My {
|
||||
constructor(arg: Int?) {
|
||||
run {
|
||||
var x = arg
|
||||
if (x == null) return@run
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package
|
||||
|
||||
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
|
||||
|
||||
public final class My {
|
||||
public constructor My(/*0*/ arg: kotlin.Int?)
|
||||
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
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
fun run(f: () -> Unit) = 0
|
||||
|
||||
fun foo(arg: Int?) = run {
|
||||
var x = arg
|
||||
if (x == null) return@run
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
|
||||
class My {
|
||||
fun foo(arg: Int?) = run {
|
||||
var x = arg
|
||||
if (x == null) return@run
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
|
||||
fun Int?.bar() = run {
|
||||
var x = this
|
||||
if (x == null) return@run
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Int
|
||||
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
|
||||
|
||||
public final class My {
|
||||
public constructor My()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun foo(/*0*/ arg: kotlin.Int?): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
public final fun kotlin.Int?.bar(): kotlin.Int
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
fun run(f: () -> Unit) = 0
|
||||
|
||||
fun foo(arg: Int?) {
|
||||
var x = arg
|
||||
if (x == null) return
|
||||
run {
|
||||
// Not safe: x = null later in the owner
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
x = null
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
|
||||
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
|
||||
@@ -0,0 +1,15 @@
|
||||
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
|
||||
|
||||
fun foo() {
|
||||
var i: Int? = 5
|
||||
if (i != null) {
|
||||
class Changing {
|
||||
fun bar() {
|
||||
i = null
|
||||
}
|
||||
}
|
||||
i<!UNSAFE_CALL!>.<!>hashCode()
|
||||
Changing().bar()
|
||||
i<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(): kotlin.Unit
|
||||
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
|
||||
@@ -0,0 +1,15 @@
|
||||
open class Base
|
||||
class Derived: Base()
|
||||
|
||||
fun bar(derived: Derived) = derived
|
||||
|
||||
fun trans(n: Int, f: (Int) -> Boolean) = if (f(n)) n else null
|
||||
|
||||
fun foo() {
|
||||
val base: Base = Derived()
|
||||
if (base is Derived) {
|
||||
fun can(n: Int) = n > 0
|
||||
trans(42, ::can)
|
||||
bar(<!DEBUG_INFO_SMARTCAST!>base<!>)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package
|
||||
|
||||
public fun bar(/*0*/ derived: Derived): Derived
|
||||
public fun foo(): kotlin.Unit
|
||||
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: (kotlin.Int) -> kotlin.Boolean): kotlin.Int?
|
||||
|
||||
public open class Base {
|
||||
public constructor Base()
|
||||
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
|
||||
}
|
||||
|
||||
public final class Derived : Base {
|
||||
public constructor Derived()
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
|
||||
|
||||
fun foo() {
|
||||
var i: Int? = 5
|
||||
if (i != null) {
|
||||
fun can(): Boolean {
|
||||
i = null
|
||||
return true
|
||||
}
|
||||
i<!UNSAFE_CALL!>.<!>hashCode()
|
||||
trans(<!TYPE_MISMATCH!>i<!>, ::can)
|
||||
i<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(): kotlin.Unit
|
||||
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
|
||||
@@ -0,0 +1,15 @@
|
||||
fun trans(n: Int, f: () -> Boolean) = if (f()) n else null
|
||||
|
||||
fun foo() {
|
||||
var i: Int? = 5
|
||||
if (i != null) {
|
||||
// Write is AFTER this place
|
||||
<!DEBUG_INFO_SMARTCAST!>i<!>.hashCode()
|
||||
object {
|
||||
fun bar() {
|
||||
i = null
|
||||
}
|
||||
}.bar()
|
||||
i<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(): kotlin.Unit
|
||||
public fun trans(/*0*/ n: kotlin.Int, /*1*/ f: () -> kotlin.Boolean): kotlin.Int?
|
||||
@@ -0,0 +1,15 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
fun run(f: () -> Unit) = 0
|
||||
|
||||
fun foo(arg: Int?) {
|
||||
var x = arg
|
||||
if (x == null) return
|
||||
run {
|
||||
// Unsafe because of owner modification
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
x = null
|
||||
}
|
||||
if (x != null) x = 42
|
||||
// Unsafe because of lambda
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun foo(/*0*/ arg: kotlin.Int?): kotlin.Unit
|
||||
public fun run(/*0*/ f: () -> kotlin.Unit): kotlin.Int
|
||||
@@ -0,0 +1,7 @@
|
||||
class My {
|
||||
init {
|
||||
var y: Int?
|
||||
y = 42
|
||||
<!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package
|
||||
|
||||
public final class My {
|
||||
public constructor My()
|
||||
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
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fun foo() {
|
||||
class My {
|
||||
val x: Int
|
||||
init {
|
||||
var y: Int?
|
||||
y = 42
|
||||
x = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun foo(): kotlin.Unit
|
||||
@@ -0,0 +1,17 @@
|
||||
var x: Int = 0
|
||||
get() {
|
||||
var y: Int? = null
|
||||
if (y != null) {
|
||||
return <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
|
||||
}
|
||||
return field
|
||||
}
|
||||
set(param) {
|
||||
var y: Int? = null
|
||||
if (y != null) {
|
||||
field = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
|
||||
}
|
||||
else {
|
||||
field = param
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public var x: kotlin.Int
|
||||
@@ -0,0 +1,12 @@
|
||||
class My {
|
||||
val x: Int
|
||||
init {
|
||||
var y: Int? = null
|
||||
if (y != null) {
|
||||
x = <!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
|
||||
}
|
||||
else {
|
||||
x = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package
|
||||
|
||||
public final class My {
|
||||
public constructor My()
|
||||
public final val x: kotlin.Int
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class My(val z: Int) {
|
||||
var x: Int = 0
|
||||
constructor(arg: Int?): this(arg ?: 42) {
|
||||
var y: Int?
|
||||
y = arg
|
||||
if (y != null) {
|
||||
x = <!DEBUG_INFO_SMARTCAST!>y<!>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
public final class My {
|
||||
public constructor My(/*0*/ z: kotlin.Int)
|
||||
public constructor My(/*0*/ arg: kotlin.Int?)
|
||||
public final var x: kotlin.Int
|
||||
public final val z: kotlin.Int
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Based on KT-8643
|
||||
public class MyClass
|
||||
{
|
||||
fun main() {
|
||||
var str: String? = null
|
||||
|
||||
if (str != null)
|
||||
callback {
|
||||
// Nodoby writes str, smart cast is possible
|
||||
method1(<!DEBUG_INFO_SMARTCAST!>str<!>)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun callback(foo: () ->Unit) = foo()
|
||||
|
||||
fun method1(str: String) = str
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package
|
||||
|
||||
public final class MyClass {
|
||||
public constructor MyClass()
|
||||
@kotlin.inline() public final fun callback(/*0*/ foo: () -> kotlin.Unit): kotlin.Unit
|
||||
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 final fun main(): kotlin.Unit
|
||||
public final fun method1(/*0*/ str: kotlin.String): kotlin.String
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
class My {
|
||||
|
||||
val y: Int
|
||||
get() {
|
||||
var x: Int?
|
||||
x = 3
|
||||
return <!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
}
|
||||
|
||||
fun test() {
|
||||
var x: Int?
|
||||
x = 2
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.hashCode()
|
||||
fun bb() {
|
||||
var <!NAME_SHADOWING!>x<!>: Any?
|
||||
x = 4
|
||||
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
|
||||
}
|
||||
x = 4
|
||||
// Really smart cast is possible but name shadowing by bb() prevents it
|
||||
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package
|
||||
|
||||
public final class My {
|
||||
public constructor My()
|
||||
public final val y: kotlin.Int
|
||||
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 final fun test(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// FILE: My.java
|
||||
|
||||
public interface My {
|
||||
String foo(String arg);
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
class Your {
|
||||
val x = My() {
|
||||
arg: String? ->
|
||||
var y = arg
|
||||
val z: String
|
||||
if (y != null) z = <!DEBUG_INFO_SMARTCAST!>y<!>
|
||||
else z = "42"
|
||||
z
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package
|
||||
|
||||
public /*synthesized*/ fun My(/*0*/ function: (kotlin.String!) -> kotlin.String!): My
|
||||
|
||||
public interface My {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract fun foo(/*0*/ arg: kotlin.String!): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class Your {
|
||||
public constructor Your()
|
||||
public final val x: My
|
||||
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
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun foo(x: Int, f: () -> Unit, y: Int) {}
|
||||
|
||||
fun bar() {
|
||||
var x: Int?
|
||||
x = 4
|
||||
foo(<!DEBUG_INFO_SMARTCAST!>x<!>, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, <!SMARTCAST_IMPOSSIBLE!>x<!>)
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
public fun bar(): kotlin.Unit
|
||||
public fun foo(/*0*/ x: kotlin.Int, /*1*/ f: () -> kotlin.Unit, /*2*/ y: kotlin.Int): kotlin.Unit
|
||||
+2
-2
@@ -9,7 +9,7 @@ public fun foo() {
|
||||
}
|
||||
if (s != null) {
|
||||
System.out.println(closure())
|
||||
// Smart cast is possible but closure makes it harder to understand
|
||||
System.out.println(s<!UNSAFE_CALL!>.<!>length)
|
||||
// Smart cast is possible, nobody modifies s
|
||||
System.out.println(<!DEBUG_INFO_SMARTCAST!>s<!>.length)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user