[FIR IDE] Extract common components of the Analysis API to separate modules

This commit is contained in:
Yan Zhulanow
2021-10-05 19:08:06 +09:00
committed by teamcityserver
parent 44a1fe668e
commit e2c9be0932
100 changed files with 966 additions and 424 deletions
@@ -16,6 +16,7 @@ dependencies {
api(project(":compiler:fir:checkers:checkers.jvm"))
api(project(":compiler:fir:java"))
api(project(":compiler:backend.common.jvm"))
api(project(":analysis:analysis-api-impl-barebone"))
testApi(project(":analysis:analysis-api-fir"))
implementation(project(":compiler:ir.psi2ir"))
implementation(project(":compiler:fir:entrypoint"))
@@ -32,6 +33,7 @@ dependencies {
testApi(toolsJar())
testApi(projectTests(":compiler:tests-common"))
testApi(projectTests(":compiler:fir:analysis-tests:legacy-fir-tests"))
testApi(projectTests(":analysis:analysis-api-impl-barebone"))
testApi(project(":kotlin-test:kotlin-test-junit"))
testApiJUnit5()
testApi(project(":kotlin-reflect"))
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider
@@ -6,7 +6,16 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.InternalForInline
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirAnonymousFunctionExpression
import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.DiagnosticsCollector
@@ -28,15 +37,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirAnonymousFunctionExpression
import org.jetbrains.kotlin.fir.expressions.FirAnonymousObjectExpression
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.psi.*
@@ -1,12 +0,0 @@
/*
* 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.analysis.low.level.api.fir.annotations
@RequiresOptIn
annotation class PrivateForInline
@RequiresOptIn
annotation class InternalForInline
@@ -1,15 +0,0 @@
/*
* 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.analysis.low.level.api.fir.annotations
@Target(AnnotationTarget.CLASS)
annotation class ThreadSafe
@Target(AnnotationTarget.CLASS)
annotation class NotThreadSafe
@Target(AnnotationTarget.CLASS)
annotation class Immutable
@@ -6,10 +6,10 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.InternalForInline
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.analysis.project.structure.KtModule
@@ -6,13 +6,12 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirIdeResolveStateService
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.InternalForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveType
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.analyzer.ModuleSourceInfoBase
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.declarations.*
@@ -6,7 +6,19 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analysis.api.impl.barebone.parentOfType
import org.jetbrains.kotlin.analysis.api.impl.barebone.parentsOfType
import org.jetbrains.kotlin.analysis.low.level.api.fir.DeclarationCopyBuilder.withBodyFrom
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.FirTypeResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.scopes.createImportingScopes
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirModuleResolveStateDepended
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirModuleResolveStateImpl
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FileTowerProvider
@@ -20,19 +32,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.firIdeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.FirIdeSourcesSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getElementTextInContext
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentOfType
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentsOfType
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.FirTypeResolveTransformer
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataContextCollector
import org.jetbrains.kotlin.fir.scopes.createImportingScopes
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
@@ -7,11 +7,11 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder
import com.intellij.psi.PsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.ThreadSafe
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.ThreadSafe
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirModuleResolveState
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.FirFileBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
@@ -5,13 +5,13 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.ThreadSafe
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
import org.jetbrains.kotlin.fir.builder.PsiHandlingMode
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirPhaseRunner
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.ThreadSafe
import org.jetbrains.kotlin.psi.KtFile
/**
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.PrivateForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.ResolveTreeBuilder
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.lockWithPCECheck
import java.util.concurrent.locks.ReentrantLock
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder
import com.intellij.concurrency.ConcurrentCollectionFactory
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.ThreadSafe
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
@@ -13,7 +14,6 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.ThreadSafe
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtFile
@@ -5,11 +5,11 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.analysis.api.impl.barebone.parentOfType
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentOfType
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.Immutable
import org.jetbrains.kotlin.analysis.api.impl.barebone.annotations.Immutable
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.ModuleFileCache
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
@@ -12,6 +12,8 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.*
import org.jetbrains.kotlin.analysis.api.impl.barebone.parentOfType
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.cfg.containingDeclarationForPseudocode
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
@@ -20,8 +22,6 @@ import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.Lock
import kotlin.reflect.KProperty
@@ -135,15 +135,4 @@ fun KtElement.getElementTextInContext(): String {
return builder.toString().trimIndent().trim()
}
private const val ELEMENT_TAG = "ELEMENT"
inline fun <reified T : PsiElement> PsiElement.parentOfType(withSelf: Boolean = false): T? {
return PsiTreeUtil.getParentOfType(this, T::class.java, !withSelf)
}
fun <T : PsiElement> PsiElement.parentsOfType(clazz: Class<out T>, withSelf: Boolean = true): Sequence<T> {
return (if (withSelf) parentsWithSelf else parents).filterIsInstance(clazz)
}
inline fun <reified T : PsiElement> PsiElement.parentsOfType(withSelf: Boolean = true): Sequence<T> =
parentsOfType(T::class.java, withSelf)
private const val ELEMENT_TAG = "ELEMENT"
@@ -7,13 +7,13 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProvider
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirRenderer
import org.jetbrains.kotlin.fir.declarations.FirImport
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
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.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
@@ -5,14 +5,14 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.AbstractCompilerBasedTest
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtModuleProvider
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile
import org.jetbrains.kotlin.analysis.low.level.api.fir.createResolveStateForNoCaching
import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.test.TestConfiguration
import org.jetbrains.kotlin.test.bind
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.builders.firHandlersStep
@@ -20,28 +20,14 @@ import org.jetbrains.kotlin.test.builders.testConfiguration
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
import org.jetbrains.kotlin.test.services.*
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.TestInfo
abstract class AbstractCompilerBasedTest : AbstractKotlinCompilerTest() {
private var _disposable: Disposable? = null
protected val disposable: Disposable get() = _disposable!!
@BeforeEach
private fun intiDisposable(testInfo: TestInfo) {
_disposable = Disposer.newDisposable("disposable for ${testInfo.displayName}")
}
@AfterEach
private fun disposeDisposable() {
_disposable?.let { Disposer.dispose(it) }
_disposable = null
}
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendFacade
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
abstract class AbstractCompilerBasedTestForFir : AbstractCompilerBasedTest() {
final override fun TestConfigurationBuilder.configuration() {
globalDefaults {
frontend = FrontendKinds.FIR
@@ -62,7 +48,6 @@ abstract class AbstractCompilerBasedTest : AbstractKotlinCompilerTest() {
open fun TestConfigurationBuilder.configureTest() {}
inner class LowLevelFirFrontendFacade(
testServices: TestServices
) : FrontendFacade<FirOutputArtifact>(testServices, FrontendKinds.FIR) {
@@ -103,14 +88,4 @@ abstract class AbstractCompilerBasedTest : AbstractKotlinCompilerTest() {
FirLazyTransformerForIDE.enableDeepEnsure = oldEnableDeepEnsure
}
}
private fun ignoreTest(filePath: String, configuration: TestConfiguration): Boolean {
val modules = configuration.moduleStructureExtractor.splitTestDataByModules(filePath, configuration.directives)
if (modules.modules.none { it.files.any { it.isKtFile } }) {
return true // nothing to highlight
}
return false
}
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider
import org.jetbrains.kotlin.test.services.*
class ModuleRegistrarPreAnalysisHandler(
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.low.level.api.fir.compiler.based
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestService
import org.jetbrains.kotlin.test.services.TestServices
class TestKtModuleProvider(
private val testServices: TestServices
) : TestService {
private val cache = mutableMapOf<String, TestKtSourceModule>()
fun registerModuleInfo(project: Project, testModule: TestModule, ktFiles: Map<TestFile, KtFile>) {
cache[testModule.name] = TestKtSourceModule(project, testModule, ktFiles, testServices)
}
internal fun getModuleInfoByKtFile(ktFile: KtFile): TestKtSourceModule =
cache.values.first { moduleSourceInfo ->
(if (ktFile.isPhysical) ktFile else ktFile.originalFile) in moduleSourceInfo.ktFiles
}
internal fun getModule(moduleName: String): TestKtSourceModule =
cache.getValue(moduleName)
}
val TestServices.projectModuleProvider: TestKtModuleProvider by TestServices.testServiceAccessor()
@@ -1,110 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.low.level.api.fir.compiler.based
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.KtLibrarySourceModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.test.frontend.fir.getAnalyzerServices
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
@OptIn(ExperimentalStdlibApi::class)
internal class TestKtSourceModule(
override val project: Project,
val testModule: TestModule,
val testFilesToKtFiles: Map<TestFile, KtFile>,
testServices: TestServices,
) : KtSourceModule {
private val moduleProvider = testServices.projectModuleProvider
private val compilerConfigurationProvider = testServices.compilerConfigurationProvider
private val configuration = compilerConfigurationProvider.getCompilerConfiguration(testModule)
val ktFiles = testFilesToKtFiles.values.toSet()
override val moduleName: String
get() = testModule.name
override val directRegularDependencies: List<KtModule> by lazy(LazyThreadSafetyMode.PUBLICATION) {
buildList {
testModule.allDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) }
addIfNotNull(
libraryByRoots(
(configuration.jvmModularRoots + configuration.jvmClasspathRoots).map(File::toPath)
)
)
}
}
override val directRefinementDependencies: List<KtModule> by lazy(LazyThreadSafetyMode.PUBLICATION) {
testModule.dependsOnDependencies
.map { moduleProvider.getModule(it.moduleName) }
}
override val directFriendDependencies: List<KtModule> by lazy(LazyThreadSafetyMode.PUBLICATION) {
buildList {
testModule.friendDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) }
addIfNotNull(
libraryByRoots(configuration[JVMConfigurationKeys.FRIEND_PATHS].orEmpty().map(Paths::get))
)
}
}
private fun libraryByRoots(roots: List<Path>): LibraryByRoots? {
if (roots.isEmpty()) return null
return LibraryByRoots(
roots,
this@TestKtSourceModule,
project,
)
}
override val contentScope: GlobalSearchScope =
TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, testFilesToKtFiles.values)
override val languageVersionSettings: LanguageVersionSettings
get() = testModule.languageVersionSettings
override val platform: TargetPlatform
get() = testModule.targetPlatform
override val analyzerServices: PlatformDependentAnalyzerServices
get() = testModule.targetPlatform.getAnalyzerServices()
}
private class LibraryByRoots(
private val roots: List<Path>,
private val sourceModule: KtSourceModule,
override val project: Project,
) : KtLibraryModule {
override val libraryName: String get() = "Test Library"
override val directRegularDependencies: List<KtModule> get() = emptyList()
override val directRefinementDependencies: List<KtModule> get() = emptyList()
override val directFriendDependencies: List<KtModule> get() = emptyList()
override val contentScope: GlobalSearchScope get() = ProjectScope.getLibrariesScope(project)
override val platform: TargetPlatform get() = sourceModule.platform
override val analyzerServices: PlatformDependentAnalyzerServices get() = sourceModule.analyzerServices
override fun getBinaryRoots(): Collection<Path> = roots
override val librarySources: KtLibrarySourceModule? get() = null
}
@@ -13,6 +13,7 @@ import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.InvalidWayOfUsingAnalysisSession
import org.jetbrains.kotlin.analysis.api.KtAnalysisSessionProvider
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.FirIdeResolveStateService
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.PackagePartProviderFactory
@@ -5,13 +5,13 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostic.compiler.based
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.AbstractCompilerBasedTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.AbstractCompilerBasedTestForFir
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.addIdeTestIgnoreHandler
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.runners.baseFirDiagnosticTestConfiguration
import org.jetbrains.kotlin.test.runners.baseFirSpecDiagnosticTestConfiguration
abstract class AbstractDiagnosisCompilerTestDataSpecTest : AbstractCompilerBasedTest() {
abstract class AbstractDiagnosisCompilerTestDataSpecTest : AbstractCompilerBasedTestForFir() {
override fun TestConfigurationBuilder.configureTest() {
baseFirDiagnosticTestConfiguration(frontendFacade = ::LowLevelFirFrontendFacade)
baseFirSpecDiagnosticTestConfiguration()
@@ -5,12 +5,12 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostic.compiler.based
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.AbstractCompilerBasedTest
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.AbstractCompilerBasedTestForFir
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.addIdeTestIgnoreHandler
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.runners.baseFirDiagnosticTestConfiguration
abstract class AbstractDiagnosisCompilerTestDataTest : AbstractCompilerBasedTest() {
abstract class AbstractDiagnosisCompilerTestDataTest : AbstractCompilerBasedTestForFir() {
override fun TestConfigurationBuilder.configureTest() {
baseFirDiagnosticTestConfiguration(frontendFacade = ::LowLevelFirFrontendFacade)
addIdeTestIgnoreHandler()
@@ -5,4 +5,10 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.test.base
abstract class AbstractLowLevelApiSingleModuleTest : AbstractLowLevelApiTest()
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.AbstractFrontendApiTest
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService
abstract class AbstractLowLevelApiSingleModuleTest : AbstractFrontendApiTest() {
override val configurator: FrontendApiTestConfiguratorService
get() = FirLowLevelFrontendApiTestConfiguratorService
}
@@ -1,193 +0,0 @@
/*
* Copyright 2010-2021 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.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.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.InTextDirectivesUtils
import org.jetbrains.kotlin.test.TestInfrastructureInternals
import org.jetbrains.kotlin.test.bind
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.builders.testConfiguration
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.impl.TemporaryDirectoryManagerImpl
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
open protected val enableTestInDependedMode: Boolean = true
@OptIn(TestInfrastructureInternals::class)
private val configure: TestConfigurationBuilder.() -> Unit = {
globalDefaults {
frontend = FrontendKinds.FIR
targetPlatform = JvmPlatforms.defaultJvmPlatform
dependencyKind = DependencyKind.Source
}
useConfigurators(
::CommonEnvironmentConfigurator,
::JvmEnvironmentConfigurator,
)
assertions = JUnit5Assertions
useAdditionalService<TemporaryDirectoryManager>(::TemporaryDirectoryManagerImpl)
useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray())
useDirectives(JvmEnvironmentConfigurationDirectives)
useSourcePreprocessor(::ExpressionMarkersSourceFilePreprocessor)
useAdditionalService { ExpressionMarkerProvider() }
useAdditionalService(::TestKtModuleProvider)
usePreAnalysisHandlers(::ModuleRegistrarPreAnalysisHandler.bind(disposable))
configureTest(this)
startingArtifactFactory = { ResultingArtifact.Source() }
this.testInfo = this@AbstractLowLevelApiTest.testInfo
}
protected lateinit var testDataPath: Path
protected fun testDataFileSibling(extension: String): Path {
val extensionWithDot = "." + extension.removePrefix(".")
return testDataPath.resolveSibling(testDataPath.nameWithoutExtension + extensionWithDot)
}
open fun configureTest(builder: TestConfigurationBuilder) {}
protected fun runTest(@TestDataFile path: String) {
testDataPath = Paths.get(path)
val testConfiguration = testConfiguration(path, configure)
Disposer.register(disposable, testConfiguration.rootDisposable)
val testServices = testConfiguration.testServices
val moduleStructure = testConfiguration.moduleStructureExtractor.splitTestDataByModules(
path,
testConfiguration.directives,
).also { testModuleStructure ->
testConfiguration.testServices.register(TestModuleStructure::class, testModuleStructure)
testConfiguration.preAnalysisHandlers.forEach { preprocessor ->
preprocessor.preprocessModuleStructure(testModuleStructure)
}
}
val singleModule = moduleStructure.modules.single()
val project = testServices.compilerConfigurationProvider.getProject(singleModule)
val moduleInfoProvider = testServices.projectModuleProvider
val moduleInfo = moduleInfoProvider.getModule(singleModule.name)
with(project as MockProject) {
registerServicesForProject(this)
}
registerApplicationServices()
val ktFiles = moduleInfo.testFilesToKtFiles.filterKeys { testFile -> !testFile.isAdditional }.values.toList()
doTestByFileStructure(ktFiles, moduleStructure, testServices)
if (!enableTestInDependedMode || ktFiles.any {
InTextDirectivesUtils.isDirectiveDefined(it.text, DISABLE_DEPENDED_MODE_DIRECTIVE)
}) {
return
}
try {
useDependedAnalysisSession = true
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() {
val application = ApplicationManager.getApplication() as MockApplication
KotlinCoreEnvironment.underApplicationLock {
registerApplicationServices(application)
}
}
protected open fun registerServicesForProject(project: MockProject) {}
protected open fun registerApplicationServices(application: MockApplication) {}
protected abstract fun doTestByFileStructure(ktFiles: List<KtFile>, moduleStructure: TestModuleStructure, testServices: TestServices)
@BeforeEach
fun initTestInfo(testInfo: TestInfo) {
this.testInfo = KotlinTestInfo(
className = testInfo.testClass.orElseGet(null)?.name ?: "_undefined_",
methodName = testInfo.testMethod.orElseGet(null)?.name ?: "_testUndefined_",
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()
companion object {
val DISABLE_DEPENDED_MODE_DIRECTIVE = "DISABLE_DEPENDED_MODE"
}
}
@@ -1,105 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.low.level.api.fir.test.base
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.analysis.low.level.api.fir.annotations.PrivateForInline
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.parentOfType
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.services.SourceFilePreprocessor
import org.jetbrains.kotlin.test.services.TestService
import org.jetbrains.kotlin.test.services.TestServices
internal class ExpressionMarkersSourceFilePreprocessor(testServices: TestServices) : SourceFilePreprocessor(testServices) {
override fun process(file: TestFile, content: String): String {
val withSelectedProcessed = processSelectedExpression(file, content)
return processCaretExpression(file, withSelectedProcessed)
}
private fun processSelectedExpression(file: TestFile, content: String): String {
val startCaretPosition = content.indexOfOrNull(TAGS.OPENING_EXPRESSION_TAG) ?: return content
val endCaretPosition = content.indexOfOrNull(TAGS.CLOSING_EXPRESSION_TAG)
?: error("${TAGS.CLOSING_EXPRESSION_TAG} was not found in the file")
check(startCaretPosition < endCaretPosition)
testServices.expressionMarkerProvider.addSelectedExpression(
file,
TextRange.create(startCaretPosition, endCaretPosition - TAGS.OPENING_EXPRESSION_TAG.length)
)
return content
.replace(TAGS.OPENING_EXPRESSION_TAG, "")
.replace(TAGS.CLOSING_EXPRESSION_TAG, "")
}
private fun processCaretExpression(file: TestFile, content: String): String {
val startCaretPosition = content.indexOfOrNull(TAGS.CARET) ?: return content
testServices.expressionMarkerProvider.addCaret(file, startCaretPosition)
return content
.replace(TAGS.CARET, "")
}
object TAGS {
const val OPENING_EXPRESSION_TAG = "<expr>"
const val CLOSING_EXPRESSION_TAG = "</expr>"
const val CARET = "<caret>"
}
}
class ExpressionMarkerProvider : TestService {
private val selected = mutableMapOf<String, TextRange>()
@PrivateForInline
val atCaret = mutableMapOf<String, Int>()
fun addSelectedExpression(file: TestFile, range: TextRange) {
selected[file.relativePath] = range
}
@OptIn(PrivateForInline::class)
fun addCaret(file: TestFile, caret: Int) {
atCaret[file.relativePath] = caret
}
@OptIn(PrivateForInline::class)
fun getCaretPosition(file: KtFile): Int {
return atCaret[file.name]
?: error("No caret found in file")
}
inline fun <reified P : KtElement> getElementOfTypAtCaret(file: KtFile): P {
val offset = getCaretPosition(file)
return file.findElementAt(offset)
?.parentOfType<P>()
?: error("No expression found at caret")
}
fun getSelectedElement(file: KtFile): KtElement {
val range = selected[file.name]
?: error("No selected expression found in file")
val elements = file.elementsInRange(range).trimWhitespaces()
if (elements.size != 1) {
error("Expected one element at rage but found ${elements.size} [${elements.joinToString { it::class.simpleName + ": " + it.text }}]")
}
return elements.single() as KtElement
}
private fun List<PsiElement>.trimWhitespaces(): List<PsiElement> =
dropWhile { it is PsiWhiteSpace }
.dropLastWhile { it is PsiWhiteSpace }
}
private fun String.indexOfOrNull(substring: String) =
indexOf(substring).takeIf { it >= 0 }
val TestServices.expressionMarkerProvider: ExpressionMarkerProvider by TestServices.testServiceAccessor()
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2021 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.analysis.low.level.api.fir.test.base
import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.ModuleRegistrarPreAnalysisHandler
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalKtFile
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.bind
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
object FirLowLevelFrontendApiTestConfiguratorService : FrontendApiTestConfiguratorService {
override fun TestConfigurationBuilder.configureTest(disposable: Disposable) {
usePreAnalysisHandlers(::ModuleRegistrarPreAnalysisHandler.bind(disposable))
}
override fun processTestFiles(files: List<KtFile>): List<KtFile> {
return files.map {
val fakeFile = it.copy() as KtFile
fakeFile.originalKtFile = it
fakeFile
}
}
override fun getOriginalFile(file: KtFile): KtFile {
return file.originalKtFile!!
}
override fun registerProjectServices(project: MockProject) {}
override fun registerApplicationServices(application: MockApplication) {}
}
@@ -1,28 +0,0 @@
/*
* Copyright 2010-2021 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.analysis.low.level.api.fir.test.base
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.TestInfo
abstract class TestWithDisposable {
private var _disposable: Disposable? = null
protected val disposable: Disposable get() = _disposable!!
@BeforeEach
private fun intiDisposable(testInfo: TestInfo) {
_disposable = Disposer.newDisposable("disposable for ${testInfo.displayName}")
}
@AfterEach
private fun disposeDisposable() {
_disposable?.let { Disposer.dispose(it) }
_disposable = null
}
}