frontend diagnostics for kotlin.jvm.overloads

#KT-7203 Fixed
This commit is contained in:
Dmitry Jemerov
2015-04-01 14:09:37 +02:00
parent 39828bfd32
commit 7b507276ed
11 changed files with 160 additions and 35 deletions
@@ -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) {
@@ -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");
@@ -42,6 +42,10 @@ public interface ErrorsJvm {
DiagnosticFactory0<JetDeclaration> PLATFORM_STATIC_NOT_IN_OBJECT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory1<JetDeclaration, DeclarationDescriptor> PLATFORM_STATIC_ILLEGAL_USAGE = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<JetDeclaration> OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE);
DiagnosticFactory0<JetDeclaration> OVERLOADS_ABSTRACT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<JetDeclaration> OVERLOADS_PRIVATE = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE);
DiagnosticFactory0<JetDeclaration> NATIVE_DECLARATION_CANNOT_BE_ABSTRACT = DiagnosticFactory0.create(ERROR, ABSTRACT_MODIFIER);
DiagnosticFactory0<JetDeclaration> NATIVE_DECLARATION_CANNOT_HAVE_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<JetDeclaration> NATIVE_DECLARATION_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
@@ -0,0 +1,7 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C {
<!OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS!>[kotlin.jvm.overloads] constructor()<!> {
}
<!OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS!>[kotlin.jvm.overloads] fun foo(s: String)<!> {}
}
@@ -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
}
@@ -0,0 +1,8 @@
trait T {
<!OVERLOADS_ABSTRACT!>[kotlin.jvm.overloads] fun foo(s: String = "OK")<!>
}
abstract class C {
<!OVERLOADS_ABSTRACT!>[kotlin.jvm.overloads] abstract fun foo(s: String = "OK")<!>
}
@@ -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
}
@@ -0,0 +1,20 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C {
<!OVERLOADS_PRIVATE!>[kotlin.jvm.overloads] private fun foo(s: String = "OK")<!> {
}
[kotlin.jvm.overloads] fun bar(s: String = "OK") {
}
}
fun foo() {
class D {
<!OVERLOADS_PRIVATE!>[kotlin.jvm.overloads] fun foo(s: String = "OK")<!> {
}
}
val <!UNUSED_VARIABLE!>x<!> = object {
<!OVERLOADS_PRIVATE!>[kotlin.jvm.overloads] fun foo(s: String = "OK")<!> {
}
}
}
@@ -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
}
@@ -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)
@@ -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