Correct handling of local class / anonymous object cases for KT-10445 / KT-10042

This commit is contained in:
Mikhail Glukhikh
2016-03-22 14:54:53 +03:00
parent b975b7d26e
commit 32e7f9e58f
3 changed files with 124 additions and 8 deletions
@@ -391,20 +391,48 @@ public class ControlFlowInformationProvider {
}
}
// Should return KtDeclarationWithBody or KtClassOrObject
@Nullable
private static KtDeclaration getElementParentDeclaration(@NotNull KtElement element) {
//noinspection unchecked
return PsiTreeUtil.getParentOfType(element, KtDeclarationWithBody.class, KtClassOrObject.class);
}
@Nullable
private DeclarationDescriptor getDeclarationDescriptor(@Nullable KtDeclaration declaration) {
DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration);
if (descriptor instanceof ClassDescriptor) {
// For a class primary constructor, we cannot directly get ConstructorDescriptor by KtClassInitializer,
// so we have to do additional conversion: KtClassInitializer -> KtClassOrObject -> ClassDescriptor -> ConstructorDescriptor
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
return classDescriptor.getUnsubstitutedPrimaryConstructor();
}
else {
return descriptor;
}
}
private boolean isCapturedWrite(
@NotNull VariableDescriptor variableDescriptor,
@NotNull WriteValueInstruction writeValueInstruction
) {
DeclarationDescriptor containingDeclarationDescriptor = variableDescriptor.getContainingDeclaration();
if (containingDeclarationDescriptor instanceof ClassDescriptor) return false;
KtElement ownerElement = writeValueInstruction.getOwner().getCorrespondingElement();
DeclarationDescriptor writeOwnerDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, ownerElement);
if (containingDeclarationDescriptor.equals(writeOwnerDescriptor)) return false;
if (writeOwnerDescriptor instanceof ClassDescriptor) {
ClassDescriptor writeOwnerClass = (ClassDescriptor) writeOwnerDescriptor;
if (containingDeclarationDescriptor.equals(writeOwnerClass.getUnsubstitutedPrimaryConstructor())) return false;
// Do not consider member / top-level properties
if (containingDeclarationDescriptor instanceof ClassOrPackageFragmentDescriptor) return false;
KtDeclaration parentDeclaration = getElementParentDeclaration(writeValueInstruction.getElement());
while (true) {
DeclarationDescriptor parentDescriptor = getDeclarationDescriptor(parentDeclaration);
if (containingDeclarationDescriptor.equals(parentDescriptor)) {
return false;
}
else if (parentDeclaration instanceof KtObjectDeclaration) {
// anonymous object counts here the same as its owner
parentDeclaration = getElementParentDeclaration(parentDeclaration);
}
else {
return true;
}
}
return true;
}
private boolean checkValReassignment(
@@ -1,3 +1,5 @@
// !DIAGNOSTICS: -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
fun foo() {
var x: String
class A {
@@ -19,3 +21,70 @@ fun bar() {
// Ok
x.length
}
fun gav() {
val x: String
class B {
init {
// Error! See KT-10445
<!CAPTURED_VAL_INITIALIZATION!>x<!> = ""
}
}
// Error! See KT-10042
<!UNINITIALIZED_VARIABLE!>x<!>.length
val y: String
class C(val s: String) {
constructor(): this("") {
// Error!
<!VAL_REASSIGNMENT!>y<!> = s
}
}
<!UNINITIALIZED_VARIABLE!>y<!>.length
}
open class Gau(val s: String)
fun gau() {
val x: String
object: Any() {
init {
// Ok
x = ""
}
}
// Ok
x.length
val y: String
fun local() {
object: Any() {
init {
// Error!
<!CAPTURED_VAL_INITIALIZATION!>y<!> = ""
}
}
}
val z: String
object: Gau(if (true) {
z = ""
z
}
else "") {}
}
class My {
init {
val x: String
class Your {
init {
// Error! See KT-10445
<!CAPTURED_VAL_INITIALIZATION!>x<!> = ""
}
}
}
}
<!MUST_BE_INITIALIZED!>val top: Int<!>
fun init() {
<!VAL_REASSIGNMENT!>top<!> = 1
}
@@ -1,4 +1,23 @@
package
public val top: kotlin.Int
public fun bar(): kotlin.Unit
public fun foo(): kotlin.Unit
public fun gau(): kotlin.Unit
public fun gav(): kotlin.Unit
public fun init(): kotlin.Unit
public open class Gau {
public constructor Gau(/*0*/ s: kotlin.String)
public final val s: kotlin.String
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 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
}