diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt index b7c5fa4acd6..a2fb9f179b3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt @@ -16,50 +16,42 @@ package org.jetbrains.kotlin.load.kotlin -import org.jetbrains.kotlin.resolve.AdditionalCheckerProvider -import org.jetbrains.kotlin.resolve.DeclarationChecker -import org.jetbrains.kotlin.resolve.annotations.hasPlatformStaticAnnotation -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm -import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.DiagnosticSink -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.resolve.annotations.hasInlineAnnotation -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.annotations.hasIntrinsicAnnotation -import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeFunChecker -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.resolve.jvm.calls.checkers.NeedSyntheticChecker -import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker -import org.jetbrains.kotlin.types.JetType -import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable +import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNotNull -import org.jetbrains.kotlin.types.isFlexible -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory -import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NullabilityInformationSource -import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor -import org.jetbrains.kotlin.lexer.JetModifierKeywordToken +import org.jetbrains.kotlin.load.java.lazy.types.isMarkedNullable +import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeFunChecker +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.annotations.hasInlineAnnotation +import org.jetbrains.kotlin.resolve.annotations.hasIntrinsicAnnotation +import org.jetbrains.kotlin.resolve.annotations.hasPlatformStaticAnnotation +import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext +import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability -import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.jvm.calls.checkers.NeedSyntheticChecker +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NullabilityInformationSource +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.expressions.SenselessComparisonChecker +import org.jetbrains.kotlin.types.isFlexible public object KotlinJvmCheckerProvider : AdditionalCheckerProvider( - additionalDeclarationCheckers = listOf(PlatformStaticAnnotationChecker(), LocalFunInlineChecker(), ReifiedTypeParameterAnnotationChecker(), NativeFunChecker()), + additionalDeclarationCheckers = listOf(PlatformStaticAnnotationChecker(), + LocalFunInlineChecker(), + ReifiedTypeParameterAnnotationChecker(), + NativeFunChecker(), + OverloadsAnnotationChecker()), additionalCallCheckers = listOf(NeedSyntheticChecker()), additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker()) ) @@ -116,6 +108,30 @@ public class PlatformStaticAnnotationChecker : DeclarationChecker { } } +public class OverloadsAnnotationChecker: DeclarationChecker { + override fun check(declaration: JetDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink) { + if (descriptor.getAnnotations().findAnnotation(FqName("kotlin.jvm.overloads")) != null) { + checkDeclaration(declaration, descriptor, diagnosticHolder) + } + } + + private fun checkDeclaration(declaration: JetDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink) { + if (descriptor !is CallableDescriptor) { + return + } + if (descriptor is FunctionDescriptor && descriptor.getModality() == Modality.ABSTRACT) { + diagnosticHolder.report(ErrorsJvm.OVERLOADS_ABSTRACT.on(declaration)) + } + else if ((!descriptor.getVisibility().isPublicAPI() && descriptor.getVisibility() != Visibilities.INTERNAL) || + DescriptorUtils.isLocal(descriptor)) { + diagnosticHolder.report(ErrorsJvm.OVERLOADS_PRIVATE.on(declaration)) + } + else if (descriptor.getValueParameters().none { it.declaresDefaultValue() }) { + diagnosticHolder.report(ErrorsJvm.OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS.on(declaration)) + } + } +} + public class ReifiedTypeParameterAnnotationChecker : DeclarationChecker { override fun check(declaration: JetDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink) { 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 ea5b71669b7..9087d7dc6f2 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 @@ -48,6 +48,9 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(ErrorsJvm.PLATFORM_STATIC_NOT_IN_OBJECT, "Only functions in named objects and companion objects of classes can be annotated with 'platformStatic'"); MAP.put(ErrorsJvm.OVERRIDE_CANNOT_BE_STATIC, "Override member cannot be 'platformStatic' in object"); MAP.put(ErrorsJvm.PLATFORM_STATIC_ILLEGAL_USAGE, "This declaration does not support ''platformStatic''", DescriptorRenderer.SHORT_NAMES_IN_TYPES); + MAP.put(ErrorsJvm.OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS, "''overloads'' annotation has no effect for methods without default arguments"); + MAP.put(ErrorsJvm.OVERLOADS_ABSTRACT, "''overloads'' annotation cannot be used on abstract methods"); + MAP.put(ErrorsJvm.OVERLOADS_PRIVATE, "''overloads'' annotation has no effect on private and local declarations"); MAP.put(ErrorsJvm.NATIVE_DECLARATION_CANNOT_BE_ABSTRACT, "Native declaration can not be abstract"); MAP.put(ErrorsJvm.NATIVE_DECLARATION_CANNOT_HAVE_BODY, "Native declaration can not have a body"); MAP.put(ErrorsJvm.NATIVE_DECLARATION_IN_TRAIT, "Members of traits can not be native"); 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 5e86539e8bd..ce586c3c61f 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 @@ -42,6 +42,10 @@ public interface ErrorsJvm { DiagnosticFactory0 PLATFORM_STATIC_NOT_IN_OBJECT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory1 PLATFORM_STATIC_ILLEGAL_USAGE = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory0 OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE); + DiagnosticFactory0 OVERLOADS_ABSTRACT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory0 OVERLOADS_PRIVATE = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE); + DiagnosticFactory0 NATIVE_DECLARATION_CANNOT_BE_ABSTRACT = DiagnosticFactory0.create(ERROR, ABSTRACT_MODIFIER); DiagnosticFactory0 NATIVE_DECLARATION_CANNOT_HAVE_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 NATIVE_DECLARATION_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/JvmOverloadWithNoDefaults.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/JvmOverloadWithNoDefaults.kt new file mode 100644 index 00000000000..72ed0c041df --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/JvmOverloadWithNoDefaults.kt @@ -0,0 +1,7 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +class C { + [kotlin.jvm.overloads] constructor() { + } + + [kotlin.jvm.overloads] fun foo(s: String) {} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/JvmOverloadWithNoDefaults.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/JvmOverloadWithNoDefaults.txt new file mode 100644 index 00000000000..94744d9c0cc --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/JvmOverloadWithNoDefaults.txt @@ -0,0 +1,9 @@ +package + +internal final class C { + kotlin.jvm.overloads() public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + kotlin.jvm.overloads() internal final fun foo(/*0*/ s: kotlin.String): kotlin.Unit + 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/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnAbstractMethods.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnAbstractMethods.kt new file mode 100644 index 00000000000..571c6ba0e53 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnAbstractMethods.kt @@ -0,0 +1,8 @@ +trait T { + [kotlin.jvm.overloads] fun foo(s: String = "OK") +} + + +abstract class C { + [kotlin.jvm.overloads] abstract fun foo(s: String = "OK") +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnAbstractMethods.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnAbstractMethods.txt new file mode 100644 index 00000000000..538ad90e3f0 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnAbstractMethods.txt @@ -0,0 +1,16 @@ +package + +internal abstract class C { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + kotlin.jvm.overloads() internal abstract fun foo(/*0*/ s: kotlin.String = ...): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +internal trait T { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + kotlin.jvm.overloads() internal abstract fun foo(/*0*/ s: kotlin.String = ...): kotlin.Unit + 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/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnPrivate.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnPrivate.kt new file mode 100644 index 00000000000..df575fc2c02 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnPrivate.kt @@ -0,0 +1,20 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +class C { + [kotlin.jvm.overloads] private fun foo(s: String = "OK") { + } + + [kotlin.jvm.overloads] fun bar(s: String = "OK") { + } +} + +fun foo() { + class D { + [kotlin.jvm.overloads] fun foo(s: String = "OK") { + } + } + + val x = object { + [kotlin.jvm.overloads] fun foo(s: String = "OK") { + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnPrivate.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnPrivate.txt new file mode 100644 index 00000000000..b05bf25a16d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnPrivate.txt @@ -0,0 +1,11 @@ +package + +internal fun foo(): kotlin.Unit + +internal final class C { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + kotlin.jvm.overloads() private final fun foo(/*0*/ s: kotlin.String = ...): kotlin.Unit + 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/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index cf0d7956814..e5cf965d29f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -55,6 +55,7 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic Annotations.AnnotationApplicability.class, Annotations.AnnotationParameterMustBeConstant.class, Annotations.AnnotationWithVarargParameter.class, + Annotations.JvmOverloads.class, Annotations.PlatformStatic.class, }) @RunWith(JUnit3RunnerWithInners.class) @@ -150,6 +151,33 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmOverloads extends AbstractJetDiagnosticsTestWithStdLib { + public void testAllFilesPresentInJvmOverloads() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("JvmOverloadWithNoDefaults.kt") + public void testJvmOverloadWithNoDefaults() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/JvmOverloadWithNoDefaults.kt"); + doTest(fileName); + } + + @TestMetadata("jvmOverloadsOnAbstractMethods.kt") + public void testJvmOverloadsOnAbstractMethods() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnAbstractMethods.kt"); + doTest(fileName); + } + + @TestMetadata("jvmOverloadsOnPrivate.kt") + public void testJvmOverloadsOnPrivate() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/jvmOverloads/jvmOverloadsOnPrivate.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/platformStatic") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt b/libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt index 4075fabbf02..055795f6598 100644 --- a/libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt +++ b/libraries/stdlib/src/kotlin/jvm/JvmPlatformAnnotations.kt @@ -21,6 +21,9 @@ import java.lang.annotation.RetentionPolicy /** * Instructs the Kotlin compiler to generate overloads for this function that substitute default parameter values. + * + * If a method has N parameters and M of which have default values, M overloads are generated: the first one + * takes N-1 parameters (all but the last one that takes a default value), the second takes N-2 parameters, and so on. */ Retention(RetentionPolicy.SOURCE) public annotation class overloads