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
}

多輪行為

將符號延遲到下一個回合

處理器可以將特定符號的處理延遲到下一個回合。當符號被延遲時,處理器會等待其他處理器提供額外資訊。它可以根據需要繼續延遲符號任意多個回合。一旦其他處理器提供了所需資訊,處理器就可以處理該延遲符號。處理器應僅延遲缺少必要資訊的無效符號。因此,處理器不應延遲來自 Classpath 的符號,KSP 也會篩選掉任何非來自原始碼的延遲符號。

例如,一個為帶註解類別建立 Builder 的處理器,可能會要求其建構函式的所有參數型別都是有效的(已解析為具體型別)。在第一個回合中,其中一個參數型別無法解析。然後在第二個回合中,由於第一個回合中產生的檔案,它變得可解析。

驗證符號

判斷符號是否應延遲的一個便捷方法是透過驗證。處理器應知道哪些資訊對於正確處理符號是必要的。請注意,驗證通常需要解析,這可能很耗費資源,因此我們建議僅檢查所需內容。延續之前的範例,對於 Builder 處理器來說,理想的驗證只會檢查帶註解符號的建構函式的所有已解析參數型別是否包含 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 會使用它來篩選出需要檢查的符號。