• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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