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");