Skip to content

Commit

Permalink
KT-43525 forbid @jvmoverloads on mangled funs and hidden constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
dnpetrov committed Nov 24, 2020
1 parent ca78261 commit 3a166f3
Show file tree
Hide file tree
Showing 18 changed files with 219 additions and 27 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.jvm.isInlineClassThatRequiresMangling
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForParameterTypes
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
import org.jetbrains.kotlin.resolve.jvm.shouldHideConstructorDueToInlineClassTypeValueParameters

class LocalFunInlineChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (InlineUtil.isInline(descriptor) &&
declaration is KtNamedFunction &&
descriptor is FunctionDescriptor &&
descriptor.visibility == DescriptorVisibilities.LOCAL) {
descriptor.visibility == DescriptorVisibilities.LOCAL
) {
context.trace.report(Errors.NOT_YET_SUPPORTED_IN_INLINE.on(declaration, "Local inline functions"))
}
}
Expand All @@ -66,7 +68,8 @@ class JvmStaticChecker(jvmTarget: JvmTarget, languageVersionSettings: LanguageVe
if (declaration is KtNamedFunction ||
declaration is KtProperty ||
declaration is KtPropertyAccessor ||
declaration is KtParameter) {
declaration is KtParameter
) {
checkDeclaration(declaration, descriptor, context.trace)
}
}
Expand All @@ -85,7 +88,8 @@ class JvmStaticChecker(jvmTarget: JvmTarget, languageVersionSettings: LanguageVe
if (!insideObject || insideCompanionObjectInInterface) {
if (insideCompanionObjectInInterface &&
supportJvmStaticInInterface &&
descriptor is DeclarationDescriptorWithVisibility) {
descriptor is DeclarationDescriptorWithVisibility
) {
checkVisibility(descriptor, diagnosticHolder, declaration)
if (isLessJVM18) {
diagnosticHolder.report(ErrorsJvm.JVM_STATIC_IN_INTERFACE_1_6.on(declaration))
Expand Down Expand Up @@ -201,7 +205,7 @@ class SynchronizedAnnotationChecker : DeclarationChecker {
(descriptor.hasJvmStaticAnnotation() || descriptor.propertyIfAccessor.hasJvmStaticAnnotation()))
}

class OverloadsAnnotationChecker: DeclarationChecker {
class OverloadsAnnotationChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
descriptor.findJvmOverloadsAnnotation()?.let { annotation ->
val annotationEntry = DescriptorToSourceUtils.getSourceFromAnnotation(annotation)
Expand All @@ -218,35 +222,49 @@ class OverloadsAnnotationChecker: DeclarationChecker {
) {
val diagnosticHolder = context.trace

if (descriptor !is CallableDescriptor) {
return
} else if ((descriptor.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.INTERFACE) {
diagnosticHolder.report(ErrorsJvm.OVERLOADS_INTERFACE.on(annotationEntry))
} else if (descriptor is FunctionDescriptor && descriptor.modality == Modality.ABSTRACT) {
diagnosticHolder.report(ErrorsJvm.OVERLOADS_ABSTRACT.on(annotationEntry))
} else if (DescriptorUtils.isLocal(descriptor)) {
diagnosticHolder.report(ErrorsJvm.OVERLOADS_LOCAL.on(annotationEntry))
} else if (descriptor.isAnnotationConstructor()) {
val diagnostic =
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitJvmOverloadsOnConstructorsOfAnnotationClasses))
ErrorsJvm.OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR
else
ErrorsJvm.OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR_WARNING

diagnosticHolder.report(diagnostic.on(annotationEntry))
} else if (!descriptor.visibility.isPublicAPI && descriptor.visibility != DescriptorVisibilities.INTERNAL) {
diagnosticHolder.report(ErrorsJvm.OVERLOADS_PRIVATE.on(annotationEntry))
} else if (descriptor.valueParameters.none { it.declaresDefaultValue() || it.isActualParameterWithCorrespondingExpectedDefault }) {
diagnosticHolder.report(ErrorsJvm.OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS.on(annotationEntry))
if (descriptor !is CallableDescriptor) return

when {
(descriptor.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.INTERFACE ->
diagnosticHolder.report(ErrorsJvm.OVERLOADS_INTERFACE.on(annotationEntry))

descriptor is FunctionDescriptor && descriptor.modality == Modality.ABSTRACT ->
diagnosticHolder.report(ErrorsJvm.OVERLOADS_ABSTRACT.on(annotationEntry))

DescriptorUtils.isLocal(descriptor) ->
diagnosticHolder.report(ErrorsJvm.OVERLOADS_LOCAL.on(annotationEntry))

descriptor.isAnnotationConstructor() -> {
val diagnostic =
if (context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitJvmOverloadsOnConstructorsOfAnnotationClasses))
ErrorsJvm.OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR
else
ErrorsJvm.OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR_WARNING

diagnosticHolder.report(diagnostic.on(annotationEntry))
}

!descriptor.visibility.isPublicAPI && descriptor.visibility != DescriptorVisibilities.INTERNAL ->
diagnosticHolder.report(ErrorsJvm.OVERLOADS_PRIVATE.on(annotationEntry))

descriptor.valueParameters.none { it.declaresDefaultValue() || it.isActualParameterWithCorrespondingExpectedDefault } ->
diagnosticHolder.report(ErrorsJvm.OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS.on(annotationEntry))

descriptor is SimpleFunctionDescriptor &&
(requiresFunctionNameManglingForParameterTypes(descriptor) || requiresFunctionNameManglingForReturnType(descriptor)) ->
diagnosticHolder.report(ErrorsJvm.OVERLOADS_ANNOTATION_MANGLED_FUNCTION.on(annotationEntry))

descriptor is ClassConstructorDescriptor && shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor) ->
diagnosticHolder.report(ErrorsJvm.OVERLOADS_ANNOTATION_HIDDEN_CONSTRUCTOR.on(annotationEntry))
}
}
}

class TypeParameterBoundIsNotArrayChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
val typeParameters = (descriptor as? CallableDescriptor)?.typeParameters
?: (descriptor as? ClassDescriptor)?.declaredTypeParameters
?: return
?: (descriptor as? ClassDescriptor)?.declaredTypeParameters
?: return

for (typeParameter in typeParameters) {
if (typeParameter.upperBounds.any { KotlinBuiltIns.isArray(it) || KotlinBuiltIns.isPrimitiveArray(it) }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(OVERLOADS_LOCAL, "'@JvmOverloads' annotation cannot be used on local declarations");
MAP.put(OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR_WARNING, "'@JvmOverloads' annotation on constructors of annotation classes is deprecated");
MAP.put(OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR, "'@JvmOverloads' annotation cannot be used on constructors of annotation classes");
MAP.put(OVERLOADS_ANNOTATION_HIDDEN_CONSTRUCTOR, "'@JvmOverloads' annotation cannot be used on constructors hidden by inline class rules");
MAP.put(OVERLOADS_ANNOTATION_MANGLED_FUNCTION, "'@JvmOverloads' annotation cannot be used on functions mangled by inline class rules");
MAP.put(INAPPLICABLE_JVM_NAME, "'@JvmName' annotation is not applicable to this declaration");
MAP.put(ILLEGAL_JVM_NAME, "Illegal JVM name");

MAP.put(VOLATILE_ON_VALUE, "'@Volatile' annotation cannot be used on immutable properties");
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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ public interface ErrorsJvm {
DiagnosticFactory0<KtAnnotationEntry> OVERLOADS_LOCAL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR_WARNING = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtAnnotationEntry> OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);

DiagnosticFactory0<KtAnnotationEntry> OVERLOADS_ANNOTATION_MANGLED_FUNCTION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> OVERLOADS_ANNOTATION_HIDDEN_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);

DiagnosticFactory0<KtDeclaration> EXTERNAL_DECLARATION_CANNOT_BE_ABSTRACT = DiagnosticFactory0.create(ERROR, ABSTRACT_MODIFIER);
DiagnosticFactory0<KtDeclaration> EXTERNAL_DECLARATION_CANNOT_HAVE_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME

inline class Str(val s: String)

@JvmOverloads
fun test(so: String = "O", sk: String = "K") = Str(so + sk)

fun box(): String =
test().s
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// WITH_RUNTIME

inline class Z(val x: Int)

@JvmOverloads
fun testTopLevelFunction(x: Int = 0): Z = Z(x)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@kotlin.Metadata
public final class JvmOverloadsOnTopLevelFunctionReturningInlineClassValueKt {
// source: 'jvmOverloadsOnTopLevelFunctionReturningInlineClassValue.kt'
public synthetic static method testTopLevelFunction$default(p0: int, p1: int, p2: java.lang.Object): int
public final static @kotlin.jvm.JvmOverloads method testTopLevelFunction(): int
public final static @kotlin.jvm.JvmOverloads method testTopLevelFunction(p0: int): int
}

@kotlin.Metadata
public final class Z {
// source: 'jvmOverloadsOnTopLevelFunctionReturningInlineClassValue.kt'
private final field x: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): Z
public static method constructor-impl(p0: int): int
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int): boolean
public final method getX(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !LANGUAGE: +InlineClasses

inline class Z(val x: Int)

@JvmOverloads
fun testTopLevelFunction1(z: Z, x: Int = 0) {}

@JvmOverloads
fun testTopLevelFunction2(x: Int, z: Z = Z(0)) {}

@JvmOverloads
fun testTopLevelFunction3(x: Int = 0): Z = Z(x)

class C {
@JvmOverloads
constructor(i: Int, z: Z = Z(0))

@JvmOverloads
constructor(s: String, z: Z, i: Int = 0)

@JvmOverloads
fun testMemberFunction1(z: Z, x: Int = 0) {}

@JvmOverloads
fun testMemberFunction2(x: Int, z: Z = Z(0)) {}

@JvmOverloads
fun testMemberFunction3(x: Int = 0): Z = Z(x)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !LANGUAGE: +InlineClasses

inline class Z(val x: Int)

<!OVERLOADS_ANNOTATION_MANGLED_FUNCTION!>@JvmOverloads<!>
fun testTopLevelFunction1(z: Z, x: Int = 0) {}

<!OVERLOADS_ANNOTATION_MANGLED_FUNCTION!>@JvmOverloads<!>
fun testTopLevelFunction2(x: Int, z: Z = Z(0)) {}

@JvmOverloads
fun testTopLevelFunction3(x: Int = 0): Z = Z(x)

class C {
<!OVERLOADS_ANNOTATION_HIDDEN_CONSTRUCTOR!>@JvmOverloads<!>
constructor(i: Int, z: Z = Z(0))

<!OVERLOADS_ANNOTATION_HIDDEN_CONSTRUCTOR!>@JvmOverloads<!>
constructor(s: String, z: Z, i: Int = 0)

<!OVERLOADS_ANNOTATION_MANGLED_FUNCTION!>@JvmOverloads<!>
fun testMemberFunction1(z: Z, x: Int = 0) {}

<!OVERLOADS_ANNOTATION_MANGLED_FUNCTION!>@JvmOverloads<!>
fun testMemberFunction2(x: Int, z: Z = Z(0)) {}

<!OVERLOADS_ANNOTATION_MANGLED_FUNCTION!>@JvmOverloads<!>
fun testMemberFunction3(x: Int = 0): Z = Z(x)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package

@kotlin.jvm.JvmOverloads public fun testTopLevelFunction1(/*0*/ z: Z, /*1*/ x: kotlin.Int = ...): kotlin.Unit
@kotlin.jvm.JvmOverloads public fun testTopLevelFunction2(/*0*/ x: kotlin.Int, /*1*/ z: Z = ...): kotlin.Unit
@kotlin.jvm.JvmOverloads public fun testTopLevelFunction3(/*0*/ x: kotlin.Int = ...): Z

public final class C {
@kotlin.jvm.JvmOverloads public constructor C(/*0*/ i: kotlin.Int, /*1*/ z: Z = ...)
@kotlin.jvm.JvmOverloads public constructor C(/*0*/ s: kotlin.String, /*1*/ z: Z, /*2*/ i: kotlin.Int = ...)
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.JvmOverloads public final fun testMemberFunction1(/*0*/ z: Z, /*1*/ x: kotlin.Int = ...): kotlin.Unit
@kotlin.jvm.JvmOverloads public final fun testMemberFunction2(/*0*/ x: kotlin.Int, /*1*/ z: Z = ...): kotlin.Unit
@kotlin.jvm.JvmOverloads public final fun testMemberFunction3(/*0*/ x: kotlin.Int = ...): Z
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}

public final inline class Z {
public constructor Z(/*0*/ x: kotlin.Int)
public final val x: kotlin.Int
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 3a166f3

Please sign in to comment.