From cc2d0059843f973f64de9c7770ff2f03b6c4ec74 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 18 Sep 2015 20:43:23 +0300 Subject: [PATCH] Make lateinit diagnostics more clear --- .../jetbrains/kotlin/diagnostics/Errors.java | 7 ++- .../rendering/DefaultErrorMessages.java | 7 ++- .../kotlin/resolve/DeclarationsChecker.java | 46 +++++++++++++------ .../codegen/box/properties/lateinit/simple.kt | 12 ----- .../box/properties/lateinit/simpleField.kt | 16 ------- .../testData/diagnostics/tests/LateInit.kt | 34 +++++++------- .../testData/diagnostics/tests/LateInit.txt | 20 ++++---- .../BlackBoxCodegenTestGenerated.java | 12 ----- 8 files changed, 70 insertions(+), 84 deletions(-) delete mode 100644 compiler/testData/codegen/box/properties/lateinit/simple.kt delete mode 100644 compiler/testData/codegen/box/properties/lateinit/simpleField.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index ada4cabf99c..80cfc145487 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -328,12 +328,17 @@ public interface Errors { DiagnosticFactory0 PROPERTY_INITIALIZER_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 PROPERTY_INITIALIZER_IN_TRAIT = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 FINAL_PROPERTY_IN_TRAIT = DiagnosticFactory0.create(ERROR, FINAL_MODIFIER); - DiagnosticFactory0 INAPPLICABLE_LATEINIT_MODIFIER = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 BACKING_FIELD_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 BACKING_FIELD_SYNTAX_DEPRECATED = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 BACKING_FIELD_USAGE_DEPRECATED = DiagnosticFactory0.create(WARNING); + 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); + DiagnosticFactory2 ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = DiagnosticFactory2.create(ERROR, ABSTRACT_MODIFIER); DiagnosticFactory0 VAL_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 924f0f18e77..1ed5e633189 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -205,8 +205,13 @@ public class DefaultErrorMessages { MAP.put(DELEGATED_PROPERTY_IN_TRAIT, "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(GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY, "Getter visibility must be the same as property visibility"); - MAP.put(INAPPLICABLE_LATEINIT_MODIFIER, "''lateinit'' modifier is allowed only on non-null member properties with a backing field"); MAP.put(BACKING_FIELD_IN_TRAIT, "Property in an interface cannot have a backing field"); MAP.put(MUST_BE_INITIALIZED, "Property must be initialized"); MAP.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index a6e01a87fdb..ebbd3053ff8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -329,18 +329,43 @@ public class DeclarationsChecker { PsiElement modifier = modifierList.getModifier(JetTokens.LATE_INIT_KEYWORD); if (modifier == null) return; + if (!propertyDescriptor.isVar()) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER_IMMUTABLE.on(modifier)); + return; + } + + boolean returnTypeIsNullable = true; + boolean returnTypeIsPrimitive = true; + + JetType returnType = propertyDescriptor.getReturnType(); + if (returnType != null) { + returnTypeIsNullable = TypeUtils.isNullableType(returnType); + returnTypeIsPrimitive = KotlinBuiltIns.isPrimitiveType(returnType); + } + + if (returnTypeIsNullable) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER_NULLABLE.on(modifier)); + return; + } + + if (propertyDescriptor.getModality() == Modality.ABSTRACT) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER_ABSTRACT_PROPERTY.on(modifier)); + return; + } + + if (property instanceof JetParameter) { + trace.report(INAPPLICABLE_LATEINIT_MODIFIER_PRIMARY_CONSTRUCTOR_PARAMETER.on(modifier)); + return; + } + boolean hasBackingField = Boolean.TRUE.equals(trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)); boolean hasDelegateOrInitializer = false; - boolean hasCorrespondingValueParameter = false; if (property instanceof JetProperty) { hasDelegateOrInitializer = ((JetProperty) property).hasDelegateExpressionOrInitializer(); } - else if (property instanceof JetParameter) { - hasCorrespondingValueParameter = true; - } PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); PropertySetterDescriptor setter = propertyDescriptor.getSetter(); @@ -353,17 +378,8 @@ public class DeclarationsChecker { customGetterOrSetter |= setter.hasBody(); } - boolean returnTypeIsNullable = true; - boolean returnTypeIsPrimitive = true; - - JetType returnType = propertyDescriptor.getReturnType(); - if (returnType != null) { - returnTypeIsNullable = TypeUtils.isNullableType(returnType); - returnTypeIsPrimitive = KotlinBuiltIns.isPrimitiveType(returnType); - } - - if (!hasBackingField || hasCorrespondingValueParameter || hasDelegateOrInitializer || customGetterOrSetter - || returnTypeIsNullable || returnTypeIsPrimitive || propertyDescriptor.getExtensionReceiverParameter() != null) { + if (!hasBackingField || hasDelegateOrInitializer || customGetterOrSetter + || returnTypeIsPrimitive || propertyDescriptor.getExtensionReceiverParameter() != null) { trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier)); } } diff --git a/compiler/testData/codegen/box/properties/lateinit/simple.kt b/compiler/testData/codegen/box/properties/lateinit/simple.kt deleted file mode 100644 index d3598cd88aa..00000000000 --- a/compiler/testData/codegen/box/properties/lateinit/simple.kt +++ /dev/null @@ -1,12 +0,0 @@ -class A { - public lateinit val str: String - - init { - str = "OK" - } -} - -fun box(): String { - val a = A() - return a.str -} \ No newline at end of file diff --git a/compiler/testData/codegen/box/properties/lateinit/simpleField.kt b/compiler/testData/codegen/box/properties/lateinit/simpleField.kt deleted file mode 100644 index 53ebee98133..00000000000 --- a/compiler/testData/codegen/box/properties/lateinit/simpleField.kt +++ /dev/null @@ -1,16 +0,0 @@ -class A { - private lateinit val str: String - - init { - str = "OK" - } - - public fun getMyStr(): String { - return str - } -} - -fun box(): String { - val a = A() - return a.getMyStr() -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/LateInit.kt b/compiler/testData/diagnostics/tests/LateInit.kt index e90f468cfc5..3fc440177ae 100644 --- a/compiler/testData/diagnostics/tests/LateInit.kt +++ b/compiler/testData/diagnostics/tests/LateInit.kt @@ -2,13 +2,13 @@ class CustomDelegate { public fun get(thisRef: Any?, prop: PropertyMetadata): String = prop.name } -public abstract class A(lateinit val 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 @@ -16,42 +16,42 @@ public abstract class A(la private set fun a() { - lateinit val a: String + lateinit var a: String } - lateinit val e1: V - lateinit val e2: String? - lateinit val e3: Int - lateinit val 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() + lateinit val e7 by CustomDelegate() - lateinit val e8: String + lateinit val e8: String get() = "A" lateinit var e9: String set(v) { field = v } - abstract lateinit val e10: String + abstract lateinit var e10: String - lateinit val String.e11: 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 val str: String + lateinit var str: String } public abstract class AbstractClass { - abstract val str: String + abstract var str: String } public class AbstractClassImpl : AbstractClass() { - override lateinit val str: String + override lateinit var str: String } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/LateInit.txt b/compiler/testData/diagnostics/tests/LateInit.txt index 4bf696f3613..82114de69c6 100644 --- a/compiler/testData/diagnostics/tests/LateInit.txt +++ b/compiler/testData/diagnostics/tests/LateInit.txt @@ -10,18 +10,18 @@ public abstract class A { private final lateinit var c: kotlin.CharSequence public final lateinit val d: kotlin.String public final lateinit var e: kotlin.String - public final lateinit val e1: V - public abstract lateinit val e10: kotlin.String - public final lateinit val e2: kotlin.String? - public final lateinit val e3: kotlin.Int - public final lateinit val e4: kotlin.Int? + public final lateinit var e1: V + public abstract lateinit var e10: kotlin.String + public final lateinit var e2: kotlin.String? + public final lateinit var e3: kotlin.Int + 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 e9: kotlin.String - public final val p2: kotlin.String - public final lateinit val kotlin.String.e11: kotlin.String + public final var p2: kotlin.String + public final lateinit var kotlin.String.e11: kotlin.String public final lateinit var kotlin.String.e12: kotlin.String public final fun a(): kotlin.Unit public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean @@ -31,7 +31,7 @@ public abstract class A { public abstract class AbstractClass { public constructor AbstractClass() - public abstract val str: kotlin.String + public abstract var str: 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 @@ -39,7 +39,7 @@ public abstract class AbstractClass { public final class AbstractClassImpl : AbstractClass { public constructor AbstractClassImpl() - public open override /*1*/ lateinit val str: kotlin.String + public open override /*1*/ lateinit var str: 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 @@ -54,7 +54,7 @@ public final class CustomDelegate { } public interface Intf { - public abstract lateinit val str: kotlin.String + public abstract lateinit var str: 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/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index a856f29ed4f..b0cb25b1510 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -6603,18 +6603,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } - @TestMetadata("simple.kt") - public void testSimple() throws Exception { - String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/simple.kt"); - doTest(fileName); - } - - @TestMetadata("simpleField.kt") - public void testSimpleField() throws Exception { - String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/simpleField.kt"); - doTest(fileName); - } - @TestMetadata("simpleVar.kt") public void testSimpleVar() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/simpleVar.kt");