KT-12152 : constructor consistency: handle open property accessing

This commit is contained in:
Mikhail Glukhikh
2015-08-11 16:17:29 +03:00
parent f7b5d67543
commit 422ea4c6cb
8 changed files with 96 additions and 10 deletions
@@ -34,6 +34,8 @@ sealed class LeakingThisDescriptor(val classOrObject: KtClassOrObject) {
class PropertyIsNull(val property: PropertyDescriptor, classOrObject: KtClassOrObject) : LeakingThisDescriptor(classOrObject)
class NonFinalClass(val klass: ClassDescriptor, classOrObject: KtClassOrObject): LeakingThisDescriptor(classOrObject)
class NonFinalProperty(val property: PropertyDescriptor, classOrObject: KtClassOrObject): LeakingThisDescriptor(classOrObject)
}
class ConstructorConsistencyChecker private constructor(
@@ -45,12 +47,26 @@ class ConstructorConsistencyChecker private constructor(
) {
private val finalClass = classDescriptor.isFinalClass
private fun checkOpenPropertyAccess(reference: KtReferenceExpression) {
if (!finalClass) {
val descriptor = trace.get(BindingContext.REFERENCE_TARGET, reference)
if (descriptor is PropertyDescriptor && descriptor.isOverridable) {
trace.record(BindingContext.LEAKING_THIS, reference, LeakingThisDescriptor.NonFinalProperty(descriptor, classOrObject))
}
}
}
private fun safeThisUsage(expression: KtThisExpression): Boolean {
val referenceDescriptor = trace.get(BindingContext.REFERENCE_TARGET, expression.instanceReference)
if (referenceDescriptor != classDescriptor) return true
val parent = expression.parent
return when (parent) {
is KtQualifiedExpression -> parent.selectorExpression is KtSimpleNameExpression
is KtQualifiedExpression ->
if (parent.selectorExpression is KtSimpleNameExpression) {
checkOpenPropertyAccess(parent.selectorExpression as KtSimpleNameExpression)
true
}
else false
is KtBinaryExpression -> OperatorConventions.IDENTITY_EQUALS_OPERATIONS.contains(parent.operationToken)
else -> false
}
@@ -109,9 +125,14 @@ class ConstructorConsistencyChecker private constructor(
}
}
is MagicInstruction ->
if (instruction.kind == MagicKind.IMPLICIT_RECEIVER && element is KtCallExpression) {
if (!safeCallUsage(element)) {
handleLeakingThis(element)
if (instruction.kind == MagicKind.IMPLICIT_RECEIVER) {
if (element is KtCallExpression) {
if (!safeCallUsage(element)) {
handleLeakingThis(element)
}
}
else if (element is KtReferenceExpression) {
checkOpenPropertyAccess(element)
}
}
}
@@ -16,12 +16,12 @@ abstract class My(val v: Int) {
set(arg) { w = 2 * arg }
constructor(): this(0) {
z = v
<!DEBUG_INFO_LEAKING_THIS!>z<!> = v
}
init {
x = 1
y = 2
u = 3
<!DEBUG_INFO_LEAKING_THIS!>x<!> = 1
<!DEBUG_INFO_LEAKING_THIS!>y<!> = 2
<!DEBUG_INFO_LEAKING_THIS!>u<!> = 3
}
}
@@ -0,0 +1,11 @@
interface Base {
val x: Int
}
open class Impl(override val x: Int) : Base {
init {
if (this.<!DEBUG_INFO_LEAKING_THIS!>x<!> != 0) foo()
}
}
fun foo() {}
@@ -0,0 +1,18 @@
package
public fun foo(): kotlin.Unit
public interface Base {
public abstract 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
}
public open class Impl : Base {
public constructor Impl(/*0*/ x: kotlin.Int)
public open override /*1*/ 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,14 @@
open class Base {
open var x: Int
open var y: Int
constructor() {
<!DEBUG_INFO_LEAKING_THIS!>x<!> = 42
this.<!DEBUG_INFO_LEAKING_THIS!>y<!> = 24
val temp = this.<!DEBUG_INFO_LEAKING_THIS!>x<!>
this.<!DEBUG_INFO_LEAKING_THIS!>x<!> = <!DEBUG_INFO_LEAKING_THIS!>y<!>
<!DEBUG_INFO_LEAKING_THIS!>y<!> = temp
}
}
@@ -0,0 +1,10 @@
package
public open class Base {
public constructor Base()
public open var x: kotlin.Int
public open var 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -1,8 +1,8 @@
sealed class My(open val x: Int?) {
init {
if (x != null) {
if (<!DEBUG_INFO_LEAKING_THIS!>x<!> != null) {
// Should be error: property is open
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
<!SMARTCAST_IMPOSSIBLE, DEBUG_INFO_LEAKING_THIS!>x<!>.hashCode()
}
}
}
@@ -2943,6 +2943,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("derivedProperty.kt")
public void testDerivedProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/derivedProperty.kt");
doTest(fileName);
}
@TestMetadata("init.kt")
public void testInit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/init.kt");
@@ -2979,6 +2985,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("openProperty.kt")
public void testOpenProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/openProperty.kt");
doTest(fileName);
}
@TestMetadata("outer.kt")
public void testOuter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/outer.kt");