KSP 如何为 Kotlin 代码建模

You can find the API definition in the KSP GitHub repository. The diagram shows an overview of how Kotlin is modeled in KSP:

class diagram{thumbnail="true" width="800" thumbnail-same-file="true"}

See the full-sized diagram.

Type and resolution

The resolution takes most of the cost of the underlying API implementation. So type references are designed to be resolved by processors explicitly (with a few exceptions). When a type (such as KSFunctionDeclaration.returnType or KSAnnotation.annotationType) is referenced, it is always a KSTypeReference, which is a KSReferenceElement with annotations and modifiers.

interface KSFunctionDeclaration : ... {
  val returnType: KSTypeReference?
  // ...

interface KSTypeReference : KSAnnotated, KSModifierListOwner {
  val type: KSReferenceElement

A KSTypeReference can be resolved to a KSType, which refers to a type in Kotlin's type system.

A KSTypeReference has a KSReferenceElement, which models Kotlin's program structure: namely, how the reference is written. It corresponds to the type element in Kotlin's grammar.

A KSReferenceElement can be a KSClassifierReference or KSCallableReference, which contains a lot of useful information without the need for resolution. For example, KSClassifierReference has referencedName, while KSCallableReference has receiverType, functionArguments, and returnType.

If the original declaration referenced by a KSTypeReference is needed, it can usually be found by resolving to KSType and accessing through KSType.declaration. Moving from where a type is mentioned to where its class is defined looks like this:

val ksType: KSType = ksTypeReference.resolve()
val ksDeclaration: KSDeclaration = ksType.declaration

Type resolution is costly and therefore has explicit form. Some of the information obtained from resolution is already available in KSReferenceElement. For example, KSClassifierReference.referencedName can filter out a lot of elements that are not interesting. You should resolve type only if you need specific information from KSDeclaration or KSType.

KSTypeReference pointing to a function type has most of its information in its element. Although it can be resolved to the family of Function0, Function1, and so on, these resolutions don't bring any more information than KSCallableReference. One use case for resolving function type references is dealing with the identity of the function's prototype.