Multiple round processing
KSP supports multiple round processing, or processing files over multiple rounds. It means that subsequent rounds use an output from previous rounds as additional input.
Changes to your processor
To use multiple round processing, the
SymbolProcessor.process() function needs to return a list of deferred symbols (
List<KSAnnotated>) for invalid symbols. Use
KSAnnotated.validate() to filter invalid symbols to be deferred to the next round.
The following sample code shows how to defer invalid symbols by using a validation check:
Multiple round behavior
Deferring symbols to the next round
Processors can defer the processing of certain symbols to the next round. When a symbol is deferred, processor is waiting for other processors to provide additional information. It can continue deferring the symbol as many rounds as needed. Once the other processors provide the required information, the processor can then process the deferred symbol. Processor should only defer invalid symbols which are lacking necessary information. Therefore, processors should not defer symbols from classpath, KSP will also filter out any deferred symbols that are not from source code.
As an example, a processor that creates a builder for an annotated class might require all parameter types of its constructors to be valid (resolved to a concrete type). In the first round, one of the parameter type is not resolvable. Then in the second round, it becomes resolvable because of the generated files from the first round.
A convenient way to decide if a symbol should be deferred is through validation. A processor should know which information is necessary to properly process the symbol. Note that validation usually requires resolution which can be expensive, so we recommend checking only what is required. Continuing with the previous example, an ideal validation for the builder processor checks only whether all resolved parameter types of the constructors of annotated symbols contain
isError == false.
KSP provides a default validation utility. For more information, see the Advanced section.
Multiple round processing terminates when a full round of processing generates no new files. If unprocessed deferred symbols still exist when the termination condition is met, KSP logs an error message for each processor with unprocessed deferred symbols.
Files accessible at each round
Both newly generated files and existing files are accessible through a
Resolver. KSP provides two APIs for accessing files:
getAllFiles() returns a combined list of both existing files and newly generated files, while
getNewFiles() returns only newly generated files.
Changes to getSymbolsAnnotatedWith()
To avoid unnecessary reprocessing of symbols,
getSymbolsAnnotatedWith() returns only those symbols found in newly generated files, together with the symbols from deferred symbols from the last round.
A processor instance is created only once, which means you can store information in the processor object to be used for later rounds.
Information consistent cross rounds
All KSP symbols will not be reusable across multiple rounds, as the resolution result can potentially change based on what was generated in a previous round. However, since KSP does not allow modifying existing code, some information such as the string value for a symbol name should still be reusable. To summarize, processors can store information from previous rounds but need to bear in mind that this information might be invalid in future rounds.
Error and exception handling
When an error (defined by processor calling
KSPLogger.error()) or exception occurs, processing stops after the current round completes. All processors will call the
onError() method and will not call the
Note that even though an error has occurred, other processors continue processing normally for that round. This means that error handling occurs after processing has completed for the round.
Upon exceptions, KSP will try to distinguish the exceptions from KSP and exceptions from processors. Exceptions will result in a termination of processing immediately and be logged as an error in KSPLogger. Exceptions from KSP should be reported to KSP developers for further investigation. At the end of the round where exceptions or errors happened, all processors will invoke onError() function to do their own error handling.
KSP provides a default no-op implementation for
onError() as part of the
SymbolProcessor interface. You can override this method to provide your own error handling logic.
Default behavior for validation
The default validation logic provided by KSP validates all directly reachable symbols inside the enclosing scope of the symbol that is being validated. Default validation checks whether references in the enclosed scope are resolvable to a concrete type but does not recursively dive into the referenced types to perform validation.
Write your own validation logic
Default validation behavior might not be suitable for all cases. You can reference
KSValidateVisitor and write your own validation logic by providing a custom
predicate lambda, which is then used by
KSValidateVisitor to filter out symbols that need to be checked.