Skip to content

多轮处理

KSP 支持 多轮处理,即对文件进行多轮处理。这意味着后续轮次会使用前几轮的输出作为额外输入。

对你的处理器的更改

要使用多轮处理,SymbolProcessor.process() 函数需要为无效符号返回一个延迟符号列表 (List<KSAnnotated>)。使用 KSAnnotated.validate() 来过滤无效符号,以便将其延迟到下一轮。

以下示例代码展示了如何通过使用验证检查来延迟无效符号:

kotlin
override fun process(resolver: Resolver): List<KSAnnotated> {
    val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder")
    val result = symbols.filter { !it.validate() }
    symbols
        .filter { it is KSClassDeclaration && it.validate() }
        .map { it.accept(BuilderVisitor(), Unit) }
    return result
}

多轮行为

将符号延迟到下一轮

处理器可以将某些符号的处理延迟到下一轮。当一个符号被延迟时,处理器会等待其他处理器提供额外信息。它可以根据需要继续延迟该符号,直到所需轮次。一旦其他处理器提供了所需信息,该处理器就可以处理该延迟符号。处理器只应延迟那些缺少必要信息的无效符号。因此,处理器不应延迟来自类路径的符号,KSP 也会过滤掉任何非源自源代码的延迟符号。

例如,一个为注解类创建构建器的处理器,可能要求其构造函数的所有参数类型都有效(解析为具体类型)。在第一轮中,其中一个参数类型可能不可解析。然后在第二轮中,由于第一轮生成的文件,它变得可解析了。

验证符号

决定是否应延迟符号的一种便捷方式是通过验证。处理器应知道正确处理该符号所需的信息。请注意,验证通常需要解析,这可能很耗时,因此我们建议只检查所需内容。延续前面的示例,对于构建器处理器来说,理想的验证仅检查注解符号的构造函数的所有已解析参数类型是否包含 isError == false

KSP 提供了一个默认的验证工具。更多信息请参阅 高级 部分。

终止条件

当一整轮处理没有生成任何新文件时,多轮处理就会终止。如果在满足终止条件时仍存在未处理的延迟符号,KSP 会为每个仍有未处理延迟符号的处理器记录一条错误消息。

每轮可访问的文件

新生成的文件和现有文件都可以通过 Resolver 访问。KSP 提供了两个用于访问文件的 API:Resolver.getAllFiles()Resolver.getNewFiles()getAllFiles() 返回现有文件和新生成文件的组合列表,而 getNewFiles() 只返回新生成的文件。

getSymbolsAnnotatedWith() 的更改

为避免不必要的符号重复处理,getSymbolsAnnotatedWith() 只返回在新生成文件中找到的符号,以及上一轮中延迟的符号。

处理器实例化

处理器实例只创建一次,这意味着你可以在处理器对象中存储信息,供后续轮次使用。

跨轮次信息一致性

所有 KSP 符号都无法在多轮中重用,因为解析结果可能会根据前一轮生成的内容而改变。然而,由于 KSP 不允许修改现有代码,因此某些信息,例如符号名称的字符串值,仍应可重用。总而言之,处理器可以存储来自前几轮的信息,但需要记住此信息在未来轮次中可能无效。

错误和异常处理

当发生错误(由处理器调用 KSPLogger.error() 定义)或异常时,当前轮次完成后处理将停止。所有处理器都将调用 onError() 方法,并且不会调用 finish() 方法。

请注意,即使发生了错误,其他处理器在该轮次中仍会正常处理。这意味着错误处理是在该轮次处理完成后进行的。

发生异常时,KSP 将尝试区分来自 KSP 的异常和来自处理器的异常。异常将导致处理立即终止,并在 KSPLogger 中记录为错误。来自 KSP 的异常应报告给 KSP 开发者以进行进一步调查。在发生异常或错误的轮次结束时,所有处理器都将调用 onError() 函数来执行自己的错误处理。

KSP 为 onError() 提供了一个默认的无操作实现,作为 SymbolProcessor 接口的一部分。你可以重写此方法来提供自己的错误处理逻辑。

高级

验证的默认行为

KSP 提供的默认验证逻辑会验证正在被验证的符号的封闭作用域内所有直接可达的符号。默认验证会检查封闭作用域中的引用是否可解析为具体类型,但不会递归深入到被引用类型中执行验证。

编写你自己的验证逻辑

默认验证行为可能不适用于所有情况。你可以参考 KSValidateVisitor,并通过提供一个自定义的 predicate lambda 表达式来编写你自己的验证逻辑,该表达式随后会由 KSValidateVisitor 用于过滤出需要检查的符号。