1 import com.google.devtools.ksp.processing.* 2 import com.google.devtools.ksp.symbol.* 3 import com.google.devtools.ksp.validate 4 import java.io.File 5 import java.io.OutputStream 6 7 8 class TestProcessor( 9 val codeGenerator: CodeGenerator, 10 val options: Map<String, String> 11 ) : SymbolProcessor { 12 lateinit var file: OutputStream 13 var invoked = false 14 emitnull15 fun emit(s: String, indent: String) { 16 file.appendText("$indent$s\n") 17 } 18 processnull19 override fun process(resolver: Resolver): List<KSAnnotated> { 20 if (invoked) { 21 return emptyList() 22 } 23 file = codeGenerator.createNewFile(Dependencies(false), "", "TestProcessor", "log") 24 emit("TestProcessor: init($options)", "") 25 26 val javaFile = codeGenerator.createNewFile(Dependencies(false), "", "Generated", "java") 27 javaFile.appendText("class Generated {}") 28 29 val fileKt = codeGenerator.createNewFile(Dependencies(false), "", "HELLO", "java") 30 fileKt.appendText("public class HELLO{\n") 31 fileKt.appendText("public int foo() { return 1234; }\n") 32 fileKt.appendText("}") 33 34 val files = resolver.getAllFiles() 35 emit("TestProcessor: process()", "") 36 val visitor = TestVisitor() 37 for (file in files) { 38 emit("TestProcessor: processing ${file.fileName}", "") 39 file.accept(visitor, "") 40 } 41 invoked = true 42 return emptyList() 43 } 44 45 inner class TestVisitor : KSVisitor<String, Unit> { 46 visitReferenceElementnull47 override fun visitReferenceElement(element: KSReferenceElement, data: String) { 48 } 49 visitModifierListOwnernull50 override fun visitModifierListOwner(modifierListOwner: KSModifierListOwner, data: String) { 51 TODO("Not yet implemented") 52 } 53 visitNodenull54 override fun visitNode(node: KSNode, data: String) { 55 TODO("Not yet implemented") 56 } 57 visitPropertyAccessornull58 override fun visitPropertyAccessor(accessor: KSPropertyAccessor, data: String) { 59 TODO("Not yet implemented") 60 } 61 visitDynamicReferencenull62 override fun visitDynamicReference(reference: KSDynamicReference, data: String) { 63 TODO("Not yet implemented") 64 } 65 val visited = HashSet<Any>() 66 checkVisitednull67 private fun checkVisited(symbol: Any): Boolean { 68 return if (visited.contains(symbol)) { 69 true 70 } else { 71 visited.add(symbol) 72 false 73 } 74 } 75 invokeCommonDeclarationApisnull76 private fun invokeCommonDeclarationApis(declaration: KSDeclaration, indent: String) { 77 emit( 78 "${declaration.modifiers.joinToString(" ")} ${declaration.simpleName.asString()}", indent 79 ) 80 declaration.annotations.forEach{ it.accept(this, "$indent ") } 81 if (declaration.parentDeclaration != null) 82 emit(" enclosing: ${declaration.parentDeclaration!!.qualifiedName?.asString()}", indent) 83 declaration.containingFile?.let { emit("${it.packageName.asString()}.${it.fileName}", indent) } 84 declaration.typeParameters.forEach { it.accept(this, "$indent ") } 85 } 86 visitFilenull87 override fun visitFile(file: KSFile, data: String) { 88 if (checkVisited(file)) return 89 file.annotations.forEach{ it.accept(this, "$data ") } 90 emit(file.packageName.asString(), data) 91 for (declaration in file.declarations) { 92 declaration.accept(this, data) 93 } 94 } 95 visitAnnotationnull96 override fun visitAnnotation(annotation: KSAnnotation, data: String) { 97 if (checkVisited(annotation)) return 98 emit("annotation", data) 99 annotation.annotationType.accept(this, "$data ") 100 annotation.arguments.forEach { it.accept(this, "$data ") } 101 } 102 visitCallableReferencenull103 override fun visitCallableReference(reference: KSCallableReference, data: String) { 104 if (checkVisited(reference)) return 105 emit("element: ", data) 106 reference.functionParameters.forEach { it.accept(this, "$data ") } 107 reference.receiverType?.accept(this, "$data receiver") 108 reference.returnType.accept(this, "$data ") 109 } 110 visitPropertyGetternull111 override fun visitPropertyGetter(getter: KSPropertyGetter, data: String) { 112 if (checkVisited(getter)) return 113 emit("propertyGetter: ", data) 114 getter.annotations.forEach { it.accept(this, "$data ") } 115 emit(getter.modifiers.joinToString(" "), data) 116 getter.returnType?.accept(this, "$data ") 117 } 118 visitPropertySetternull119 override fun visitPropertySetter(setter: KSPropertySetter, data: String) { 120 if (checkVisited(setter)) return 121 emit("propertySetter: ", data) 122 setter.annotations.forEach { it.accept(this, "$data ") } 123 emit(setter.modifiers.joinToString(" "), data) 124 } 125 visitTypeArgumentnull126 override fun visitTypeArgument(typeArgument: KSTypeArgument, data: String) { 127 if (checkVisited(typeArgument)) return 128 typeArgument.annotations.forEach{ it.accept(this, "$data ") } 129 emit( 130 when (typeArgument.variance) { 131 Variance.STAR -> "*" 132 Variance.COVARIANT -> "out" 133 Variance.CONTRAVARIANT -> "in" 134 else -> "" 135 }, data 136 ) 137 typeArgument.type?.accept(this, "$data ") 138 } 139 visitTypeParameternull140 override fun visitTypeParameter(typeParameter: KSTypeParameter, data: String) { 141 if (checkVisited(typeParameter)) return 142 typeParameter.annotations.forEach{ it.accept(this, "$data ") } 143 if (typeParameter.isReified) { 144 emit("reified ", data) 145 } 146 emit( 147 when (typeParameter.variance) { 148 Variance.COVARIANT -> "out " 149 Variance.CONTRAVARIANT -> "in " 150 else -> "" 151 } + typeParameter.name.asString(), data 152 ) 153 if (typeParameter.bounds.toList().isNotEmpty()) { 154 typeParameter.bounds.forEach { it.accept(this, "$data ") } 155 } 156 } 157 visitValueParameternull158 override fun visitValueParameter(valueParameter: KSValueParameter, data: String) { 159 if (checkVisited(valueParameter)) return 160 valueParameter.annotations.forEach { it.accept(this, "$data ") } 161 if (valueParameter.isVararg) { 162 emit("vararg", "$data ") 163 } 164 if (valueParameter.isNoInline) { 165 emit("noinline", "$data ") 166 } 167 if (valueParameter.isCrossInline) { 168 emit("crossinline ", "$data ") 169 } 170 emit(valueParameter.name?.asString() ?: "_", "$data ") 171 valueParameter.type.accept(this, "$data ") 172 } 173 visitFunctionDeclarationnull174 override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: String) { 175 if (checkVisited(function)) return 176 invokeCommonDeclarationApis(function, data) 177 for (declaration in function.declarations) { 178 declaration.accept(this, "$data ") 179 } 180 function.parameters.forEach { it.accept(this, "$data ") } 181 function.typeParameters.forEach { it.accept(this, "$data ") } 182 function.extensionReceiver?.accept(this, "$data extension:") 183 emit("returnType:", data) 184 function.returnType?.accept(this, "$data ") 185 } 186 visitClassDeclarationnull187 override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: String) { 188 if (checkVisited(classDeclaration)) return 189 invokeCommonDeclarationApis(classDeclaration, data) 190 emit(classDeclaration.classKind.type, data) 191 for (declaration in classDeclaration.declarations) { 192 declaration.accept(this, "$data ") 193 } 194 classDeclaration.superTypes.forEach { it.accept(this, "$data ") } 195 classDeclaration.primaryConstructor?.accept(this, "$data ") 196 } 197 visitPropertyDeclarationnull198 override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: String) { 199 if (checkVisited(property)) return 200 invokeCommonDeclarationApis(property, data) 201 property.type.accept(this, "$data ") 202 property.extensionReceiver?.accept(this, "$data extension:") 203 property.setter?.accept(this, "$data ") 204 property.getter?.accept(this, "$data ") 205 } 206 visitTypeReferencenull207 override fun visitTypeReference(typeReference: KSTypeReference, data: String) { 208 if (checkVisited(typeReference)) return 209 typeReference.annotations.forEach{ it.accept(this, "$data ") } 210 val type = typeReference.resolve() 211 type.let { 212 emit("resolved to: ${it.declaration.qualifiedName?.asString()}", data) 213 } 214 try { 215 typeReference.element?.accept(this, "$data ") 216 } catch (e: IllegalStateException) { 217 emit("TestProcessor: exception: $e", data) 218 } 219 } 220 visitAnnotatednull221 override fun visitAnnotated(annotated: KSAnnotated, data: String) { 222 } 223 visitDeclarationnull224 override fun visitDeclaration(declaration: KSDeclaration, data: String) { 225 } 226 visitDeclarationContainernull227 override fun visitDeclarationContainer(declarationContainer: KSDeclarationContainer, data: String) { 228 } 229 visitParenthesizedReferencenull230 override fun visitParenthesizedReference(reference: KSParenthesizedReference, data: String) { 231 } 232 visitClassifierReferencenull233 override fun visitClassifierReference(reference: KSClassifierReference, data: String) { 234 if (checkVisited(reference)) return 235 if (reference.typeArguments.isNotEmpty()) { 236 reference.typeArguments.forEach { it.accept(this, "$data ") } 237 } 238 } 239 visitTypeAliasnull240 override fun visitTypeAlias(typeAlias: KSTypeAlias, data: String) { 241 } 242 visitValueArgumentnull243 override fun visitValueArgument(valueArgument: KSValueArgument, data: String) { 244 if (checkVisited(valueArgument)) return 245 val name = valueArgument.name?.asString() ?: "<no name>" 246 emit("$name: ${valueArgument.value}", data) 247 valueArgument.annotations.forEach { it.accept(this, "$data ") } 248 } 249 } 250 251 } 252 253 class TestProcessorProvider : SymbolProcessorProvider { createnull254 override fun create( 255 environment: SymbolProcessorEnvironment 256 ): SymbolProcessor { 257 return TestProcessor(environment.codeGenerator, environment.options) 258 } 259 } 260