Skip to content

Commit

Permalink
Opt-out from annotations type safety analysis (#246)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr3zee authored Dec 5, 2024
1 parent e5789b8 commit 42c8b04
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,27 @@ class RpcCommandLineProcessor : CommandLineProcessor {
StrictModeCliOptions.SUSPENDING_SERVER_STREAMING,
StrictModeCliOptions.NOT_TOP_LEVEL_SERVER_FLOW,
StrictModeCliOptions.FIELDS,
RpcFirCliOptions.ANNOTATION_TYPE_SAFETY,
)

override fun processOption(
option: AbstractCliOption,
value: String,
configuration: CompilerConfiguration,
) {
option.processAsStrictModeOption(value, configuration)
if (option.processAsStrictModeOption(value, configuration)) {
return
}

when (option) {
RpcFirCliOptions.ANNOTATION_TYPE_SAFETY -> {
@Suppress("NullableBooleanElvis")
configuration.put(
RpcFirConfigurationKeys.ANNOTATION_TYPE_SAFETY,
value.toBooleanStrictOrNull() ?: true,
)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import kotlinx.rpc.codegen.checkers.FirCheckedAnnotationHelper
import kotlinx.rpc.codegen.checkers.FirRpcDeclarationCheckers
import kotlinx.rpc.codegen.checkers.FirRpcExpressionCheckers
import kotlinx.rpc.codegen.checkers.diagnostics.FirRpcStrictModeDiagnostics
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
Expand All @@ -20,14 +21,19 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
class FirRpcAdditionalCheckers(
session: FirSession,
serializationIsPresent: Boolean,
modes: StrictModeAggregator,
configuration: CompilerConfiguration,
) : FirAdditionalCheckersExtension(session) {
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirRpcPredicates.rpc)
register(FirRpcPredicates.checkedAnnotationMeta)
}

private val ctx = FirCheckersContext(session, serializationIsPresent, modes)
private val ctx = FirCheckersContext(
session = session,
serializationIsPresent = serializationIsPresent,
annotationTypeSafetyEnabled = configuration.get(RpcFirConfigurationKeys.ANNOTATION_TYPE_SAFETY, true),
modes = configuration.strictModeAggregator(),
)

override val declarationCheckers: DeclarationCheckers = FirRpcDeclarationCheckers(ctx)
override val expressionCheckers: ExpressionCheckers = FirRpcExpressionCheckers(ctx)
Expand All @@ -36,6 +42,7 @@ class FirRpcAdditionalCheckers(
class FirCheckersContext(
private val session: FirSession,
val serializationIsPresent: Boolean,
val annotationTypeSafetyEnabled: Boolean,
modes: StrictModeAggregator,
) {
val strictModeDiagnostics = FirRpcStrictModeDiagnostics(modes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension.Facto
class FirRpcExtensionRegistrar(private val configuration: CompilerConfiguration) : FirExtensionRegistrar() {
override fun ExtensionRegistrarContext.configurePlugin() {
val logger = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
val modes = configuration.strictModeAggregator()

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

+CFactory { FirRpcAdditionalCheckers(it, serializationIsPresent, modes) }
+CFactory { FirRpcAdditionalCheckers(it, serializationIsPresent, configuration) }

+SFactory { FirRpcSupertypeGenerator(it, logger) }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc.codegen

import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.config.CompilerConfigurationKey

object RpcFirCliOptions {
val ANNOTATION_TYPE_SAFETY = CliOption(
optionName = "annotation-type-safety",
valueDescription = "true or false",
description = "Enables/disables annotation type safety analysis.",
required = false,
allowMultipleOccurrences = false,
)
}

object RpcFirConfigurationKeys {
val ANNOTATION_TYPE_SAFETY = CompilerConfigurationKey.create<Boolean>("annotation type safety")
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ object FirCheckedAnnotationHelper {
typeArgumentsMapper: (TypeArgument) -> ConeTypeProjection?,
sourceProvider: (Origin, TypeArgument) -> KtSourceElement?,
) {
if (!ctx.annotationTypeSafetyEnabled) {
return
}

val originTransformed = originMapper(origin) ?: return
val symbol = symbolProvider(originTransformed) ?: return

Expand Down
10 changes: 10 additions & 0 deletions gradle-plugin/src/main/kotlin/kotlinx/rpc/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,19 @@ import org.gradle.api.Action
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.kotlin.dsl.newInstance
import org.gradle.kotlin.dsl.property
import javax.inject.Inject

open class RpcExtension @Inject constructor(objects: ObjectFactory) {
/**
* Controls `@Rpc` [annotation type-safety](https://github.com/Kotlin/kotlinx-rpc/pull/240) compile-time checkers.
*
* CAUTION: Disabling is considered unsafe.
* This option is only needed to prevent cases where type-safety analysis fails and valid code can't be compiled.
*/
@RpcDangerousApi
val annotationTypeSafetyEnabled = objects.property<Boolean>().convention(true)

/**
* Strict mode settings.
* Allows configuring the reporting state of deprecated features.
Expand Down
8 changes: 8 additions & 0 deletions gradle-plugin/src/main/kotlin/kotlinx/rpc/RpcDangerousApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc

@RequiresOptIn("This API is dangerous. It is not recommended to use it, unless you know what you are doing.")
annotation class RpcDangerousApi
2 changes: 2 additions & 0 deletions gradle-plugin/src/main/kotlin/kotlinx/rpc/compilerPlugins.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class CompilerPluginCli : KotlinCompilerPluginSupportPlugin by compilerPlugin({
// ),
SubpluginOption("strict-not-top-level-server-flow", strict.notTopLevelServerFlow.get().toCompilerArg()),
SubpluginOption("strict-fields", strict.fields.get().toCompilerArg()),
@OptIn(RpcDangerousApi::class)
SubpluginOption("annotation-type-safety", extension.annotationTypeSafetyEnabled.get().toString()),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package kotlinx.rpc.codegen.test.services

import kotlinx.rpc.codegen.RpcFirConfigurationKeys
import kotlinx.rpc.codegen.StrictMode
import kotlinx.rpc.codegen.StrictModeConfigurationKeys
import kotlinx.rpc.codegen.registerRpcExtensions
Expand Down Expand Up @@ -36,6 +37,14 @@ class ExtensionRegistrarConfigurator(testServices: TestServices) : EnvironmentCo
configuration.put(StrictModeConfigurationKeys.FIELDS, mode)
}

val annotationTypeSafety = module.directives[RpcDirectives.ANNOTATION_TYPE_SAFETY]
if (annotationTypeSafety.isNotEmpty()) {
configuration.put(
RpcFirConfigurationKeys.ANNOTATION_TYPE_SAFETY,
annotationTypeSafety.single().toBooleanStrict(),
)
}

registerRpcExtensions(configuration)

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

object RpcDirectives : SimpleDirectivesContainer() {
val RPC_STRICT_MODE by stringDirective("none, warning or error", DirectiveApplicability.Module)
val ANNOTATION_TYPE_SAFETY by stringDirective("true or false", DirectiveApplicability.Module)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
FILE: checkedAnnotation.kt
Module: disabled
FILE: a.kt
@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| {
public constructor(): R|NotWorkingChecked| {
super<R|kotlin/Any|>()
}

}
private final fun <@R|NotWorkingChecked|() T> checked(): R|kotlin/Unit| {
}
public final fun test(): R|kotlin/Unit| {
R|/checked|<R|kotlin/Int|>()
}
Module: main
FILE: module_main_checkedAnnotation.kt
@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| {
public constructor(): R|Checked| {
super<R|kotlin/Any|>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

// MODULE: disabled
// ANNOTATION_TYPE_SAFETY: false
// FILE: a.kt

import kotlinx.rpc.annotations.CheckedTypeAnnotation

@CheckedTypeAnnotation
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.CLASS)
private annotation class NotWorkingChecked

private fun <@NotWorkingChecked T> checked() {}

fun test() {
checked<Int>()
}

// MODULE: main

import kotlinx.rpc.annotations.CheckedTypeAnnotation

// annotations
Expand Down

0 comments on commit 42c8b04

Please sign in to comment.