Skip to content

Commit 390b0d7

Browse files
committed
StackOverflowError on circular reference #376
1 parent 25a42ab commit 390b0d7

File tree

11 files changed

+65
-32
lines changed

11 files changed

+65
-32
lines changed

compiler-plugin/compiler-plugin-k2/src/main/kotlin/kotlinx/rpc/codegen/checkers/FirRpcStrictModeClassChecker.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ object FirRpcStrictModeClassChecker {
7474
} memoryOptimizedPlus (function.resolvedReturnTypeRef.source to function.resolvedReturnTypeRef)
7575

7676
types.forEach { (source, symbol) ->
77-
checkSerializableTypes<FirClassLikeSymbol<*>>(
77+
checkSerializableTypes(
7878
context = context,
7979
typeRef = symbol,
8080
serializablePropertiesProvider = serializablePropertiesProvider,
@@ -96,8 +96,6 @@ object FirRpcStrictModeClassChecker {
9696
}
9797
}
9898
}
99-
100-
symbol
10199
}
102100
}
103101

@@ -106,25 +104,27 @@ object FirRpcStrictModeClassChecker {
106104
}
107105
}
108106

109-
private fun <ContextElement> checkSerializableTypes(
107+
private fun checkSerializableTypes(
110108
context: CheckerContext,
111109
typeRef: FirTypeRef,
112110
serializablePropertiesProvider: FirSerializablePropertiesProvider,
113-
parentContext: List<ContextElement> = emptyList(),
114-
checker: (FirClassLikeSymbol<*>, List<ContextElement>) -> ContextElement?,
111+
parentContext: List<FirClassLikeSymbol<*>> = emptyList(),
112+
checker: (FirClassLikeSymbol<*>, List<FirClassLikeSymbol<*>>) -> Unit,
115113
) {
116114
val symbol = typeRef.toClassLikeSymbol(context.session) ?: return
117-
val newElement = checker(symbol, parentContext)
118-
val nextContext = if (newElement != null) {
119-
parentContext memoryOptimizedPlus newElement
120-
} else {
121-
parentContext
115+
116+
if (symbol in parentContext) {
117+
return
122118
}
123119

120+
checker(symbol, parentContext)
121+
124122
if (symbol !is FirClassSymbol<*>) {
125123
return
126124
}
127125

126+
val nextContext = parentContext memoryOptimizedPlus symbol
127+
128128
val extracted = extractArgumentsTypeRefAndSource(typeRef)
129129
.orEmpty()
130130
.withIndex()

compiler-plugin/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ org.gradle.parallel=true
1515
org.gradle.workers.max=8
1616
org.gradle.caching=true
1717
org.gradle.configuration-cache=true
18-
org.gradle.configureondemand=true
18+
#org.gradle.configureondemand=true // breaks compiler tests
1919

2020
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
2121
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true

dokka-plugin/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ org.gradle.parallel=true
1515
org.gradle.workers.max=8
1616
org.gradle.caching=true
1717
org.gradle.configuration-cache=true
18-
org.gradle.configureondemand=true
18+
#org.gradle.configureondemand=true // breaks compiler tests
1919

2020
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
2121
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true

gradle-conventions/src/main/kotlin/conventions-common.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
import io.gitlab.arturbosch.detekt.Detekt

gradle-conventions/src/main/kotlin/conventions-kmp.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
@file:OptIn(ExperimentalAbiValidation::class)

gradle-conventions/src/main/kotlin/conventions-publishing.gradle.kts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,6 @@ fun RepositoryHandler.configureLocalDevRepository() {
138138
}
139139
}
140140

141-
val sonatypeRepositoryUri: String?
142-
get() {
143-
val repositoryId: String = project.getSensitiveProperty("libs.repository.id")
144-
?.takeIf { it.isNotBlank() }
145-
?: return null
146-
147-
return "https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId"
148-
}
149-
150141
fun configureEmptyJavadocArtifact(): TaskProvider<Jar?> {
151142
val javadocJar by project.tasks.registering(Jar::class) {
152143
archiveClassifier.set("javadoc")

gradle-plugin/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ org.gradle.parallel=true
1515
org.gradle.workers.max=8
1616
org.gradle.caching=true
1717
org.gradle.configuration-cache=true
18-
org.gradle.configureondemand=true
18+
#org.gradle.configureondemand=true // breaks compiler tests
1919

2020
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
2121
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ org.gradle.parallel=true
1515
org.gradle.workers.max=8
1616
org.gradle.caching=true
1717
org.gradle.configuration-cache=true
18-
org.gradle.configureondemand=true
18+
#org.gradle.configureondemand=true // breaks compiler tests
1919

2020
org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
2121
org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,37 @@ FILE: b.kt
4848

4949
public final fun copy(flow1: R|kotlinx/coroutines/flow/Flow<kotlin/Int>| = this@R|/MultiFlow|.R|/MultiFlow.flow1|, flow2: R|kotlinx/coroutines/flow/Flow<kotlin/Int>| = this@R|/MultiFlow|.R|/MultiFlow.flow2|, flow3: R|kotlinx/coroutines/flow/Flow<kotlin/Int>| = this@R|/MultiFlow|.R|/MultiFlow.flow3|): R|MultiFlow|
5050

51+
}
52+
public final data class ComplexFilter : R|kotlin/Any| {
53+
public constructor(a: R|SimpleFilter?|, b: R|ComplexFilter?|): R|ComplexFilter| {
54+
super<R|kotlin/Any|>()
55+
}
56+
57+
public final val a: R|SimpleFilter?| = R|<local>/a|
58+
public get(): R|SimpleFilter?|
59+
60+
public final val b: R|ComplexFilter?| = R|<local>/b|
61+
public get(): R|ComplexFilter?|
62+
63+
public final operator fun component1(): R|SimpleFilter?|
64+
65+
public final operator fun component2(): R|ComplexFilter?|
66+
67+
public final fun copy(a: R|SimpleFilter?| = this@R|/ComplexFilter|.R|/ComplexFilter.a|, b: R|ComplexFilter?| = this@R|/ComplexFilter|.R|/ComplexFilter.b|): R|ComplexFilter|
68+
69+
}
70+
public final data class SimpleFilter : R|kotlin/Any| {
71+
public constructor(text: R|kotlin/String|): R|SimpleFilter| {
72+
super<R|kotlin/Any|>()
73+
}
74+
75+
public final val text: R|kotlin/String| = R|<local>/text|
76+
public get(): R|kotlin/String|
77+
78+
public final operator fun component1(): R|kotlin/String|
79+
80+
public final fun copy(text: R|kotlin/String| = this@R|/SimpleFilter|.R|/SimpleFilter.text|): R|SimpleFilter|
81+
5182
}
5283
@R|kotlinx/rpc/annotations/Rpc|() public abstract interface MyService : R|kotlin/Any| {
5384
public abstract val flow: R|kotlinx/coroutines/flow/Flow<kotlin/Int>|
@@ -105,6 +136,8 @@ FILE: b.kt
105136

106137
public abstract fun nonSuspendNoFlowString(): R|kotlin/String|
107138

139+
public abstract suspend fun complex(filter: R|ComplexFilter|): R|kotlin/String|
140+
108141
public final class $rpcServiceStub : R|kotlin/Any| {
109142
public final companion object Companion : R|kotlin/Any| {
110143
}

tests/compiler-plugin-tests/src/testData/diagnostics/strictMode.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ data class MultiFlow(
2929
val flow3: Flow<Int>,
3030
)
3131

32+
data class ComplexFilter(
33+
val a: SimpleFilter?,
34+
val b: ComplexFilter?
35+
)
36+
37+
data class SimpleFilter(val text: String)
38+
3239
@Rpc
3340
interface MyService {
3441
<!FIELD_IN_RPC_SERVICE!>val flow: Flow<Int><!>
@@ -44,17 +51,18 @@ interface MyService {
4451
<!NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE!>fun notTopLevelServerFlow(): <!NON_TOP_LEVEL_SERVER_STREAMING_IN_RPC_SERVICE!>InnerFlow<!><!>
4552
<!NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE!>fun wrappedNotTopLevelServerFlow(): <!NON_TOP_LEVEL_SERVER_STREAMING_IN_RPC_SERVICE!>Wrapper<Flow<Int>><!><!>
4653
suspend fun wrappedALot(): Wrapper<Wrapper<Wrapper<Wrapper<Int>>>>
47-
<!NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE!>fun wrappedALotFlow(): <!NON_TOP_LEVEL_SERVER_STREAMING_IN_RPC_SERVICE!>Wrapper<Wrapper<Wrapper<Flow<Int>>>><!><!>
54+
<!NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE!>fun wrappedALotFlow(): Wrapper<Wrapper<Wrapper<Flow<Int>>>><!>
4855
suspend fun notTopLevelClientFlow(flow: InnerFlow)
49-
fun nestedServerFlow(): <!NESTED_STREAMING_IN_RPC_SERVICE!>Flow<Flow<Int>><!>
50-
fun nestedServerTrickyFlow(): <!NESTED_STREAMING_IN_RPC_SERVICE!>Flow<Wrapper<Flow<Int>>><!>
56+
fun nestedServerFlow(): Flow<Flow<Int>>
57+
fun nestedServerTrickyFlow(): Flow<Wrapper<Flow<Int>>>
5158
<!NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE!>fun serverMultiFlow(): <!NON_TOP_LEVEL_SERVER_STREAMING_IN_RPC_SERVICE, NON_TOP_LEVEL_SERVER_STREAMING_IN_RPC_SERVICE, NON_TOP_LEVEL_SERVER_STREAMING_IN_RPC_SERVICE!>MultiFlow<!><!>
5259
suspend fun clientMultiFlow(flow: MultiFlow)
5360
suspend fun clientMultiFlowMany(flow: MultiFlow, flow2: MultiFlow, flow3: MultiFlow)
5461
suspend fun clientMultiFlowPlain(flow: Flow<Int>, flow2: Flow<Int>, flow3: Flow<Int>)
5562
suspend fun clientInnerFlow(inner: InnerFlow)
56-
suspend fun clientNestedFlow(<!NESTED_STREAMING_IN_RPC_SERVICE!>inner: Flow<Flow<Int>><!>)
57-
suspend fun clientNestedTrickyFlow(<!NESTED_STREAMING_IN_RPC_SERVICE!>inner: Wrapper<Flow<Wrapper<Flow<Int>>>><!>)
63+
suspend fun clientNestedFlow(inner: Flow<Flow<Int>>)
64+
suspend fun clientNestedTrickyFlow(inner: Wrapper<Flow<Wrapper<Flow<Int>>>>)
5865
<!NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE!>fun nonSuspendNoFlow()<!>
5966
<!NON_SUSPENDING_REQUEST_WITHOUT_STREAMING_RETURN_TYPE!>fun nonSuspendNoFlowString(): String<!>
67+
suspend fun complex(filter: ComplexFilter): String // doesn't fail on circular dependency
6068
}

0 commit comments

Comments
 (0)