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:
{thumbnail=”true” width=”800” thumbnail-same-file=”true”}
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.