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.
This commit is contained in:
Mikhail Glukhikh
2015-10-14 15:25:25 +03:00
parent 9c9ab671b3
commit c0faf82f77
9 changed files with 112 additions and 26 deletions
@@ -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;
}
}
@@ -659,9 +659,6 @@ public interface Errors {
// Properties / locals
DiagnosticFactory1<JetExpression, DeclarationDescriptor> INITIALIZATION_USING_BACKING_FIELD_CUSTOM_SETTER = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetExpression, DeclarationDescriptor> INITIALIZATION_USING_BACKING_FIELD_OPEN_SETTER = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<JetTypeReference> LOCAL_EXTENSION_PROPERTY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetPropertyAccessor> LOCAL_VARIABLE_WITH_GETTER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetPropertyAccessor> LOCAL_VARIABLE_WITH_SETTER = DiagnosticFactory0.create(ERROR);
@@ -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");
@@ -289,4 +289,11 @@ public object ModifierCheckerCore {
val list = listOwner.modifierList ?: return
checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets)
}
}
fun foo(p: Pair<Int?, Int?>) {
val list: MutableList<Int> = LinkedList()
if (p.first != null) {
list.add(p.first as Int)
}
}
@@ -0,0 +1,30 @@
class My(val v: Int) {
// Ok: setter is just private
var x: Int
private set
<!MUST_BE_INITIALIZED!>var y: Int<!>
set(arg) { field = arg }
<!MUST_BE_INITIALIZED!>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
}
}
@@ -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
}
@@ -0,0 +1,27 @@
abstract class My(val v: Int) {
// Ok: variable is just abstract
abstract var x: Int
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>open var y: Int<!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>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
}
}
@@ -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
}
@@ -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)