[Analysis API] Add IGNORE_SELF dangling file resolution mode

In the 'IGNORE_SELF' mode, dangling files don't have their own
declarations in providers. As a result, all references there resolve to
declarations of the original file. It is conceptually similar to that we
had in on-air resolve, however, now it's possible to work with the whole
content of the in-memory 'FirFile'.

As it can be seen in 'ProjectStructureProvider.kt'
(KtFile.danglingFileResolutionMode), the 'IGNORE_SELF' mode is
automatically applied for non-physical files with an original file being
set. For other scenarios, now there is a new 'analyzeCopy()' function
that allows to pass the analysis mode explicitly.
This commit is contained in:
Yan Zhulanow
2023-11-22 18:21:18 +09:00
committed by Space Team
parent ae6b690d4f
commit 452d22e14f
84 changed files with 1394 additions and 50 deletions
@@ -6,15 +6,44 @@
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.analyzeCopy
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.references.AbstractDanglingFileReferenceResolveTest.Directives.COPY_RESOLUTION_MODE
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.project.structure.DanglingFileResolutionMode
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestModuleStructure
import org.jetbrains.kotlin.test.services.TestServices
abstract class AbstractDanglingFileReferenceResolveTest : AbstractReferenceResolveTest() {
override fun configureTest(builder: TestConfigurationBuilder) {
super.configureTest(builder)
builder.apply {
useDirectives(Directives)
forTestsMatching("analysis/analysis-api/testData/danglingFileReferenceResolve/ignoreSelf/*") {
defaultDirectives {
COPY_RESOLUTION_MODE.with(DanglingFileResolutionMode.IGNORE_SELF)
}
}
forTestsMatching("analysis/analysis-api/testData/danglingFileReferenceResolve/preferSelf/*") {
defaultDirectives {
COPY_RESOLUTION_MODE.with(DanglingFileResolutionMode.PREFER_SELF)
}
}
}
}
override fun KtAnalysisSession.getAdditionalSymbolInfo(symbol: KtSymbol): String? {
val containingFile = symbol.psi?.containingFile ?: return null
return containingFile.name
@@ -29,6 +58,25 @@ abstract class AbstractDanglingFileReferenceResolveTest : AbstractReferenceResol
val ktPsiFactory = KtPsiFactory.contextual(mainKtFile, markGenerated = true, eventSystemEnabled = true)
val fakeKtFile = ktPsiFactory.createFile("fake.kt", mainKtFile.text)
if (mainModule.directives.contains(COPY_RESOLUTION_MODE)) {
fakeKtFile.originalFile = mainKtFile
}
doTestByFileStructure(fakeKtFile, caretPosition, mainModule, testServices)
}
override fun <R> analyzeReferenceElement(element: KtElement, mainModule: TestModule, action: KtAnalysisSession.() -> R): R {
val resolutionMode = mainModule.directives.singleOrZeroValue(COPY_RESOLUTION_MODE)
return if (resolutionMode != null) {
analyzeCopy(element, resolutionMode) { action() }
} else {
analyze(element) { action() }
}
}
private object Directives : SimpleDirectivesContainer() {
val COPY_RESOLUTION_MODE by enumDirective(description = "Dangling file resolution mode for a copy") {
DanglingFileResolutionMode.valueOf(it)
}
}
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModulePr
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.utils.unwrapMultiReferences
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.ConfigurationDirectives
@@ -74,12 +75,11 @@ abstract class AbstractReferenceResolveTest : AbstractAnalysisApiBasedTest() {
testServices.assertions.fail { "No references at caret found" }
}
val resolvedTo =
analyseForTest(ktReferences.first().element) {
val symbols = ktReferences.flatMap { it.resolveToSymbols() }
checkReferenceResultForValidity(ktReferences, mainModule, testServices, symbols)
renderResolvedTo(symbols, renderingOptions) { getAdditionalSymbolInfo(it) }
}
val resolvedTo = analyzeReferenceElement(ktReferences.first().element, mainModule) {
val symbols = ktReferences.flatMap { it.resolveToSymbols() }
checkReferenceResultForValidity(ktReferences, mainModule, testServices, symbols)
renderResolvedTo(symbols, renderingOptions) { getAdditionalSymbolInfo(it) }
}
if (Directives.UNRESOLVED_REFERENCE in mainModule.directives) {
return
@@ -89,6 +89,10 @@ abstract class AbstractReferenceResolveTest : AbstractAnalysisApiBasedTest() {
testServices.assertions.assertEqualsToTestDataFileSibling(actual)
}
protected open fun <R> analyzeReferenceElement(element: KtElement, mainModule: TestModule, action: KtAnalysisSession.() -> R): R {
return analyseForTest(element) { action() }
}
open fun KtAnalysisSession.getAdditionalSymbolInfo(symbol: KtSymbol): String? = null
private fun findReferencesAtCaret(mainKtFile: KtFile, caretPosition: Int): List<KtReference> =