[LL FIR] do not treat phase from FirFile as indicator for entire file
`resolvePhase` from `FirFile` is not an indicator of resolution state for the entire file, so we shouldn't depend on it. The right check will be performed during `lazyResolveTargets` over `LLFirResolveTarget` ^KTIJ-26666 Fixed ^KT-61296
This commit is contained in:
committed by
Space Team
parent
337d87ad54
commit
0487bd7b6a
+8
-2
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirSingleRe
|
|||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeClassResolveTarget
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeClassResolveTarget
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
|
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.LLFirLazyResolverRunner
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.LLFirLazyResolverRunner
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.withOnAirDesignation
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.withOnAirDesignation
|
||||||
@@ -43,7 +42,14 @@ internal class LLFirModuleLazyDeclarationResolver(val moduleComponents: LLFirMod
|
|||||||
toPhase: FirResolvePhase,
|
toPhase: FirResolvePhase,
|
||||||
) {
|
) {
|
||||||
val fromPhase = target.resolvePhase
|
val fromPhase = target.resolvePhase
|
||||||
if (fromPhase >= toPhase) return
|
|
||||||
|
/**
|
||||||
|
* Currently [lazyResolve] on file means [LLFirWholeFileResolveTarget], but also [FirFile] itself
|
||||||
|
* has [resolvePhase] which does not match with the entire file resolution state.
|
||||||
|
* This additional [FirFile] condition can be dropped after KT-61296
|
||||||
|
*/
|
||||||
|
if (target !is FirFile && fromPhase >= toPhase) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resolveContainingFileToImports(target)
|
resolveContainingFileToImports(target)
|
||||||
if (toPhase == FirResolvePhase.IMPORTS) return
|
if (toPhase == FirResolvePhase.IMPORTS) return
|
||||||
|
|||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// DUMP_FILE
|
||||||
|
|
||||||
|
fun foo(): Int {
|
||||||
|
return <expr>doSmth</expr>("str")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doSmth(i: String) = 4
|
||||||
analysis/low-level-api-fir/testdata/inBlockModification/topLevelFunctionWithTypeWithBodyDumpFile.txt
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
BEFORE MODIFICATION:
|
||||||
|
FILE: [ResolvedTo(BODY_RESOLVE)] topLevelFunctionWithTypeWithBodyDumpFile.kt
|
||||||
|
public final [ResolvedTo(BODY_RESOLVE)] fun foo(): R|kotlin/Int| {
|
||||||
|
^foo R|/doSmth|(String(str))
|
||||||
|
}
|
||||||
|
public final [ResolvedTo(BODY_RESOLVE)] fun doSmth([ResolvedTo(BODY_RESOLVE)] i: R|kotlin/String|): R|kotlin/Int| {
|
||||||
|
^doSmth Int(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
AFTER MODIFICATION:
|
||||||
|
FILE: [ResolvedTo(BODY_RESOLVE)] topLevelFunctionWithTypeWithBodyDumpFile.kt
|
||||||
|
public final [ResolvedTo(ANNOTATIONS_ARGUMENTS_MAPPING)] fun foo(): R|kotlin/Int| { LAZY_BLOCK }
|
||||||
|
public final [ResolvedTo(BODY_RESOLVE)] fun doSmth([ResolvedTo(BODY_RESOLVE)] i: R|kotlin/String|): R|kotlin/Int| {
|
||||||
|
^doSmth Int(4)
|
||||||
|
}
|
||||||
+1
-1
@@ -21,7 +21,7 @@ abstract class AbstractCodeFragmentInBlockModificationTest : AbstractLowLevelApi
|
|||||||
|
|
||||||
assertNull(targetElement.getNonLocalReanalyzableContainingDeclaration())
|
assertNull(targetElement.getNonLocalReanalyzableContainingDeclaration())
|
||||||
|
|
||||||
val (before, after) = testInBlockModification(ktCodeFragment, ktCodeFragment, testServices)
|
val (before, after) = testInBlockModification(ktCodeFragment, ktCodeFragment, testServices, dumpFirFile = false)
|
||||||
val actualText = "BEFORE MODIFICATION:\n$before\nAFTER MODIFICATION:\n$after"
|
val actualText = "BEFORE MODIFICATION:\n$before\nAFTER MODIFICATION:\n$after"
|
||||||
|
|
||||||
testServices.assertions.assertEqualsToTestDataFileSibling(actualText)
|
testServices.assertions.assertEqualsToTestDataFileSibling(actualText)
|
||||||
|
|||||||
+63
-25
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
|
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
|
||||||
|
|
||||||
import com.intellij.extapi.psi.ASTDelegatePsiElement
|
import com.intellij.extapi.psi.ASTDelegatePsiElement
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirOfType
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirOfType
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazyResolveRenderer
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazyResolveRenderer
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolveWithCaches
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolveWithCaches
|
||||||
@@ -16,15 +17,24 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.Analys
|
|||||||
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
|
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
|
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotated
|
import org.jetbrains.kotlin.psi.KtAnnotated
|
||||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
|
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||||
|
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||||
import org.jetbrains.kotlin.test.services.TestModuleStructure
|
import org.jetbrains.kotlin.test.services.TestModuleStructure
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.assertions
|
import org.jetbrains.kotlin.test.services.assertions
|
||||||
|
|
||||||
abstract class AbstractInBlockModificationTest : AbstractLowLevelApiSingleFileTest() {
|
abstract class AbstractInBlockModificationTest : AbstractLowLevelApiSingleFileTest() {
|
||||||
|
override fun configureTest(builder: TestConfigurationBuilder) {
|
||||||
|
super.configureTest(builder)
|
||||||
|
builder.useDirectives(Directives)
|
||||||
|
}
|
||||||
|
|
||||||
override fun doTestByFileStructure(ktFile: KtFile, moduleStructure: TestModuleStructure, testServices: TestServices) {
|
override fun doTestByFileStructure(ktFile: KtFile, moduleStructure: TestModuleStructure, testServices: TestServices) {
|
||||||
val selectedElement = testServices.expressionMarkerProvider.getSelectedElementOfTypeByDirective(
|
val selectedElement = testServices.expressionMarkerProvider.getSelectedElementOfTypeByDirective(
|
||||||
ktFile = ktFile,
|
ktFile = ktFile,
|
||||||
@@ -33,7 +43,13 @@ abstract class AbstractInBlockModificationTest : AbstractLowLevelApiSingleFileTe
|
|||||||
|
|
||||||
val declaration = selectedElement.getNonLocalReanalyzableContainingDeclaration()
|
val declaration = selectedElement.getNonLocalReanalyzableContainingDeclaration()
|
||||||
val actual = if (declaration != null) {
|
val actual = if (declaration != null) {
|
||||||
val (before, after) = testInBlockModification(ktFile, declaration, testServices)
|
val (before, after) = testInBlockModification(
|
||||||
|
file = ktFile,
|
||||||
|
declaration = declaration,
|
||||||
|
testServices = testServices,
|
||||||
|
dumpFirFile = Directives.DUMP_FILE in moduleStructure.allDirectives,
|
||||||
|
)
|
||||||
|
|
||||||
"BEFORE MODIFICATION:\n$before\nAFTER MODIFICATION:\n$after"
|
"BEFORE MODIFICATION:\n$before\nAFTER MODIFICATION:\n$after"
|
||||||
} else {
|
} else {
|
||||||
"IN-BLOCK MODIFICATION IS NOT APPLICABLE FOR THIS PLACE"
|
"IN-BLOCK MODIFICATION IS NOT APPLICABLE FOR THIS PLACE"
|
||||||
@@ -41,33 +57,55 @@ abstract class AbstractInBlockModificationTest : AbstractLowLevelApiSingleFileTe
|
|||||||
|
|
||||||
testServices.assertions.assertEqualsToTestDataFileSibling(actual)
|
testServices.assertions.assertEqualsToTestDataFileSibling(actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object Directives : SimpleDirectivesContainer() {
|
||||||
|
val DUMP_FILE by directive("Dump the entire FirFile to the output")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun testInBlockModification(file: KtFile, declaration: KtAnnotated, testServices: TestServices): Pair<String, String> {
|
internal fun testInBlockModification(
|
||||||
return resolveWithCaches(file) { firSession ->
|
file: KtFile,
|
||||||
val firDeclarationBefore = declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
|
declaration: KtAnnotated,
|
||||||
val declarationTextBefore = firDeclarationBefore.render()
|
testServices: TestServices,
|
||||||
|
dumpFirFile: Boolean,
|
||||||
declaration.modifyBody()
|
): Pair<String, String> = resolveWithCaches(file) { firSession ->
|
||||||
invalidateAfterInBlockModification(declaration)
|
val firDeclarationBefore = declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
|
||||||
|
val declarationToRender = if (dumpFirFile) {
|
||||||
val declarationTextAfterModification = firDeclarationBefore.render()
|
file.getOrBuildFirFile(firSession).also { it.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE) }
|
||||||
testServices.assertions.assertNotEquals(declarationTextBefore, declarationTextAfterModification) {
|
} else {
|
||||||
"The declaration before and after modification must be in different state"
|
firDeclarationBefore
|
||||||
}
|
|
||||||
|
|
||||||
val firDeclarationAfter = declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
|
|
||||||
testServices.assertions.assertEquals(firDeclarationBefore, firDeclarationAfter) {
|
|
||||||
"The declaration before and after must be the same"
|
|
||||||
}
|
|
||||||
|
|
||||||
val declarationTextAfter = firDeclarationAfter.render()
|
|
||||||
testServices.assertions.assertEquals(declarationTextBefore, declarationTextAfter) {
|
|
||||||
"The declaration must have the same in the resolved state"
|
|
||||||
}
|
|
||||||
|
|
||||||
Pair(declarationTextBefore, declarationTextAfterModification)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val textBefore = declarationToRender.render()
|
||||||
|
|
||||||
|
declaration.modifyBody()
|
||||||
|
invalidateAfterInBlockModification(declaration)
|
||||||
|
|
||||||
|
val textAfterModification = declarationToRender.render()
|
||||||
|
testServices.assertions.assertNotEquals(textBefore, textAfterModification) {
|
||||||
|
"The declaration before and after modification must be in different state"
|
||||||
|
}
|
||||||
|
|
||||||
|
val textAfter = if (dumpFirFile) {
|
||||||
|
// we should resolve the entire file instead of the declaration to be sure that this declaration will be
|
||||||
|
// resolved by file resolution as well
|
||||||
|
declarationToRender.lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
|
||||||
|
declarationToRender.render()
|
||||||
|
} else {
|
||||||
|
declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
|
||||||
|
declarationToRender.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
val firDeclarationAfter = declaration.getOrBuildFirOfType<FirDeclaration>(firSession)
|
||||||
|
testServices.assertions.assertEquals(firDeclarationBefore, firDeclarationAfter) {
|
||||||
|
"The declaration before and after must be the same"
|
||||||
|
}
|
||||||
|
|
||||||
|
testServices.assertions.assertEquals(textBefore, textAfter) {
|
||||||
|
"The declaration must have the same in the resolved state"
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(textBefore, textAfterModification)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
@@ -288,6 +288,12 @@ public class OutOfContentRootInBlockModificationTestGenerated extends AbstractOu
|
|||||||
runTest("analysis/low-level-api-fir/testdata/inBlockModification/topLevelFunctionWithTypeWithBody.kt");
|
runTest("analysis/low-level-api-fir/testdata/inBlockModification/topLevelFunctionWithTypeWithBody.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("topLevelFunctionWithTypeWithBodyDumpFile.kt")
|
||||||
|
public void testTopLevelFunctionWithTypeWithBodyDumpFile() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/inBlockModification/topLevelFunctionWithTypeWithBodyDumpFile.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("topLevelFunctionWithTypeWithoutBody.kt")
|
@TestMetadata("topLevelFunctionWithTypeWithoutBody.kt")
|
||||||
public void testTopLevelFunctionWithTypeWithoutBody() throws Exception {
|
public void testTopLevelFunctionWithTypeWithoutBody() throws Exception {
|
||||||
|
|||||||
+6
@@ -288,6 +288,12 @@ public class SourceInBlockModificationTestGenerated extends AbstractSourceInBloc
|
|||||||
runTest("analysis/low-level-api-fir/testdata/inBlockModification/topLevelFunctionWithTypeWithBody.kt");
|
runTest("analysis/low-level-api-fir/testdata/inBlockModification/topLevelFunctionWithTypeWithBody.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("topLevelFunctionWithTypeWithBodyDumpFile.kt")
|
||||||
|
public void testTopLevelFunctionWithTypeWithBodyDumpFile() throws Exception {
|
||||||
|
runTest("analysis/low-level-api-fir/testdata/inBlockModification/topLevelFunctionWithTypeWithBodyDumpFile.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("topLevelFunctionWithTypeWithoutBody.kt")
|
@TestMetadata("topLevelFunctionWithTypeWithoutBody.kt")
|
||||||
public void testTopLevelFunctionWithTypeWithoutBody() throws Exception {
|
public void testTopLevelFunctionWithTypeWithoutBody() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user