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