解析器能力扩展_analysis-api-extend-ka-resolver
以下为本文档的中文说明
analysis-api-extend-ka-resolver 是 JetBrains 平台中用于扩展 Kotlin 分析 API 解析器能力的技能。该技能的核心功能是为指定的 Kt* PSI(程序结构接口)类型添加 resolveSymbol() 和可选的 resolveCall() 方法支持,通过遵循已有解析器支持提交中建立的标准模式来实现。工作过程分为多个阶段:第一阶段是信息收集,首先在 compiler/psi/ 目录中搜索 PSI 类型的源文件(.java 或 .kt),读取文件以理解类层次结构,检查该类型是否已经实现了 KtResolvable 或 KtResolvableCall 接口;然后检查 KaResolver.kt 文件中是否已存在对该 PSI 类型的支持。使用场景包括:当 JetBrains 平台的 Kotlin 分析引擎需要为新的 PSI 类型添加符号解析和调用解析能力时;在开发 Kotlin 编译器插件或分析工具时需要扩展解析能力时。核心原则是严格遵循现有代码库中已建立的模式,不创造新的解析机制,而是扩展现有的 KaResolver 框架。该技能要求使用者对 Kotlin 编译器的 PSI 系统有深入理解,特别是 KtResolvable 和 KtResolvableCall 接口的语义和实现模式。通过系统化地按照已有提交的模式进行扩展,确保新增的解析支持与现有代码保持一致的风格和质量标准。
Add KaResolver support for a PSI type
This skill addsresolveSymbol()and optionallyresolveCall()support for a givenKt*PSI type
by following the established pattern from existing resolver support commits.
The argument is the PSI type name, e.g.KtDestructuringDeclarationEntry.
Phase 1: Gather information
Find the PSI type source file.Search
compiler/psi/for<KtPsiType>.javaor<KtPsiType>.kt.
Read the file to understand the class hierarchy and whether it already implementsKtResolvableorKtResolvableCall.Check KaResolver for existing support.Read
analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KaResolver.kt
and search for the PSI type. If it already hasresolveSymbol()/resolveCall()methods, inform the user and stop.Check for existing test data.Search
analysis/analysis-api/testData/components/resolver/for test files
mentioning the PSI type or related scenarios.Read the Analysis API AGENTS.mdat
analysis/AGENTS.mdfor area-specific guidelines.
Phase 2: Ask user questions
UseAskUserQuestionto ask these questions (all in one call):
Question 1: Resolution kind
Header:“Resolution”
Question:“Should<KtPsiType>support symbol-only resolution (KtResolvable) or both symbol and call resolution (KtResolvableCall)?”
KtResolvable— symbol resolution only (resolveSymbol())KtResolvableCall— both symbol and call resolution (resolveSymbol()+resolveCall())
Question 2: Symbol return type
Header:“Symbol type”
Question:“What shouldresolveSymbol()return for<KtPsiType>?”
KaConstructorSymbolKaFunctionSymbolKaNamedFunctionSymbolKaCallableSymbol
(Allow “Other” for types likeKaDeclarationSymbol, etc.)
Question 3: Call return type (only ifKtResolvableCall)
Header:“Call type”
Question:“What shouldresolveCall()return for<KtPsiType>?”
KaFunctionCall<KaConstructorSymbol>KaDelegatedConstructorCallKaAnnotationCallKaFunctionCall<KaNamedFunctionSymbol>
(Allow “Other” for types likeKaSingleCall<*, *>,KaFunctionCall<*>, etc.)
Phase 3: Execute changes
Use the answers from Phase 2 to determine:RESOLUTION_KIND(KtResolvableorKtResolvableCall),SYMBOL_TYPE(e.g.KaCallableSymbol), andCALL_TYPE(e.g.KaSingleCall<*, *>).
Step 1: PSI type — add interface implementation
File:The PSI source file found in Phase 1 (undercompiler/psi/).
- If the PSI type does NOT already implement
KtResolvable/KtResolvableCall, add it:- For
KtResolvable: addimplements KtResolvable(Java) or: KtResolvable(Kotlin) - For
KtResolvableCall: addimplements KtResolvableCall(Java) or: KtResolvableCall(Kotlin) KtResolvableCallextendsKtResolvable, so only one is needed.
- For
- Add the necessary import (
org.jetbrains.kotlin.resolution.KtResolvableororg.jetbrains.kotlin.resolution.KtResolvableCall).
Step 2: KaResolver interface — add methods
File:analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KaResolver.kt
2a: AddresolveSymbol()interface method
Insertafterthe last existing typedresolveSymbol()method (currentlyKtDestructuringDeclarationEntry.resolveSymbol())
andbeforetryResolveCall().
Follow the exact KDoc pattern — copy from a similar existing method and adapt:
/** * Resolves the <description> by the given [<KtPsiType>]. * * #### Example * * ```kotlin * <code example with // ^^^^ markers> * ``` * * Calling `resolveSymbol()` on a [<KtPsiType>] ... returns the [<SYMBOL_TYPE>] ... * if resolution succeeds; otherwise, it returns `null` (e.g., when unresolved or ambiguous). * * This is a specialized counterpart of [KtResolvable.resolveSymbol] focused specifically on <description> * * @see tryResolveSymbols * @see KtResolvable.resolveSymbol */@KaExperimentalApipublicfun<KtPsiTy pe>.resolveSymbol():<SYMBOL_TYPE>?2b: AddresolveCall()interface method (only ifKtResolvableCall)
Insertafterthe last existing typedresolveCall()method (currentlyKtDestructuringDeclarationEntry.resolveCall())
andbeforecollectCallCandidates().
/** * Resolves the given [<KtPsiType>] to a <call description>. * * #### Example * * ```kotlin * <code example with // ^^^^ markers> * ``` * * Returns the corresponding [<CALL_TYPE short name>] if resolution succeeds; otherwise, it returns `null` * (e.g., when unresolved or ambiguous). * * This is a specialized counterpart of [KtResolvableCall.resolveCall] focused specifically on <description> * * @see tryResolveCall * @see KtResolvableCall.resolveCall */@KaExperimentalApipublicfun<KtPsiType>.resolveCall():<CALL_TYPE>?2c: AddresolveSymbol()bridge function
Insertafterthe last existingresolveSymbolbridge (currentlyKtDestructuringDeclarationEntry.resolveSymbolbridge)
andbeforethetryResolveCallbridge.
/** * <Same KDoc as the interface method> */// Auto-generated bridge. DO NOT EDIT MANUALLY!@KaExperimentalApi@KaContextParameterApicontext(session:KaSession)publicfun<KtPsiType>.resolveSymbol():<SYMBOL_TYPE>?{returnwith(session){resolveSymbol()}}2d: AddresolveCall()bridge function (only ifKtResolvableCall)
Insertafterthe last existingresolveCallbridge (currentlyKtDestructuringDeclarationEntry.resolveCallbridge)
andbeforethecollectCallCandidatesbridge.
/** * <Same KDoc as the interface method> */// Auto-generated bridge. DO NOT EDIT MANUALLY!@KaExperimentalApi@KaContextParameterApicontext(session:KaSession)publicfun<KtPsiType>.resolveCall():<CALL_TYPE>?{returnwith(session){resolveCall()}}Step 3: KaBaseResolver — add override implementations
File:analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/KaBaseResolver.kt
3a: AddresolveSymbol()override (always)
Insertafterthe last existingresolveSymbolSafe()line (currentlyKtDestructuringDeclarationEntry.resolveSymbol())
andbeforeKtReference.resolveToSymbol().
finaloverridefun<KtPsiType>.resolveSymbol():<SYMBOL_TYPE>?=resolveSymbolSafe()3b: AddresolveCall()override (only ifKtResolvableCall)
Insertafterthe last existingresolveCallSafe()/resolveSingleCallSafe()line
(currentlyKtDestructuringDeclarationEntry.resolveCall()) andbeforeKtElement.resolveToCall().
Choose the helper based on the call return type:
- If CALL_TYPE contains wildcards (
*) → useresolveCallSafe():finaloverridefun<KtPsiType>.resolveCall():<CALL_TYPE>?=resolveCallSafe() - If CALL_TYPE is fully specified (no wildcards) → use
resolveSingleCallSafe():finaloverridefun<KtPsiType>.resolveCall():<CALL_TYPE>?=resolveSingleCallSafe()
3c: Add tocanBeResolvedAsCall(only ifKtResolvableCall)
In thecanBeResolvedAsCallfunction, add a new branchbeforeelse -> false:
is<KtPsiType>->trueStep 4: Investigate FIR/FE10 resolver changes
This step requires investigation — do NOT skip it.
Read the FIR resolver:
- File:
analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KaFirResolver.kt
The FIR resolver works by callinggetOrBuildFir(psi)and dispatching on the FIR element type in awhenblock.
Investigate:
- What FIR element type does
getOrBuildFir(<KtPsiType instance>)return? - Is that FIR element type already handled in the
whenblocks ofperformSymbolResolution()andperformCallResolution()? - If NOT handled, add appropriate handling (new branch in the
when, possibly with unwrapping logic).
Examples of when FIR changes were needed:
KtDestructuri ngDeclarationEntry→ maps toFirProperty(a declaration), needed to unwrapFirProperty.initializerKtLabelReferenceExpression→ FIR doesn’t have a dedicated label element, needed to extract fromFirThisReceiverExpression.calleeReferenceKtConstructorDelegationReferenceExpression→ needed to addFirReferenceas a handled caseKtReturnExpression→FirReturnExpressionwasn’t handled, added a new branch + helper
Check if theBindingContext-based resolution handles the PSI type. Examples of needed changes:
KtCallableReferenceExpression→ redirects topsi.callableReferenceKtWhenConditionInRange→ redirects topsi.operationReferenceKtReturnExpression→ custom logic to find enclosing function viaparents()
If changes are needed, implement them following the existing patterns in those files.
Step 5: Update PSI API dump
Run:
./gradlew :compiler:psi:psi-api:updateKotlinAbiThis updatescompiler/psi/psi-api/api/psi-api.apito reflect the new interface implementation.
Phase 4: Verify
Step 1: Static analysis
Runget_file_problemswitherrorsOnly=falseon each modified file. Fix any warnings related to the changes.
Step 2: Update test data
./gradlew updateTestData\\-Porg.jetbrains.kotlin.testDataManager.options.incremental=true\\-Porg.jetbrains.kotlin.testDataManager.options.testDataPath=analysis/analysis-api/testData/components/resolver/Step 3: Validate generated test data
Read the newly generated/updated golden.txtfiles and sanity-check:
.symbol.txt— should containKaSymbolResolutionSuccesswith the expected symbol type matchingSYMBOL_TYPE.references.txt— should containKaSymbolResolutionSuccesswith the expected symbol representingSYMBOL_TYPE.call.txt— (ifKtResolvableCall) should containKaCallResolutionSuccesswith the expected call type- If any file shows
nullor unexpected resolution failures, investigate whether FIR/FE10 changes (Step 4 in Phase 3) are missing
For quick investigation of individual tests, run on a specific subdirectory or file:
# By subdirectory./gradlew manageTestDataGlobally--mode=check --golden-only --test-data-path=analysis/analysis-api/testData/components/resolver/singleByPsi/<specific-subdir>/# By individual file./gradlew manageTestDataGlobally--mode=check --test-data-path=analysis/analysis-api/testData/components/resolver/singleByPsi/<subdir>/TestName.ktPhase 5: Commit
Create a commit with the message:
[Analysis API] resolver: support new API for `<KtPsiType>` ^KT-66039Before committing, readdocs/code_authoring_and_core_review.mdfor commit guidelines.
