diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 60d6ffcaa24..4ba5be461fe 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -462,6 +462,8 @@ public interface Errors { DiagnosticFactory0 MUST_BE_INITIALIZED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 MUST_BE_INITIALIZED_OR_BE_ABSTRACT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory0 EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT = + DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 UNNECESSARY_LATEINIT = DiagnosticFactory0.create(WARNING, LATEINIT_MODIFIER); DiagnosticFactory0 EXTENSION_PROPERTY_WITH_BACKING_FIELD = 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 baf8cf61690..f048c18a8ca 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -231,6 +231,7 @@ public class DefaultErrorMessages { MAP.put(BACKING_FIELD_IN_INTERFACE, "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"); + MAP.put(EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, "Extension property must have accessors or be abstract"); MAP.put(UNNECESSARY_LATEINIT, "Lateinit is unnecessary: definitely initialized in constructors"); MAP.put(PROPERTY_INITIALIZER_IN_INTERFACE, "Property initializers are not allowed in interfaces"); MAP.put(PRIVATE_PROPERTY_IN_INTERFACE, "Abstract property in an interface cannot be private"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index 00d05374773..bea371ae3ed 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -668,19 +668,19 @@ class DeclarationsChecker( val hasAccessorImplementation = propertyDescriptor.hasAccessorImplementation() val containingDeclaration = propertyDescriptor.containingDeclaration - val inTrait = containingDeclaration is ClassDescriptor && containingDeclaration.kind == ClassKind.INTERFACE + val inInterface = DescriptorUtils.isInterface(containingDeclaration) if (propertyDescriptor.modality == Modality.ABSTRACT) { if (!property.hasDelegateExpressionOrInitializer() && property.typeReference == null) { trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property)) } - if (inTrait && property.hasModifier(KtTokens.PRIVATE_KEYWORD) && !property.hasModifier(KtTokens.ABSTRACT_KEYWORD)) { + if (inInterface && property.hasModifier(KtTokens.PRIVATE_KEYWORD) && !property.hasModifier(KtTokens.ABSTRACT_KEYWORD)) { trace.report(PRIVATE_PROPERTY_IN_INTERFACE.on(property)) } return } val backingFieldRequired = trace.bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor) ?: false - if (inTrait && backingFieldRequired && hasAccessorImplementation) { + if (inInterface && backingFieldRequired && hasAccessorImplementation) { trace.report(BACKING_FIELD_IN_INTERFACE.on(property)) } @@ -688,7 +688,7 @@ class DeclarationsChecker( val delegate = property.delegate val isHeader = propertyDescriptor.isHeader if (initializer != null) { - if (inTrait) { + if (inInterface) { trace.report(PROPERTY_INITIALIZER_IN_INTERFACE.on(initializer)) } else if (isHeader) { @@ -702,15 +702,18 @@ class DeclarationsChecker( } } else if (delegate != null) { - if (inTrait) { + if (inInterface) { trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate)) } } else { val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false val isExternal = propertyDescriptor.isEffectivelyExternal() - if (backingFieldRequired && !inTrait && !propertyDescriptor.isLateInit && !isHeader && isUninitialized && !isExternal) { - if (containingDeclaration !is ClassDescriptor || hasAccessorImplementation) { + if (backingFieldRequired && !inInterface && !propertyDescriptor.isLateInit && !isHeader && isUninitialized && !isExternal) { + if (propertyDescriptor.extensionReceiverParameter != null && !hasAccessorImplementation) { + trace.report(EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT.on(property)) + } + else if (containingDeclaration !is ClassDescriptor || hasAccessorImplementation) { trace.report(MUST_BE_INITIALIZED.on(property)) } else { @@ -723,7 +726,7 @@ class DeclarationsChecker( else if (noExplicitTypeOrGetterType(property)) { trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property)) } - if (backingFieldRequired && !inTrait && propertyDescriptor.isLateInit && !isUninitialized && + if (backingFieldRequired && !inInterface && propertyDescriptor.isLateInit && !isUninitialized && trace[MUST_BE_LATEINIT, propertyDescriptor] != true) { trace.report(UNNECESSARY_LATEINIT.on(property)) } diff --git a/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt b/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt index 718d0585865..634e3be3390 100644 --- a/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt +++ b/compiler/testData/diagnostics/tests/extensions/ExtensionFunctions.kt @@ -36,7 +36,7 @@ fun test() { val Int.abs : Int get() = if (this > 0) this else -this; -val T.foo : T +val T.foo : T fun Int.foo() = this diff --git a/compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.kt b/compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.kt new file mode 100644 index 00000000000..e3e81475d4d --- /dev/null +++ b/compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.kt @@ -0,0 +1,25 @@ +val String.test1: Int +var String.test2: Int + +var String.test3: Int; public set + +class C { + val String.test1: Int + var String.test2: Int + var String.test3: Int; public set +} + +interface I { + val String.test1: Int + var String.test2: Int + var String.test3: Int; public set +} + +abstract class A { + val String.test1: Int + var String.test2: Int + var String.test3: Int; public set + + abstract val String.testA1: Int + abstract var String.testA2: Int +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.txt b/compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.txt new file mode 100644 index 00000000000..14fd17b3b4c --- /dev/null +++ b/compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.txt @@ -0,0 +1,36 @@ +package + +public val kotlin.String.test1: kotlin.Int +public var kotlin.String.test2: kotlin.Int +public var kotlin.String.test3: kotlin.Int + +public abstract class A { + public constructor A() + public final val kotlin.String.test1: kotlin.Int + public final var kotlin.String.test2: kotlin.Int + public final var kotlin.String.test3: kotlin.Int + public abstract val kotlin.String.testA1: kotlin.Int + public abstract var kotlin.String.testA2: 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 +} + +public final class C { + public constructor C() + public final val kotlin.String.test1: kotlin.Int + public final var kotlin.String.test2: kotlin.Int + public final var kotlin.String.test3: 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 +} + +public interface I { + public abstract val kotlin.String.test1: kotlin.Int + public abstract var kotlin.String.test2: kotlin.Int + public abstract var kotlin.String.test3: 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 +} diff --git a/compiler/testData/diagnostics/tests/script/topLevelPropertiesWithGetSet.kts b/compiler/testData/diagnostics/tests/script/topLevelPropertiesWithGetSet.kts index 6d1fb5ea652..40d2fcc8b97 100644 --- a/compiler/testData/diagnostics/tests/script/topLevelPropertiesWithGetSet.kts +++ b/compiler/testData/diagnostics/tests/script/topLevelPropertiesWithGetSet.kts @@ -34,6 +34,6 @@ set(value) {} get() = 42 set(value) {} -var String.testExtVarLineBreakSemi: Int; +var String.testExtVarLineBreakSemi: Int; get() = 42 set(value) {} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index c2e1ba64bdc..21e2f510b2e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -15838,6 +15838,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/properties"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("extensionPropertyMustHaveAccessorsOrBeAbstract.kt") + public void testExtensionPropertyMustHaveAccessorsOrBeAbstract() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/diagnostics/tests/properties/inferenceFromGetters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)