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:
@@ -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);
|
||||
|
||||
-5
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user