diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 23239895548..bf9a1d8346d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -360,12 +360,7 @@ public interface Errors { DiagnosticFactory0 BACKING_FIELD_OLD_SYNTAX = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 BACKING_FIELD_USAGE_FORBIDDEN = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 INAPPLICABLE_LATEINIT_MODIFIER = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 INAPPLICABLE_LATEINIT_MODIFIER_IMMUTABLE = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 INAPPLICABLE_LATEINIT_MODIFIER_ABSTRACT_PROPERTY = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 INAPPLICABLE_LATEINIT_MODIFIER_PRIMARY_CONSTRUCTOR_PARAMETER = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 INAPPLICABLE_LATEINIT_MODIFIER_NULLABLE = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 INAPPLICABLE_LATEINIT_MODIFIER_PRIMITIVE = DiagnosticFactory0.create(ERROR); + DiagnosticFactory1 INAPPLICABLE_LATEINIT_MODIFIER = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = DiagnosticFactory2.create(ERROR, ABSTRACT_MODIFIER); 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 d362dc046f3..8749f6a7089 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -214,12 +214,7 @@ public class DefaultErrorMessages { MAP.put(DELEGATED_PROPERTY_IN_INTERFACE, "Delegated properties are not allowed in interfaces"); MAP.put(LOCAL_VARIABLE_WITH_DELEGATE, "Local variables are not allowed to have delegates"); - MAP.put(INAPPLICABLE_LATEINIT_MODIFIER, "''lateinit'' modifier is allowed only on member properties with a backing field"); - MAP.put(INAPPLICABLE_LATEINIT_MODIFIER_IMMUTABLE, "''lateinit'' modifier is allowed only on mutable properties"); - MAP.put(INAPPLICABLE_LATEINIT_MODIFIER_ABSTRACT_PROPERTY, "''lateinit'' modifier is not allowed on abstract properties"); - MAP.put(INAPPLICABLE_LATEINIT_MODIFIER_PRIMARY_CONSTRUCTOR_PARAMETER, "''lateinit'' modifier is not allowed on primary constructor parameters"); - MAP.put(INAPPLICABLE_LATEINIT_MODIFIER_NULLABLE, "''lateinit'' modifier is not allowed on nullable properties"); - MAP.put(INAPPLICABLE_LATEINIT_MODIFIER_PRIMITIVE, "''lateinit'' modifier is not allowed on primitive type properties"); + MAP.put(INAPPLICABLE_LATEINIT_MODIFIER, "''lateinit'' modifier {0}", STRING); MAP.put(GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY, "Getter visibility must be the same as property visibility"); MAP.put(PRIVATE_SETTER_ON_NON_PRIVATE_LATE_INIT_VAR, "Private setter is not allowed on non-private lateinit property"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index 46b1a785645..e75992abd4b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -514,8 +514,7 @@ public class DeclarationsChecker { if (modifier == null) return; if (!propertyDescriptor.isVar()) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER_IMMUTABLE.on(modifier)); - return; + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is allowed only on mutable properties")); } boolean returnTypeIsNullable = true; @@ -528,32 +527,29 @@ public class DeclarationsChecker { } if (returnTypeIsNullable) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER_NULLABLE.on(modifier)); - return; + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on nullable properties")); } if (returnTypeIsPrimitive) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER_PRIMITIVE.on(modifier)); - return; + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on primitive type properties")); } - if (propertyDescriptor.getModality() == Modality.ABSTRACT) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER_ABSTRACT_PROPERTY.on(modifier)); - return; + boolean isAbstract = propertyDescriptor.getModality() == Modality.ABSTRACT; + if (isAbstract) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on abstract properties")); } if (property instanceof JetParameter) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER_PRIMARY_CONSTRUCTOR_PARAMETER.on(modifier)); - return; + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on primary constructor parameters")); } - boolean hasBackingField = - Boolean.TRUE.equals(trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)); - - boolean hasDelegateOrInitializer = false; - + boolean hasDelegateExpressionOrInitializer = false; if (property instanceof JetProperty) { - hasDelegateOrInitializer = ((JetProperty) property).hasDelegateExpressionOrInitializer(); + hasDelegateExpressionOrInitializer = ((JetProperty) property).hasDelegateExpressionOrInitializer(); + if (hasDelegateExpressionOrInitializer) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, + "is not allowed on properties with initializer or on delegated properties")); + } } PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); @@ -567,9 +563,19 @@ public class DeclarationsChecker { customGetterOrSetter |= setter.hasBody(); } - if (!hasBackingField || hasDelegateOrInitializer || customGetterOrSetter - || propertyDescriptor.getExtensionReceiverParameter() != null) { - trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier)); + if (!hasDelegateExpressionOrInitializer && customGetterOrSetter) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on properties with a custom getter or setter")); + } + + boolean hasBackingField = + Boolean.TRUE.equals(trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)); + + if (!isAbstract && !customGetterOrSetter && !hasDelegateExpressionOrInitializer && !hasBackingField) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on properties without backing field")); + } + + if (propertyDescriptor.getExtensionReceiverParameter() != null) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier, "is not allowed on extension properties")); } } diff --git a/compiler/testData/diagnostics/tests/LateInit.kt b/compiler/testData/diagnostics/tests/LateInit.kt index 83d33ebe9d3..2c8dde40cba 100644 --- a/compiler/testData/diagnostics/tests/LateInit.kt +++ b/compiler/testData/diagnostics/tests/LateInit.kt @@ -2,15 +2,16 @@ import kotlin.reflect.KProperty class CustomDelegate { operator fun getValue(thisRef: Any?, prop: KProperty<*>): String = prop.name + operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String) {} } -public abstract class A(lateinit var p2: String) { +public abstract class A(lateinit var p2: String) { - public lateinit val a: String - lateinit val b: T + public lateinit val a: String + lateinit val b: T private lateinit var c: CharSequence - lateinit val d: String + lateinit val d: String get public lateinit var e: String @@ -21,33 +22,35 @@ public abstract class A(lateinit var a: String } - lateinit var e1: V - lateinit var e2: String? - lateinit var e3: Int - lateinit var e4: Int? + lateinit var e1: V + lateinit var e2: String? + lateinit var e3: Int + lateinit var e4: Int? lateinit var e5 = "A" - lateinit var e6 = 3 - lateinit val e7 by CustomDelegate() + // With initializer, primitive + lateinit var e6 = 3 - lateinit val e8: String + lateinit var e7 by CustomDelegate() + + lateinit var e8: String get() = "A" lateinit var e9: String set(v) { field = v } - abstract lateinit var e10: String + abstract lateinit var e10: String lateinit var String.e11: String lateinit var String.e12: String } -lateinit val topLevel: String +lateinit val topLevel: String lateinit var topLevelMutable: String public interface Intf { - lateinit var str: String + lateinit var str: String } public abstract class AbstractClass { diff --git a/compiler/testData/diagnostics/tests/LateInit.txt b/compiler/testData/diagnostics/tests/LateInit.txt index 8cad99618a4..17e549155e6 100644 --- a/compiler/testData/diagnostics/tests/LateInit.txt +++ b/compiler/testData/diagnostics/tests/LateInit.txt @@ -17,8 +17,8 @@ public abstract class A { public final lateinit var e4: kotlin.Int? public final lateinit var e5: kotlin.String public final lateinit var e6: kotlin.Int - public final lateinit val e7: kotlin.String - public final lateinit val e8: kotlin.String + public final lateinit var e7: kotlin.String + public final lateinit var e8: kotlin.String public final lateinit var e9: kotlin.String public final var p2: kotlin.String public final lateinit var kotlin.String.e11: kotlin.String @@ -58,6 +58,7 @@ public final class CustomDelegate { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.reflect.KProperty<*>): kotlin.String public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun setValue(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.reflect.KProperty<*>, /*2*/ value: kotlin.String): kotlin.Unit public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String }