diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index a6d8ac3990c..673578edacf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -322,6 +322,7 @@ public interface Errors { DiagnosticFactory0 REDUNDANT_MODIFIER_IN_GETTER = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 PRIVATE_SETTER_ON_NON_PRIVATE_LATE_INIT_VAR = DiagnosticFactory0.create(ERROR); DiagnosticFactory2 WRONG_GETTER_RETURN_TYPE = DiagnosticFactory2.create(ERROR); DiagnosticFactory0 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 51a30464d98..cdf6cc038a8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -221,6 +221,7 @@ public class DefaultErrorMessages { MAP.put(INAPPLICABLE_LATEINIT_MODIFIER_PRIMITIVE, "''lateinit'' modifier is not allowed on primitive type properties"); 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"); 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 1cb6a92b4f1..d6bca77fb1a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.lexer.JetModifierKeywordToken; import org.jetbrains.kotlin.lexer.JetTokens; @@ -672,22 +673,39 @@ public class DeclarationsChecker { assert propertyAccessorDescriptor != null : "No property accessor descriptor for " + property.getText(); modifiersChecker.checkModifiersForDeclaration(accessor, propertyAccessorDescriptor); } - JetPropertyAccessor getter = property.getGetter(); - PropertyGetterDescriptor getterDescriptor = propertyDescriptor.getGetter(); - JetModifierList getterModifierList = getter != null ? getter.getModifierList() : null; - if (getterModifierList != null && getterDescriptor != null) { - Map tokens = modifiersChecker.getTokensCorrespondingToModifiers(getterModifierList, Sets + checkAccessor(propertyDescriptor, property.getGetter(), propertyDescriptor.getGetter()); + checkAccessor(propertyDescriptor, property.getSetter(), propertyDescriptor.getSetter()); + } + + private void reportVisibilityModifierDiagnostics(Collection tokens, DiagnosticFactory0 diagnostic) { + for (PsiElement token : tokens) { + trace.report(diagnostic.on(token)); + } + } + + private void checkAccessor( + @NotNull PropertyDescriptor propertyDescriptor, + @Nullable JetPropertyAccessor accessor, + @Nullable PropertyAccessorDescriptor accessorDescriptor + ) { + if (accessor == null) return; + JetModifierList accessorModifierList = accessor.getModifierList(); + if (accessorModifierList != null && accessorDescriptor != null) { + Map tokens = modifiersChecker.getTokensCorrespondingToModifiers(accessorModifierList, Sets .newHashSet(JetTokens.PUBLIC_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PRIVATE_KEYWORD, JetTokens.INTERNAL_KEYWORD)); - if (getterDescriptor.getVisibility() != propertyDescriptor.getVisibility()) { - for (PsiElement token : tokens.values()) { - trace.report(Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY.on(token)); + if (accessor.isGetter()) { + if (accessorDescriptor.getVisibility() != propertyDescriptor.getVisibility()) { + reportVisibilityModifierDiagnostics(tokens.values(), Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY); + } + else { + reportVisibilityModifierDiagnostics(tokens.values(), Errors.REDUNDANT_MODIFIER_IN_GETTER); } } - else { - for (PsiElement token : tokens.values()) { - trace.report(Errors.REDUNDANT_MODIFIER_IN_GETTER.on(token)); - } + else if (accessorDescriptor.getVisibility() == Visibilities.PRIVATE + && propertyDescriptor.getVisibility() != Visibilities.PRIVATE + && propertyDescriptor.isLateInit()) { + reportVisibilityModifierDiagnostics(tokens.values(), Errors.PRIVATE_SETTER_ON_NON_PRIVATE_LATE_INIT_VAR); } } } diff --git a/compiler/testData/diagnostics/tests/LateInit.kt b/compiler/testData/diagnostics/tests/LateInit.kt index a2f60e45140..6b7a126019d 100644 --- a/compiler/testData/diagnostics/tests/LateInit.kt +++ b/compiler/testData/diagnostics/tests/LateInit.kt @@ -13,7 +13,7 @@ public abstract class A(private set fun a() { lateinit var a: String diff --git a/compiler/testData/diagnostics/tests/LateInitSetter.kt b/compiler/testData/diagnostics/tests/LateInitSetter.kt new file mode 100644 index 00000000000..6d1ad1374c1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/LateInitSetter.kt @@ -0,0 +1,16 @@ +class My { + + lateinit var x: String + private set + + lateinit var y: String + // Ok, non-private setter + internal set + + lateinit protected var z: String + private set + + lateinit private var w: String + // Ok, private var + private set +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/LateInitSetter.txt b/compiler/testData/diagnostics/tests/LateInitSetter.txt new file mode 100644 index 00000000000..da7eb07ecf4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/LateInitSetter.txt @@ -0,0 +1,12 @@ +package + +public final class My { + public constructor My() + private final lateinit var w: kotlin.String + public final lateinit var x: kotlin.String + public final lateinit var y: kotlin.String + protected final lateinit var z: 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/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index d3d74d5c149..cc64d2581dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -325,6 +325,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("LateInitSetter.kt") + public void testLateInitSetter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/LateInitSetter.kt"); + doTest(fileName); + } + @TestMetadata("LiteralAsResult.kt") public void testLiteralAsResult() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/LiteralAsResult.kt"); diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index c2d54edf35a..f0ffe84cc46 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -116,6 +116,7 @@ public class QuickFixRegistrar : QuickFixContributor { val removeModifierFactory = RemoveModifierFix.createRemoveModifierFactory() GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY.registerFactory(removeModifierFactory) + PRIVATE_SETTER_ON_NON_PRIVATE_LATE_INIT_VAR.registerFactory(removeModifierFactory) REDUNDANT_MODIFIER_IN_GETTER.registerFactory(removeRedundantModifierFactory) WRONG_MODIFIER_TARGET.registerFactory(removeModifierFactory) REDUNDANT_MODIFIER_FOR_TARGET.registerFactory(removeModifierFactory)