Skip to content

Commit 42c8b04

Browse files
authored
Opt-out from annotations type safety analysis (#246)
1 parent e5789b8 commit 42c8b04

File tree

11 files changed

+113
-6
lines changed

11 files changed

+113
-6
lines changed

compiler-plugin/compiler-plugin-cli/src/main/latest/kotlinx/rpc/codegen/RpcCompilerPlugin.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,27 @@ class RpcCommandLineProcessor : CommandLineProcessor {
2525
StrictModeCliOptions.SUSPENDING_SERVER_STREAMING,
2626
StrictModeCliOptions.NOT_TOP_LEVEL_SERVER_FLOW,
2727
StrictModeCliOptions.FIELDS,
28+
RpcFirCliOptions.ANNOTATION_TYPE_SAFETY,
2829
)
2930

3031
override fun processOption(
3132
option: AbstractCliOption,
3233
value: String,
3334
configuration: CompilerConfiguration,
3435
) {
35-
option.processAsStrictModeOption(value, configuration)
36+
if (option.processAsStrictModeOption(value, configuration)) {
37+
return
38+
}
39+
40+
when (option) {
41+
RpcFirCliOptions.ANNOTATION_TYPE_SAFETY -> {
42+
@Suppress("NullableBooleanElvis")
43+
configuration.put(
44+
RpcFirConfigurationKeys.ANNOTATION_TYPE_SAFETY,
45+
value.toBooleanStrictOrNull() ?: true,
46+
)
47+
}
48+
}
3649
}
3750
}
3851

compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcAdditionalCheckers.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import kotlinx.rpc.codegen.checkers.FirCheckedAnnotationHelper
88
import kotlinx.rpc.codegen.checkers.FirRpcDeclarationCheckers
99
import kotlinx.rpc.codegen.checkers.FirRpcExpressionCheckers
1010
import kotlinx.rpc.codegen.checkers.diagnostics.FirRpcStrictModeDiagnostics
11+
import org.jetbrains.kotlin.config.CompilerConfiguration
1112
import org.jetbrains.kotlin.fir.FirSession
1213
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
1314
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
@@ -20,14 +21,19 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
2021
class FirRpcAdditionalCheckers(
2122
session: FirSession,
2223
serializationIsPresent: Boolean,
23-
modes: StrictModeAggregator,
24+
configuration: CompilerConfiguration,
2425
) : FirAdditionalCheckersExtension(session) {
2526
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
2627
register(FirRpcPredicates.rpc)
2728
register(FirRpcPredicates.checkedAnnotationMeta)
2829
}
2930

30-
private val ctx = FirCheckersContext(session, serializationIsPresent, modes)
31+
private val ctx = FirCheckersContext(
32+
session = session,
33+
serializationIsPresent = serializationIsPresent,
34+
annotationTypeSafetyEnabled = configuration.get(RpcFirConfigurationKeys.ANNOTATION_TYPE_SAFETY, true),
35+
modes = configuration.strictModeAggregator(),
36+
)
3137

