FIR IDE: make all HL tests covering depended mode

Also changed tests to do the analysis on the KtElement of
interest instead of the KtFile where possible because currently depended
mode does not support analysing the entire file. Maybe we should support
analysing the whole file at some point.
This commit is contained in:
Tianyu Geng
2021-10-04 13:33:19 -07:00
committed by Roman Golyshev
parent 0f016c89aa
commit a08ab45e22
14 changed files with 76 additions and 67 deletions
@@ -7,17 +7,18 @@ package org.jetbrains.kotlin.analysis.api.fir
import com.intellij.psi.PsiReference
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleModuleTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions
import org.jetbrains.kotlin.analysis.api.components.RendererModifier
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleModuleTest
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
@@ -45,11 +46,12 @@ abstract class AbstractReferenceResolveTest : AbstractHLApiSingleModuleTest() {
testServices.assertions.fail { "No references at caret found" }
}
val resolvedTo = analyse(mainKtFile) {
val symbols = ktReferences.flatMap { it.resolveToSymbols() }
checkReferenceResultForValidity(module, testServices, symbols)
renderResolvedTo(symbols)
}
val resolvedTo =
analyseForTest(PsiTreeUtil.findElementOfClassAtOffset(mainKtFile, caretPosition, KtDeclaration::class.java, false) ?: mainKtFile) {
val symbols = ktReferences.flatMap { it.resolveToSymbols() }
checkReferenceResultForValidity(module, testServices, symbols)
renderResolvedTo(symbols)
}
if (Directives.UNRESOLVED_REFERENCE in module.directives) {
return
@@ -5,11 +5,9 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.symbols.markers.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtValueArgument
@@ -26,11 +24,11 @@ abstract class AbstractCompileTimeConstantEvaluatorTest : AbstractHLApiSingleFil
else -> null
} ?: testServices.assertions.fail { "Unsupported expression: $element" }
val constantValue = executeOnPooledThreadInReadAction {
analyse(expression) { expression.evaluate() }
analyseForTest(expression) { expression.evaluate() }
}
val actual = buildString {
appendLine("expression: ${expression.text}")
appendLine("constant_value: ${analyse(ktFile) { constantValue?.stringRepresentation() }}")
appendLine("constant_value: ${analyseForTest(expression) { constantValue?.stringRepresentation() }}")
appendLine("constant: ${(constantValue as? KtLiteralConstantValue<*>)?.toConst()}")
}
testServices.assertions.assertEqualsToFile(testDataFileSibling(".txt"), actual)
@@ -5,10 +5,8 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
@@ -20,7 +18,7 @@ abstract class AbstractExpectedExpressionTypeTest : AbstractHLApiSingleFileTest(
val expressionAtCaret = testServices.expressionMarkerProvider.getElementOfTypAtCaret(ktFile) as KtExpression
val actualExpectedTypeText: String? = executeOnPooledThreadInReadAction {
analyse(ktFile) {
analyseForTest(expressionAtCaret) {
expressionAtCaret.getExpectedType()?.asStringForDebugging()
}
}
@@ -5,15 +5,11 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AbstractLowLevelApiSingleFileTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestModuleStructure
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
@@ -21,7 +17,7 @@ abstract class AbstractHLExpressionTypeTest : AbstractHLApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val expression = testServices.expressionMarkerProvider.getSelectedElement(ktFile) as KtExpression
val type = executeOnPooledThreadInReadAction {
analyse(expression) { expression.getKtType()?.render() }
analyseForTest(expression) { expression.getKtType()?.render() }
}
val actual = buildString {
appendLine("expression: ${expression.text}")
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleModuleTest
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
@@ -15,7 +14,7 @@ import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractHLImportOptimizerTest : AbstractHLApiSingleModuleTest() {
override fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
val mainKtFile = ktFiles.singleOrNull() ?: ktFiles.first { it.name == "main.kt" }
val unusedImports = analyse(mainKtFile) { analyseImports(mainKtFile).unusedImports }
val unusedImports = analyseForTest(mainKtFile) { analyseImports(mainKtFile).unusedImports }
val unusedImportPaths = unusedImports
.map { it.importPath ?: error("Import $it should have an import path, instead was ${it.text}") }
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.analysis.api.fir.components
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFile
@@ -27,7 +26,7 @@ abstract class AbstractHasCommonSubtypeTest : AbstractHLApiSingleFileTest() {
val errors = mutableListOf<String>()
val originalText = ktFile.text
val actualTextBuilder = StringBuilder()
analyse(ktFile) {
analyseForTest(ktFile) {
val visitor = object : KtTreeVisitorVoid() {
override fun visitElement(element: PsiElement) {
if (element.firstChild == null) {
@@ -5,12 +5,10 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleModuleTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentsOfType
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
@@ -26,7 +24,7 @@ abstract class AbstractOverriddenDeclarationProviderTest : AbstractHLApiSingleMo
val declaration = testServices.expressionMarkerProvider.getElementOfTypAtCaret<KtDeclaration>(ktFiles.first())
val actual = executeOnPooledThreadInReadAction {
analyse(ktFiles.first()) {
analyseForTest(declaration) {
val symbol = declaration.getSymbol() as KtCallableSymbol
val allOverriddenSymbols = symbol.getAllOverriddenSymbols().map { renderSignature(it) }
val directlyOverriddenSymbols = symbol.getDirectlyOverriddenSymbols().map { renderSignature(it) }
@@ -5,15 +5,11 @@
package org.jetbrains.kotlin.analysis.api.fir.components
import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AbstractLowLevelApiSingleFileTest
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions
import org.jetbrains.kotlin.analysis.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestModuleStructure
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
@@ -28,7 +24,7 @@ abstract class AbstractRendererTest : AbstractHLApiSingleFileTest() {
val actual = executeOnPooledThreadInReadAction {
buildString {
ktFile.declarations.forEach {
analyse(it) {
analyseForTest(it) {
append(it.getSymbol().render(options))
appendLine()
}
@@ -7,12 +7,10 @@ package org.jetbrains.kotlin.analysis.api.fir.components
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.calls.KtCall
import org.jetbrains.kotlin.analysis.api.calls.KtDelegatedConstructorCallKind
import org.jetbrains.kotlin.analysis.api.calls.KtErrorCallTarget
import org.jetbrains.kotlin.analysis.api.calls.KtSuccessCallTarget
import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleModuleTest
import org.jetbrains.kotlin.analysis.api.fir.types.KtFirSubstitutor
import org.jetbrains.kotlin.analysis.api.symbols.*
@@ -36,7 +34,7 @@ abstract class AbstractResolveCallTest : AbstractHLApiSingleModuleTest() {
val expression = testServices.expressionMarkerProvider.getSelectedElement(ktFile)
val actual = executeOnPooledThreadInReadAction {
analyse(ktFile) {
analyseForTest(expression) {
resolveCall(expression)?.stringRepresentation()
}
} ?: "null"
@@ -5,9 +5,7 @@
package org.jetbrains.kotlin.analysis.api.fir.scopes
import org.jetbrains.kotlin.analysis.api.fir.executeOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.symbols.DebugSymbolRenderer
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
@@ -17,7 +15,7 @@ import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractFileScopeTest : AbstractHLApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val actual = executeOnPooledThreadInReadAction {
analyse(ktFile) {
analyseForTest(ktFile) {
val symbol = ktFile.getFileSymbol()
val scope = symbol.getFileScope()
with(DebugSymbolRenderer) {
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.analysis.api.fir.symbols
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory
import org.jetbrains.kotlin.analysis.api.fir.analyseOnPooledThreadInReadAction
import org.jetbrains.kotlin.analysis.api.fir.test.framework.AbstractHLApiSingleFileTest
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.DebugSymbolRenderer
@@ -5,28 +5,8 @@
package org.jetbrains.kotlin.analysis.api.fir
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Computable
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.DuplicatedFirSourceElementsException
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
inline fun <T> runReadAction(crossinline runnable: () -> T): T {
return ApplicationManager.getApplication().runReadAction(Computable { runnable() })
}
fun <R> executeOnPooledThreadInReadAction(action: () -> R): R =
ApplicationManager.getApplication().executeOnPooledThread<R> { runReadAction(action) }.get()
inline fun <R> analyseOnPooledThreadInReadAction(context: KtElement, crossinline action: KtAnalysisSession.() -> R): R =
executeOnPooledThreadInReadAction {
analyse(context) { action() }
}
/**
* Temporary
@@ -23,7 +23,7 @@ class TestKtModuleProvider(
internal fun getModuleInfoByKtFile(ktFile: KtFile): TestKtSourceModule =
cache.values.first { moduleSourceInfo ->
ktFile in moduleSourceInfo.ktFiles
(if (ktFile.isPhysical) ktFile else ktFile.originalFile) in moduleSourceInfo.ktFiles
}
internal fun getModule(moduleName: String): TestKtSourceModule =
@@ -8,13 +8,19 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.test.base
import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.TestDataFile
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.analyseInDependedAnalysisSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.ModuleRegistrarPreAnalysisHandler
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.TestKtModuleProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.projectModuleProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalKtFile
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.TestInfrastructureInternals
import org.jetbrains.kotlin.test.bind
@@ -33,10 +39,12 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.TestInfo
import java.nio.file.Path
import java.nio.file.Paths
import java.util.concurrent.ExecutionException
import kotlin.io.path.nameWithoutExtension
abstract class AbstractLowLevelApiTest : TestWithDisposable() {
private lateinit var testInfo: KotlinTestInfo
private var useDependedAnalysisSession: Boolean = false
@OptIn(TestInfrastructureInternals::class)
private val configure: TestConfigurationBuilder.() -> Unit = {
@@ -102,11 +110,24 @@ abstract class AbstractLowLevelApiTest : TestWithDisposable() {
registerApplicationServices()
doTestByFileStructure(
moduleInfo.testFilesToKtFiles.filter { (testFile, _) -> !testFile.isAdditional }.values.toList(),
moduleStructure,
testServices
)
val ktFiles = moduleInfo.testFilesToKtFiles.filterKeys { testFile -> !testFile.isAdditional }.values.toList()
doTestByFileStructure(ktFiles, moduleStructure, testServices)
useDependedAnalysisSession = true
try {
doTestByFileStructure(ktFiles.map {
val fakeFile = it.copy() as KtFile
fakeFile.originalKtFile = it
fakeFile
}, moduleStructure, testServices)
} catch (e: SkipDependedModeException) {
// Skip the test if needed
} catch (e: ExecutionException) {
if (e.cause !is SkipDependedModeException)
throw Exception("Test succeeded in normal analysis mode but failed in depended analysis mode.", e)
} catch (e: Exception) {
throw Exception("Test succeeded in normal analysis mode but failed in depended analysis mode.", e)
}
}
private fun registerApplicationServices() {
@@ -130,5 +151,32 @@ abstract class AbstractLowLevelApiTest : TestWithDisposable() {
tags = testInfo.tags
)
}
protected inline fun <R> analyseOnPooledThreadInReadAction(context: KtElement, crossinline action: KtAnalysisSession.() -> R): R =
executeOnPooledThreadInReadAction {
analyseForTest(context) { action() }
}
protected inline fun <T> runReadAction(crossinline runnable: () -> T): T {
return ApplicationManager.getApplication().runReadAction(Computable { runnable() })
}
protected inline fun <R> executeOnPooledThreadInReadAction(crossinline action: () -> R): R =
ApplicationManager.getApplication().executeOnPooledThread<R> { runReadAction(action) }.get()
protected fun <R> analyseForTest(contextElement: KtElement, action: KtAnalysisSession.() -> R): R {
return if (useDependedAnalysisSession) {
// Depended mode does not support analysing a KtFile. See org.jetbrains.kotlin.analysis.low.level.api.fir.api.LowLevelFirApiFacadeForResolveOnAir#getResolveStateForDependentCopy
if (contextElement is KtFile) throw SkipDependedModeException()
require(!contextElement.isPhysical)
analyseInDependedAnalysisSession(contextElement.containingKtFile.originalKtFile!!, contextElement, action)
} else {
analyse(contextElement, action)
}
}
private class SkipDependedModeException : Exception()
}