FIR IDE: add tests for lazy resolving single declaration in file
This commit is contained in:
@@ -84,6 +84,7 @@ import org.jetbrains.kotlin.idea.filters.AbstractKotlinExceptionFilterTest
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyResolveTest
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolveTest
|
||||
import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqualityChecker
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyDeclarationResolveTest
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleLazyResolveTest
|
||||
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
|
||||
@@ -1035,6 +1036,9 @@ fun main(args: Array<String>) {
|
||||
testClass<AbstractFirMultiModuleLazyResolveTest> {
|
||||
model("multiModuleLazyResolve", recursive = false, extension = null)
|
||||
}
|
||||
testClass<AbstractFirLazyDeclarationResolveTest> {
|
||||
model("lazyResolve")
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/idea-fir/tests", "idea") {
|
||||
|
||||
+25
-12
@@ -11,10 +11,12 @@ import com.intellij.openapi.util.ModificationTracker
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.fir.dependenciesWithoutSelf
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionFactory
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProvider
|
||||
@@ -31,22 +33,33 @@ internal class FirIdeResolveStateService(project: Project) {
|
||||
ConcurrentHashMap<IdeaModuleInfo, FirModuleResolveStateImpl>()
|
||||
}
|
||||
|
||||
private fun createResolveStateFor(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl {
|
||||
require(moduleInfo is ModuleSourceInfo)
|
||||
val sessionProvider = sessionProviderStorage.getSessionProvider(moduleInfo)
|
||||
val firFileBuilder = sessionProvider.rootModuleSession.firFileBuilder
|
||||
return FirModuleResolveStateImpl(
|
||||
moduleInfo,
|
||||
sessionProvider,
|
||||
firFileBuilder,
|
||||
FirLazyDeclarationResolver(firFileBuilder),
|
||||
)
|
||||
}
|
||||
|
||||
fun getResolveState(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl =
|
||||
stateCache.computeIfAbsent(moduleInfo) { createResolveStateFor(moduleInfo) }
|
||||
stateCache.computeIfAbsent(moduleInfo) { createResolveStateFor(moduleInfo, sessionProviderStorage) }
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): FirIdeResolveStateService = project.service()
|
||||
|
||||
internal fun createResolveStateFor(
|
||||
moduleInfo: IdeaModuleInfo,
|
||||
sessionProviderStorage: FirIdeSessionProviderStorage
|
||||
): FirModuleResolveStateImpl {
|
||||
require(moduleInfo is ModuleSourceInfo)
|
||||
val sessionProvider = sessionProviderStorage.getSessionProvider(moduleInfo)
|
||||
val firFileBuilder = sessionProvider.rootModuleSession.firFileBuilder
|
||||
return FirModuleResolveStateImpl(
|
||||
moduleInfo,
|
||||
sessionProvider,
|
||||
firFileBuilder,
|
||||
FirLazyDeclarationResolver(firFileBuilder),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun createResolveStateForNoCaching(
|
||||
moduleInfo: IdeaModuleInfo,
|
||||
): FirModuleResolveState = FirIdeResolveStateService.createResolveStateFor(moduleInfo, FirIdeSessionProviderStorage(moduleInfo.project!!))
|
||||
|
||||
|
||||
|
||||
+4
@@ -79,4 +79,8 @@ internal class FirModuleResolveStateForCompletion(
|
||||
override fun findNonLocalSourceFirDeclaration(ktDeclaration: KtDeclaration): FirDeclaration {
|
||||
error("Should not be used in completion")
|
||||
}
|
||||
|
||||
override fun getBuiltFirFileOrNull(ktFile: KtFile): FirFile? {
|
||||
error("Should not be used in completion")
|
||||
}
|
||||
}
|
||||
+5
@@ -57,6 +57,11 @@ internal class FirModuleResolveStateImpl(
|
||||
override fun collectDiagnosticsForFile(ktFile: KtFile): Collection<Diagnostic> =
|
||||
diagnosticsCollector.collectDiagnosticsForFile(ktFile)
|
||||
|
||||
override fun getBuiltFirFileOrNull(ktFile: KtFile): FirFile? {
|
||||
val cache = sessionProvider.getModuleCache(ktFile.getModuleInfo() as ModuleSourceInfo)
|
||||
return firFileBuilder.getBuiltFirFileOrNull(ktFile, cache)
|
||||
}
|
||||
|
||||
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
||||
error("Should be called only from FirModuleResolveStateForCompletion")
|
||||
}
|
||||
|
||||
+4
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.low.level.api.api
|
||||
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
@@ -43,6 +44,9 @@ abstract class FirModuleResolveState {
|
||||
|
||||
internal abstract fun collectDiagnosticsForFile(ktFile: KtFile): Collection<Diagnostic>
|
||||
|
||||
@TestOnly
|
||||
internal abstract fun getBuiltFirFileOrNull(ktFile: KtFile): FirFile?
|
||||
|
||||
abstract fun findNonLocalSourceFirDeclaration(
|
||||
ktDeclaration: KtDeclaration,
|
||||
): FirDeclaration
|
||||
|
||||
+3
@@ -36,6 +36,9 @@ internal class FirFileBuilder(
|
||||
RawFirBuilder(cache.session, scopeProvider, stubMode = false, lazyBodiesMode = lazyBodiesMode).buildFirFile(ktFile)
|
||||
}
|
||||
|
||||
fun getBuiltFirFileOrNull(ktFile: KtFile, cache: ModuleFileCache): FirFile? =
|
||||
cache.getCachedFirFile(ktFile)
|
||||
|
||||
fun isFirFileBuilt(
|
||||
ktFile: KtFile,
|
||||
cache: ModuleFileCache
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
class A {
|
||||
fun resolveMe() {
|
||||
receive(functionWithLazyBody())
|
||||
}
|
||||
|
||||
val x: Int = 10
|
||||
get() = field
|
||||
set(value) {
|
||||
field = value
|
||||
}
|
||||
|
||||
fun receive(value: String) {}
|
||||
|
||||
fun functionWithLazyBody(): String {
|
||||
return "42"
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
FILE: classMembers.kt
|
||||
public final [BODY_RESOLVE] class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
this@R|/A|.R|/A.receive|(this@R|/A|.R|/A.functionWithLazyBody|())
|
||||
}
|
||||
|
||||
public final [STATUS] fun receive(value: R|kotlin/String|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
|
||||
public final [STATUS] fun functionWithLazyBody(): R|kotlin/String| { LAZY_BLOCK }
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fun resolveMe() {
|
||||
receive(withGetter)
|
||||
}
|
||||
|
||||
fun receive(value: Int){}
|
||||
|
||||
val withGetter: Int
|
||||
get() = 42
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
FILE: propertyWithGetter.kt
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|/receive|(R|/withGetter|)
|
||||
}
|
||||
public final [STATUS] fun receive(value: R|kotlin/Int|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [STATUS] val withGetter: R|kotlin/Int|
|
||||
public get(): R|kotlin/Int| { LAZY_BLOCK }
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
fun resolveMe() {
|
||||
receive(withGetterAndSetter)
|
||||
withGetterAndSetter = 123
|
||||
}
|
||||
|
||||
fun receive(value: Int) {}
|
||||
|
||||
var withGetterAndSetter: Int = 42
|
||||
get() = field
|
||||
set(value) {
|
||||
field = value
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
FILE: propertyWithGetterAndSetter.kt
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|/receive|(R|/withGetterAndSetter|)
|
||||
R|/withGetterAndSetter| = Int(123)
|
||||
}
|
||||
public final [STATUS] fun receive(value: R|kotlin/Int|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [STATUS] var withGetterAndSetter: R|kotlin/Int| = IntegerLiteral(42)
|
||||
public get(): R|kotlin/Int| { LAZY_BLOCK }
|
||||
public set(value: R|kotlin/Int|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fun resolveMe() {
|
||||
receive(A(42))
|
||||
}
|
||||
|
||||
fun receive(value: A){}
|
||||
|
||||
class A {
|
||||
constructor(x: Int) {
|
||||
val a = x
|
||||
}
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
FILE: secondaryConstructor.kt
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|/receive|(R|/A.A|(Int(42)))
|
||||
}
|
||||
public final [STATUS] fun receive(value: R|A|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [STATUS] class A : R|kotlin/Any| {
|
||||
public constructor(x: R|kotlin/Int|): R|A| { LAZY_BLOCK }
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
fun resolveMe() {
|
||||
receive(functionWithLazyBody())
|
||||
}
|
||||
|
||||
fun receive(value: String){}
|
||||
|
||||
fun functionWithLazyBody(): String {
|
||||
return "42"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
FILE: topLevelFunctions.kt
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|/receive|(R|/functionWithLazyBody|())
|
||||
}
|
||||
public final [STATUS] fun receive(value: R|kotlin/String|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [STATUS] fun functionWithLazyBody(): R|kotlin/String| { LAZY_BLOCK }
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun resolveMe() {
|
||||
receive(functionWithLazyBody())
|
||||
}
|
||||
|
||||
fun receive(value: String){}
|
||||
|
||||
fun functionWithLazyBody(): String = "42"
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
FILE: topLevelFunctionsWithExpressionBodyAndExplicitType.kt
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|/receive|(R|/functionWithLazyBody|())
|
||||
}
|
||||
public final [STATUS] fun receive(value: R|kotlin/String|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [STATUS] fun functionWithLazyBody(): R|kotlin/String| { LAZY_BLOCK }
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun resolveMe() {
|
||||
receive(functionWithLazyBody())
|
||||
}
|
||||
|
||||
fun receive(value: String){}
|
||||
|
||||
fun functionWithLazyBody() = "42"
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
FILE: topLevelFunctionsWithImplicitType.kt
|
||||
public final [BODY_RESOLVE] fun resolveMe(): R|kotlin/Unit| {
|
||||
R|/receive|(R|/functionWithLazyBody|())
|
||||
}
|
||||
public final [STATUS] fun receive(value: R|kotlin/String|): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public final [IMPLICIT_TYPES_BODY_RESOLVE] fun functionWithLazyBody(): R|kotlin/String| {
|
||||
^functionWithLazyBody String(42)
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.low.level.api
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.fir.FirRenderer
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Test that we do not resolve declarations we do not need & do not build bodies for them
|
||||
*/
|
||||
abstract class AbstractFirLazyDeclarationResolveTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
override fun isFirPlugin(): Boolean = true
|
||||
|
||||
fun doTest(path: String) {
|
||||
val testDataFile = File(path)
|
||||
val ktFile = myFixture.configureByText(testDataFile.name, FileUtil.loadFile(testDataFile)) as KtFile
|
||||
val lazyDeclarations = ktFile.collectDescendantsOfType<KtDeclaration> { ktDeclaration -> !KtPsiUtil.isLocal(ktDeclaration) }
|
||||
|
||||
val declarationToResolve = lazyDeclarations.firstOrNull { it.name?.toLowerCase() == "resolveme" }
|
||||
?: error("declaration with name `resolveMe` was not found")
|
||||
resolveWithClearCaches(ktFile) { firModuleResolveState ->
|
||||
val rendered = LowLevelFirApiFacade.withFirDeclaration(
|
||||
declarationToResolve,
|
||||
firModuleResolveState,
|
||||
FirResolvePhase.BODY_RESOLVE
|
||||
) @Suppress("UNUSED_ANONYMOUS_PARAMETER") { firDeclaration ->
|
||||
val firFile = firModuleResolveState.getBuiltFirFileOrNull(ktFile)!!
|
||||
firFile.render(FirRenderer.RenderMode.WithResolvePhases)
|
||||
}
|
||||
val expectedFileName = testDataFile.name.replace(".kt", ".txt")
|
||||
KotlinTestUtils.assertEqualsToFile(testDataFile.parentFile.resolve(expectedFileName), rendered)
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.fir.low.level.api;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class FirLazyDeclarationResolveTestGenerated extends AbstractFirLazyDeclarationResolveTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInLazyResolve() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("classMembers.kt")
|
||||
public void testClassMembers() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve/classMembers.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithGetter.kt")
|
||||
public void testPropertyWithGetter() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve/propertyWithGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithGetterAndSetter.kt")
|
||||
public void testPropertyWithGetterAndSetter() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve/propertyWithGetterAndSetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("secondaryConstructor.kt")
|
||||
public void testSecondaryConstructor() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve/secondaryConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunctions.kt")
|
||||
public void testTopLevelFunctions() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve/topLevelFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunctionsWithExpressionBodyAndExplicitType.kt")
|
||||
public void testTopLevelFunctionsWithExpressionBodyAndExplicitType() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve/topLevelFunctionsWithExpressionBodyAndExplicitType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunctionsWithImplicitType.kt")
|
||||
public void testTopLevelFunctionsWithImplicitType() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/lazyResolve/topLevelFunctionsWithImplicitType.kt");
|
||||
}
|
||||
}
|
||||
+10
@@ -7,5 +7,15 @@ package org.jetbrains.kotlin.idea.fir.low.level.api
|
||||
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeSessionProviderStorage
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
internal fun Project.allModules() = ModuleManager.getInstance(this).modules.toList()
|
||||
|
||||
inline fun resolveWithClearCaches(context: KtElement, action: (FirModuleResolveState) -> Unit) {
|
||||
val resolveState = createResolveStateForNoCaching(context.getModuleInfo())
|
||||
action(resolveState)
|
||||
}
|
||||
Reference in New Issue
Block a user