Prohibit synchronized methods on interface members
#KT-9310 Fixed
This commit is contained in:
-9
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+20
-3
@@ -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
|
||||
}
|
||||
|
||||
+1
@@ -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
|
||||
|
||||
Vendored
+23
@@ -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) {}
|
||||
}
|
||||
}
|
||||
compiler/testData/diagnostics/testsWithStdLib/annotations/SynchronizedOnInterfaceCompanionMember.txt
Vendored
+19
@@ -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
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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<!>
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java
Generated
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user