KT-12152 : constructor consistency: handle open property accessing
This commit is contained in:
@@ -34,6 +34,8 @@ 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 NonFinalClass(val klass: ClassDescriptor, classOrObject: KtClassOrObject): LeakingThisDescriptor(classOrObject)
|
||||||
|
|
||||||
|
class NonFinalProperty(val property: PropertyDescriptor, classOrObject: KtClassOrObject): LeakingThisDescriptor(classOrObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConstructorConsistencyChecker private constructor(
|
class ConstructorConsistencyChecker private constructor(
|
||||||
@@ -45,12 +47,26 @@ class ConstructorConsistencyChecker private constructor(
|
|||||||
) {
|
) {
|
||||||
private val finalClass = classDescriptor.isFinalClass
|
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 {
|
private fun safeThisUsage(expression: KtThisExpression): Boolean {
|
||||||
val referenceDescriptor = trace.get(BindingContext.REFERENCE_TARGET, expression.instanceReference)
|
val referenceDescriptor = trace.get(BindingContext.REFERENCE_TARGET, expression.instanceReference)
|
||||||
if (referenceDescriptor != classDescriptor) return true
|
if (referenceDescriptor != classDescriptor) return true
|
||||||
val parent = expression.parent
|
val parent = expression.parent
|
||||||
return when (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)
|
is KtBinaryExpression -> OperatorConventions.IDENTITY_EQUALS_OPERATIONS.contains(parent.operationToken)
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@@ -109,9 +125,14 @@ class ConstructorConsistencyChecker private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is MagicInstruction ->
|
is MagicInstruction ->
|
||||||
if (instruction.kind == MagicKind.IMPLICIT_RECEIVER && element is KtCallExpression) {
|
if (instruction.kind == MagicKind.IMPLICIT_RECEIVER) {
|
||||||
if (!safeCallUsage(element)) {
|
if (element is KtCallExpression) {
|
||||||
handleLeakingThis(element)
|
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 }
|
set(arg) { w = 2 * arg }
|
||||||
|
|
||||||
constructor(): this(0) {
|
constructor(): this(0) {
|
||||||
z = v
|
<!DEBUG_INFO_LEAKING_THIS!>z<!> = v
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
x = 1
|
<!DEBUG_INFO_LEAKING_THIS!>x<!> = 1
|
||||||
y = 2
|
<!DEBUG_INFO_LEAKING_THIS!>y<!> = 2
|
||||||
u = 3
|
<!DEBUG_INFO_LEAKING_THIS!>u<!> = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -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() {}
|
||||||
+18
@@ -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?) {
|
sealed class My(open val x: Int?) {
|
||||||
init {
|
init {
|
||||||
if (x != null) {
|
if (<!DEBUG_INFO_LEAKING_THIS!>x<!> != null) {
|
||||||
// Should be error: property is open
|
// 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);
|
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")
|
@TestMetadata("init.kt")
|
||||||
public void testInit() throws Exception {
|
public void testInit() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/init.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/constructorConsistency/init.kt");
|
||||||
@@ -2979,6 +2985,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@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