From 7d963e8e074248ffa0019e67a4e565cf1aea3ed8 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 17 Feb 2015 10:23:07 +0300 Subject: [PATCH] Differ this/non-this instances for vars initialization in class When deal with constructed object (not this) treat it like it's fully initialized. Otherwise (this or access with no receiver) access instruction should be handled as it was before. #KT-6788 Fixed #KT-4126 Fixed --- .../cfg/JetFlowInformationProvider.java | 68 ++++++++----------- .../kotlin/cfg/PseudocodeVariablesData.java | 5 ++ .../kotlin/cfg/pseudocode/PseudocodeUtil.java | 61 +++++++++++++++-- .../VariablesInitialization.instructions | 16 ++--- .../tests/controlFlowAnalysis/kt4126.kt | 3 + .../tests/controlFlowAnalysis/kt4126.txt | 14 ++++ .../tests/controlFlowAnalysis/kt6788.kt | 10 +++ .../tests/controlFlowAnalysis/kt6788.txt | 19 ++++++ .../propertiesInitWithOtherInstance.kt | 12 ++++ .../propertiesInitWithOtherInstance.txt | 10 +++ .../propertiesInitWithOtherInstanceInner.kt | 21 ++++++ .../propertiesInitWithOtherInstanceInner.txt | 17 +++++ ...ropertiesInitWithOtherInstanceThisLabel.kt | 12 ++++ ...opertiesInitWithOtherInstanceThisLabel.txt | 10 +++ .../initializationFromOtherInstance.kt | 15 ++++ .../initializationFromOtherInstance.txt | 21 ++++++ .../checkers/JetDiagnosticsTestGenerated.java | 36 ++++++++++ 17 files changed, 300 insertions(+), 50 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.txt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.txt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.txt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.txt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.txt create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.kt create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java index b9a1d8dc753..499c060a011 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java @@ -57,9 +57,7 @@ import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; -import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils; @@ -324,19 +322,24 @@ public class JetFlowInformationProvider { new VariableInitContext(instruction, reportedDiagnosticMap, in, out, lexicalScopeVariableInfo); if (ctxt.variableDescriptor == null) return; if (instruction instanceof ReadValueInstruction) { - JetElement element = ((ReadValueInstruction) instruction).getElement(); + ReadValueInstruction readValueInstruction = (ReadValueInstruction) instruction; + JetElement element = readValueInstruction.getElement(); boolean error = checkBackingField(ctxt, element); - if (!error && declaredVariables.contains(ctxt.variableDescriptor)) { + if (!error && + PseudocodeUtil.isThisOrNoDispatchReceiver(readValueInstruction, trace.getBindingContext()) && + declaredVariables.contains(ctxt.variableDescriptor)) { checkIsInitialized(ctxt, element, varWithUninitializedErrorGenerated); } return; } if (!(instruction instanceof WriteValueInstruction)) return; - JetElement element = ((WriteValueInstruction) instruction).getlValue(); + WriteValueInstruction writeValueInstruction = (WriteValueInstruction) instruction; + JetElement element = writeValueInstruction.getlValue(); boolean error = checkBackingField(ctxt, element); if (!(element instanceof JetExpression)) return; if (!error) { - error = checkValReassignment(ctxt, (JetExpression) element, varWithValReassignErrorGenerated); + error = checkValReassignment(ctxt, (JetExpression) element, writeValueInstruction, + varWithValReassignErrorGenerated); } if (!error && processClassOrObject) { error = checkAssignmentBeforeDeclaration(ctxt, (JetExpression) element); @@ -367,6 +370,7 @@ public class JetFlowInformationProvider { ) { if (!(element instanceof JetSimpleNameExpression)) return; + boolean isInitialized = ctxt.exitInitState.isInitialized; VariableDescriptor variableDescriptor = ctxt.variableDescriptor; if (variableDescriptor instanceof PropertyDescriptor) { @@ -391,6 +395,7 @@ public class JetFlowInformationProvider { private boolean checkValReassignment( @NotNull VariableInitContext ctxt, @NotNull JetExpression expression, + @NotNull WriteValueInstruction writeValueInstruction, @NotNull Collection varWithValReassignErrorGenerated ) { VariableDescriptor variableDescriptor = ctxt.variableDescriptor; @@ -429,8 +434,9 @@ public class JetFlowInformationProvider { return true; } } - if ((isInitializedNotHere || !hasBackingField) && !variableDescriptor.isVar() - && !varWithValReassignErrorGenerated.contains(variableDescriptor)) { + boolean isThisOrNoDispatchReceiver = + PseudocodeUtil.isThisOrNoDispatchReceiver(writeValueInstruction, trace.getBindingContext()); + if ((isInitializedNotHere || !hasBackingField || !isThisOrNoDispatchReceiver) && !variableDescriptor.isVar()) { boolean hasReassignMethodReturningUnit = false; JetSimpleNameExpression operationReference = null; PsiElement parent = expression.getParent(); @@ -460,8 +466,14 @@ public class JetFlowInformationProvider { } } if (!hasReassignMethodReturningUnit) { - varWithValReassignErrorGenerated.add(variableDescriptor); - report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt); + if (!isThisOrNoDispatchReceiver || !varWithValReassignErrorGenerated.contains(variableDescriptor)) { + report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt); + } + if (isThisOrNoDispatchReceiver) { + // try to get rid of repeating VAL_REASSIGNMENT diagnostic only for vars with no receiver + // or when receiver is this + varWithValReassignErrorGenerated.add(variableDescriptor); + } return true; } } @@ -811,7 +823,14 @@ public class JetFlowInformationProvider { new TailRecursionDetector(subroutine, callInstruction) ); - boolean sameDispatchReceiver = sameDispatchReceiver(resolvedCall); + // A tail call is not allowed to change dispatch receiver + // class C { + // fun foo(other: C) { + // other.foo(this) // not a tail call + // } + // } + boolean sameDispatchReceiver = + PseudocodeUtil.isThisOrNoDispatchReceiver(resolvedCall, trace.getBindingContext()); TailRecursionKind kind = isTail && sameDispatchReceiver ? TAIL_CALL : NON_TAIL; @@ -848,33 +867,6 @@ public class JetFlowInformationProvider { } } - private boolean sameDispatchReceiver(ResolvedCall resolvedCall) { - // A tail call is not allowed to change dispatch receiver - // class C { - // fun foo(other: C) { - // other.foo(this) // not a tail call - // } - // } - ReceiverParameterDescriptor dispatchReceiverParameter = resolvedCall.getResultingDescriptor().getDispatchReceiverParameter(); - ReceiverValue dispatchReceiverValue = resolvedCall.getDispatchReceiver(); - if (dispatchReceiverParameter == null || !dispatchReceiverValue.exists()) return true; - - DeclarationDescriptor classDescriptor = null; - if (dispatchReceiverValue instanceof ThisReceiver) { - // foo() -- implicit receiver - classDescriptor = ((ThisReceiver) dispatchReceiverValue).getDeclarationDescriptor(); - } - else if (dispatchReceiverValue instanceof ExpressionReceiver) { - JetExpression expression = JetPsiUtil.deparenthesize(((ExpressionReceiver) dispatchReceiverValue).getExpression()); - if (expression instanceof JetThisExpression) { - // this.foo() -- explicit receiver - JetThisExpression thisExpression = (JetThisExpression) expression; - classDescriptor = trace.get(BindingContext.REFERENCE_TARGET, thisExpression.getInstanceReference()); - } - } - return dispatchReceiverParameter.getContainingDeclaration() == classDescriptor; - } - private static TailRecursionKind combineKinds(TailRecursionKind kind, @Nullable TailRecursionKind existingKind) { TailRecursionKind resultingKind; if (existingKind == null || existingKind == kind) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.java index 4bc9d76f813..3ee40cb1353 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.java @@ -202,6 +202,11 @@ public class PseudocodeVariablesData { } Map exitInstructionData = Maps.newHashMap(enterInstructionData); if (instruction instanceof WriteValueInstruction) { + // if writing to already initialized object + if (!PseudocodeUtil.isThisOrNoDispatchReceiver((WriteValueInstruction) instruction, bindingContext)) { + return enterInstructionData; + } + VariableInitState enterInitState = enterInstructionData.get(variable); VariableInitState initializationAtThisElement = VariableInitState.create(((WriteValueInstruction) instruction).getElement() instanceof JetProperty, enterInitState); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeUtil.java index 93a7a2982ac..ae6c497fae9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/PseudocodeUtil.java @@ -20,16 +20,20 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.cfg.JetControlFlowProcessor; import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction; -import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction; -import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstruction; +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*; import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction; +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor; import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.diagnostics.Diagnostic; -import org.jetbrains.kotlin.psi.JetDeclaration; -import org.jetbrains.kotlin.psi.JetElement; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.BindingTrace; +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver; import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice; import org.jetbrains.kotlin.util.slicedMap.WritableSlice; @@ -85,4 +89,53 @@ public class PseudocodeUtil { } return BindingContextUtils.extractVariableDescriptorIfAny(bindingContext, element, onlyReference); } + + // When deal with constructed object (not this) treat it like it's fully initialized + // Otherwise (this or access with empty receiver) access instruction should be handled as usual + public static boolean isThisOrNoDispatchReceiver( + @NotNull AccessValueInstruction instruction, + @NotNull BindingContext bindingContext + ) { + if (instruction.getReceiverValues().isEmpty()) { + return true; + } + AccessTarget accessTarget = instruction.getTarget(); + if (accessTarget instanceof AccessTarget.BlackBox) return false; + assert accessTarget instanceof AccessTarget.Call : + "AccessTarget.Declaration has no receivers and it's not BlackBox, so it should be Call"; + + ResolvedCall accessResolvedCall = ((AccessTarget.Call) accessTarget).getResolvedCall(); + return isThisOrNoDispatchReceiver(accessResolvedCall, bindingContext); + } + + public static boolean isThisOrNoDispatchReceiver( + @NotNull ResolvedCall resolvedCall, + @NotNull BindingContext bindingContext + ) { + // it returns true if call has no dispatch receiver (e.g. resulting descriptor is top-level function or local variable) + // or call receiver is effectively `this` instance (explicitly or implicitly) of resulting descriptor + // class A(other: A) { + // val x + // val y = other.x // return false for `other.x` as it's receiver is not `this` + // } + ReceiverParameterDescriptor dispatchReceiverParameter = resolvedCall.getResultingDescriptor().getDispatchReceiverParameter(); + ReceiverValue dispatchReceiverValue = resolvedCall.getDispatchReceiver(); + if (dispatchReceiverParameter == null || !dispatchReceiverValue.exists()) return true; + + DeclarationDescriptor classDescriptor = null; + if (dispatchReceiverValue instanceof ThisReceiver) { + // foo() -- implicit receiver + classDescriptor = ((ThisReceiver) dispatchReceiverValue).getDeclarationDescriptor(); + } + else if (dispatchReceiverValue instanceof ExpressionReceiver) { + JetExpression expression = JetPsiUtil.deparenthesize(((ExpressionReceiver) dispatchReceiverValue).getExpression()); + if (expression instanceof JetThisExpression) { + // this.foo() -- explicit receiver + JetThisExpression thisExpression = (JetThisExpression) expression; + classDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, thisExpression.getInstanceReference()); + } + } + return dispatchReceiverParameter.getContainingDeclaration() == classDescriptor; + } + } diff --git a/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions b/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions index 9774487df54..28c356a8d57 100644 --- a/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions +++ b/compiler/testData/cfg-variables/basic/VariablesInitialization.instructions @@ -37,18 +37,18 @@ L0: w(foo|) INIT: in: {foo=D} out: {foo=ID} 2 mark({ foo.c foo.c = 2 42 }) INIT: in: {foo=ID} out: {foo=ID} mark(foo.c) - r(foo) -> USE: in: {c=READ, foo=READ} out: {c=READ, foo=READ} - r(c|) -> USE: in: {c=ONLY_WRITTEN_NEVER_READ, foo=READ} out: {c=READ, foo=READ} - r(foo) -> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ, foo=READ} - r(2) -> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ} - w(foo.c|, ) INIT: in: {foo=ID} out: {c=I, foo=ID} USE: in: {} out: {c=ONLY_WRITTEN_NEVER_READ} - r(42) -> INIT: in: {c=I, foo=ID} out: {c=I, foo=ID} + r(foo) -> USE: in: {c=READ, foo=READ} out: {c=READ, foo=READ} + r(c|) -> USE: in: {c=ONLY_WRITTEN_NEVER_READ, foo=READ} out: {c=READ, foo=READ} + r(foo) -> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ, foo=READ} + r(2) -> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ} + w(foo.c|, ) USE: in: {} out: {c=ONLY_WRITTEN_NEVER_READ} + r(42) -> L1: 1 error: INIT: in: {} out: {} sink: - INIT: in: {c=I, foo=ID} out: {c=I, foo=ID} USE: in: {} out: {} + INIT: in: {foo=ID} out: {foo=ID} USE: in: {} out: {} ===================== == Foo == trait Foo { @@ -64,4 +64,4 @@ error: INIT: in: {} out: {} sink: INIT: in: {c=D} out: {c=D} USE: in: {} out: {} -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.kt new file mode 100644 index 00000000000..9e1db48a512 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.kt @@ -0,0 +1,3 @@ +public data class ProductGroup(val short_name: String, val parent: ProductGroup?) { + val name: String = if (parent == null) short_name else "${parent.name} $short_name" +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.txt new file mode 100644 index 00000000000..a800b324853 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.txt @@ -0,0 +1,14 @@ +package + +kotlin.data() public final class ProductGroup { + public constructor ProductGroup(/*0*/ short_name: kotlin.String, /*1*/ parent: ProductGroup?) + internal final val name: kotlin.String + internal final val parent: ProductGroup? + internal final val short_name: kotlin.String + internal final /*synthesized*/ fun component1(): kotlin.String + internal final /*synthesized*/ fun component2(): ProductGroup? + public final /*synthesized*/ fun copy(/*0*/ short_name: kotlin.String = ..., /*1*/ parent: ProductGroup? = ...): ProductGroup + 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 +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.kt new file mode 100644 index 00000000000..1adb2ece935 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.kt @@ -0,0 +1,10 @@ +class A(val next: A? = null) { + val x: String + init { + next?.x = "a" + } +} + +class B(val next: B? = null) { + var x: String = next?.x ?: "default" // it's ok to use `x` of next +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.txt new file mode 100644 index 00000000000..023f0bf8666 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.txt @@ -0,0 +1,19 @@ +package + +internal final class A { + public constructor A(/*0*/ next: A? = ...) + internal final val next: A? + internal final val x: 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 +} + +internal final class B { + public constructor B(/*0*/ next: B? = ...) + internal final val next: B? + internal final var x: 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 +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.kt new file mode 100644 index 00000000000..ae1af050d7b --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.kt @@ -0,0 +1,12 @@ +class A(val next: A? = null) { + val x: String + init { + next?.x = "a" + x = "b" + this.x = "c" + x = "d" // don't repeat the same diagnostic again with this receiver + this.x = "e" + + next?.x = "f" + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.txt new file mode 100644 index 00000000000..b5dc050a7b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.txt @@ -0,0 +1,10 @@ +package + +internal final class A { + public constructor A(/*0*/ next: A? = ...) + internal final val next: A? + internal final val x: 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 +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.kt new file mode 100644 index 00000000000..ee36d864d8e --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.kt @@ -0,0 +1,21 @@ +class Outer { + val outerProp: String + inner class Inner(inner: Inner, outer: Outer) { + val innerProp: String + init { + outerProp // use of outerProp is ok because we're suppose that Outer instance should be initialized + this@Outer.outerProp + + this@Outer.outerProp = "1" + outerProp = "2" // do not repeat the same diagnostic with this receiver of outer class + outer.outerProp = "3" + + innerProp = "4" + inner.innerProp + this@Inner.innerProp = "5" + innerProp = "6" // do not repeat the same diagnostic with this receiver + this@Inner.innerProp = "7" + + inner.innerProp = "8" + } + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.txt new file mode 100644 index 00000000000..a698c795bb4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.txt @@ -0,0 +1,17 @@ +package + +internal final class Outer { + public constructor Outer() + internal final val outerProp: 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 + + internal final inner class Inner { + public constructor Inner(/*0*/ inner: Outer.Inner, /*1*/ outer: Outer) + internal final val innerProp: 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 + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.kt new file mode 100644 index 00000000000..c07023a0852 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.kt @@ -0,0 +1,12 @@ +class A(val next: A? = null) { + val x: String + init { + next?.x = "a" + this@A.x = "b" + this.x = "c" + x = "d" // don't repeat the same diagnostic again with this receiver + this@A.x = "e" + + next?.x = "f" + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.txt new file mode 100644 index 00000000000..b5dc050a7b2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.txt @@ -0,0 +1,10 @@ +package + +internal final class A { + public constructor A(/*0*/ next: A? = ...) + internal final val next: A? + internal final val x: 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 +} diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.kt new file mode 100644 index 00000000000..b5b7c98f7ce --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.kt @@ -0,0 +1,15 @@ +class A { + val x: Int + val y: Int + constructor(x: Int, y: Int) { + this.x = x + this.y = y + } + constructor(other: A) { + x = other.x + y = other.y + } +} +class A1(val x: Int, val y: Int) { + constructor(other: A1): this(other.x, other.y) {} +} diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.txt b/compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.txt new file mode 100644 index 00000000000..eb5e2051b46 --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.txt @@ -0,0 +1,21 @@ +package + +internal final class A { + public constructor A(/*0*/ other: A) + public constructor A(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final val x: kotlin.Int + internal final val 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 +} + +internal final class A1 { + public constructor A1(/*0*/ other: A1) + public constructor A1(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + internal final val x: kotlin.Int + internal final val 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 114fb235f8a..3984702db31 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -1764,6 +1764,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt4126.kt") + public void testKt4126() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4126.kt"); + doTest(fileName); + } + @TestMetadata("kt4405.kt") public void testKt4405() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt4405.kt"); @@ -1794,6 +1800,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt6788.kt") + public void testKt6788() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt6788.kt"); + doTest(fileName); + } + @TestMetadata("kt776.kt") public void testKt776() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt776.kt"); @@ -1818,6 +1830,24 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("propertiesInitWithOtherInstance.kt") + public void testPropertiesInitWithOtherInstance() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstance.kt"); + doTest(fileName); + } + + @TestMetadata("propertiesInitWithOtherInstanceInner.kt") + public void testPropertiesInitWithOtherInstanceInner() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceInner.kt"); + doTest(fileName); + } + + @TestMetadata("propertiesInitWithOtherInstanceThisLabel.kt") + public void testPropertiesInitWithOtherInstanceThisLabel() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesInitWithOtherInstanceThisLabel.kt"); + doTest(fileName); + } + @TestMetadata("propertiesOrderInPackage.kt") public void testPropertiesOrderInPackage() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt"); @@ -10451,6 +10481,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("initializationFromOtherInstance.kt") + public void testInitializationFromOtherInstance() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/initializationFromOtherInstance.kt"); + doTest(fileName); + } + @TestMetadata("memberAccessBeforeSuperCall.kt") public void testMemberAccessBeforeSuperCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/memberAccessBeforeSuperCall.kt");