xref: /aosp_15_r20/external/moshi/moshi-kotlin-codegen/src/main/java/com/squareup/moshi/kotlin/codegen/ksp/KspUtil.kt (revision 238ab3e782f339ab327592a602fa7df0a3f729ad)
1 /*
<lambda>null2  * Copyright (C) 2021 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.moshi.kotlin.codegen.ksp
17 
18 import com.google.devtools.ksp.processing.KSPLogger
19 import com.google.devtools.ksp.processing.Resolver
20 import com.google.devtools.ksp.symbol.ClassKind
21 import com.google.devtools.ksp.symbol.KSAnnotated
22 import com.google.devtools.ksp.symbol.KSAnnotation
23 import com.google.devtools.ksp.symbol.KSClassDeclaration
24 import com.google.devtools.ksp.symbol.KSName
25 import com.google.devtools.ksp.symbol.KSNode
26 import com.google.devtools.ksp.symbol.KSType
27 import com.google.devtools.ksp.symbol.KSTypeAlias
28 import com.google.devtools.ksp.symbol.Origin.KOTLIN
29 import com.google.devtools.ksp.symbol.Origin.KOTLIN_LIB
30 import com.squareup.kotlinpoet.AnnotationSpec
31 import com.squareup.kotlinpoet.ClassName
32 import com.squareup.kotlinpoet.CodeBlock
33 import com.squareup.kotlinpoet.ksp.toClassName
34 
35 internal fun KSClassDeclaration.asType() = asType(emptyList())
36 
37 internal fun KSClassDeclaration.isKotlinClass(): Boolean {
38   return origin == KOTLIN ||
39     origin == KOTLIN_LIB ||
40     isAnnotationPresent(Metadata::class)
41 }
42 
findAnnotationWithTypenull43 internal inline fun <reified T : Annotation> KSAnnotated.findAnnotationWithType(): T? {
44   return getAnnotationsByType(T::class).firstOrNull()
45 }
46 
unwrapTypeAliasnull47 internal fun KSType.unwrapTypeAlias(): KSType {
48   return if (this.declaration is KSTypeAlias) {
49     (this.declaration as KSTypeAlias).type.resolve()
50   } else {
51     this
52   }
53 }
54 
toAnnotationSpecnull55 internal fun KSAnnotation.toAnnotationSpec(resolver: Resolver): AnnotationSpec {
56   val element = annotationType.resolve().unwrapTypeAlias().declaration as KSClassDeclaration
57   val builder = AnnotationSpec.builder(element.toClassName())
58   for (argument in arguments) {
59     val member = CodeBlock.builder()
60     val name = argument.name!!.getShortName()
61     member.add("%L = ", name)
62     addValueToBlock(argument.value!!, resolver, member)
63     builder.addMember(member.build())
64   }
65   return builder.build()
66 }
67 
addValueToBlocknull68 private fun addValueToBlock(value: Any, resolver: Resolver, member: CodeBlock.Builder) {
69   when (value) {
70     is List<*> -> {
71       // Array type
72       member.add("arrayOf(⇥⇥")
73       value.forEachIndexed { index, innerValue ->
74         if (index > 0) member.add(", ")
75         addValueToBlock(innerValue!!, resolver, member)
76       }
77       member.add("⇤⇤)")
78     }
79     is KSType -> {
80       val unwrapped = value.unwrapTypeAlias()
81       val isEnum = (unwrapped.declaration as KSClassDeclaration).classKind == ClassKind.ENUM_ENTRY
82       if (isEnum) {
83         val parent = unwrapped.declaration.parentDeclaration as KSClassDeclaration
84         val entry = unwrapped.declaration.simpleName.getShortName()
85         member.add("%T.%L", parent.toClassName(), entry)
86       } else {
87         member.add("%T::class", unwrapped.toClassName())
88       }
89     }
90     is KSName ->
91       member.add(
92         "%T.%L", ClassName.bestGuess(value.getQualifier()),
93         value.getShortName()
94       )
95     is KSAnnotation -> member.add("%L", value.toAnnotationSpec(resolver))
96     else -> member.add(memberForValue(value))
97   }
98 }
99 
100 /**
101  * Creates a [CodeBlock] with parameter `format` depending on the given `value` object.
102  * Handles a number of special cases, such as appending "f" to `Float` values, and uses
103  * `%L` for other types.
104  */
memberForValuenull105 internal fun memberForValue(value: Any) = when (value) {
106   is Class<*> -> CodeBlock.of("%T::class", value)
107   is Enum<*> -> CodeBlock.of("%T.%L", value.javaClass, value.name)
108   is String -> CodeBlock.of("%S", value)
109   is Float -> CodeBlock.of("%Lf", value)
110   is Double -> CodeBlock.of("%L", value)
111   is Char -> CodeBlock.of("$value.toChar()")
112   is Byte -> CodeBlock.of("$value.toByte()")
113   is Short -> CodeBlock.of("$value.toShort()")
114   // Int or Boolean
115   else -> CodeBlock.of("%L", value)
116 }
117 
checknull118 internal inline fun KSPLogger.check(condition: Boolean, message: () -> String) {
119   check(condition, null, message)
120 }
121 
checknull122 internal inline fun KSPLogger.check(condition: Boolean, element: KSNode?, message: () -> String) {
123   if (!condition) {
124     error(message(), element)
125   }
126 }
127