diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.java index 6a72cd1814c..b932aa92064 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.java @@ -17,9 +17,11 @@ package org.jetbrains.kotlin.resolve.calls.checkers; import com.intellij.psi.PsiElement; +import com.sun.mirror.declaration.PackageDeclaration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.FunctionTypesKt; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.lexer.KtToken; @@ -50,13 +52,13 @@ import static org.jetbrains.kotlin.resolve.inline.InlineUtil.checkNonLocalReturn class InlineChecker implements CallChecker { private final FunctionDescriptor descriptor; private final Set inlinableParameters = new LinkedHashSet(); - private final boolean isEffectivelyPublicApiFunction; + private final boolean isEffectivelyPublicOrPublishedApiFunction; private final boolean isEffectivelyPrivateApiFunction; public InlineChecker(@NotNull FunctionDescriptor descriptor) { assert InlineUtil.isInline(descriptor) : "This extension should be created only for inline functions: " + descriptor; this.descriptor = descriptor; - this.isEffectivelyPublicApiFunction = DescriptorUtilsKt.isEffectivelyPublicApi(descriptor); + this.isEffectivelyPublicOrPublishedApiFunction = isEffectivelyPublicOrPublishedApi(descriptor); this.isEffectivelyPrivateApiFunction = DescriptorUtilsKt.isEffectivelyPrivateApi(descriptor); for (ValueParameterDescriptor param : descriptor.getValueParameters()) { if (isInlinableParameter(param)) { @@ -242,10 +244,10 @@ class InlineChecker implements CallChecker { @NotNull KtElement expression, @NotNull CallCheckerContext context ) { - boolean declarationDescriptorIsPublicApi = DescriptorUtilsKt.isEffectivelyPublicApi(declarationDescriptor) || - isDefinedInInlineFunction(declarationDescriptor); - if (isEffectivelyPublicApiFunction && - !declarationDescriptorIsPublicApi && + boolean declarationDescriptorIsPublicOrPublishedApi = isEffectivelyPublicOrPublishedApi(declarationDescriptor) || + isDefinedInInlineFunction(declarationDescriptor); + if (isEffectivelyPublicOrPublishedApiFunction && + !declarationDescriptorIsPublicOrPublishedApi && declarationDescriptor.getVisibility() != Visibilities.LOCAL) { context.getTrace().report(Errors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE.on(expression, declarationDescriptor, descriptor)); } @@ -254,6 +256,13 @@ class InlineChecker implements CallChecker { } } + private static boolean isEffectivelyPublicOrPublishedApi(@NotNull CallableDescriptor descriptor) { + EffectiveVisibility visibility = EffectiveVisibilityKt.effectiveVisibility(descriptor, descriptor.getVisibility()); + if (visibility.getPublicApi()) return true; + + return false; + } + private void checkPrivateClassMemberAccess( @NotNull DeclarationDescriptor declarationDescriptor, @NotNull KtElement expression, diff --git a/compiler/testData/diagnostics/tests/inline/nonPublicMember/publishedApi.kt b/compiler/testData/diagnostics/tests/inline/nonPublicMember/publishedApi.kt new file mode 100644 index 00000000000..a6136f84109 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/nonPublicMember/publishedApi.kt @@ -0,0 +1,48 @@ +// !DIAGNOSTICS: -EXPOSED_PARAMETER_TYPE -NOTHING_TO_INLINE + + +inline fun call(a: A) { + a.test() + + privateFun() + + internalFun() +} + +@PublishedApi +internal inline fun callFromPublishedApi(a: A) { + a.test() + + privateFun() + + internalFun() +} + +internal class A { + @PublishedApi + internal fun test() { + test() + + privateFun() + + internalFun() + } + + @PublishedApi + internal fun testInline() { + test() + + privateFun() + + internalFun() + } +} + + +private fun privateFun() { + +} + +internal fun internalFun() { + +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inline/nonPublicMember/publishedApi.txt b/compiler/testData/diagnostics/tests/inline/nonPublicMember/publishedApi.txt new file mode 100644 index 00000000000..b988a37b3b4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/nonPublicMember/publishedApi.txt @@ -0,0 +1,15 @@ +package + +public inline fun call(/*0*/ a: A): kotlin.Unit +@kotlin.PublishedApi internal inline fun callFromPublishedApi(/*0*/ a: A): kotlin.Unit +internal fun internalFun(): kotlin.Unit +private fun privateFun(): kotlin.Unit + +internal final class A { + public constructor A() + 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.PublishedApi internal final fun test(): kotlin.Unit + @kotlin.PublishedApi internal final fun testInline(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inline/publishedApi.kt b/compiler/testData/diagnostics/tests/inline/publishedApi.kt new file mode 100644 index 00000000000..6ff39d4e6be --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/publishedApi.kt @@ -0,0 +1,42 @@ +// !DIAGNOSTICS: -EXPOSED_PARAMETER_TYPE -NOTHING_TO_INLINE +inline fun call(a: A) { + a.test() + testTopLevel() +} + +@PublishedApi +internal class A { + @PublishedApi + internal fun test() { + publicFun() + internalFun() + privateFun() + } + + @PublishedApi + internal inline fun testInline() { + publicFun() + internalFun() + privateFun() + } +} + + +@PublishedApi +internal fun testTopLevel() { + publicFun() + internalFun() +} + +@PublishedApi +inline internal fun testTopLevelInline() { + publicFun() + internalFun() + privateFun() +} + +fun publicFun() {} + +internal fun internalFun() {} + +private fun privateFun() {} diff --git a/compiler/testData/diagnostics/tests/inline/publishedApi.txt b/compiler/testData/diagnostics/tests/inline/publishedApi.txt new file mode 100644 index 00000000000..f67ea45b77e --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/publishedApi.txt @@ -0,0 +1,17 @@ +package + +public inline fun call(/*0*/ a: A): kotlin.Unit +internal fun internalFun(): kotlin.Unit +private fun privateFun(): kotlin.Unit +public fun publicFun(): kotlin.Unit +@kotlin.PublishedApi internal fun testTopLevel(): kotlin.Unit +@kotlin.PublishedApi internal inline fun testTopLevelInline(): kotlin.Unit + +@kotlin.PublishedApi internal final class A { + public constructor A() + 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.PublishedApi internal final fun test(): kotlin.Unit + @kotlin.PublishedApi internal final inline fun testInline(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/multimodule/publishedApiInternal.kt b/compiler/testData/diagnostics/tests/multimodule/publishedApiInternal.kt new file mode 100644 index 00000000000..4d0e81ec47f --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/publishedApiInternal.kt @@ -0,0 +1,56 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE +// MODULE: m1 +// FILE: a.kt + +package p + +public class A { + @PublishedApi + internal val a = A() + @PublishedApi + internal var v = A() + @PublishedApi + internal fun a() = A() + @PublishedApi + internal inner class B +} + +@PublishedApi +internal val a = A() +@PublishedApi +internal var v = A() +@PublishedApi +internal fun a() = A() +@PublishedApi +internal class B + +// MODULE: m2(m1) +// FILE: b.kt + +import p.* + +fun test() { + val _a = a + val _v = v + a() + B() + + val inst = A() + val ia = inst.a + val iv = inst.v + inst.a() + inst.B() +} + +inline fun testInline() { + val _a = a + val _v = v + a() + B() + + val inst = A() + val ia = inst.a + val iv = inst.v + inst.a() + inst.B() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/publishedApiInternal.txt b/compiler/testData/diagnostics/tests/multimodule/publishedApiInternal.txt new file mode 100644 index 00000000000..f917c24cbf0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/publishedApiInternal.txt @@ -0,0 +1,42 @@ +// -- Module: -- +package + +package p { + @kotlin.PublishedApi internal val a: p.A + @kotlin.PublishedApi internal var v: p.A + @kotlin.PublishedApi internal fun a(): p.A + + public final class A { + public constructor A() + @kotlin.PublishedApi internal final val a: p.A + @kotlin.PublishedApi internal final var v: p.A + @kotlin.PublishedApi internal final fun a(): p.A + 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 + + @kotlin.PublishedApi internal final inner class B { + public constructor B() + 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 + } + } + + @kotlin.PublishedApi internal final class B { + public constructor B() + 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 + } +} + + +// -- Module: -- +package + +public fun test(): kotlin.Unit +public inline fun testInline(): kotlin.Unit + +package p { +} diff --git a/compiler/testData/diagnostics/tests/publishedApi.txt b/compiler/testData/diagnostics/tests/publishedApi.txt new file mode 100644 index 00000000000..8bbf6e203f1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/publishedApi.txt @@ -0,0 +1,54 @@ +package + +@kotlin.PublishedApi public var ap: kotlin.Int +@kotlin.PublishedApi internal var bp: kotlin.Int +@kotlin.PublishedApi internal var c: kotlin.Int +@kotlin.PublishedApi public fun a(): kotlin.Unit +@kotlin.PublishedApi internal fun b(): kotlin.Unit +@kotlin.PublishedApi internal fun c(): kotlin.Unit + +@kotlin.PublishedApi public final class A { + public constructor A() + 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 +} + +@kotlin.PublishedApi internal final class B { + public constructor B() + 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 +} + +@kotlin.PublishedApi private final class C { + public constructor C() + 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 final class D { + @kotlin.PublishedApi public constructor D() + @kotlin.PublishedApi public constructor D(/*0*/ a: kotlin.String) + @kotlin.PublishedApi private constructor D(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String) + @kotlin.PublishedApi internal constructor D(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String, /*2*/ c: 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 + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class E { + public constructor E() + @kotlin.PublishedApi public final val ap: kotlin.Int = 1 + @kotlin.PublishedApi internal final val bp: kotlin.Int = 1 + @kotlin.PublishedApi protected final val c: kotlin.Int = 1 + @kotlin.PublishedApi private final val d: kotlin.Int = 1 + @kotlin.PublishedApi public final fun a(): kotlin.Unit + @kotlin.PublishedApi internal final fun b(): kotlin.Unit + @kotlin.PublishedApi private final fun c(): kotlin.Unit + @kotlin.PublishedApi protected final fun d(): kotlin.Unit + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 52ea0e9b4e2..ee8439452b2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10618,6 +10618,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("publishedApi.kt") + public void testPublishedApi() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/publishedApi.kt"); + doTest(fileName); + } + @TestMetadata("recursion.kt") public void testRecursion() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/recursion.kt"); @@ -10895,6 +10901,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/nonPublicMember/localFun.kt"); doTest(fileName); } + + @TestMetadata("publishedApi.kt") + public void testPublishedApi() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/nonPublicMember/publishedApi.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/inline/property") @@ -12328,6 +12340,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("publishedApiInternal.kt") + public void testPublishedApiInternal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multimodule/publishedApiInternal.kt"); + doTest(fileName); + } + @TestMetadata("varargConflict.kt") public void testVarargConflict() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multimodule/varargConflict.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index fa90f446a54..1528d5a705c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -291,6 +291,7 @@ public abstract class KotlinBuiltIns { public final FqName repeatable = annotationName("Repeatable"); public final FqName mustBeDocumented = annotationName("MustBeDocumented"); public final FqName unsafeVariance = fqName("UnsafeVariance"); + public final FqName publishedApi = fqName("PublishedApi"); public final FqName iterator = collectionsFqName("Iterator"); public final FqName iterable = collectionsFqName("Iterable");