Skip to content

Commit

Permalink
Added minor checks for @Rpc services
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr3zee committed Dec 3, 2024
1 parent 9847059 commit 4288b20
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class FirRpcDeclarationCheckers(ctx: FirCheckersContext) : DeclarationCheckers()
override val regularClassCheckers: Set<FirRegularClassChecker> = setOfNotNull(
FirRpcAnnotationChecker(ctx),
if (ctx.serializationIsPresent) FirRpcStrictModeClassChecker(ctx) else null,
FirRpcServiceDeclarationChecker(ctx),
)

override val classCheckers: Set<FirClassChecker> = setOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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.diagnostics.FirRpcDiagnostics
import kotlinx.rpc.codegen.common.RpcClassId
import kotlinx.rpc.codegen.vsApi
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
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.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.types.coneType

class FirRpcServiceDeclarationChecker(
@Suppress("unused")
private val ctx: FirCheckersContext,
) : FirClassChecker(MppCheckerKind.Common) {
override fun check(
declaration: FirClass,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (!context.session.predicateBasedProvider.matches(FirRpcPredicates.rpc, declaration)) {
return
}

if (declaration.typeParameters.isNotEmpty()) {
reporter.reportOn(
source = declaration.source,
factory = FirRpcDiagnostics.TYPE_PARAMETERS_IN_RPC_INTERFACE,
context = context,
)
}

declaration.declarations.filterIsInstance<FirSimpleFunction>().onEach { function ->
if (function.typeParameters.isNotEmpty()) {
reporter.reportOn(
source = function.source,
factory = FirRpcDiagnostics.TYPE_PARAMETERS_IN_RPC_FUNCTION,
context = context,
)
}

val returnType = vsApi { function.returnTypeRef.coneType.toClassSymbolVS(context.session) }
?: return@onEach

if (returnType.classId != RpcClassId.flow && !function.isSuspend) {
reporter.reportOn(
source = function.source,
factory = FirRpcDiagnostics.NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE,
context = context,
)
}
}
.groupBy { it.name }
.filter { (_, list) -> list.size > 1 }
.forEach { name, functions ->
functions.forEach { function ->
reporter.reportOn(
source = function.source,
factory = FirRpcDiagnostics.AD_HOC_POLYMORPHISM_IN_RPC_SERVICE,
a = functions.size,
b = name,
context = context,
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

@file:Suppress("unused")

package kotlinx.rpc.codegen.checkers.diagnostics

import kotlinx.rpc.codegen.StrictMode
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory0
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.utils.DummyDelegate
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty

inline fun <reified T> modded0(mode: StrictMode): DiagnosticFactory0DelegateProviderOnNull {
return DiagnosticFactory0DelegateProviderOnNull(mode, T::class)
}

class DiagnosticFactory0DelegateProviderOnNull(
private val mode: StrictMode,
private val psiType: KClass<*>,
) {
operator fun provideDelegate(
@Suppress("unused")
thisRef: Any?,
prop: KProperty<*>,
): ReadOnlyProperty<Any?, KtDiagnosticFactory0?> {
val severity = when (mode) {
StrictMode.ERROR -> Severity.ERROR
StrictMode.WARNING -> Severity.WARNING
StrictMode.NONE -> null
} ?: return DummyDelegate(null)

return DummyDelegate(
KtDiagnosticFactory0(
name = prop.name,
severity = severity,
defaultPositioningStrategy = SourceElementPositioningStrategies.DEFAULT,
psiType = psiType,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,27 @@

package kotlinx.rpc.codegen.checkers.diagnostics

import kotlinx.rpc.codegen.StrictMode
import kotlinx.rpc.codegen.StrictModeAggregator
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory0
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.diagnostics.error0
import org.jetbrains.kotlin.diagnostics.error1
import org.jetbrains.kotlin.diagnostics.error2
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.warning0
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.utils.DummyDelegate
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass
import kotlin.reflect.KProperty

object FirRpcDiagnostics {
val MISSING_RPC_ANNOTATION by error0<KtAnnotationEntry>()
val MISSING_SERIALIZATION_MODULE by warning0<KtAnnotationEntry>()
val WRONG_RPC_ANNOTATION_TARGET by error0<KtAnnotationEntry>()
val CHECKED_ANNOTATION_VIOLATION by error1<KtAnnotationEntry, ConeKotlinType>()
val NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE by error0<PsiElement>()
val AD_HOC_POLYMORPHISM_IN_RPC_SERVICE by error2<PsiElement, Int, Name>()
val TYPE_PARAMETERS_IN_RPC_FUNCTION by error0<PsiElement>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
val TYPE_PARAMETERS_IN_RPC_INTERFACE by error0<PsiElement>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)

init {
RootDiagnosticRendererFactory.registerFactory(RpcDiagnosticRendererFactory)
Expand All @@ -46,33 +45,3 @@ class FirRpcStrictModeDiagnostics(val modes: StrictModeAggregator) {
RootDiagnosticRendererFactory.registerFactory(RpcStrictModeDiagnosticRendererFactory(this))
}
}

private inline fun <reified T> modded0(mode: StrictMode): DiagnosticFactory0DelegateProviderOnNull {
return DiagnosticFactory0DelegateProviderOnNull(mode, T::class)
}

class DiagnosticFactory0DelegateProviderOnNull(
private val mode: StrictMode,
private val psiType: KClass<*>,
) {
operator fun provideDelegate(
@Suppress("unused")
thisRef: Any?,
prop: KProperty<*>,
): ReadOnlyProperty<Any?, KtDiagnosticFactory0?> {
val severity = when (mode) {
StrictMode.ERROR -> Severity.ERROR
StrictMode.WARNING -> Severity.WARNING
StrictMode.NONE -> null
} ?: return DummyDelegate(null)

return DummyDelegate(
KtDiagnosticFactory0(
name = prop.name,
severity = severity,
defaultPositioningStrategy = SourceElementPositioningStrategies.DEFAULT,
psiType = psiType,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import kotlinx.rpc.codegen.StrictMode
import kotlinx.rpc.codegen.StrictModeAggregator
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.Renderer
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers

object RpcDiagnosticRendererFactory : BaseDiagnosticRendererFactory() {
Expand Down Expand Up @@ -37,6 +38,28 @@ object RpcDiagnosticRendererFactory : BaseDiagnosticRendererFactory() {
"must be annotated with {0} or an annotation annotated with {0}.",
rendererA = FirDiagnosticRenderers.RENDER_TYPE,
)

put(
factory = FirRpcDiagnostics.NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE,
message = "Non suspending request function is not allowed for functions that doesn't return Flow.",
)

put(
factory = FirRpcDiagnostics.AD_HOC_POLYMORPHISM_IN_RPC_SERVICE,
message = "Ad-hoc polymorphism is not allowed in @Rpc services. Found {0} '{1}' functions.",
rendererA = Renderer { it.toString() },
rendererB = Renderer { it.asString() },
)

put(
factory = FirRpcDiagnostics.TYPE_PARAMETERS_IN_RPC_FUNCTION,
message = "Type parameters are not allowed in Rpc functions.",
)

put(
factory = FirRpcDiagnostics.TYPE_PARAMETERS_IN_RPC_INTERFACE,
message = "Type parameters are not allowed in @Rpc interfaces.",
)
}
}

Expand Down

0 comments on commit 4288b20

Please sign in to comment.