diff --git a/build.gradle.kts b/build.gradle.kts index cbfdc3a4f..2570a4f9b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ plugins { alias(libs.plugins.conventions.kover) alias(libs.plugins.conventions.gradle.doctor) alias(libs.plugins.atomicfu) + id("build-util") } configureProjectReport() diff --git a/compiler-plugin/build.gradle.kts b/compiler-plugin/build.gradle.kts index cc9fe7e33..00c18576f 100644 --- a/compiler-plugin/build.gradle.kts +++ b/compiler-plugin/build.gradle.kts @@ -7,6 +7,7 @@ import util.whenForIde plugins { alias(libs.plugins.conventions.gradle.doctor) + id("build-util") } val rpcVersion: String = libs.versions.kotlinx.rpc.get() diff --git a/compiler-plugin/compiler-plugin-common/src/main/core/kotlinx/rpc/codegen/common/Names.kt b/compiler-plugin/compiler-plugin-common/src/main/core/kotlinx/rpc/codegen/common/Names.kt index 58310b450..e19d0fff8 100644 --- a/compiler-plugin/compiler-plugin-common/src/main/core/kotlinx/rpc/codegen/common/Names.kt +++ b/compiler-plugin/compiler-plugin-common/src/main/core/kotlinx/rpc/codegen/common/Names.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.name.Name object RpcClassId { val remoteServiceInterface = ClassId(FqName("kotlinx.rpc"), Name.identifier("RemoteService")) val rpcAnnotation = ClassId(FqName("kotlinx.rpc.annotations"), Name.identifier("Rpc")) + val checkedTypeAnnotation = ClassId(FqName("kotlinx.rpc.annotations"), Name.identifier("CheckedTypeAnnotation")) val serializableAnnotation = ClassId(FqName("kotlinx.serialization"), Name.identifier("Serializable")) val contextualAnnotation = ClassId(FqName("kotlinx.serialization"), Name.identifier("Contextual")) diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcCheckers.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcCheckers.kt index 4c3289e04..8d4ef578f 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcCheckers.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcCheckers.kt @@ -4,16 +4,32 @@ package kotlinx.rpc.codegen +import kotlinx.rpc.codegen.checkers.FirCheckedAnnotationHelper import kotlinx.rpc.codegen.checkers.FirRpcDeclarationCheckers +import kotlinx.rpc.codegen.checkers.FirRpcExpressionCheckers import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension +import org.jetbrains.kotlin.fir.caches.createCache +import org.jetbrains.kotlin.fir.caches.firCachesFactory import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol -class FirRpcCheckers(session: FirSession) : FirAdditionalCheckersExtension(session) { +class FirRpcCheckers(session: FirSession, serializationIsPresent: Boolean) : FirAdditionalCheckersExtension(session) { override fun FirDeclarationPredicateRegistrar.registerPredicates() { register(FirRpcPredicates.rpc) + register(FirRpcPredicates.checkedAnnotationMeta) } - override val declarationCheckers: DeclarationCheckers = FirRpcDeclarationCheckers + private val ctx = FirCheckersContext(session, serializationIsPresent) + + override val declarationCheckers: DeclarationCheckers = FirRpcDeclarationCheckers(ctx) + override val expressionCheckers: ExpressionCheckers = FirRpcExpressionCheckers(ctx) +} + +class FirCheckersContext(private val session: FirSession, val serializationIsPresent: Boolean) { + val typeParametersCache = session.firCachesFactory.createCache { typeParameter: FirTypeParameterSymbol -> + FirCheckedAnnotationHelper.checkedAnnotations(session, typeParameter) + } } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcExtensionRegistrar.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcExtensionRegistrar.kt index 07c07447d..2a8728f98 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcExtensionRegistrar.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcExtensionRegistrar.kt @@ -5,6 +5,7 @@ package kotlinx.rpc.codegen import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar @@ -16,8 +17,20 @@ class FirRpcExtensionRegistrar(private val configuration: CompilerConfiguration) override fun ExtensionRegistrarContext.configurePlugin() { val logger = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) - +CFactory { FirRpcCheckers(it) } - +GFactory { FirRpcServiceGenerator(it, logger) } + val serializationIsPresent = try { + Class.forName("org.jetbrains.kotlinx.serialization.compiler.fir.SerializationFirResolveExtension") + +GFactory { FirRpcServiceGenerator(it, logger) } + true + } catch (_ : ClassNotFoundException) { + logger.report( + severity = CompilerMessageSeverity.INFO, + message = "Serialization plugin is not found, so generated services are not available", + ) + false + } + + +CFactory { FirRpcCheckers(it, serializationIsPresent) } + +SFactory { FirRpcSupertypeGenerator(it, logger) } } } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt index 8ec8e808c..0ae4f2974 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt @@ -11,4 +11,8 @@ object FirRpcPredicates { internal val rpc = DeclarationPredicate.create { annotated(RpcClassId.rpcAnnotation.asSingleFqName()) // @Rpc } + + internal val checkedAnnotationMeta = DeclarationPredicate.create { + metaAnnotated(RpcClassId.checkedTypeAnnotation.asSingleFqName(), includeItself = false) + } } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcServiceGenerator.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcServiceGenerator.kt index aacd48377..1482183d6 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcServiceGenerator.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcServiceGenerator.kt @@ -67,7 +67,10 @@ class FirRpcServiceGenerator( @Suppress("unused") private val logger: MessageCollector, ) : FirDeclarationGenerationExtension(session) { - private val serializationExtension = SerializationFirResolveExtension(session) + private val serializationExtension by lazy { + SerializationFirResolveExtension(session) + } + private val isJvmOrMetadata = !session.moduleData.platform.run { isJs() || isWasm() || isNative() } override fun FirDeclarationPredicateRegistrar.registerPredicates() { diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirVersionSpecificApi.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirVersionSpecificApi.kt index a892e886d..9ce9d2129 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirVersionSpecificApi.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirVersionSpecificApi.kt @@ -5,6 +5,8 @@ package kotlinx.rpc.codegen import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef @@ -16,10 +18,20 @@ interface FirVersionSpecificApi { delegatedTypeRef: FirTypeRef? = null, ): FirResolvedTypeRef + fun ConeKotlinType.toClassSymbolVS( + session: FirSession, + ): FirClassSymbol<*>? + var FirResolvedTypeRefBuilder.coneTypeVS: ConeKotlinType } -fun vsApi(body: FirVersionSpecificApi.() -> T) : T { - val klass = Class.forName("kotlinx.rpc.codegen.FirVersionSpecificApiImpl") - return (klass.kotlin.objectInstance as FirVersionSpecificApi).body() +val vsApiClass by lazy { + runCatching { + Class.forName("kotlinx.rpc.codegen.FirVersionSpecificApiImpl") + }.getOrNull() +} + +inline fun vsApi(body: FirVersionSpecificApi.() -> T): T { + val kClass = vsApiClass?.kotlin ?: error("FirVersionSpecificApi is not present") + return (kClass.objectInstance as FirVersionSpecificApi).body() } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirCheckedAnnotationCheckers.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirCheckedAnnotationCheckers.kt new file mode 100644 index 000000000..cc798f318 --- /dev/null +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirCheckedAnnotationCheckers.kt @@ -0,0 +1,338 @@ +/* + * 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.checkers + +import kotlinx.rpc.codegen.FirCheckersContext +import kotlinx.rpc.codegen.FirRpcPredicates +import kotlinx.rpc.codegen.checkers.FirCheckedAnnotationHelper.checkTypeArguments +import kotlinx.rpc.codegen.checkers.diagnostics.FirRpcDiagnostics +import kotlinx.rpc.codegen.common.RpcClassId +import kotlinx.rpc.codegen.vsApi +import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.analysis.checkers.FirTypeRefSource +import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirTypeParameterChecker +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker +import org.jetbrains.kotlin.fir.analysis.checkers.extractArgumentsTypeRefAndSource +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirFunction +import org.jetbrains.kotlin.fir.declarations.FirTypeParameter +import org.jetbrains.kotlin.fir.declarations.hasAnnotation +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider +import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.ClassId + +class FirCheckedAnnotationFunctionCallChecker( + private val ctx: FirCheckersContext, +) : FirFunctionCallChecker(MppCheckerKind.Common) { + override fun check( + expression: FirFunctionCall, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + checkTypeArguments( + context = context, + reporter = reporter, + ctx = ctx, + origin = expression, + originMapper = { it }, + symbolProvider = { it.calleeReference.toResolvedCallableSymbol() }, + typeParameterSymbolsProvider = { it.typeParameterSymbols }, + typeArgumentsProvider = { it.typeArguments }, + typeArgumentsMapper = { it.toConeTypeProjection() }, + sourceProvider = { _, type -> type.source }, + ) + } +} + +class FirCheckedAnnotationTypeParameterChecker( + private val ctx: FirCheckersContext, +) : FirTypeParameterChecker(MppCheckerKind.Common) { + override fun check( + declaration: FirTypeParameter, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + @Suppress("DuplicatedCode") + declaration.bounds.forEach { bound -> + checkTypeArguments( + context = context, + reporter = reporter, + ctx = ctx, + origin = bound, + originMapper = { it.coneType }, + symbolProvider = { vsApi { it.toClassSymbolVS(context.session) } }, + typeParameterSymbolsProvider = { it.typeParameterSymbols }, + typeArgumentsProvider = { it.typeArguments.toList() }, + typeArgumentsMapper = { it }, + sourceProvider = { _, _ -> declaration.source }, + ) + } + } +} + +class FirCheckedAnnotationFirClassChecker( + private val ctx: FirCheckersContext, +) : FirClassChecker(MppCheckerKind.Common) { + override fun check( + declaration: FirClass, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + @Suppress("DuplicatedCode") + declaration.superTypeRefs.forEach { superType -> + checkTypeArguments( + context = context, + reporter = reporter, + ctx = ctx, + origin = superType, + originMapper = { it.coneType }, + symbolProvider = { vsApi { it.toClassSymbolVS(context.session) } }, + typeParameterSymbolsProvider = { it.typeParameterSymbols }, + typeArgumentsProvider = { it.typeArguments.toList() }, + typeArgumentsMapper = { it }, + sourceProvider = { ref, _ -> ref.source }, + ) + } + } +} + +class FirCheckedAnnotationFirFunctionChecker( + private val ctx: FirCheckersContext, +) : FirFunctionChecker(MppCheckerKind.Common) { + override fun check( + declaration: FirFunction, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + declaration.valueParameters.forEach { valueParameter -> + checkTypeArguments( + context = context, + reporter = reporter, + ctx = ctx, + origin = valueParameter.returnTypeRef, + originMapper = { it.coneType }, + symbolProvider = { vsApi { it.toClassSymbolVS(context.session) } }, + typeParameterSymbolsProvider = { it.typeParameterSymbols }, + typeArgumentsProvider = { it.typeArguments.toList() }, + typeArgumentsMapper = { it }, + sourceProvider = { ref, _ -> ref.source }, + ) + } + } +} + +object FirCheckedAnnotationHelper { + @Suppress("detekt.LongParameterList", "detekt.CyclomaticComplexMethod", "detekt.LongMethod") + fun checkTypeArguments( + context: CheckerContext, + reporter: DiagnosticReporter, + ctx: FirCheckersContext, + origin: Origin, + originTypeRefSource: FirTypeRefSource? = null, + originMapper: (Origin) -> OriginTransformed?, + symbolProvider: (OriginTransformed) -> Symbol?, + typeParameterSymbolsProvider: (Symbol) -> List?, + typeArgumentsProvider: (OriginTransformed) -> List, + typeArgumentsMapper: (TypeArgument) -> ConeTypeProjection?, + sourceProvider: (Origin, TypeArgument) -> KtSourceElement?, + ) { + val originTransformed = originMapper(origin) ?: return + val symbol = symbolProvider(originTransformed) ?: return + + val parametersWithAnnotations = checkedAnnotationsOnTypeParameters( + session = context.session, + ctx = ctx, + typeParameterSymbols = typeParameterSymbolsProvider(symbol), + ) + + val typeArguments = typeArgumentsProvider(originTransformed) + + val extractedOriginSources = extractArgumentsTypeRefAndSource( + typeRef = originTypeRefSource?.typeRef + ?: (origin as? FirTypeProjectionWithVariance)?.typeRef + ?: (origin as? FirTypeRef) + ).orEmpty() + + parametersWithAnnotations.forEach { (i, annotations) -> + val typeArgument = typeArguments[i] + val type = typeArgumentsMapper(typeArgument)?.type + val classSymbol = vsApi { type?.toClassSymbolVS(context.session) } + + val symbol = when { + classSymbol != null -> { + classSymbol + } + + typeArgument is ConeTypeParameterType -> { + typeArgument.lookupTag.typeParameterSymbol + } + + typeArgument is FirTypeProjectionWithVariance -> { + (typeArgument.typeRef.coneType as? ConeTypeParameterType) + ?.lookupTag + ?.typeParameterSymbol + } + + else -> { + null + } + } ?: return@forEach + + val originSource = extractedOriginSources.getOrNull(i)?.source + ?: originTypeRefSource?.source + ?: (origin as? FirTypeProjectionWithVariance)?.source + ?: (origin as? FirTypeRef)?.source + + val source = when { + classSymbol != null -> { + originSource ?: sourceProvider(origin, typeArgument) + } + + typeArgument is ConeTypeParameterType -> { + originSource ?: typeArgument.lookupTag.typeParameterSymbol.source + } + + typeArgument is FirTypeProjectionWithVariance -> { + extractArgumentsTypeRefAndSource(typeArgument.typeRef) + ?.getOrNull(i) + ?.source + ?: typeArgument.source + } + + else -> { + null + } + } + + checkSymbolAnnotated( + annotations = annotations, + classSymbol = symbol, + source = source, + context = context, + reporter = reporter, + ) + } + + typeArguments.forEachIndexed { i, typeArgument -> + val nextOriginSource = (origin as? FirTypeProjectionWithVariance) + ?.let { FirTypeRefSource(it.typeRef, it.source) } + ?: (origin as? FirTypeRef)?.let { FirTypeRefSource(it, it.source) } + + checkTypeArguments, ConeTypeProjection>( + context = context, + reporter = reporter, + ctx = ctx, + origin = typeArgument, + originMapper = { + when (typeArgument) { + is ConeKotlinTypeProjection -> typeArgument.type + is FirTypeProjectionWithVariance -> typeArgument.toConeTypeProjection() + else -> null + }?.type + }, + originTypeRefSource = extractedOriginSources.getOrNull(i) ?: nextOriginSource ?: originTypeRefSource, + symbolProvider = { vsApi { it.toClassSymbolVS(context.session) } }, + typeParameterSymbolsProvider = { it.typeParameterSymbols }, + typeArgumentsProvider = { it.typeArguments.toList() }, + typeArgumentsMapper = { it }, + sourceProvider = { arg, _ -> + when (arg) { + is FirElement -> arg.source + is ConeKotlinTypeProjection -> sourceProvider(origin, arg) + else -> null + } + }, + ) + } + } + + private fun checkedAnnotationsOnTypeParameters( + session: FirSession, + ctx: FirCheckersContext, + typeParameterSymbols: List?, + ): List>>> { + return typeParameterSymbols.orEmpty().withIndex().filter { (_, parameter) -> + session.predicateBasedProvider.matches( + predicate = FirRpcPredicates.checkedAnnotationMeta, + declaration = parameter, + ) + }.map { (i, parameter) -> + i to ctx.typeParametersCache.getValue(parameter) + } + } + + fun checkedAnnotations( + session: FirSession, + symbol: FirBasedSymbol<*>, + visited: Set> = emptySet(), + ): List> { + return symbol.annotations.mapNotNull { + vsApi { it.resolvedType.toClassSymbolVS(session) } + }.filter { annotation -> + when { + annotation in visited -> false + annotation.hasAnnotation(RpcClassId.checkedTypeAnnotation, session) -> true + else -> checkedAnnotations(session, annotation, visited + annotation).isNotEmpty() + } + } + } + + private fun checkSymbolAnnotated( + annotations: List>, + classSymbol: FirBasedSymbol<*>, + source: KtSourceElement?, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + for (annotationClass in annotations) { + val hasCheckedAnnotation = hasCheckedAnnotation( + session = context.session, + symbol = classSymbol, + annotationId = annotationClass.classId, + ) + + if (!hasCheckedAnnotation) { + reporter.reportOn( + source = source, + factory = FirRpcDiagnostics.CHECKED_ANNOTATION_VIOLATION, + a = annotationClass.defaultType(), + context = context, + ) + } + } + } + + private fun hasCheckedAnnotation( + session: FirSession, + symbol: FirBasedSymbol<*>, + annotationId: ClassId, + visited: Set> = emptySet(), + ): Boolean { + return when { + symbol in visited -> false + symbol.hasAnnotation(annotationId, session) -> true + else -> symbol.annotations.any { annotation -> + vsApi { annotation.resolvedType.toClassSymbolVS(session) }?.let { + hasCheckedAnnotation(session, it, annotationId, visited + symbol) + } == true + } + } + } +} diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt index b0060fc22..a7e053662 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt @@ -4,6 +4,7 @@ package kotlinx.rpc.codegen.checkers +import kotlinx.rpc.codegen.FirCheckersContext import kotlinx.rpc.codegen.FirRpcPredicates import kotlinx.rpc.codegen.checkers.diagnostics.FirRpcDiagnostics import kotlinx.rpc.codegen.isRemoteService @@ -18,7 +19,7 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.utils.isInterface import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider -object FirRpcAnnotationChecker : FirRegularClassChecker(MppCheckerKind.Common) { +class FirRpcAnnotationChecker(private val ctx: FirCheckersContext) : FirRegularClassChecker(MppCheckerKind.Common) { override fun check( declaration: FirRegularClass, context: CheckerContext, @@ -41,5 +42,13 @@ object FirRpcAnnotationChecker : FirRegularClassChecker(MppCheckerKind.Common) { context = context, ) } + + if (rpcAnnotated && !ctx.serializationIsPresent) { + reporter.reportOn( + source = declaration.symbol.rpcAnnotationSource(context.session), + factory = FirRpcDiagnostics.MISSING_SERIALIZATION_MODULE, + context = context, + ) + } } } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcDeclarationCheckers.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcDeclarationCheckers.kt index 45c3d5742..affcbaba8 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcDeclarationCheckers.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/FirRpcDeclarationCheckers.kt @@ -4,11 +4,35 @@ package kotlinx.rpc.codegen.checkers +import kotlinx.rpc.codegen.FirCheckersContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChecker +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirTypeParameterChecker +import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker -object FirRpcDeclarationCheckers : DeclarationCheckers() { +class FirRpcDeclarationCheckers(ctx: FirCheckersContext) : DeclarationCheckers() { override val regularClassCheckers: Set = setOf( - FirRpcAnnotationChecker, + FirRpcAnnotationChecker(ctx), + ) + + override val classCheckers: Set = setOf( + FirCheckedAnnotationFirClassChecker(ctx), + ) + + override val functionCheckers: Set = setOf( + FirCheckedAnnotationFirFunctionChecker(ctx) + ) + + override val typeParameterCheckers: Set = setOf( + FirCheckedAnnotationTypeParameterChecker(ctx) + ) +} + +class FirRpcExpressionCheckers(ctx: FirCheckersContext) : ExpressionCheckers() { + override val functionCallCheckers: Set = setOf( + FirCheckedAnnotationFunctionCallChecker(ctx), ) } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt index 2b93e2fda..d9a9008c9 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/FirRpcDiagnostics.kt @@ -5,12 +5,17 @@ package kotlinx.rpc.codegen.checkers.diagnostics import org.jetbrains.kotlin.diagnostics.error0 +import org.jetbrains.kotlin.diagnostics.error1 import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory +import org.jetbrains.kotlin.diagnostics.warning0 +import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.psi.KtAnnotationEntry object FirRpcDiagnostics { val MISSING_RPC_ANNOTATION by error0() + val MISSING_SERIALIZATION_MODULE by warning0() val WRONG_RPC_ANNOTATION_TARGET by error0() + val CHECKED_ANNOTATION_VIOLATION by error1() init { RootDiagnosticRendererFactory.registerFactory(RpcDiagnosticRendererFactory) diff --git a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt index fd1254044..a6559b93e 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt @@ -6,6 +6,7 @@ package kotlinx.rpc.codegen.checkers.diagnostics import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers object RpcDiagnosticRendererFactory : BaseDiagnosticRendererFactory() { override val MAP = KtDiagnosticFactoryToRendererMap("Rpc").apply { @@ -16,9 +17,23 @@ object RpcDiagnosticRendererFactory : BaseDiagnosticRendererFactory() { "must be annotated with kotlinx.rpc.annotations.Rpc", ) + put( + factory = FirRpcDiagnostics.MISSING_SERIALIZATION_MODULE, + message = "Missing kotlinx.serialization plugin in the module. " + + "Service generation will not be available. " + + "Add kotlin(\"plugin.serialization\") to your build.gradle.kts plugins section." + ) + put( factory = FirRpcDiagnostics.WRONG_RPC_ANNOTATION_TARGET, message = "@Rpc annotation is only applicable to interfaces.", ) + + put( + factory = FirRpcDiagnostics.CHECKED_ANNOTATION_VIOLATION, + message = "Type argument marked with {0} annotation " + + "must be annotated with {0} or an annotation annotated with {0}.", + rendererA = FirDiagnosticRenderers.RENDER_TYPE, + ) } } diff --git a/compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt b/compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt index c0f6af8ec..9b619df75 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt @@ -5,11 +5,14 @@ package kotlinx.rpc.codegen import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.toFirResolvedTypeRef import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.builder.FirResolvedTypeRefBuilder +import org.jetbrains.kotlin.fir.types.toClassSymbol @Suppress("unused") object FirVersionSpecificApiImpl : FirVersionSpecificApi { @@ -20,6 +23,10 @@ object FirVersionSpecificApiImpl : FirVersionSpecificApi { return toFirResolvedTypeRef(source, delegatedTypeRef) } + override fun ConeKotlinType.toClassSymbolVS(session: FirSession): FirClassSymbol<*>? { + return toClassSymbol(session) + } + override var FirResolvedTypeRefBuilder.coneTypeVS: ConeKotlinType get() = coneType set(value) { diff --git a/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_10/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt b/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_10/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt index 1603e8f8d..c45c42192 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_10/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_10/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt @@ -5,11 +5,15 @@ package kotlinx.rpc.codegen import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.FirResolvedTypeRefBuilder +import org.jetbrains.kotlin.fir.types.toClassSymbol @Suppress("unused") object FirVersionSpecificApiImpl : FirVersionSpecificApi { @@ -20,6 +24,10 @@ object FirVersionSpecificApiImpl : FirVersionSpecificApi { return toFirResolvedTypeRef(source, delegatedTypeRef) } + override fun ConeKotlinType.toClassSymbolVS(session: FirSession): FirClassSymbol<*>? { + return (this as? ConeClassLikeType)?.toClassSymbol(session) + } + override var FirResolvedTypeRefBuilder.coneTypeVS: ConeKotlinType get() = type set(value) { diff --git a/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_21/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt b/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_21/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt index 989bd77ed..b92a1be57 100644 --- a/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_21/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt +++ b/compiler-plugin/compiler-plugin-k2/src/main/pre_2_0_21/kotlinx/rpc/codegen/FirVersionSpecificApiImpl.kt @@ -5,11 +5,14 @@ package kotlinx.rpc.codegen import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol import org.jetbrains.kotlin.fir.toFirResolvedTypeRef import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.builder.FirResolvedTypeRefBuilder +import org.jetbrains.kotlin.fir.types.toClassSymbol @Suppress("unused") object FirVersionSpecificApiImpl : FirVersionSpecificApi { @@ -20,6 +23,10 @@ object FirVersionSpecificApiImpl : FirVersionSpecificApi { return toFirResolvedTypeRef(source, delegatedTypeRef) } + override fun ConeKotlinType.toClassSymbolVS(session: FirSession): FirClassSymbol<*>? { + return toClassSymbol(session) + } + override var FirResolvedTypeRefBuilder.coneTypeVS: ConeKotlinType get() = type set(value) { diff --git a/core/api/core.api b/core/api/core.api index fe8c0b979..15add4b48 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -1,6 +1,6 @@ public final class kotlinx/rpc/AwaitFieldInitializationKt { - public static final fun awaitFieldInitialization (Lkotlinx/rpc/RemoteService;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun awaitFieldInitialization (Lkotlinx/rpc/RemoteService;Lkotlin/reflect/KClass;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitFieldInitialization (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun awaitFieldInitialization (Ljava/lang/Object;Lkotlin/reflect/KClass;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlinx/rpc/RegisterFieldKt { @@ -48,8 +48,11 @@ public final class kotlinx/rpc/UninitializedRpcFieldException : java/lang/Except } public final class kotlinx/rpc/WithServiceKt { - public static final fun withService (Lkotlinx/rpc/RpcClient;Lkotlin/reflect/KClass;)Lkotlinx/rpc/RemoteService; - public static final fun withService (Lkotlinx/rpc/RpcClient;Lkotlin/reflect/KType;)Lkotlinx/rpc/RemoteService; + public static final fun withService (Lkotlinx/rpc/RpcClient;Lkotlin/reflect/KClass;)Ljava/lang/Object; + public static final fun withService (Lkotlinx/rpc/RpcClient;Lkotlin/reflect/KType;)Ljava/lang/Object; +} + +public abstract interface annotation class kotlinx/rpc/annotations/CheckedTypeAnnotation : java/lang/annotation/Annotation { } public abstract interface annotation class kotlinx/rpc/annotations/Rpc : java/lang/annotation/Annotation { @@ -68,11 +71,11 @@ public abstract interface class kotlinx/rpc/descriptor/RpcInvokator { } public abstract interface class kotlinx/rpc/descriptor/RpcInvokator$Field : kotlinx/rpc/descriptor/RpcInvokator { - public abstract fun call (Lkotlinx/rpc/RemoteService;)Ljava/lang/Object; + public abstract fun call (Ljava/lang/Object;)Ljava/lang/Object; } public abstract interface class kotlinx/rpc/descriptor/RpcInvokator$Method : kotlinx/rpc/descriptor/RpcInvokator { - public abstract fun call (Lkotlinx/rpc/RemoteService;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun call (Ljava/lang/Object;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class kotlinx/rpc/descriptor/RpcParameter { @@ -82,7 +85,7 @@ public final class kotlinx/rpc/descriptor/RpcParameter { } public abstract interface class kotlinx/rpc/descriptor/RpcServiceDescriptor { - public abstract fun createInstance (JLkotlinx/rpc/RpcClient;)Lkotlinx/rpc/RemoteService; + public abstract fun createInstance (JLkotlinx/rpc/RpcClient;)Ljava/lang/Object; public abstract fun getCallable (Ljava/lang/String;)Lkotlinx/rpc/descriptor/RpcCallable; public abstract fun getFqName ()Ljava/lang/String; } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index f8c29adfe..d60f2eacc 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -7,6 +7,7 @@ import util.applyAtomicfuPlugin plugins { alias(libs.plugins.conventions.kmp) alias(libs.plugins.serialization) + alias(libs.plugins.kotlinx.rpc) } applyAtomicfuPlugin() diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/RpcServer.kt b/core/src/commonMain/kotlin/kotlinx/rpc/RpcServer.kt index fa260af06..887a5b29e 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/RpcServer.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/RpcServer.kt @@ -5,6 +5,7 @@ package kotlinx.rpc import kotlinx.coroutines.CoroutineScope +import kotlinx.rpc.annotations.Rpc import kotlin.coroutines.CoroutineContext import kotlin.reflect.KClass @@ -27,7 +28,7 @@ public interface RpcServer : CoroutineScope { * @param serviceKClass [KClass] of the [Service]. * @param serviceFactory function that produces the actual implementation of the service that will handle the calls. */ - public fun registerService( + public fun <@Rpc Service : Any> registerService( serviceKClass: KClass, serviceFactory: (CoroutineContext) -> Service, ) @@ -42,7 +43,7 @@ public interface RpcServer : CoroutineScope { * type `MyService` should be specified explicitly. * @param serviceFactory function that produces the actual implementation of the service that will handle the calls. */ -public inline fun RpcServer.registerService( +public inline fun <@Rpc reified Service : Any> RpcServer.registerService( noinline serviceFactory: (CoroutineContext) -> Service, ) { registerService(Service::class, serviceFactory) diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/annotations/CheckedTypeAnnotation.kt b/core/src/commonMain/kotlin/kotlinx/rpc/annotations/CheckedTypeAnnotation.kt new file mode 100644 index 000000000..3f461a6fd --- /dev/null +++ b/core/src/commonMain/kotlin/kotlinx/rpc/annotations/CheckedTypeAnnotation.kt @@ -0,0 +1,42 @@ +/* + * 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.annotations + +/** + * Marks an annotation as a one that marks + * a type argument as a one that requires its resolved type to be annotated this annotation. + * + * Example: + * ```kotlin + * @CheckedTypeAnnotation + * annotation class Rpc + * + * @Rpc + * interface MyService + * + * interface NotService + * + * fun <@Rpc T> acceptRpcService() { ... } + * + * acceptRpcService() // OK + * acceptRpcService() // Error + * + * fun <@Rpc T> nested() { + * acceptRpcService() // OK + * } + * ``` + * + * Also works for nested annotations: + * ```kotlin + * @Rpc + * annotation class Grpc + * + * @Grpc + * interface MyGrpcService + * + * acceptRpcService() // OK + */ +@Target(AnnotationTarget.ANNOTATION_CLASS) +public annotation class CheckedTypeAnnotation diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/annotations/Rpc.kt b/core/src/commonMain/kotlin/kotlinx/rpc/annotations/Rpc.kt index 3f4a07c7e..d122b1852 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/annotations/Rpc.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/annotations/Rpc.kt @@ -38,6 +38,7 @@ import kotlinx.rpc.RemoteService * * @see [RemoteService] */ -@Target(AnnotationTarget.CLASS) +@CheckedTypeAnnotation +@Target(AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER) //@Retention(AnnotationRetention.RUNTIME) // Runtime is the default retention, also see KT-41082 public annotation class Rpc diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt b/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt index 30dcf39e2..9491d0cec 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt @@ -4,6 +4,7 @@ package kotlinx.rpc +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.serviceDescriptorOf import kotlinx.rpc.internal.RpcDeferredField import kotlin.reflect.KClass @@ -26,7 +27,7 @@ import kotlin.reflect.KClass * @param getter function that returns the field of the context service to wait for. * @return service filed after it was initialized. */ -public suspend fun T.awaitFieldInitialization(getter: T.() -> R): R { +public suspend fun <@Rpc T : Any, R> T.awaitFieldInitialization(getter: T.() -> R): R { val field = getter() if (field is RpcDeferredField<*>) { @@ -55,7 +56,7 @@ public suspend fun T.awaitFieldInitialization(getter: T.( * @param T service type * @return specified service, after all of it's field were initialized. */ -public suspend inline fun T.awaitFieldInitialization(): T { +public suspend inline fun <@Rpc reified T : Any> T.awaitFieldInitialization(): T { return awaitFieldInitialization(T::class) } @@ -78,7 +79,7 @@ public suspend inline fun T.awaitFieldInitialization * @param kClass [KClass] of the [T] type. * @return specified service, after all of it's field were initialized. */ -public suspend fun T.awaitFieldInitialization(kClass: KClass): T { +public suspend fun <@Rpc T : Any> T.awaitFieldInitialization(kClass: KClass): T { serviceDescriptorOf(kClass) .getFields(this) .forEach { field -> diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt b/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt index fc3965b03..288f17481 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/descriptor/RpcServiceDescriptor.kt @@ -4,8 +4,8 @@ package kotlinx.rpc.descriptor -import kotlinx.rpc.RemoteService import kotlinx.rpc.RpcClient +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.internal.* import kotlinx.rpc.internal.utils.ExperimentalRpcApi import kotlinx.rpc.internal.utils.InternalRpcApi @@ -13,17 +13,17 @@ import kotlin.reflect.KClass import kotlin.reflect.KType @ExperimentalRpcApi -public inline fun serviceDescriptorOf(): RpcServiceDescriptor { +public inline fun <@Rpc reified T : Any> serviceDescriptorOf(): RpcServiceDescriptor { return serviceDescriptorOf(T::class) } @ExperimentalRpcApi -public fun serviceDescriptorOf(kType: KType): RpcServiceDescriptor { +public fun <@Rpc T : Any> serviceDescriptorOf(kType: KType): RpcServiceDescriptor { return serviceDescriptorOf(kType.kClass()) } @ExperimentalRpcApi -public fun serviceDescriptorOf(kClass: KClass): RpcServiceDescriptor { +public fun <@Rpc T : Any> serviceDescriptorOf(kClass: KClass): RpcServiceDescriptor { val maybeDescriptor = internalServiceDescriptorOf(kClass) ?: internalError("Unable to find a service descriptor of the $kClass") @@ -40,7 +40,7 @@ public fun serviceDescriptorOf(kClass: KClass): RpcServic } @ExperimentalRpcApi -public interface RpcServiceDescriptor { +public interface RpcServiceDescriptor<@Rpc T : Any> { public val fqName: String @InternalRpcApi @@ -52,7 +52,7 @@ public interface RpcServiceDescriptor { } @ExperimentalRpcApi -public class RpcCallable( +public class RpcCallable<@Rpc T : Any>( public val name: String, public val dataType: RpcType, public val returnType: RpcType, @@ -61,14 +61,14 @@ public class RpcCallable( ) @ExperimentalRpcApi -public sealed interface RpcInvokator { +public sealed interface RpcInvokator<@Rpc T : Any> { @ExperimentalRpcApi - public fun interface Method : RpcInvokator { + public fun interface Method<@Rpc T : Any> : RpcInvokator { public suspend fun call(service: T, data: Any?): Any? } @ExperimentalRpcApi - public fun interface Field : RpcInvokator { + public fun interface Field<@Rpc T : Any> : RpcInvokator { public fun call(service: T): Any? } } diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.kt b/core/src/commonMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.kt index 263c0144b..2485aa103 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.kt @@ -4,7 +4,7 @@ package kotlinx.rpc.internal -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlin.reflect.KClass -internal expect fun internalServiceDescriptorOf(kClass: KClass): Any? +internal expect fun <@Rpc T : Any> internalServiceDescriptorOf(kClass: KClass): Any? diff --git a/core/src/commonMain/kotlin/kotlinx/rpc/withService.kt b/core/src/commonMain/kotlin/kotlinx/rpc/withService.kt index 57697dd39..b82e6e519 100644 --- a/core/src/commonMain/kotlin/kotlinx/rpc/withService.kt +++ b/core/src/commonMain/kotlin/kotlinx/rpc/withService.kt @@ -5,6 +5,7 @@ package kotlinx.rpc import kotlinx.atomicfu.atomic +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.serviceDescriptorOf import kotlinx.rpc.internal.kClass import kotlin.reflect.KClass @@ -18,7 +19,7 @@ import kotlin.reflect.KType * @param T the exact type of the service to be created. * @return instance of the generated service. */ -public inline fun RpcClient.withService(): T { +public inline fun <@Rpc reified T : Any> RpcClient.withService(): T { return withService(T::class) } @@ -31,7 +32,7 @@ public inline fun RpcClient.withService(): T { * @param serviceKType [KType] of the service to be created. * @return instance of the generated service. */ -public fun RpcClient.withService(serviceKType: KType): T { +public fun <@Rpc T : Any> RpcClient.withService(serviceKType: KType): T { return withService(serviceKType.kClass()) } @@ -50,7 +51,7 @@ private val SERVICE_ID = atomic(0L) * @param serviceKClass [KClass] of the service to be created. * @return instance of the generated service. */ -public fun RpcClient.withService(serviceKClass: KClass): T { +public fun <@Rpc T : Any> RpcClient.withService(serviceKClass: KClass): T { val descriptor = serviceDescriptorOf(serviceKClass) val id = SERVICE_ID.incrementAndGet() diff --git a/core/src/jsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.js.kt b/core/src/jsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.js.kt index ecd75e40a..a7c89136a 100644 --- a/core/src/jsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.js.kt +++ b/core/src/jsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.js.kt @@ -7,7 +7,7 @@ package kotlinx.rpc.internal import js.objects.Object -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.RpcServiceDescriptor import kotlinx.rpc.internal.utils.InternalRpcApi import kotlin.reflect.AssociatedObjectKey @@ -21,10 +21,10 @@ import kotlin.reflect.findAssociatedObject @Target(AnnotationTarget.CLASS) public annotation class WithServiceDescriptor( @Suppress("unused") - val stub: KClass>, + val stub: KClass>, ) -internal actual fun internalServiceDescriptorOf(kClass: KClass): Any? { +internal actual fun <@Rpc T : Any> internalServiceDescriptorOf(kClass: KClass): Any? { return kClass.findAssociatedObjectImpl(WithServiceDescriptor::class) } @@ -34,7 +34,7 @@ internal actual fun internalServiceDescriptorOf(kClass: KCla * * This function uses std-lib's implementation and accounts for the bug in the compiler */ -internal fun KClass<*>.findAssociatedObjectImpl(annotationClass: KClass): Any? { +internal fun KClass<*>.findAssociatedObjectImpl(annotationClass: KClass<*>): Any? { val key = annotationClass.js.asDynamic().`$metadata$`?.associatedObjectKey?.unsafeCast() ?: return null val map = js.asDynamic().`$metadata$`?.associatedObjects ?: return null val factory = map[key] ?: return fallbackFindAssociatedObjectImpl(map) diff --git a/core/src/jvmMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.jvm.kt b/core/src/jvmMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.jvm.kt index 382fc763d..145f60899 100644 --- a/core/src/jvmMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.jvm.kt +++ b/core/src/jvmMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.jvm.kt @@ -4,13 +4,13 @@ package kotlinx.rpc.internal -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlin.reflect.KClass import kotlin.reflect.full.companionObjectInstance private const val RPC_SERVICE_STUB_SIMPLE_NAME = "\$rpcServiceStub" -internal actual fun internalServiceDescriptorOf(kClass: KClass): Any? { +internal actual fun <@Rpc T : Any> internalServiceDescriptorOf(kClass: KClass): Any? { val className = "${kClass.qualifiedName}\$$RPC_SERVICE_STUB_SIMPLE_NAME" return kClass.java.classLoader diff --git a/core/src/nativeMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.native.kt b/core/src/nativeMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.native.kt index 9ddbdf8c6..606b12e77 100644 --- a/core/src/nativeMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.native.kt +++ b/core/src/nativeMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.native.kt @@ -6,7 +6,7 @@ package kotlinx.rpc.internal -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.RpcServiceDescriptor import kotlinx.rpc.internal.utils.InternalRpcApi import kotlin.reflect.AssociatedObjectKey @@ -20,10 +20,10 @@ import kotlin.reflect.findAssociatedObject @Target(AnnotationTarget.CLASS) public annotation class WithServiceDescriptor( @Suppress("unused") - val stub: KClass>, + val stub: KClass>, ) @OptIn(ExperimentalAssociatedObjects::class) -internal actual fun internalServiceDescriptorOf(kClass: KClass): Any? { +internal actual fun <@Rpc T : Any> internalServiceDescriptorOf(kClass: KClass): Any? { return kClass.findAssociatedObject() } diff --git a/core/src/wasmJsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmJs.kt b/core/src/wasmJsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmJs.kt index 4206ffd78..052a51e19 100644 --- a/core/src/wasmJsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmJs.kt +++ b/core/src/wasmJsMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmJs.kt @@ -4,7 +4,7 @@ package kotlinx.rpc.internal -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.RpcServiceDescriptor import kotlinx.rpc.internal.utils.InternalRpcApi import kotlin.reflect.AssociatedObjectKey @@ -18,10 +18,10 @@ import kotlin.reflect.findAssociatedObject @Target(AnnotationTarget.CLASS) public annotation class WithServiceDescriptor( @Suppress("unused") - val stub: KClass>, + val stub: KClass>, ) @OptIn(ExperimentalAssociatedObjects::class) -internal actual fun internalServiceDescriptorOf(kClass: KClass): Any? { +internal actual fun <@Rpc T : Any> internalServiceDescriptorOf(kClass: KClass): Any? { return kClass.findAssociatedObject() } diff --git a/core/src/wasmWasiMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmWasi.kt b/core/src/wasmWasiMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmWasi.kt index 4206ffd78..052a51e19 100644 --- a/core/src/wasmWasiMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmWasi.kt +++ b/core/src/wasmWasiMain/kotlin/kotlinx/rpc/internal/internalServiceDescriptorOf.wasmWasi.kt @@ -4,7 +4,7 @@ package kotlinx.rpc.internal -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.RpcServiceDescriptor import kotlinx.rpc.internal.utils.InternalRpcApi import kotlin.reflect.AssociatedObjectKey @@ -18,10 +18,10 @@ import kotlin.reflect.findAssociatedObject @Target(AnnotationTarget.CLASS) public annotation class WithServiceDescriptor( @Suppress("unused") - val stub: KClass>, + val stub: KClass>, ) @OptIn(ExperimentalAssociatedObjects::class) -internal actual fun internalServiceDescriptorOf(kClass: KClass): Any? { +internal actual fun <@Rpc T : Any> internalServiceDescriptorOf(kClass: KClass): Any? { return kClass.findAssociatedObject() } diff --git a/krpc/krpc-client/build.gradle.kts b/krpc/krpc-client/build.gradle.kts index d1871a40c..39f18e9a9 100644 --- a/krpc/krpc-client/build.gradle.kts +++ b/krpc/krpc-client/build.gradle.kts @@ -7,6 +7,7 @@ import util.applyAtomicfuPlugin plugins { alias(libs.plugins.conventions.kmp) alias(libs.plugins.serialization) + alias(libs.plugins.kotlinx.rpc) } applyAtomicfuPlugin() diff --git a/krpc/krpc-core/build.gradle.kts b/krpc/krpc-core/build.gradle.kts index 906052f29..40f24dfd8 100644 --- a/krpc/krpc-core/build.gradle.kts +++ b/krpc/krpc-core/build.gradle.kts @@ -7,6 +7,7 @@ import util.applyAtomicfuPlugin plugins { alias(libs.plugins.conventions.kmp) alias(libs.plugins.serialization) + alias(libs.plugins.kotlinx.rpc) } applyAtomicfuPlugin() diff --git a/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts b/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts index 9b1ca88ff..b3d2a7af1 100644 --- a/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts +++ b/krpc/krpc-ktor/krpc-ktor-client/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.conventions.kmp) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/krpc/krpc-ktor/krpc-ktor-server/build.gradle.kts b/krpc/krpc-ktor/krpc-ktor-server/build.gradle.kts index 753b68673..042078e46 100644 --- a/krpc/krpc-ktor/krpc-ktor-server/build.gradle.kts +++ b/krpc/krpc-ktor/krpc-ktor-server/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.conventions.kmp) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/krpc/krpc-ktor/krpc-ktor-server/src/jvmMain/kotlin/kotlinx/rpc/krpc/ktor/server/KrpcRoute.kt b/krpc/krpc-ktor/krpc-ktor-server/src/jvmMain/kotlin/kotlinx/rpc/krpc/ktor/server/KrpcRoute.kt index d653ef1ef..0647529b1 100644 --- a/krpc/krpc-ktor/krpc-ktor-server/src/jvmMain/kotlin/kotlinx/rpc/krpc/ktor/server/KrpcRoute.kt +++ b/krpc/krpc-ktor/krpc-ktor-server/src/jvmMain/kotlin/kotlinx/rpc/krpc/ktor/server/KrpcRoute.kt @@ -5,8 +5,8 @@ package kotlinx.rpc.krpc.ktor.server import io.ktor.server.websocket.* -import kotlinx.rpc.RemoteService import kotlinx.rpc.RpcServer +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.krpc.KrpcConfigBuilder import kotlin.coroutines.CoroutineContext import kotlin.reflect.KClass @@ -45,7 +45,7 @@ public class KrpcRoute( * @param serviceKClass [KClass] of the [Service]. * @param serviceFactory function that produces the actual implementation of the service that will handle the calls. */ - public fun registerService( + public fun <@Rpc Service : Any> registerService( serviceKClass: KClass, serviceFactory: (CoroutineContext) -> Service, ) { @@ -63,7 +63,7 @@ public class KrpcRoute( * type `MyService` should be specified explicitly. * @param serviceFactory function that produces the actual implementation of the service that will handle the calls. */ - public inline fun registerService( + public inline fun <@Rpc reified Service : Any> registerService( noinline serviceFactory: (CoroutineContext) -> Service, ) { registerService(Service::class, serviceFactory) diff --git a/krpc/krpc-logging/build.gradle.kts b/krpc/krpc-logging/build.gradle.kts index 096359f6f..11ec6d04e 100644 --- a/krpc/krpc-logging/build.gradle.kts +++ b/krpc/krpc-logging/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.conventions.kmp) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/krpc/krpc-serialization/krpc-serialization-cbor/build.gradle.kts b/krpc/krpc-serialization/krpc-serialization-cbor/build.gradle.kts index b44abacca..e576764e1 100644 --- a/krpc/krpc-serialization/krpc-serialization-cbor/build.gradle.kts +++ b/krpc/krpc-serialization/krpc-serialization-cbor/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.conventions.kmp) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/krpc/krpc-serialization/krpc-serialization-core/build.gradle.kts b/krpc/krpc-serialization/krpc-serialization-core/build.gradle.kts index 3006fee8a..4a5686c4c 100644 --- a/krpc/krpc-serialization/krpc-serialization-core/build.gradle.kts +++ b/krpc/krpc-serialization/krpc-serialization-core/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.conventions.kmp) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/krpc/krpc-serialization/krpc-serialization-json/build.gradle.kts b/krpc/krpc-serialization/krpc-serialization-json/build.gradle.kts index f0741cec9..fd222d43b 100644 --- a/krpc/krpc-serialization/krpc-serialization-json/build.gradle.kts +++ b/krpc/krpc-serialization/krpc-serialization-json/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.conventions.kmp) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/krpc/krpc-serialization/krpc-serialization-protobuf/build.gradle.kts b/krpc/krpc-serialization/krpc-serialization-protobuf/build.gradle.kts index 28744557e..8d831275a 100644 --- a/krpc/krpc-serialization/krpc-serialization-protobuf/build.gradle.kts +++ b/krpc/krpc-serialization/krpc-serialization-protobuf/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.conventions.kmp) + alias(libs.plugins.kotlinx.rpc) } kotlin { diff --git a/krpc/krpc-server/build.gradle.kts b/krpc/krpc-server/build.gradle.kts index 2d9879d06..2413cb4c3 100644 --- a/krpc/krpc-server/build.gradle.kts +++ b/krpc/krpc-server/build.gradle.kts @@ -7,6 +7,7 @@ import util.applyAtomicfuPlugin plugins { alias(libs.plugins.conventions.kmp) alias(libs.plugins.serialization) + alias(libs.plugins.kotlinx.rpc) } applyAtomicfuPlugin() diff --git a/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/KrpcServer.kt b/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/KrpcServer.kt index 7c89fb935..46ae5cc77 100644 --- a/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/KrpcServer.kt +++ b/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/KrpcServer.kt @@ -5,8 +5,8 @@ package kotlinx.rpc.krpc.server import kotlinx.coroutines.* -import kotlinx.rpc.RemoteService import kotlinx.rpc.RpcServer +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.RpcServiceDescriptor import kotlinx.rpc.descriptor.serviceDescriptorOf import kotlinx.rpc.internal.utils.InternalRpcApi @@ -103,7 +103,7 @@ public abstract class KrpcServer( } } - final override fun registerService( + final override fun <@Rpc Service : Any> registerService( serviceKClass: KClass, serviceFactory: (CoroutineContext) -> Service, ) { @@ -126,7 +126,7 @@ public abstract class KrpcServer( } } - private fun createNewServiceInstance( + private fun <@Rpc Service : Any> createNewServiceInstance( descriptor: RpcServiceDescriptor, serviceFactory: (CoroutineContext) -> Service, ): KrpcServerService { diff --git a/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/internal/KrpcServerService.kt b/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/internal/KrpcServerService.kt index 1814a1bb7..684e547c0 100644 --- a/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/internal/KrpcServerService.kt +++ b/krpc/krpc-server/src/commonMain/kotlin/kotlinx/rpc/krpc/server/internal/KrpcServerService.kt @@ -5,7 +5,7 @@ package kotlinx.rpc.krpc.server.internal import kotlinx.coroutines.* -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.descriptor.RpcInvokator import kotlinx.rpc.descriptor.RpcServiceDescriptor import kotlinx.rpc.internal.utils.map.ConcurrentHashMap @@ -19,7 +19,7 @@ import kotlinx.serialization.BinaryFormat import kotlinx.serialization.StringFormat import kotlin.coroutines.CoroutineContext -internal class KrpcServerService( +internal class KrpcServerService<@Rpc T : Any>( private val service: T, private val descriptor: RpcServiceDescriptor, override val config: KrpcConfig.Server, diff --git a/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/TransportTest.kt b/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/TransportTest.kt index 48db081bc..3607bce66 100644 --- a/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/TransportTest.kt +++ b/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/TransportTest.kt @@ -235,7 +235,7 @@ class TransportTest { assertTrue(echoServices.single().coroutineContext.job.isCancelled) } - private inline fun RpcServer.registerServiceAndReturn( + private inline fun <@Rpc reified Service : Any, reified Impl : Service> RpcServer.registerServiceAndReturn( crossinline body: (CoroutineContext) -> Impl, ): List { val instances = mutableListOf() diff --git a/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/api/WireSamplingTestScope.kt b/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/api/WireSamplingTestScope.kt index 5f68d4629..6196dc68c 100644 --- a/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/api/WireSamplingTestScope.kt +++ b/krpc/krpc-test/src/jvmTest/kotlin/kotlinx/rpc/krpc/test/api/WireSamplingTestScope.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestResult import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest -import kotlinx.rpc.RemoteService +import kotlinx.rpc.annotations.Rpc import kotlinx.rpc.internal.utils.hex.hexToReadableBinary import kotlinx.rpc.krpc.KrpcTransportMessage import kotlinx.rpc.krpc.internal.logging.CommonLogger @@ -246,7 +246,7 @@ private class WireToolkit(scope: CoroutineScope, format: SamplingFormat, val log DumpLoggerContainer.set(dumpLogger) } - private inline fun Service.withConsistentServiceId(): Service = apply { + private inline fun <@Rpc reified Service : Any> Service.withConsistentServiceId(): Service = apply { val clazz = this::class.java val prop = clazz .declaredFields diff --git a/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/DiagnosticTestGenerated.java b/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/DiagnosticTestGenerated.java new file mode 100644 index 000000000..c54736002 --- /dev/null +++ b/tests/compiler-plugin-tests/src/test-gen/kotlinx/rpc/codegen/test/runners/DiagnosticTestGenerated.java @@ -0,0 +1,38 @@ + + +/* + * 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.test.runners; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link kotlinx.rpc.codegen.test.GenerateTestsKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("src/testData/diagnostics") +@TestDataPath("$PROJECT_ROOT") +public class DiagnosticTestGenerated extends AbstractDiagnosticTest { + @Test + public void testAllFilesPresentInDiagnostics() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("src/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("checkedAnnotation.kt") + public void testCheckedAnnotation() { + runTest("src/testData/diagnostics/checkedAnnotation.kt"); + } + + @Test + @TestMetadata("rpcChecked.kt") + public void testRpcChecked() { + runTest("src/testData/diagnostics/rpcChecked.kt"); + } +} diff --git a/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt b/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt index c828d595a..12eb91624 100644 --- a/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt +++ b/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/GenerateTests.kt @@ -5,15 +5,15 @@ package kotlinx.rpc.codegen.test import kotlinx.rpc.codegen.test.runners.AbstractBoxTest +import kotlinx.rpc.codegen.test.runners.AbstractDiagnosticTest import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5 fun main() { generateTestGroupSuiteWithJUnit5 { testGroup(testDataRoot = "src/testData", testsRoot = "src/test-gen") { - // todo enable after diagnostics are done -// testClass { -// model("diagnostics") -// } + testClass { + model("diagnostics") + } testClass { model("box") diff --git a/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/runners/AbstractDiagnosticTest.kt b/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/runners/AbstractDiagnosticTest.kt index fe39e1474..6615505e0 100644 --- a/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/runners/AbstractDiagnosticTest.kt +++ b/tests/compiler-plugin-tests/src/test/kotlin/kotlinx/rpc/codegen/test/runners/AbstractDiagnosticTest.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.test.services.KotlinStandardLibrariesPathProvider abstract class AbstractDiagnosticTest : BaseTestRunner() { override fun TestConfigurationBuilder.configuration() { commonFirWithPluginFrontendConfiguration() - configureFirParser(FirParser.Psi) + configureFirParser(FirParser.LightTree) } override fun createKotlinStandardLibrariesPathProvider(): KotlinStandardLibrariesPathProvider { diff --git a/tests/compiler-plugin-tests/src/testData/diagnostics/checkedAnnotation.fir.txt b/tests/compiler-plugin-tests/src/testData/diagnostics/checkedAnnotation.fir.txt new file mode 100644 index 000000000..a94a05e55 --- /dev/null +++ b/tests/compiler-plugin-tests/src/testData/diagnostics/checkedAnnotation.fir.txt @@ -0,0 +1,368 @@ +FILE: 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|Checked|() @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 SubChecked : R|kotlin/Annotation| { + public constructor(): R|SubChecked| { + super() + } + + } + @R|SubChecked|() @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 SubSubChecked : R|kotlin/Annotation| { + public constructor(): R|SubSubChecked| { + super() + } + + } + @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 Checked2 : R|kotlin/Annotation| { + public constructor(): R|Checked2| { + super() + } + + } + @R|Checked|() public open class AnnotatedChecked : R|kotlin/Any| { + public constructor(): R|AnnotatedChecked| { + super() + } + + } + public final class AnnotatedCheckedImpl : R|AnnotatedChecked| { + public constructor(): R|AnnotatedCheckedImpl| { + super() + } + + } + @R|SubChecked|() public final class AnnotatedSubChecked : R|kotlin/Any| { + public constructor(): R|AnnotatedSubChecked| { + super() + } + + } + @R|SubSubChecked|() public final class AnnotatedSubSubChecked : R|kotlin/Any| { + public constructor(): R|AnnotatedSubSubChecked| { + super() + } + + } + @R|Checked2|() public final class AnnotatedChecked2 : R|kotlin/Any| { + public constructor(): R|AnnotatedChecked2| { + super() + } + + } + @R|Checked|() @R|Checked2|() public final class AnnotatedCheckedMulti : R|kotlin/Any| { + public constructor(): R|AnnotatedCheckedMulti| { + super() + } + + } + public final fun <@R|Checked|() T> checked(arg: R|T|): R|kotlin/Unit| { + } + public final fun <@R|SubChecked|() T> subChecked(arg: R|T|): R|kotlin/Unit| { + } + public final fun <@R|SubSubChecked|() T> subSubChecked(arg: R|T|): R|kotlin/Unit| { + } + public final fun <@R|Checked2|() T> checked2(arg: R|T|): R|kotlin/Unit| { + } + public final fun <@R|Checked|() @R|Checked2|() T> checkedMulti(arg: R|T|): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T1, @R|Checked2|() T2> checkedTwoParams(arg1: R|T1|, arg2: R|T2|): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T1, T2> checkedTwoParamsSingleAnnotation(arg1: R|T1|, arg2: R|T2|): R|kotlin/Unit| { + } + public open class CheckedClass<@R|Checked|() T> : R|kotlin/Any| { + public constructor<@R|Checked|() T>(arg: R|T|): R|CheckedClass| { + super() + } + + } + @R|Checked|() public final class CheckedClassChild<@R|Checked|() T> : R|CheckedClass>| { + public constructor<@R|Checked|() T>(arg: R|T|): R|CheckedClassChild| { + super>|>(R|/CheckedClassChild.CheckedClassChild|(R|/arg|)) + } + + } + public final class CheckedClassChildFail<@R|Checked|() T> : R|CheckedClass>| { + public constructor<@R|Checked|() T>(arg: R|T|): R|CheckedClassChildFail| { + super>|>(R|/CheckedClassChildFail.CheckedClassChildFail|(R|/arg|)) + } + + } + public final class CheckedClassChildFail2 : R|CheckedClass>| { + public constructor(arg: R|T|): R|CheckedClassChildFail2| { + super>|>(R|/CheckedClassChildFail2.CheckedClassChildFail2|(R|/arg|)) + } + + } + public final class CheckedClassChildFail3 : R|CheckedClass| { + public constructor(arg: R|T|): R|CheckedClassChildFail3| { + super|>(R|/arg|) + } + + } + public final class CheckedClassChildOk<@R|Checked|() T> : R|CheckedClass| { + public constructor<@R|Checked|() T>(arg: R|T|): R|CheckedClassChildOk| { + super|>(R|/arg|) + } + + } + @R|Checked|() public final class CheckedClassChildOk2 : R|CheckedClass>| { + public constructor(arg: R|T|): R|CheckedClassChildOk2| { + super>|>(R|/CheckedClassChildOk2.CheckedClassChildOk2|(R|/arg|)) + } + + } + public final class SubCheckedClass<@R|SubChecked|() T> : R|kotlin/Any| { + public constructor<@R|SubChecked|() T>(arg: R|T|): R|SubCheckedClass| { + super() + } + + } + public final class SubSubCheckedClass<@R|SubSubChecked|() T> : R|kotlin/Any| { + public constructor<@R|SubSubChecked|() T>(arg: R|T|): R|SubSubCheckedClass| { + super() + } + + } + public final class Checked2Class<@R|Checked2|() T> : R|kotlin/Any| { + public constructor<@R|Checked2|() T>(arg: R|T|): R|Checked2Class| { + super() + } + + } + public final class CheckedMultiClass<@R|Checked|() @R|Checked2|() T> : R|kotlin/Any| { + public constructor<@R|Checked|() @R|Checked2|() T>(arg: R|T|): R|CheckedMultiClass| { + super() + } + + } + public final class CheckedTwoParamsClass<@R|Checked|() T1, @R|Checked2|() T2> : R|kotlin/Any| { + public constructor<@R|Checked|() T1, @R|Checked2|() T2>(arg1: R|T1|, arg2: R|T2|): R|CheckedTwoParamsClass| { + super() + } + + } + public final class CheckedTwoParamsSingleAnnotationClass<@R|Checked|() T1, T2> : R|kotlin/Any| { + public constructor<@R|Checked|() T1, T2>(arg1: R|T1|, arg2: R|T2|): R|CheckedTwoParamsSingleAnnotationClass| { + super() + } + + } + public final class GenericClass : R|kotlin/Any| { + public constructor(arg: R|T|): R|GenericClass| { + super() + } + + } + public final fun <@R|Checked|() T : R|CheckedClass|> nestedCheckedOk(): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T : R|CheckedTwoParamsClass|> nestedCheckedThoParamsOk(): R|kotlin/Unit| { + } + public final fun <@R|SubChecked|() T : R|CheckedClass|> nestedSubCheckedOk(): R|kotlin/Unit| { + } + public final fun <@R|SubSubChecked|() T : R|CheckedClass|> nestedSubSubCheckedOk(): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T, C : R|CheckedClass|> nestedTwoCheckedOk(): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T, C : R|GenericClass>>|> deeplyNestedTwoCheckedOk(): R|kotlin/Unit| { + } + public final fun |> nestedTwoCheckedFail(): R|kotlin/Unit| { + } + public final fun <@R|Checked2|() T : R|CheckedClass|> nestedChecked2Fail(): R|kotlin/Unit| { + } + public final fun |> nestedCheckedFail(): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T> nestedArgumetTypeCheckedOk(arg: R|CheckedClass|): R|kotlin/Unit| { + } + public final fun nestedArgumetTypeCheckedFail(arg: R|CheckedClass|): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T> doubleNestedArgumetTypeCheckedOk(arg: R|GenericClass>|): R|kotlin/Unit| { + } + public final fun doubleNestedArgumetTypeCheckedFail(arg: R|GenericClass>|): R|kotlin/Unit| { + } + public final class NestedArgumetTypeCheckedOk<@R|Checked|() T> : R|kotlin/Any| { + public constructor<@R|Checked|() T>(arg: R|CheckedClass|): R|NestedArgumetTypeCheckedOk| { + super() + } + + } + public final class NestedArgumetTypeCheckedFail : R|kotlin/Any| { + public constructor(arg: R|CheckedClass|): R|NestedArgumetTypeCheckedFail| { + super() + } + + } + public final class DoubleNestedArgumetTypeCheckedOk<@R|Checked|() T> : R|kotlin/Any| { + public constructor<@R|Checked|() T>(arg: R|GenericClass>|): R|DoubleNestedArgumetTypeCheckedOk| { + super() + } + + } + public final class DoubleNestedArgumetTypeCheckedFail : R|kotlin/Any| { + public constructor(arg: R|GenericClass>|): R|DoubleNestedArgumetTypeCheckedFail| { + super() + } + + } + public final class CheckedNestedClass<@R|Checked|() T : R|CheckedClass|> : R|kotlin/Any| { + public constructor<@R|Checked|() T : R|CheckedClass|>(): R|CheckedNestedClass| { + super() + } + + } + public final class CheckedNestedClassFail|> : R|kotlin/Any| { + public constructor|>(): R|CheckedNestedClassFail| { + super() + } + + } + public final fun <@R|Checked|() T : R|CheckedNestedClass|> doubleNestedCheckedOk(): R|kotlin/Unit| { + } + public final fun |> doubleNestedCheckedFail(): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T : R|GenericClass>|> tripleNestedCheckedOk(): R|kotlin/Unit| { + } + public final fun >|> tripleNestedCheckedFail(): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T : R|GenericClass>>|> quadrupleNestedCheckedOk(): R|kotlin/Unit| { + } + public final fun >>|> quadrupleNestedCheckedFail(): R|kotlin/Unit| { + } + public final fun <@R|SubSubChecked|() T : R|GenericClass>>>|> quintupleNestedCheckedOk(): R|kotlin/Unit| { + } + public final fun >>>|> quintupleNestedCheckedFail(): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + R|/checked|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checked|(R|/AnnotatedCheckedImpl.AnnotatedCheckedImpl|()) + R|/checked|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/checked|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/checked|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/subChecked|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/subChecked|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/subChecked|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/subChecked|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/subSubChecked|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/subSubChecked|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/subSubChecked|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/subSubChecked|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checked2|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checked2|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/checked2|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/checked2|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedMulti|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checkedMulti|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedMulti|(R|/AnnotatedCheckedMulti.AnnotatedCheckedMulti|()) + R|/checkedTwoParams|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParams|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParams|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checkedTwoParams|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checkedTwoParamsSingleAnnotation|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParamsSingleAnnotation|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParamsSingleAnnotation|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checkedTwoParamsSingleAnnotation|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/nestedCheckedOk||>() + R|/nestedCheckedOk||>() + R|/nestedTwoCheckedOk||>() + R|/nestedTwoCheckedOk||>() + R|/deeplyNestedTwoCheckedOk|>>|>() + R|/deeplyNestedTwoCheckedOk|>>|>() + R|/CheckedClass.CheckedClass|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedCheckedImpl.AnnotatedCheckedImpl|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedCheckedImpl.AnnotatedCheckedImpl|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/SubCheckedClass.SubCheckedClass|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/SubCheckedClass.SubCheckedClass|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/SubCheckedClass.SubCheckedClass|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/SubCheckedClass.SubCheckedClass|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/SubSubCheckedClass.SubSubCheckedClass|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/SubSubCheckedClass.SubSubCheckedClass|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/SubSubCheckedClass.SubSubCheckedClass|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/SubSubCheckedClass.SubSubCheckedClass|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/Checked2Class.Checked2Class|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/Checked2Class.Checked2Class|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/Checked2Class.Checked2Class|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/Checked2Class.Checked2Class|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedMultiClass.CheckedMultiClass|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedMultiClass.CheckedMultiClass|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedMultiClass.CheckedMultiClass|(R|/AnnotatedCheckedMulti.AnnotatedCheckedMulti|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedTwoParamsSingleAnnotationClass.CheckedTwoParamsSingleAnnotationClass|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedTwoParamsSingleAnnotationClass.CheckedTwoParamsSingleAnnotationClass|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedTwoParamsSingleAnnotationClass.CheckedTwoParamsSingleAnnotationClass|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedTwoParamsSingleAnnotationClass.CheckedTwoParamsSingleAnnotationClass|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checked|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checked|(R|/AnnotatedCheckedImpl.AnnotatedCheckedImpl|()) + R|/checked|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/checked|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/checked|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedCheckedImpl.AnnotatedCheckedImpl|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedSubChecked.AnnotatedSubChecked|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedSubSubChecked.AnnotatedSubSubChecked|()) + R|/CheckedClass.CheckedClass|(R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParams|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParams|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParams|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/checkedTwoParams|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/checkedTwoParams|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked2.AnnotatedChecked2|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked.AnnotatedChecked|(), R|/AnnotatedChecked.AnnotatedChecked|()) + R|/CheckedTwoParamsClass.CheckedTwoParamsClass|(R|/AnnotatedChecked2.AnnotatedChecked2|(), R|/AnnotatedChecked.AnnotatedChecked|()) + } + public final fun <@R|Checked|() T, C : R|CheckedClass|> nestedTwoWithArgCheckedOk(arg: R|C|): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T, C : R|GenericClass>>|> deeplyNestedTwoWithArgCheckedOk(arg: R|C|): R|kotlin/Unit| { + } + public final fun <@R|Checked|() T> unknownTypeOk(arg: R|T|): R|kotlin/Unit| { + R|/checked|(R|/arg|) + R|/checked|(R|/arg|) + R|/CheckedClass.CheckedClass|(R|/arg|) + R|/CheckedClass.CheckedClass|(R|/arg|) + R|/nestedTwoCheckedOk||>() + R|/nestedTwoCheckedOk||>() + R|/deeplyNestedTwoCheckedOk|>>|>() + R|/deeplyNestedTwoCheckedOk|>>|>() + R|/nestedTwoWithArgCheckedOk||>(R|/CheckedClass.CheckedClass|(R|/arg|)) + R|/deeplyNestedTwoWithArgCheckedOk|>>|>(R|/GenericClass.GenericClass|>|>(R|/GenericClass.GenericClass||>(R|/CheckedClass.CheckedClass|(R|/arg|)))) + } + public final fun unknownTypeFail(arg: R|T|): R|kotlin/Unit| { + R|/checked|(R|/arg|) + } + public final fun unknownTypeFail2(arg: R|T|): R|kotlin/Unit| { + R|/checked|(R|/arg|) + } + public final fun unknownTypeFail3(arg: R|T|): R|kotlin/Unit| { + R|/CheckedClass.CheckedClass|(R|/arg|) + } + public final fun unknownTypeFail4(arg: R|T|): R|kotlin/Unit| { + R|/CheckedClass.CheckedClass|(R|/arg|) + } + public final fun unknownTypeFail5(arg: R|T|): R|kotlin/Unit| { + R|/nestedTwoCheckedOk||>() + } + public final fun unknownTypeFail6(arg: R|T|): R|kotlin/Unit| { + R|/nestedTwoCheckedOk||>() + } + public final fun unknownTypeFail7(arg: R|T|): R|kotlin/Unit| { + R|/nestedTwoWithArgCheckedOk||>(R|/CheckedClass.CheckedClass|(R|/arg|)) + } + public final fun unknownTypeFail8(arg: R|T|): R|kotlin/Unit| { + R|/deeplyNestedTwoCheckedOk|>>|>() + } + public final fun unknownTypeFail9(arg: R|T|): R|kotlin/Unit| { + R|/deeplyNestedTwoCheckedOk|>>|>() + } + public final fun unknownTypeFail10(arg: R|T|): R|kotlin/Unit| { + R|/deeplyNestedTwoWithArgCheckedOk|>>|>(R|/GenericClass.GenericClass|>|>(R|/GenericClass.GenericClass||>(R|/CheckedClass.CheckedClass|(R|/arg|)))) + } diff --git a/tests/compiler-plugin-tests/src/testData/diagnostics/checkedAnnotation.kt b/tests/compiler-plugin-tests/src/testData/diagnostics/checkedAnnotation.kt new file mode 100644 index 000000000..726b5c2b9 --- /dev/null +++ b/tests/compiler-plugin-tests/src/testData/diagnostics/checkedAnnotation.kt @@ -0,0 +1,288 @@ +/* + * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import kotlinx.rpc.annotations.CheckedTypeAnnotation + +// annotations + +@CheckedTypeAnnotation +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.CLASS) +annotation class Checked + +@Checked +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.CLASS) +annotation class SubChecked + +@SubChecked +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.CLASS) +annotation class SubSubChecked + +@CheckedTypeAnnotation +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.CLASS) +annotation class Checked2 + +// annotated + +@Checked +open class AnnotatedChecked + +class AnnotatedCheckedImpl : AnnotatedChecked() + +@SubChecked +class AnnotatedSubChecked + +@SubSubChecked +class AnnotatedSubSubChecked + +@Checked2 +class AnnotatedChecked2 + +@Checked +@Checked2 +class AnnotatedCheckedMulti + +// functions + +fun <@Checked T> checked(arg: T) {} +fun <@SubChecked T> subChecked(arg: T) {} +fun <@SubSubChecked T> subSubChecked(arg: T) {} +fun <@Checked2 T> checked2(arg: T) {} +fun <@Checked @Checked2 T> checkedMulti(arg: T) {} +fun <@Checked T1, @Checked2 T2> checkedTwoParams(arg1: T1, arg2: T2) {} +fun <@Checked T1, T2> checkedTwoParamsSingleAnnotation(arg1: T1, arg2: T2) {} + +// classes + +open class CheckedClass<@Checked T>(arg: T) +@Checked +class CheckedClassChild<@Checked T>(arg: T) : CheckedClass>(CheckedClassChild(arg)) +class CheckedClassChildFail<@Checked T>(arg: T) : CheckedClass<CheckedClassChildFail>(CheckedClassChildFail(arg)) +class CheckedClassChildFail2(arg: T) : CheckedClass<CheckedClassChildFail2>(CheckedClassChildFail2(arg)) +class CheckedClassChildFail3(arg: T) : CheckedClass<T>(arg) +class CheckedClassChildOk<@Checked T>(arg: T) : CheckedClass(arg) +@Checked +class CheckedClassChildOk2(arg: T) : CheckedClass>(CheckedClassChildOk2(arg)) + +class SubCheckedClass<@SubChecked T>(arg: T) +class SubSubCheckedClass<@SubSubChecked T>(arg: T) +class Checked2Class<@Checked2 T>(arg: T) +class CheckedMultiClass<@Checked @Checked2 T>(arg: T) +class CheckedTwoParamsClass<@Checked T1, @Checked2 T2>(arg1: T1, arg2: T2) +class CheckedTwoParamsSingleAnnotationClass<@Checked T1, T2>(arg1: T1, arg2: T2) + +// nested + +class GenericClass(arg: T) + +fun <@Checked T : CheckedClass> nestedCheckedOk() {} +fun <@Checked T : CheckedTwoParamsClass> nestedCheckedThoParamsOk() {} +fun <@SubChecked T : CheckedClass> nestedSubCheckedOk() {} +fun <@SubSubChecked T : CheckedClass> nestedSubSubCheckedOk() {} +fun <@Checked T, C : CheckedClass> nestedTwoCheckedOk() {} +fun <@Checked T, C : GenericClass>>> deeplyNestedTwoCheckedOk() {} +fun T>> nestedTwoCheckedFail() {} +fun <@Checked2 T : CheckedClass<T>> nestedChecked2Fail() {} +fun T>> nestedCheckedFail() {} + +fun <@Checked T> nestedArgumetTypeCheckedOk(arg: CheckedClass) {} +fun nestedArgumetTypeCheckedFail(arg: CheckedClass<T>) {} + +fun <@Checked T> doubleNestedArgumetTypeCheckedOk(arg: GenericClass>) {} +fun doubleNestedArgumetTypeCheckedFail(arg: GenericClassT>>) {} + +class NestedArgumetTypeCheckedOk<@Checked T>(arg: CheckedClass) {} +class NestedArgumetTypeCheckedFail(arg: CheckedClass<T>) {} + +class DoubleNestedArgumetTypeCheckedOk<@Checked T> (arg: GenericClass>) {} +class DoubleNestedArgumetTypeCheckedFail(arg: GenericClassT>>) {} + +class CheckedNestedClass<@Checked T : CheckedClass>() +class CheckedNestedClassFailT>>() + +fun <@Checked T : CheckedNestedClass> doubleNestedCheckedOk() {} +fun T>> doubleNestedCheckedFail() {} + +fun <@Checked T : GenericClass>> tripleNestedCheckedOk() {} +fun T>>> tripleNestedCheckedFail() {} +fun <@Checked T : GenericClass>>> quadrupleNestedCheckedOk() {} +fun T>>>> quadrupleNestedCheckedFail() {} +fun <@SubSubChecked T : GenericClass>>>> quintupleNestedCheckedOk() {} +fun T>>>>> quintupleNestedCheckedFail() {} + +fun main() { + // ### Explicit types + + // functions + checked(AnnotatedChecked()) + checked<AnnotatedCheckedImpl>(AnnotatedCheckedImpl()) + checked(AnnotatedSubChecked()) + checked(AnnotatedSubSubChecked()) + checked<AnnotatedChecked2>(AnnotatedChecked2()) + + subChecked<AnnotatedChecked>(AnnotatedChecked()) + subChecked(AnnotatedSubChecked()) + subChecked(AnnotatedSubSubChecked()) + subChecked<AnnotatedChecked2>(AnnotatedChecked2()) + + subSubChecked<AnnotatedChecked>(AnnotatedChecked()) + subSubChecked<AnnotatedSubChecked>(AnnotatedSubChecked()) + subSubChecked(AnnotatedSubSubChecked()) + subSubChecked<AnnotatedChecked2>(AnnotatedChecked2()) + + checked2<AnnotatedChecked>(AnnotatedChecked()) + checked2<AnnotatedSubChecked>(AnnotatedSubChecked()) + checked2<AnnotatedSubSubChecked>(AnnotatedSubSubChecked()) + checked2(AnnotatedChecked2()) + + checkedMulti<AnnotatedChecked>(AnnotatedChecked()) + checkedMulti<AnnotatedChecked2>(AnnotatedChecked2()) + checkedMulti(AnnotatedCheckedMulti()) + + checkedTwoParams(AnnotatedChecked(), AnnotatedChecked2()) + checkedTwoParams<AnnotatedChecked2, AnnotatedChecked2>(AnnotatedChecked2(), AnnotatedChecked2()) + checkedTwoParamsAnnotatedChecked>(AnnotatedChecked(), AnnotatedChecked()) + checkedTwoParams<AnnotatedChecked2, AnnotatedChecked>(AnnotatedChecked2(), AnnotatedChecked()) + + checkedTwoParamsSingleAnnotation(AnnotatedChecked(), AnnotatedChecked2()) + checkedTwoParamsSingleAnnotation<AnnotatedChecked2, AnnotatedChecked2>(AnnotatedChecked2(), AnnotatedChecked2()) + checkedTwoParamsSingleAnnotation(AnnotatedChecked(), AnnotatedChecked()) + checkedTwoParamsSingleAnnotation<AnnotatedChecked2, AnnotatedChecked>(AnnotatedChecked2(), AnnotatedChecked()) + + nestedCheckedOk>() + nestedCheckedOkAnnotatedCheckedImpl>>() + nestedTwoCheckedOk>() + nestedTwoCheckedOk<AnnotatedCheckedImpl, CheckedClass<AnnotatedCheckedImpl>>() + + deeplyNestedTwoCheckedOk>>>() + deeplyNestedTwoCheckedOk<AnnotatedCheckedImpl, GenericClassAnnotatedCheckedImpl>>>>() + + // classes + CheckedClass(AnnotatedChecked()) + CheckedClass(AnnotatedCheckedImpl()) + CheckedClass<AnnotatedCheckedImpl>(AnnotatedCheckedImpl()) + CheckedClass(AnnotatedSubChecked()) + CheckedClass(AnnotatedSubSubChecked()) + CheckedClass<AnnotatedChecked2>(AnnotatedChecked2()) + + SubCheckedClass<AnnotatedChecked>(AnnotatedChecked()) + SubCheckedClass(AnnotatedSubChecked()) + SubCheckedClass(AnnotatedSubSubChecked()) + SubCheckedClass<AnnotatedChecked2>(AnnotatedChecked2()) + + SubSubCheckedClass<AnnotatedChecked>(AnnotatedChecked()) + SubSubCheckedClass<AnnotatedSubChecked>(AnnotatedSubChecked()) + SubSubCheckedClass(AnnotatedSubSubChecked()) + SubSubCheckedClass<AnnotatedChecked2>(AnnotatedChecked2()) + + Checked2Class<AnnotatedChecked>(AnnotatedChecked()) + Checked2Class<AnnotatedSubChecked>(AnnotatedSubChecked()) + Checked2Class<AnnotatedSubSubChecked>(AnnotatedSubSubChecked()) + Checked2Class(AnnotatedChecked2()) + + CheckedMultiClass<AnnotatedChecked>(AnnotatedChecked()) + CheckedMultiClass<AnnotatedChecked2>(AnnotatedChecked2()) + CheckedMultiClass(AnnotatedCheckedMulti()) + + CheckedTwoParamsClass(AnnotatedChecked(), AnnotatedChecked2()) + CheckedTwoParamsClass<AnnotatedChecked2, AnnotatedChecked2>(AnnotatedChecked2(), AnnotatedChecked2()) + CheckedTwoParamsClassAnnotatedChecked>(AnnotatedChecked(), AnnotatedChecked()) + CheckedTwoParamsClass<AnnotatedChecked2, AnnotatedChecked>(AnnotatedChecked2(), AnnotatedChecked()) + + CheckedTwoParamsSingleAnnotationClass(AnnotatedChecked(), AnnotatedChecked2()) + CheckedTwoParamsSingleAnnotationClass<AnnotatedChecked2, AnnotatedChecked2>(AnnotatedChecked2(), AnnotatedChecked2()) + CheckedTwoParamsSingleAnnotationClass(AnnotatedChecked(), AnnotatedChecked()) + CheckedTwoParamsSingleAnnotationClass<AnnotatedChecked2, AnnotatedChecked>(AnnotatedChecked2(), AnnotatedChecked()) + + // ### implicit types + + // functions + + checked(AnnotatedChecked()) + checked(AnnotatedCheckedImpl()) + checked(AnnotatedSubChecked()) + checked(AnnotatedSubSubChecked()) + checked(AnnotatedChecked2()) + + // classes + + CheckedClass(AnnotatedChecked()) + CheckedClass(AnnotatedCheckedImpl()) + CheckedClass(AnnotatedSubChecked()) + CheckedClass(AnnotatedSubSubChecked()) + CheckedClass(AnnotatedChecked2()) + + // ### partially implicit types + + // functions + + checkedTwoParams(AnnotatedChecked(), AnnotatedChecked2()) + checkedTwoParams<_, AnnotatedChecked2>(AnnotatedChecked(), AnnotatedChecked2()) + checkedTwoParams<_, AnnotatedChecked2>(AnnotatedChecked2(), AnnotatedChecked2()) + checkedTwoParams_>(AnnotatedChecked(), AnnotatedChecked()) + checkedTwoParams<_, _>(AnnotatedChecked2(), AnnotatedChecked()) + + // classes + + CheckedTwoParamsClass(AnnotatedChecked(), AnnotatedChecked2()) + CheckedTwoParamsClass<_, AnnotatedChecked2>(AnnotatedChecked(), AnnotatedChecked2()) + CheckedTwoParamsClass<_, AnnotatedChecked2>(AnnotatedChecked2(), AnnotatedChecked2()) + CheckedTwoParamsClass_>(AnnotatedChecked(), AnnotatedChecked()) + CheckedTwoParamsClass<_, _>(AnnotatedChecked2(), AnnotatedChecked()) +} + +fun <@Checked T, C : CheckedClass> nestedTwoWithArgCheckedOk(arg: C) {} +fun <@Checked T, C : GenericClass>>> deeplyNestedTwoWithArgCheckedOk(arg: C) {} + +fun <@Checked T> unknownTypeOk(arg: T) { + checked(arg) + checked(arg) + CheckedClass(arg) + CheckedClass(arg) + nestedTwoCheckedOk>() + nestedTwoCheckedOk() + deeplyNestedTwoCheckedOk>>>() + deeplyNestedTwoCheckedOk() + nestedTwoWithArgCheckedOk(CheckedClass(arg)) + deeplyNestedTwoWithArgCheckedOk(GenericClass(GenericClass(CheckedClass(arg)))) +} + +fun unknownTypeFail(arg: T) { + checked<T>(arg) +} + +fun unknownTypeFail2(arg: T) { + checked(arg) +} + +fun unknownTypeFail3(arg: T) { + CheckedClass<T>(arg) +} + +fun unknownTypeFail4(arg: T) { + CheckedClass(arg) +} + +fun unknownTypeFail5(arg: T) { + nestedTwoCheckedOk<T, CheckedClass<T>>() +} + +fun unknownTypeFail6(arg: T) { + nestedTwoCheckedOk<T, _>() +} + +fun unknownTypeFail7(arg: T) { + nestedTwoWithArgCheckedOk(CheckedClass(arg)) +} + +fun unknownTypeFail8(arg: T) { + deeplyNestedTwoCheckedOk<T, GenericClassT>>>>() +} + +fun unknownTypeFail9(arg: T) { + deeplyNestedTwoCheckedOk<T, _>() +} + +fun unknownTypeFail10(arg: T) { + deeplyNestedTwoWithArgCheckedOk(GenericClass(GenericClass(CheckedClass(arg)))) +} diff --git a/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.fir.txt b/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.fir.txt new file mode 100644 index 000000000..4b07b807d --- /dev/null +++ b/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.fir.txt @@ -0,0 +1,87 @@ +FILE: rpcChecked.kt + @FILE:R|kotlin/OptIn|(markerClass = vararg((Q|kotlinx/rpc/internal/utils/ExperimentalRpcApi|))) + @R|kotlinx/rpc/annotations/Rpc|() public abstract interface MyService : R|kotlin/Any|, R|kotlinx/rpc/RemoteService| { + public final class $rpcServiceStub : R|kotlin/Any| { + public final companion object Companion : R|kotlin/Any| { + } + + } + + } + public final class NotAService : R|kotlin/Any| { + public constructor(coroutineContext: R|kotlin/coroutines/CoroutineContext|): R|NotAService| { + super() + } + + public final val coroutineContext: R|kotlin/coroutines/CoroutineContext| = R|/coroutineContext| + public get(): R|kotlin/coroutines/CoroutineContext| + + } + public final class MyServiceImpl : R|MyService| { + public constructor(coroutineContext: R|kotlin/coroutines/CoroutineContext|): R|MyServiceImpl| { + super() + } + + public open override val coroutineContext: R|kotlin/coroutines/CoroutineContext| = R|/coroutineContext| + public get(): R|kotlin/coroutines/CoroutineContext| + + } + public final inline suspend fun <@R|kotlinx/rpc/annotations/Rpc|() reified T : R|kotlin/Any|> ok(client: R|kotlinx/rpc/RpcClient|, server: R|kotlinx/rpc/RpcServer|, impl: R|T|, myServiceImpl: R|MyService|): R|kotlin/Unit| { + R|/client|.R|kotlinx/rpc/withService|() + R|/client|.R|kotlinx/rpc/withService|() + R|/server|.R|kotlinx/rpc/registerService|( = registerService@fun (it: R|kotlin/coroutines/CoroutineContext|): R|MyService| { + ^ R|/MyServiceImpl.MyServiceImpl|(R|/it|) + } + ) + R|/server|.R|kotlinx/rpc/registerService|( = registerService@fun (it: R|kotlin/coroutines/CoroutineContext|): R|T| { + ^ R|/impl| + } + ) + R|/myServiceImpl|.R|kotlinx/rpc/awaitFieldInitialization|() + R|/myServiceImpl|.R|kotlinx/rpc/awaitFieldInitialization|( = awaitFieldInitialization@fun R|MyService|.(): R|kotlin/Int| { + ^ Int(1) + } + ) + R|/impl|.R|kotlinx/rpc/awaitFieldInitialization|() + R|/impl|.R|kotlinx/rpc/awaitFieldInitialization|( = awaitFieldInitialization@fun R|T|.(): R|kotlin/Int| { + ^ Int(1) + } + ) + R|kotlinx/rpc/descriptor/serviceDescriptorOf|() + R|kotlinx/rpc/descriptor/serviceDescriptorOf|() + } + public final inline suspend fun fail(client: R|kotlinx/rpc/RpcClient|, server: R|kotlinx/rpc/RpcServer|, impl: R|T|, myServiceImpl: R|MyServiceImpl|, notAServiceImpl: R|NotAService|): R|kotlin/Unit| { + R|/client|.R|kotlinx/rpc/withService|() + R|/client|.R|kotlinx/rpc/withService|() + R|/client|.R|kotlinx/rpc/withService|() + R|/server|.R|kotlinx/rpc/registerService|( = registerService@fun (it: R|kotlin/coroutines/CoroutineContext|): R|MyServiceImpl| { + ^ R|/MyServiceImpl.MyServiceImpl|(R|/it|) + } + ) + R|/server|.R|kotlinx/rpc/registerService|( = registerService@fun (it: R|kotlin/coroutines/CoroutineContext|): R|NotAService| { + ^ R|/NotAService.NotAService|(R|/it|) + } + ) + R|/server|.R|kotlinx/rpc/registerService|( = registerService@fun (it: R|kotlin/coroutines/CoroutineContext|): R|T| { + ^ R|/impl| + } + ) + R|/myServiceImpl|.R|kotlinx/rpc/awaitFieldInitialization|() + R|/myServiceImpl|.R|kotlinx/rpc/awaitFieldInitialization|( = awaitFieldInitialization@fun R|MyServiceImpl|.(): R|kotlin/Int| { + ^ Int(1) + } + ) + R|/notAServiceImpl|.R|kotlinx/rpc/awaitFieldInitialization|() + R|/notAServiceImpl|.R|kotlinx/rpc/awaitFieldInitialization|( = awaitFieldInitialization@fun R|NotAService|.(): R|kotlin/Int| { + ^ Int(1) + } + ) + R|/impl|.R|kotlinx/rpc/awaitFieldInitialization|() + R|/impl|.R|kotlinx/rpc/awaitFieldInitialization|( = awaitFieldInitialization@fun R|T|.(): R|kotlin/Int| { + ^ Int(1) + } + ) + R|kotlinx/rpc/descriptor/serviceDescriptorOf|() + R|kotlinx/rpc/descriptor/serviceDescriptorOf|() + R|kotlinx/rpc/descriptor/serviceDescriptorOf|() + } diff --git a/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.kt b/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.kt new file mode 100644 index 000000000..51efbb45e --- /dev/null +++ b/tests/compiler-plugin-tests/src/testData/diagnostics/rpcChecked.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:OptIn(ExperimentalRpcApi::class) + +import kotlin.coroutines.* +import kotlinx.coroutines.* +import kotlinx.rpc.annotations.Rpc +import kotlinx.rpc.withService +import kotlinx.rpc.RpcClient +import kotlinx.rpc.RpcServer +import kotlinx.rpc.registerService +import kotlinx.rpc.descriptor.serviceDescriptorOf +import kotlinx.rpc.awaitFieldInitialization +import kotlinx.rpc.internal.utils.ExperimentalRpcApi + +@Rpc +interface MyService + +class NotAService(val coroutineContext: CoroutineContext) + +class MyServiceImpl(override val coroutineContext: CoroutineContext) : MyService + +inline suspend fun <@Rpc reified T : Any> ok(client: RpcClient, server: RpcServer, impl: T, myServiceImpl: MyService) { + client.withService() + client.withService() + + server.registerService { MyServiceImpl(it) } + server.registerService { impl } + + myServiceImpl.awaitFieldInitialization() + myServiceImpl.awaitFieldInitialization { 1 } + + impl.awaitFieldInitialization() + impl.awaitFieldInitialization { 1 } + + serviceDescriptorOf() + serviceDescriptorOf() +} + +inline suspend fun fail(client: RpcClient, server: RpcServer, impl: T, myServiceImpl: MyServiceImpl, notAServiceImpl: NotAService) { + client.withService<MyServiceImpl>() + client.withService<NotAService>() + client.withService<T>() + + server.registerService<MyServiceImpl> { MyServiceImpl(it) } + server.registerService<NotAService> { NotAService(it) } + server.registerService<T> { impl } + + myServiceImpl.awaitFieldInitialization<MyServiceImpl>() + myServiceImpl.awaitFieldInitialization<MyServiceImpl, Int> { 1 } + + notAServiceImpl.awaitFieldInitialization<NotAService>() + notAServiceImpl.awaitFieldInitialization<NotAService, Int> { 1 } + + impl.awaitFieldInitialization<T>() + impl.awaitFieldInitialization<T, Int> { 1 } + + serviceDescriptorOf<MyServiceImpl>() + serviceDescriptorOf<NotAService>() + serviceDescriptorOf<T>() +}