3238
override val declarationCheckers: DeclarationCheckers = FirRpcDeclarationCheckers(ctx)
3339
override val expressionCheckers: ExpressionCheckers = FirRpcExpressionCheckers(ctx)
@@ -36,6 +42,7 @@ class FirRpcAdditionalCheckers(
3642
class FirCheckersContext(
3743
private val session: FirSession,
3844
val serializationIsPresent: Boolean,
45+
val annotationTypeSafetyEnabled: Boolean,
3946
modes: StrictModeAggregator,
4047
) {
4148
val strictModeDiagnostics = FirRpcStrictModeDiagnostics(modes)

compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcExtensionRegistrar.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension.Facto
1616
class FirRpcExtensionRegistrar(private val configuration: CompilerConfiguration) : FirExtensionRegistrar() {
1717
override fun ExtensionRegistrarContext.configurePlugin() {
1818
val logger = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
19-
val modes = configuration.strictModeAggregator()
2019

2120
val serializationIsPresent = try {
2221
Class.forName("org.jetbrains.kotlinx.serialization.compiler.fir.SerializationFirResolveExtension")
@@ -30,7 +29,7 @@ class FirRpcExtensionRegistrar(private val configuration: CompilerConfiguration)
3029
false
3130
}
3231

33-
+CFactory { FirRpcAdditionalCheckers(it, serializationIsPresent, modes) }
32+
+CFactory { FirRpcAdditionalCheckers(it, serializationIsPresent, configuration) }
3433

3534
+SFactory { FirRpcSupertypeGenerator(it, logger) }
3635
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.rpc.codegen
6+
7+
import org.jetbrains.kotlin.compiler.plugin.CliOption
8+
import org.jetbrains.kotlin.config.CompilerConfigurationKey
9+
10+
object RpcFirCliOptions {
11+
val ANNOTATION_TYPE_SAFETY = CliOption(
12+
optionName = "annotation-type-safety",
13+
valueDescription = "true or false",
14+
description = "Enables/disables annotation type safety analysis.",
15+
required = false,
16+
allowMultipleOccurrences = false,
17+
)
18+
}
19+
20+
object RpcFirConfigurationKeys {
21+
val ANNOTATION_TYPE_SAFETY = CompilerConfigurationKey.create<Boolean>("annotation type safety")
22+
}

compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirCheckedAnnotationCheckers.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ object FirCheckedAnnotationHelper {
153153
typeArgumentsMapper: (TypeArgument) -> ConeTypeProjection?,
154154
sourceProvider: (Origin, TypeArgument) -> KtSourceElement?,
155155
) {
156+
if (!ctx.annotationTypeSafetyEnabled) {
157+
return
158+
}
159+
156160
val originTransformed = originMapper(origin) ?: return
157161
val symbol = symbolProvider(originTransformed) ?: return
158162

gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@ import org.gradle.api.Action
1010
import org.gradle.api.model.ObjectFactory
1111
import org.gradle.api.provider.Property
1212
import org.gradle.kotlin.dsl.newInstance
13+
import org.gradle.kotlin.dsl.property
1314
import javax.inject.Inject
1415

1516
open class RpcExtension @Inject constructor(objects: ObjectFactory) {
17+
/**
18+
* Controls `@Rpc` [annotation type-safety](https://github.com/Kotlin/kotlinx-rpc/pull/240) compile-time checkers.
19+
*
20+
* CAUTION: Disabling is considered unsafe.
21+
* This option is only needed to prevent cases where type-safety analysis fails and valid code can't be compiled.
22+
*/
23+
@RpcDangerousApi
24+
val annotationTypeSafetyEnabled = objects.property<Boolean>().convention(true)
25+
1626
/**
1727
* Strict mode settings.
1828
* Allows configuring the reporting state of deprecated features.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.rpc
6+
7+
@RequiresOptIn("This API is dangerous. It is not recommended to use it, unless you know what you are doing.")
8+
annotation class RpcDangerousApi

gradle-plugin/src/main/kotlin/kotlinx/rpc/compilerPlugins.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class CompilerPluginCli : KotlinCompilerPluginSupportPlugin by compilerPlugin({
4444
// ),
4545
SubpluginOption("strict-not-top-level-server-flow", strict.notTopLevelServerFlow.get().toCompilerArg()),
4646
SubpluginOption("strict-fields", strict.fields.get().toCompilerArg()),
47+
@OptIn(RpcDangerousApi::class)
48+
SubpluginOption("annotation-type-safety", extension.annotationTypeSafetyEnabled.get().toString()),
4749
)
4850
}
4951
}

tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/services/ExtensionRegistrarConfigurator.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package kotlinx.rpc.codegen.test.services
66

7+
import kotlinx.rpc.codegen.RpcFirConfigurationKeys
78
import kotlinx.rpc.codegen.StrictMode
89
import kotlinx.rpc.codegen.StrictModeConfigurationKeys
910
import kotlinx.rpc.codegen.registerRpcExtensions
@@ -36,6 +37,14 @@ class ExtensionRegistrarConfigurator(testServices: TestServices) : EnvironmentCo
3637
configuration.put(StrictModeConfigurationKeys.FIELDS, mode)
3738
}
3839

40+
val annotationTypeSafety = module.directives[RpcDirectives.ANNOTATION_TYPE_SAFETY]
41+
if (annotationTypeSafety.isNotEmpty()) {
42+
configuration.put(
43+
RpcFirConfigurationKeys.ANNOTATION_TYPE_SAFETY,
44+
annotationTypeSafety.single().toBooleanStrict(),
45+
)
46+
}
47+
3948
registerRpcExtensions(configuration)
4049

4150
// libs
@@ -45,4 +54,5 @@ class ExtensionRegistrarConfigurator(testServices: TestServices) : EnvironmentCo
4554

4655
object RpcDirectives : SimpleDirectivesContainer() {
4756
val RPC_STRICT_MODE by stringDirective("none, warning or error", DirectiveApplicability.Module)
57+
val ANNOTATION_TYPE_SAFETY by stringDirective("true or false", DirectiveApplicability.Module)
4858
}

tests/compiler-plugin-tests/src/testData/diagnostics/checkedAnnotation.fir.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
FILE: checkedAnnotation.kt
1+
Module: disabled
2+
FILE: a.kt
3+
@R|kotlinx/rpc/annotations/CheckedTypeAnnotation|() @R|kotlin/annotation/Target|(allowedTargets = vararg(Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.ANNOTATION_CLASS|, Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.TYPE_PARAMETER|, Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.CLASS|)) private final annotation class NotWorkingChecked : R|kotlin/Annotation| {
4+
public constructor(): R|NotWorkingChecked| {
5+
super<R|kotlin/Any|>()
6+
}
7+
8+
}
9+
private final fun <@R|NotWorkingChecked|() T> checked(): R|kotlin/Unit| {
10+
}
11+
public final fun test(): R|kotlin/Unit| {
12+
R|/checked|<R|kotlin/Int|>()
13+
}
14+
Module: main
15+
FILE: module_main_checkedAnnotation.kt
216
@R|kotlinx/rpc/annotations/CheckedTypeAnnotation|() @R|kotlin/annotation/Target|(allowedTargets = vararg(Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.ANNOTATION_CLASS|, Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.TYPE_PARAMETER|, Q|kotlin/annotation/AnnotationTarget|.R|kotlin/annotation/AnnotationTarget.CLASS|)) public final annotation class Checked : R|kotlin/Annotation| {
317
public constructor(): R|Checked| {
418
super<R|kotlin/Any|>()

0 commit comments

Comments
 (0)