From ac514849f40c95675e29c07f2edaa5f405fa70df Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 15 Nov 2022 16:40:15 +0100 Subject: [PATCH] K1: add deprecation for implicit non-public calls #KT-54762 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 6 +++ ...irOldFrontendDiagnosticsTestGenerated.java | 6 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++ .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../resolve/calls/checkers/InlineChecker.kt | 31 ++++++++--- .../inlineDeprecationsOnImplicitCalls.fir.kt | 42 +++++++++++++++ .../inlineDeprecationsOnImplicitCalls.kt | 42 +++++++++++++++ .../inlineDeprecationsOnImplicitCalls.txt | 52 +++++++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ 10 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt create mode 100644 compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.txt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 8426d47ce9a..7064e43ffcf 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -375,6 +375,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/initializedAfterRethrow.kt"); } + @Test + @TestMetadata("inlineDeprecationsOnImplicitCalls.kt") + public void testInlineDeprecationsOnImplicitCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt"); + } + @Test @TestMetadata("InvokeAndRecursiveResolve.kt") public void testInvokeAndRecursiveResolve() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index f3444881001..4d23ae08d57 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -375,6 +375,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/initializedAfterRethrow.kt"); } + @Test + @TestMetadata("inlineDeprecationsOnImplicitCalls.kt") + public void testInlineDeprecationsOnImplicitCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt"); + } + @Test @TestMetadata("InvokeAndRecursiveResolve.kt") public void testInvokeAndRecursiveResolve() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 7f9d1e2d121..4711f1fe4ae 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -375,6 +375,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/initializedAfterRethrow.kt"); } + @Test + @TestMetadata("inlineDeprecationsOnImplicitCalls.kt") + public void testInlineDeprecationsOnImplicitCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt"); + } + @Test @TestMetadata("InvokeAndRecursiveResolve.kt") public void testInvokeAndRecursiveResolve() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 2564c8014e3..7567d6faa31 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -1272,6 +1272,7 @@ public interface Errors { //Inline and inlinable parameters DiagnosticFactory2 NON_PUBLIC_CALL_FROM_PUBLIC_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT); + DiagnosticFactory0 DEPRECATED_IMPLICIT_NON_PUBLIC_API_ACCESS = DiagnosticFactory0.create(WARNING, CALL_ELEMENT); DiagnosticFactory2 PRIVATE_CLASS_MEMBER_FROM_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT); DiagnosticFactory1 NON_LOCAL_RETURN_NOT_ALLOWED = DiagnosticFactory1.create(ERROR, CALL_ELEMENT); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 3306632b06d..e3ad52927a9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -1106,6 +1106,7 @@ public class DefaultErrorMessages { //Inline MAP.put(NON_PUBLIC_CALL_FROM_PUBLIC_INLINE, "Public-API inline function cannot access non-public-API ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES); + MAP.put(DEPRECATED_IMPLICIT_NON_PUBLIC_API_ACCESS, "Deprecated implicit access of non-public-API from public-API inline function"); MAP.put(PRIVATE_CLASS_MEMBER_FROM_INLINE, "Non-private inline function cannot access members of private classes: ''{0}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES); MAP.put(NOT_YET_SUPPORTED_IN_INLINE, "{0} are not yet supported in inline functions", STRING); MAP.put(DECLARATION_CANT_BE_INLINED, "'inline' modifier is not allowed on virtual members. Only private or final members can be inlined"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt index 5506eb5f6ca..b607f8e6cb5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/InlineChecker.kt @@ -91,8 +91,9 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC } } - checkVisibilityAndAccess(targetDescriptor, expression, context, call) - checkRecursion(context, targetDescriptor, expression) + val replacementForReport = (call.dispatchReceiver as? ExpressionReceiver)?.expression + checkVisibilityAndAccess(targetDescriptor, expression, replacementForReport, context, call) + checkRecursion(context, targetDescriptor, expression, replacementForReport) } private fun checkNotInDefaultParameter(context: CallCheckerContext, expression: KtExpression) = @@ -233,10 +234,12 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC private fun checkRecursion( context: CallCheckerContext, targetDescriptor: CallableDescriptor, - expression: KtElement + expression: KtElement, + replacementForReport: KtElement? ) { if (targetDescriptor.original === descriptor) { context.trace.report(RECURSION_IN_INLINE.on(expression, expression, descriptor)) + context.reportDeprecationOnReplacement(expression, replacementForReport) } } @@ -255,6 +258,7 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC private fun checkVisibilityAndAccess( calledDescriptor: CallableDescriptor, expression: KtElement, + replacementForReport: KtElement?, context: CallCheckerContext, call: Call ) { @@ -267,10 +271,12 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC val isInlineFunPublicOrPublishedApi = inlineFunEffectiveVisibility.publicApi if (isInlineFunPublicOrPublishedApi && !isCalledFunPublicOrPublishedApi && - calledDescriptor.visibility !== DescriptorVisibilities.LOCAL) { + calledDescriptor.visibility !== DescriptorVisibilities.LOCAL + ) { context.trace.report(NON_PUBLIC_CALL_FROM_PUBLIC_INLINE.on(expression, calledDescriptor, descriptor)) + context.reportDeprecationOnReplacement(expression, replacementForReport) } else { - checkPrivateClassMemberAccess(calledDescriptor, expression, context) + checkPrivateClassMemberAccess(calledDescriptor, expression, replacementForReport, context) if (isInlineFunPublicOrPublishedApi) { checkSuperCalls(calledDescriptor, call, expression, context) } @@ -280,7 +286,8 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC if ((!isConstructorCall || expression !is KtConstructorCalleeExpression) && isInlineFunPublicOrPublishedApi && inlineFunEffectiveVisibility.toVisibility() !== Visibilities.Protected && - calledFunEffectiveVisibility.toVisibility() === Visibilities.Protected) { + calledFunEffectiveVisibility.toVisibility() === Visibilities.Protected + ) { when { isConstructorCall -> { context.trace.report( @@ -293,17 +300,20 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC ) } } + context.reportDeprecationOnReplacement(expression, replacementForReport) } } private fun checkPrivateClassMemberAccess( declarationDescriptor: DeclarationDescriptor, expression: KtElement, + replacementForReport: KtElement?, context: CallCheckerContext ) { if (!isEffectivelyPrivateApiFunction) { if (declarationDescriptor.isInsidePrivateClass) { context.trace.report(PRIVATE_CLASS_MEMBER_FROM_INLINE.on(expression, declarationDescriptor, descriptor)) + context.reportDeprecationOnReplacement(expression, replacementForReport) } } } @@ -356,4 +366,13 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC context.trace.report(NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage)) } } + + private fun CallCheckerContext.reportDeprecationOnReplacement( + expression: KtElement, + replacementForReport: KtElement? + ) { + if (!expression.isPhysical && replacementForReport != null) { + trace.report(DEPRECATED_IMPLICIT_NON_PUBLIC_API_ACCESS.on(replacementForReport)) + } + } } diff --git a/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.fir.kt b/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.fir.kt new file mode 100644 index 00000000000..e03d896602f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.fir.kt @@ -0,0 +1,42 @@ +// !DIAGNOSTICS: -NOTHING_TO_INLINE + +@PublishedApi +internal class InternalClassPrivateConstructor private constructor() { + companion object { + internal inline operator fun invoke(): InternalClassPrivateConstructor = InternalClassPrivateConstructor() + + internal inline operator fun invoke(i: Int): InternalClassPrivateConstructor = InternalClassPrivateConstructor(i) + } +} + +private class PrivateClass public constructor() { + + operator fun invoke() = 42 +} + +open class OpenClass { + + protected operator fun invoke() = 42 + + inline fun foo() { + this() + } +} + +@PublishedApi +internal open class InternalClassProtectedConstructor protected constructor() { + companion object { + internal inline operator fun invoke(): InternalClassProtectedConstructor = InternalClassProtectedConstructor() + } +} + +inline fun publicInline() { + InternalClassPrivateConstructor() + InternalClassPrivateConstructor.invoke() + InternalClassProtectedConstructor() +} + +internal inline fun internalInline() { + val pc = PrivateClass() + pc() +} diff --git a/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt b/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt new file mode 100644 index 00000000000..4475608d273 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt @@ -0,0 +1,42 @@ +// !DIAGNOSTICS: -NOTHING_TO_INLINE + +@PublishedApi +internal class InternalClassPrivateConstructor private constructor() { + companion object { + internal inline operator fun invoke(): InternalClassPrivateConstructor = InternalClassPrivateConstructor() + + internal inline operator fun invoke(i: Int): InternalClassPrivateConstructor = InternalClassPrivateConstructor(i) + } +} + +private class PrivateClass public constructor() { + + operator fun invoke() = 42 +} + +open class OpenClass { + + protected operator fun invoke() = 42 + + inline fun foo() { + this() + } +} + +@PublishedApi +internal open class InternalClassProtectedConstructor protected constructor() { + companion object { + internal inline operator fun invoke(): InternalClassProtectedConstructor = InternalClassProtectedConstructor() + } +} + +inline fun publicInline() { + InternalClassPrivateConstructor() + InternalClassPrivateConstructor.invoke() + InternalClassProtectedConstructor() +} + +internal inline fun internalInline() { + val pc = PrivateClass() + pc() +} diff --git a/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.txt b/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.txt new file mode 100644 index 00000000000..8734ee85bee --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.txt @@ -0,0 +1,52 @@ +package + +internal inline fun internalInline(): kotlin.Unit +public inline fun publicInline(): kotlin.Unit + +@kotlin.PublishedApi internal final class InternalClassPrivateConstructor { + private constructor InternalClassPrivateConstructor() + 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() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final inline operator fun invoke(): InternalClassPrivateConstructor + internal final inline operator fun invoke(/*0*/ i: kotlin.Int): InternalClassPrivateConstructor + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +@kotlin.PublishedApi internal open class InternalClassProtectedConstructor { + protected constructor InternalClassProtectedConstructor() + 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() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + internal final inline operator fun invoke(): InternalClassProtectedConstructor + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public open class OpenClass { + public constructor OpenClass() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final inline fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + protected final operator fun invoke(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +private final class PrivateClass { + public constructor PrivateClass() + 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 final operator fun invoke(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 19f3f4bea60..ada24f06ed2 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -375,6 +375,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/initializedAfterRethrow.kt"); } + @Test + @TestMetadata("inlineDeprecationsOnImplicitCalls.kt") + public void testInlineDeprecationsOnImplicitCalls() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineDeprecationsOnImplicitCalls.kt"); + } + @Test @TestMetadata("InvokeAndRecursiveResolve.kt") public void testInvokeAndRecursiveResolve() throws Exception {