From c0faf82f77686540f2a7ceb8c46744addf4879b2 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 14 Oct 2015 15:25:25 +0300 Subject: [PATCH] Initializer is now required for mutable properties with backing fields and open or custom setter #KT-9449 Fixed Setters without body are not taken into account accordingly to KT-9449. Old INITIALIZATION_USING_BACKING_FIELD_SETTER are both dropped. --- .../cfg/JetFlowInformationProvider.java | 14 ++++----- .../jetbrains/kotlin/diagnostics/Errors.java | 3 -- .../rendering/DefaultErrorMessages.java | 5 ---- .../kotlin/resolve/ModifiersChecker.kt | 7 +++++ .../tests/backingField/InitCustomSetter.kt | 30 +++++++++++++++++++ .../tests/backingField/InitCustomSetter.txt | 15 ++++++++++ .../tests/backingField/InitOpenSetter.kt | 27 +++++++++++++++++ .../tests/backingField/InitOpenSetter.txt | 15 ++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 22 +++++++------- 9 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/backingField/InitCustomSetter.kt create mode 100644 compiler/testData/diagnostics/tests/backingField/InitCustomSetter.txt create mode 100644 compiler/testData/diagnostics/tests/backingField/InitOpenSetter.kt create mode 100644 compiler/testData/diagnostics/tests/backingField/InitOpenSetter.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java index 9a5fbc63e8e..f8f53aad06e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java @@ -70,6 +70,7 @@ import static org.jetbrains.kotlin.cfg.TailRecursionKind.*; import static org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD; import static org.jetbrains.kotlin.diagnostics.Errors.*; import static org.jetbrains.kotlin.resolve.BindingContext.CAPTURED_IN_CLOSURE; +import static org.jetbrains.kotlin.resolve.BindingContext.IS_UNINITIALIZED; import static org.jetbrains.kotlin.resolve.BindingContext.TAIL_RECURSION_CALL; import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCall; import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE; @@ -495,12 +496,14 @@ public class JetFlowInformationProvider { private boolean checkInitializationUsingBackingField(@NotNull VariableInitContext ctxt, @NotNull JetExpression expression) { VariableDescriptor variableDescriptor = ctxt.variableDescriptor; if (variableDescriptor instanceof PropertyDescriptor - && !ctxt.enterInitState.mayBeInitialized() && ctxt.exitInitState.mayBeInitialized()) { + && !ctxt.enterInitState.mayBeInitialized() + && ctxt.exitInitState.mayBeInitialized()) { if (!variableDescriptor.isVar()) return false; if (!trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor)) return false; PsiElement property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor); assert property instanceof JetProperty; - if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && ((JetProperty) property).getSetter() == null) { + JetPropertyAccessor setter = ((JetProperty) property).getSetter(); + if (((PropertyDescriptor) variableDescriptor).getModality() == Modality.FINAL && (setter == null || !setter.hasBody())) { return false; } JetExpression variable = expression; @@ -512,12 +515,7 @@ public class JetFlowInformationProvider { if (variable instanceof JetSimpleNameExpression) { JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) variable; if (simpleNameExpression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) { - if (((PropertyDescriptor) variableDescriptor).getModality() != Modality.FINAL) { - report(Errors.INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER.on(expression, variableDescriptor), ctxt); - } - else { - report(Errors.INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER.on(expression, variableDescriptor), ctxt); - } + trace.record(IS_UNINITIALIZED, (PropertyDescriptor) variableDescriptor); return true; } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 4b390ea2a13..82b4cc8c60b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -659,9 +659,6 @@ public interface Errors { // Properties / locals - DiagnosticFactory1 INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1 INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER = DiagnosticFactory1.create(ERROR); - DiagnosticFactory0 LOCAL_EXTENSION_PROPERTY = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 LOCAL_VARIABLE_WITH_GETTER = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 LOCAL_VARIABLE_WITH_SETTER = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index c01f4a5cffe..e86155f1545 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -282,11 +282,6 @@ public class DefaultErrorMessages { MAP.put(VAL_OR_VAR_ON_CATCH_PARAMETER, "''{0}'' on catch parameter is not allowed", TO_STRING); MAP.put(VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER, "''{0}'' on secondary constructor parameter is not allowed", TO_STRING); - MAP.put(INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER, - "This property cannot be initialized inside ''init'' block because it has a custom setter", NAME); - MAP.put(INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER, - "This property cannot be initialized inside ''init'' block because it has an open setter", NAME); - MAP.put(UNREACHABLE_CODE, "Unreachable code", TO_STRING); MAP.put(MANY_COMPANION_OBJECTS, "Only one companion object is allowed per class"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt index 0509ea741af..f037df85a79 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt @@ -289,4 +289,11 @@ public object ModifierCheckerCore { val list = listOwner.modifierList ?: return checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets) } +} + +fun foo(p: Pair) { + val list: MutableList = LinkedList() + if (p.first != null) { + list.add(p.first as Int) + } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/backingField/InitCustomSetter.kt b/compiler/testData/diagnostics/tests/backingField/InitCustomSetter.kt new file mode 100644 index 00000000000..11c04fa4808 --- /dev/null +++ b/compiler/testData/diagnostics/tests/backingField/InitCustomSetter.kt @@ -0,0 +1,30 @@ +class My(val v: Int) { + // Ok: setter is just private + var x: Int + private set + + var y: Int + set(arg) { field = arg } + + var z: Int + set(arg) { field = arg } + + // Ok: initializer available + var w: Int = v + set(arg) { field = arg } + + // Ok: no backing field + var u: Int + get() = w + set(arg) { w = 2 * arg } + + constructor(): this(0) { + z = v + } + + init { + x = 1 + y = 2 + u = 3 + } +} diff --git a/compiler/testData/diagnostics/tests/backingField/InitCustomSetter.txt b/compiler/testData/diagnostics/tests/backingField/InitCustomSetter.txt new file mode 100644 index 00000000000..0360b81b79b --- /dev/null +++ b/compiler/testData/diagnostics/tests/backingField/InitCustomSetter.txt @@ -0,0 +1,15 @@ +package + +public final class My { + public constructor My() + public constructor My(/*0*/ v: kotlin.Int) + public final var u: kotlin.Int + public final val v: kotlin.Int + public final var w: kotlin.Int + public final var x: kotlin.Int + public final var y: kotlin.Int + public final var z: 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/testData/diagnostics/tests/backingField/InitOpenSetter.kt b/compiler/testData/diagnostics/tests/backingField/InitOpenSetter.kt new file mode 100644 index 00000000000..8173d1ec81b --- /dev/null +++ b/compiler/testData/diagnostics/tests/backingField/InitOpenSetter.kt @@ -0,0 +1,27 @@ +abstract class My(val v: Int) { + // Ok: variable is just abstract + abstract var x: Int + + open var y: Int + + open var z: Int + + // Ok: initializer available + open var w: Int = v + set(arg) { field = arg } + + // Ok: no backing field, no initializer possible + open var u: Int + get() = w + set(arg) { w = 2 * arg } + + constructor(): this(0) { + z = v + } + + init { + x = 1 + y = 2 + u = 3 + } +} diff --git a/compiler/testData/diagnostics/tests/backingField/InitOpenSetter.txt b/compiler/testData/diagnostics/tests/backingField/InitOpenSetter.txt new file mode 100644 index 00000000000..2a88325e1da --- /dev/null +++ b/compiler/testData/diagnostics/tests/backingField/InitOpenSetter.txt @@ -0,0 +1,15 @@ +package + +public abstract class My { + public constructor My() + public constructor My(/*0*/ v: kotlin.Int) + public open var u: kotlin.Int + public final val v: kotlin.Int + public open var w: kotlin.Int + public abstract var x: kotlin.Int + public open var y: kotlin.Int + public open var z: 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 8ed4a3f1072..6fd090b5a0b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -1524,6 +1524,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("InitCustomSetter.kt") + public void testInitCustomSetter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/backingField/InitCustomSetter.kt"); + doTest(fileName); + } + + @TestMetadata("InitOpenSetter.kt") + public void testInitOpenSetter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/backingField/InitOpenSetter.kt"); + doTest(fileName); + } + @TestMetadata("kt782packageLevel.kt") public void testKt782packageLevel() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/backingField/kt782packageLevel.kt"); @@ -9328,16 +9340,6 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } } - @TestMetadata("compiler/testData/diagnostics/tests/jdk-annotations") - @TestDataPath("$PROJECT_ROOT") - @RunWith(JUnit3RunnerWithInners.class) - public static class Jdk_annotations extends AbstractJetDiagnosticsTest { - public void testAllFilesPresentInJdk_annotations() throws Exception { - JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/jdk-annotations"), Pattern.compile("^(.+)\\.kt$"), true); - } - - } - @TestMetadata("compiler/testData/diagnostics/tests/labels") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)