Prohibit synchronized methods on interface members

#KT-9310 Fixed
This commit is contained in:
Alexander Udalov
2019-08-13 10:05:11 +02:00
parent 815ff3c860
commit 312205f376
11 changed files with 88 additions and 16 deletions
@@ -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
}
}
@@ -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
}
@@ -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");
@@ -49,6 +49,7 @@ public interface ErrorsJvm {
DiagnosticFactory0<KtAnnotationEntry> VOLATILE_ON_DELEGATE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> SYNCHRONIZED_ON_ABSTRACT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtElement> SYNCHRONIZED_ON_INLINE = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtAnnotationEntry> SYNCHRONIZED_IN_INTERFACE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtAnnotationEntry> OVERLOADS_ABSTRACT = DiagnosticFactory0.create(ERROR);
@@ -1,13 +1,17 @@
import kotlin.jvm.Synchronized
interface My {
<!SYNCHRONIZED_ON_ABSTRACT!>@Synchronized<!> fun foo()
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> fun foo()
@Synchronized fun bar() = 1
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> fun bar() = 1
@Synchronized fun baz(): String {
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> fun baz(): String {
return "abc"
}
var v: String
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> get() = ""
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> set(<!UNUSED_PARAMETER!>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(<!UNUSED_PARAMETER!>value<!>) {}
}
@Synchronized fun gav() = 1
@@ -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
@@ -0,0 +1,23 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !LANGUAGE: +JvmStaticInInterface
// !JVM_TARGET: 1.8
interface I {
companion object {
@Synchronized fun syncFun() {}
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> @JvmStatic fun syncFunJvmStatic() {}
var syncProp: String
@Synchronized get() = ""
@Synchronized set(value) {}
@JvmStatic var syncPropJvmStatic: String
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> get() = ""
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> set(value) {}
var syncPropJvmStaticAccessors: String
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> @JvmStatic get() = ""
<!SYNCHRONIZED_IN_INTERFACE!>@Synchronized<!> @JvmStatic set(value) {}
}
}
@@ -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
}
}
@@ -5,7 +5,7 @@ interface Test {
<!INAPPLICABLE_JVM_NAME!>@get:JvmName("1")<!>
val b: Int
<!SYNCHRONIZED_ON_ABSTRACT!>@get:Synchronized<!>
<!SYNCHRONIZED_IN_INTERFACE!>@get:Synchronized<!>
val c: Int
<!OVERLOADS_INTERFACE, WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET!>@get:JvmOverloads<!>
@@ -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");
@@ -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");