Skip to content
This repository was archived by the owner on Jan 28, 2025. It is now read-only.

Commit 16e5cbe

Browse files
Gregory Lureauglureau
authored andcommitted
Report error when generating classes with Exception or subclasses.
1 parent ddeb103 commit 16e5cbe

File tree

2 files changed

+44
-33
lines changed

2 files changed

+44
-33
lines changed

compiler/src/main/kotlin/deezer/kustomexport/compiler/js/pattern/ClassDeclarationParser.kt

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package deezer.kustomexport.compiler.js.pattern
2121

22+
import com.google.devtools.ksp.containingFile
23+
import com.google.devtools.ksp.getAllSuperTypes
2224
import com.google.devtools.ksp.getConstructors
2325
import com.google.devtools.ksp.getDeclaredFunctions
2426
import com.google.devtools.ksp.getDeclaredProperties
@@ -28,8 +30,11 @@ import com.google.devtools.ksp.symbol.ClassKind
2830
import com.google.devtools.ksp.symbol.KSClassDeclaration
2931
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
3032
import com.google.devtools.ksp.symbol.KSName
33+
import com.google.devtools.ksp.symbol.KSNode
3134
import com.google.devtools.ksp.symbol.KSTypeParameter
35+
import com.google.devtools.ksp.symbol.KSTypeReference
3236
import com.google.devtools.ksp.symbol.Modifier
37+
import com.squareup.kotlinpoet.THROWABLE
3338
import com.squareup.kotlinpoet.TypeName
3439
import com.squareup.kotlinpoet.ksp.KotlinPoetKspPreview
3540
import com.squareup.kotlinpoet.ksp.TypeParameterResolver
@@ -70,12 +75,7 @@ fun parseClass(
7075
forcedConcreteTypeParameters: List<Pair<String, TypeName>>? = null,
7176
exportedClassSimpleName: String
7277
): Descriptor {
73-
if (classDeclaration.isThrowable()) {
74-
error(
75-
"Cannot parse a class that is Throwable: " +
76-
(classDeclaration.qualifiedName?.asString() ?: classDeclaration.simpleName.asString())
77-
)
78-
}
78+
classDeclaration.assertNotThrowable(classDeclaration)
7979
val typeParamResolver = classDeclaration.typeParameters.toTypeParameterResolver()
8080

8181
val concreteTypeParameters: MutableList<TypeParameterDescriptor> =
@@ -87,11 +87,7 @@ fun parseClass(
8787
val superTypes = classDeclaration.superTypes
8888
.map { superType ->
8989
val superTypeName = superType.toTypeNamePatch(typeParamResolver).cached(concreteTypeParameters)
90-
9190
val declaration = superType.resolve().declaration
92-
//val qualifiedName = (declaration as? KSClassDeclaration)?.qualifiedName
93-
//val isKotlinException = ALL_KOTLIN_EXCEPTIONS.any { it.canonicalName == qualifiedName }
94-
9591
if (declaration is KSClassDeclaration) {
9692
val ctors = declaration.getConstructors().toList()
9793
val superParams = if (ctors.isNotEmpty()) emptyList<ParameterDescriptor>() else null
@@ -104,6 +100,7 @@ fun parseClass(
104100
.toList()
105101

106102
val constructorParams = classDeclaration.primaryConstructor?.parameters?.map {
103+
it.type.assertNotThrowable(it)
107104
ParameterDescriptor(
108105
name = it.name!!.asString(),
109106
type = it.type.toTypeNamePatch(typeParamResolver).cached(concreteTypeParameters)
@@ -192,8 +189,8 @@ private fun buildConcreteTypeParameters(
192189
type.asClassName()
193190
} catch (t: Throwable) {
194191
Logger.error(
195-
"Cannot use @KustomException on a not concrete generics class.",
196-
firstTypeParameterProvider()
192+
message = "Cannot use @KustomExport on a not concrete generics class.",
193+
symbol = firstTypeParameterProvider()
197194
)
198195
error(t)
199196
}
@@ -214,16 +211,6 @@ private val nonExportableFunctions = listOf(
214211
"toString",
215212
"copy",
216213

217-
// For exceptions :
218-
"getLocalizedMessage",
219-
"initCause",
220-
"printStackTrace",
221-
"fillInStackTrace",
222-
"getStackTrace",
223-
"setStackTrace",
224-
"addSuppressed",
225-
"getSuppressed",
226-
227214
) + (1..30).map { "component$it" }
228215

229216
@OptIn(KotlinPoetKspPreview::class)
@@ -244,19 +231,22 @@ private fun KSFunctionDeclaration.toDescriptor(
244231
declaredNames: Sequence<KSName>,
245232
typeParamResolver: TypeParameterResolver,
246233
concreteTypeParameters: MutableList<TypeParameterDescriptor>
247-
) =
248-
FunctionDescriptor(
234+
): FunctionDescriptor {
235+
returnType?.assertNotThrowable(this)
236+
return FunctionDescriptor(
249237
name = simpleName.asString(),
250238
isOverride = findOverridee() != null || !declaredNames.contains(simpleName),
251239
isSuspend = modifiers.contains(Modifier.SUSPEND),
252240
returnType = returnType!!.toTypeNamePatch(typeParamResolver).cached(concreteTypeParameters),
253241
parameters = parameters.map { p ->
242+
p.type.assertNotThrowable(p)
254243
ParameterDescriptor(
255244
name = p.name?.asString() ?: TODO("not sure what we want here"),
256245
type = p.type.toTypeNamePatch(typeParamResolver).cached(concreteTypeParameters),
257246
)
258247
}
259248
)
249+
}
260250

261251
@OptIn(KotlinPoetKspPreview::class)
262252
fun KSClassDeclaration.parseProperties(
@@ -266,10 +256,11 @@ fun KSClassDeclaration.parseProperties(
266256
val declaredNames = getDeclaredProperties().mapNotNull { it.simpleName }
267257
return getAllProperties().mapNotNull { prop ->
268258
// TODO: rework configuration by naming
269-
val classExtendsException = this.simpleName.asString().endsWith("Exception")
270259
if (prop.isPrivate()) {
271260
null // Cannot be accessed
272261
} else {
262+
prop.type.assertNotThrowable(prop)
263+
273264
val type = prop.type.toTypeNamePatch(typeParamResolver)
274265
// Retrieve the names of function arguments, like: (*MyName*: String) -> Unit
275266
// Problem: we use KotlinPoet TypeName for the mapping, and it doesn't have the value available.
@@ -279,7 +270,6 @@ fun KSClassDeclaration.parseProperties(
279270
}
280271
} else emptyList()
281272
*/
282-
283273
PropertyDescriptor(
284274
name = prop.simpleName.asString(),
285275
type = type.cached(concreteTypeParameters),
@@ -291,21 +281,42 @@ fun KSClassDeclaration.parseProperties(
291281
}.toList()
292282
}
293283

284+
fun KSTypeReference.assertNotThrowable(symbol: KSNode) {
285+
val decl = resolve().declaration
286+
if (decl is KSClassDeclaration) {
287+
decl.assertNotThrowable(symbol)
288+
}
289+
}
290+
291+
fun KSClassDeclaration.assertNotThrowable(symbol: KSNode) {
292+
if (isThrowable() && qualifiedName?.asString() != THROWABLE.canonicalName) {
293+
Logger.error(
294+
message = "Cannot export ${this.qualifiedName?.asString() ?: simpleName.asString()}.\n" +
295+
"You cannot export an Exception or subclasses of Exception but you can export a Throwable instead.",
296+
symbol = symbol
297+
)
298+
}
299+
}
300+
294301
fun KSClassDeclaration.isThrowable(): Boolean {
295-
superTypes
302+
if (toClassName() in ALL_KOTLIN_EXCEPTIONS) {
303+
return true
304+
}
305+
306+
getAllSuperTypes()
296307
.forEach { superType ->
297-
val superTypeResolved = superType.resolve()
298-
if (superTypeResolved.toClassName() in ALL_KOTLIN_EXCEPTIONS) {
308+
if (superType.toClassName() in ALL_KOTLIN_EXCEPTIONS) {
299309
return true
300310
}
301311

302-
val declaration = superTypeResolved.declaration
312+
val declaration = superType.declaration
303313
if (declaration is KSClassDeclaration && declaration.isThrowable()) {
304314
return true
305315
}
306316
}
307317
return false
308318
}
309319

310-
fun TypeName.cached(concreteTypeParameters: List<TypeParameterDescriptor>) =
311-
OriginTypeName(this, concreteTypeParameters)
320+
fun TypeName.cached(concreteTypeParameters: List<TypeParameterDescriptor>): OriginTypeName {
321+
return OriginTypeName(this, concreteTypeParameters)
322+
}

samples/src/commonMain/kotlin/sample/_exception/Exceptions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package sample._exception
33
import deezer.kustomexport.KustomExport
44

55
@KustomExport
6-
class MyEx(val cause: IllegalStateException = IllegalStateException("hello"))
6+
class MyEx(val cause: Throwable = IllegalStateException("hello"))
77

88
@KustomExport
99
class ExceptionConsumer {

0 commit comments

Comments
 (0)