KT-12152 : constructor consistency: handle non-final classes
This commit is contained in:
@@ -24,9 +24,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
|
|||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction
|
||||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder
|
||||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
@@ -34,6 +32,8 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
|||||||
|
|
||||||
sealed class LeakingThisDescriptor(val classOrObject: KtClassOrObject) {
|
sealed class LeakingThisDescriptor(val classOrObject: KtClassOrObject) {
|
||||||
class PropertyIsNull(val property: PropertyDescriptor, classOrObject: KtClassOrObject) : LeakingThisDescriptor(classOrObject)
|
class PropertyIsNull(val property: PropertyDescriptor, classOrObject: KtClassOrObject) : LeakingThisDescriptor(classOrObject)
|
||||||
|
|
||||||
|
class NonFinalClass(val klass: ClassDescriptor, classOrObject: KtClassOrObject): LeakingThisDescriptor(classOrObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConstructorConsistencyChecker private constructor(
|
class ConstructorConsistencyChecker private constructor(
|
||||||
@@ -43,6 +43,7 @@ class ConstructorConsistencyChecker private constructor(
|
|||||||
private val pseudocode: Pseudocode,
|
private val pseudocode: Pseudocode,
|
||||||
private val variablesData: PseudocodeVariablesData
|
private val variablesData: PseudocodeVariablesData
|
||||||
) {
|
) {
|
||||||
|
private val finalClass = classDescriptor.isFinalClass
|
||||||
|
|
||||||
private fun safeThisUsage(expression: KtThisExpression): Boolean {
|
private fun safeThisUsage(expression: KtThisExpression): Boolean {
|
||||||
val referenceDescriptor = trace.get(BindingContext.REFERENCE_TARGET, expression.instanceReference)
|
val referenceDescriptor = trace.get(BindingContext.REFERENCE_TARGET, expression.instanceReference)
|
||||||
@@ -81,10 +82,16 @@ class ConstructorConsistencyChecker private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun handleLeakingThis(expression: KtExpression) {
|
fun handleLeakingThis(expression: KtExpression) {
|
||||||
val uninitializedProperty = firstUninitializedNotNullProperty()
|
if (!finalClass) {
|
||||||
if (uninitializedProperty != null) {
|
|
||||||
trace.record(BindingContext.LEAKING_THIS, target(expression),
|
trace.record(BindingContext.LEAKING_THIS, target(expression),
|
||||||
LeakingThisDescriptor.PropertyIsNull(uninitializedProperty, classOrObject))
|
LeakingThisDescriptor.NonFinalClass(classDescriptor, classOrObject))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val uninitializedProperty = firstUninitializedNotNullProperty()
|
||||||
|
if (uninitializedProperty != null) {
|
||||||
|
trace.record(BindingContext.LEAKING_THIS, target(expression),
|
||||||
|
LeakingThisDescriptor.PropertyIsNull(uninitializedProperty, classOrObject))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
open class Base {
|
||||||
|
init {
|
||||||
|
register(<!DEBUG_INFO_LEAKING_THIS!>this<!>)
|
||||||
|
<!DEBUG_INFO_LEAKING_THIS!>foo<!>()
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun register(arg: Base) {
|
||||||
|
arg.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived(val x: Int) : Base() {
|
||||||
|
override fun foo() {
|
||||||
|
x.hashCode() // NPE in Base constructor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class MyEnum {
|
||||||
|
FIRST() {
|
||||||
|
val x: Int = 42
|
||||||
|
|
||||||
|
override fun foo() {
|
||||||
|
x.hashCode() // NPE in MyEnum constructor
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
init {
|
||||||
|
<!DEBUG_INFO_LEAKING_THIS!>foo<!>()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun register(/*0*/ arg: Base): kotlin.Unit
|
||||||
|
|
||||||
|
public open class Base {
|
||||||
|
public constructor Base()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open fun foo(): kotlin.Unit
|
||||||
|
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(/*0*/ x: kotlin.Int)
|
||||||
|
public final val x: kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ fun foo(): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final enum class MyEnum : kotlin.Enum<MyEnum> {
|
||||||
|
enum entry FIRST
|
||||||
|
|
||||||
|
private constructor MyEnum()
|
||||||
|
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||||
|
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||||
|
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||||
|
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int
|
||||||
|
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
|
||||||
|
public abstract fun foo(): kotlin.Unit
|
||||||
|
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<MyEnum!>!
|
||||||
|
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|
||||||
|
// Static members
|
||||||
|
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyEnum
|
||||||
|
public final /*synthesized*/ fun values(): kotlin.Array<MyEnum>
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ fun foo() = ""
|
|||||||
|
|
||||||
open class B: A() {
|
open class B: A() {
|
||||||
init {
|
init {
|
||||||
val a: Int = foo()
|
val a: Int = <!DEBUG_INFO_LEAKING_THIS!>foo<!>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -10,8 +10,8 @@ open class A<T> : J() {
|
|||||||
init {
|
init {
|
||||||
foo()
|
foo()
|
||||||
bar()
|
bar()
|
||||||
val a: Int = <!TYPE_MISMATCH!>baz()<!>
|
val a: Int = <!TYPE_MISMATCH!><!DEBUG_INFO_LEAKING_THIS!>baz<!>()<!>
|
||||||
val b: T = baz()
|
val b: T = <!DEBUG_INFO_LEAKING_THIS!>baz<!>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test1() {
|
fun test1() {
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ open class C: A() {
|
|||||||
fun foo() = ""
|
fun foo() = ""
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val a: String = foo()
|
val a: String = <!DEBUG_INFO_LEAKING_THIS!>foo<!>()
|
||||||
val b: String = bar
|
val b: String = bar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2973,6 +2973,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("open.kt")
|
||||||
|
public void testOpen() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/open.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("outer.kt")
|
@TestMetadata("outer.kt")
|
||||||
public void testOuter() throws Exception {
|
public void testOuter() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/outer.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/outer.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user