diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java index b3dce48caf5..66e5e60ab04 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.java @@ -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( diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt index a6088c4dc74..4f93a22cb82 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.kt @@ -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 + x = "" + } + } + // Error! See KT-10042 + x.length + val y: String + class C(val s: String) { + constructor(): this("") { + // Error! + y = s + } + } + 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! + y = "" + } + } + } + val z: String + object: Gau(if (true) { + z = "" + z + } + else "") {} +} + +class My { + init { + val x: String + class Your { + init { + // Error! See KT-10445 + x = "" + } + } + } +} + +val top: Int + +fun init() { + top = 1 +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.txt index a34eb4a98fa..95ca234f911 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.txt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalClass.txt @@ -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 +}