diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt index de0a6f5d4e2..6694da5f4cc 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmFieldApplicabilityChecker.kt @@ -28,7 +28,6 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext -import org.jetbrains.kotlin.resolve.isInlineClassType import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation import org.jetbrains.kotlin.resolve.jvm.checkers.JvmFieldApplicabilityChecker.Problem.* import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm @@ -112,12 +111,4 @@ class JvmFieldApplicabilityChecker : DeclarationChecker { private fun PropertyDescriptor.hasBackingField(bindingContext: BindingContext) = bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, this) ?: false - - private fun PropertyDescriptor.isInsideCompanionObjectOfInterface(): Boolean { - val containingClass = containingDeclaration as? ClassDescriptor ?: return false - if (!DescriptorUtils.isCompanionObject(containingClass)) return false - - val outerClassKind = (containingClass.containingDeclaration as? ClassDescriptor)?.kind - return outerClassKind == ClassKind.INTERFACE || outerClassKind == ClassKind.ANNOTATION_CLASS - } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/declarationCheckers.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/declarationCheckers.kt index 97a94dd37e7..3476e20380a 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/declarationCheckers.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/declarationCheckers.kt @@ -34,8 +34,8 @@ import org.jetbrains.kotlin.resolve.calls.components.isActualParameterWithCorres import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor +import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor import org.jetbrains.kotlin.resolve.inline.InlineUtil -import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.resolve.jvm.annotations.VOLATILE_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation import org.jetbrains.kotlin.resolve.jvm.annotations.findSynchronizedAnnotation @@ -183,11 +183,20 @@ class VolatileAnnotationChecker : DeclarationChecker { class SynchronizedAnnotationChecker : DeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { val synchronizedAnnotation = descriptor.findSynchronizedAnnotation() - if (synchronizedAnnotation != null && descriptor is FunctionDescriptor && descriptor.modality == Modality.ABSTRACT) { + if (synchronizedAnnotation != null && descriptor is FunctionDescriptor) { val annotationEntry = DescriptorToSourceUtils.getSourceFromAnnotation(synchronizedAnnotation) ?: return - context.trace.report(ErrorsJvm.SYNCHRONIZED_ON_ABSTRACT.on(annotationEntry)) + if (isInInterface(descriptor)) { + context.trace.report(ErrorsJvm.SYNCHRONIZED_IN_INTERFACE.on(annotationEntry)) + } else if (descriptor.modality == Modality.ABSTRACT) { + context.trace.report(ErrorsJvm.SYNCHRONIZED_ON_ABSTRACT.on(annotationEntry)) + } } } + + private fun isInInterface(descriptor: FunctionDescriptor): Boolean = + DescriptorUtils.isInterface(descriptor.containingDeclaration) || + (descriptor.propertyIfAccessor.isInsideCompanionObjectOfInterface() && + (descriptor.hasJvmStaticAnnotation() || descriptor.propertyIfAccessor.hasJvmStaticAnnotation())) } class OverloadsAnnotationChecker: DeclarationChecker { @@ -245,3 +254,11 @@ class TypeParameterBoundIsNotArrayChecker : DeclarationChecker { } } } + +internal fun CallableMemberDescriptor.isInsideCompanionObjectOfInterface(): Boolean { + val containingClass = containingDeclaration as? ClassDescriptor ?: return false + if (!DescriptorUtils.isCompanionObject(containingClass)) return false + + val outerClassKind = (containingClass.containingDeclaration as? ClassDescriptor)?.kind + return outerClassKind == ClassKind.INTERFACE || outerClassKind == ClassKind.ANNOTATION_CLASS +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 878ad264595..a9d7d220086 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -58,6 +58,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(VOLATILE_ON_DELEGATE, "'@Volatile' annotation cannot be used on delegated properties"); MAP.put(SYNCHRONIZED_ON_ABSTRACT, "'@Synchronized' annotation cannot be used on abstract functions"); MAP.put(SYNCHRONIZED_ON_INLINE, "'@Synchronized' annotation has no effect on inline functions"); + MAP.put(SYNCHRONIZED_IN_INTERFACE, "'@Synchronized' annotation cannot be used on interface members"); MAP.put(EXTERNAL_DECLARATION_CANNOT_BE_ABSTRACT, "External declaration can not be abstract"); MAP.put(EXTERNAL_DECLARATION_CANNOT_HAVE_BODY, "External declaration can not have a body"); MAP.put(EXTERNAL_DECLARATION_IN_INTERFACE, "Members of interfaces can not be external"); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 76d86ed968e..42041273cfb 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -49,6 +49,7 @@ public interface ErrorsJvm { DiagnosticFactory0 VOLATILE_ON_DELEGATE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SYNCHRONIZED_ON_ABSTRACT = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SYNCHRONIZED_ON_INLINE = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 SYNCHRONIZED_IN_INTERFACE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 OVERLOADS_ABSTRACT = DiagnosticFactory0.create(ERROR); diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.kt index c5a3f130a50..8f98e552660 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.kt @@ -1,13 +1,17 @@ import kotlin.jvm.Synchronized interface My { - @Synchronized fun foo() + @Synchronized fun foo() - @Synchronized fun bar() = 1 + @Synchronized fun bar() = 1 - @Synchronized fun baz(): String { + @Synchronized fun baz(): String { return "abc" } + + var v: String + @Synchronized get() = "" + @Synchronized set(value) {} } abstract class Your { @@ -18,6 +22,10 @@ abstract class Your { @Synchronized open fun baz(): String { return "xyz" } + + var v: String + @Synchronized get() = "" + @Synchronized set(value) {} } @Synchronized fun gav() = 1 diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.txt index 85eeb6ef2e7..298738b2ee9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.txt @@ -3,6 +3,7 @@ package @kotlin.jvm.Synchronized public fun gav(): kotlin.Int public interface My { + @get:kotlin.jvm.Synchronized @set:kotlin.jvm.Synchronized public open var v: kotlin.String @kotlin.jvm.Synchronized public open fun bar(): kotlin.Int @kotlin.jvm.Synchronized public open fun baz(): kotlin.String public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean @@ -13,6 +14,7 @@ public interface My { public abstract class Your { public constructor Your() + @get:kotlin.jvm.Synchronized @set:kotlin.jvm.Synchronized public final var v: kotlin.String @kotlin.jvm.Synchronized public final fun bar(): kotlin.Int @kotlin.jvm.Synchronized public open fun baz(): kotlin.String public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.kt new file mode 100644 index 00000000000..cd1f6028ee2 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.kt @@ -0,0 +1,23 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: +JvmStaticInInterface +// !JVM_TARGET: 1.8 + +interface I { + companion object { + @Synchronized fun syncFun() {} + + @Synchronized @JvmStatic fun syncFunJvmStatic() {} + + var syncProp: String + @Synchronized get() = "" + @Synchronized set(value) {} + + @JvmStatic var syncPropJvmStatic: String + @Synchronized get() = "" + @Synchronized set(value) {} + + var syncPropJvmStaticAccessors: String + @Synchronized @JvmStatic get() = "" + @Synchronized @JvmStatic set(value) {} + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.txt new file mode 100644 index 00000000000..0bf2df744d4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.txt @@ -0,0 +1,19 @@ +package + +public interface I { + 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 companion object Companion { + private constructor Companion() + @get:kotlin.jvm.Synchronized @set:kotlin.jvm.Synchronized public final var syncProp: kotlin.String + @kotlin.jvm.JvmStatic @get:kotlin.jvm.Synchronized @set:kotlin.jvm.Synchronized public final var syncPropJvmStatic: kotlin.String + @get:kotlin.jvm.Synchronized @get:kotlin.jvm.JvmStatic @set:kotlin.jvm.Synchronized @set:kotlin.jvm.JvmStatic public final var syncPropJvmStaticAccessors: 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 + @kotlin.jvm.Synchronized public final fun syncFun(): kotlin.Unit + @kotlin.jvm.Synchronized @kotlin.jvm.JvmStatic public final fun syncFunJvmStatic(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/annotationsOnUseSiteTargets.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/annotationsOnUseSiteTargets.kt index d3ca26e8032..4967b873b6f 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/annotationsOnUseSiteTargets.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/annotationsOnUseSiteTargets.kt @@ -5,7 +5,7 @@ interface Test { @get:JvmName("1") val b: Int - @get:Synchronized + @get:Synchronized val c: Int @get:JvmOverloads diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 4955e12400b..aab7de620c2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -206,6 +206,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.kt"); } + @TestMetadata("SynchronizedOnInterfaceCompanionMember.kt") + public void testSynchronizedOnInterfaceCompanionMember() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.kt"); + } + @TestMetadata("targetuse.kt") public void testTargetuse() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index ecff3d1f8f9..0bc867591f4 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -206,6 +206,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/Synchronized.kt"); } + @TestMetadata("SynchronizedOnInterfaceCompanionMember.kt") + public void testSynchronizedOnInterfaceCompanionMember() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.kt"); + } + @TestMetadata("targetuse.kt") public void testTargetuse() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/targetuse.kt");