[TEST] Add implementation of new infrastructure services for compiler tests
All of new classes lays in lays in :compiler:tests-common-new module which includes classes for FE 1.0 and FIR diagnostics tests and JVM black boxtests
This commit is contained in:
@@ -458,6 +458,20 @@ class KotlinCoreEnvironment private constructor(
|
||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun createForTests(
|
||||
projectEnvironment: ProjectEnvironment, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
return KotlinCoreEnvironment(projectEnvironment, initialConfiguration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
return ProjectEnvironment(parentDisposable, appEnv)
|
||||
}
|
||||
|
||||
// used in the daemon for jar cache cleanup
|
||||
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
||||
|
||||
|
||||
@@ -456,6 +456,20 @@ class KotlinCoreEnvironment private constructor(
|
||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun createForTests(
|
||||
projectEnvironment: ProjectEnvironment, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
return KotlinCoreEnvironment(projectEnvironment, initialConfiguration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
return ProjectEnvironment(parentDisposable, appEnv)
|
||||
}
|
||||
|
||||
// used in the daemon for jar cache cleanup
|
||||
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ abstract class JvmPlatform : SimplePlatform("JVM") {
|
||||
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
object JvmPlatforms {
|
||||
private val UNSPECIFIED_SIMPLE_JVM_PLATFORM = JdkPlatform(JvmTarget.JVM_1_6)
|
||||
private val UNSPECIFIED_SIMPLE_JVM_PLATFORM = JdkPlatform(JvmTarget.DEFAULT)
|
||||
private val jvmTargetToJdkPlatform: Map<JvmTarget, TargetPlatform> =
|
||||
JvmTarget.values().map { it to JdkPlatform(it).toTargetPlatform() }.toMap()
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
|
||||
|
||||
class FirAnalyzerFacade(val session: FirSession, val languageVersionSettings: LanguageVersionSettings, val ktFiles: List<KtFile>) {
|
||||
class FirAnalyzerFacade(val session: FirSession, val languageVersionSettings: LanguageVersionSettings, val ktFiles: Collection<KtFile>) {
|
||||
private var firFiles: List<FirFile>? = null
|
||||
private var scopeSession: ScopeSession? = null
|
||||
private var collectedDiagnostics: Map<FirFile, List<FirDiagnostic<*>>>? = null
|
||||
|
||||
@@ -16,7 +16,10 @@ class FirJvmModuleInfo(override val name: Name, val dependencies: List<ModuleInf
|
||||
companion object {
|
||||
val LIBRARIES_MODULE_NAME = Name.special("<dependencies>")
|
||||
|
||||
fun createForLibraries(): FirJvmModuleInfo = FirJvmModuleInfo(LIBRARIES_MODULE_NAME, emptyList())
|
||||
fun createForLibraries(mainModuleName: String? = null): FirJvmModuleInfo {
|
||||
val name = mainModuleName?.let { Name.special("<dependencies of $it>") } ?: LIBRARIES_MODULE_NAME
|
||||
return FirJvmModuleInfo(name, emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
constructor(moduleName: String, dependencies: List<ModuleInfo>) : this(Name.identifier(moduleName), dependencies)
|
||||
|
||||
@@ -149,7 +149,7 @@ object CheckerTestUtil {
|
||||
return diagnostics
|
||||
}
|
||||
|
||||
private fun getDebugInfoDiagnostics(
|
||||
fun getDebugInfoDiagnostics(
|
||||
root: PsiElement,
|
||||
bindingContext: BindingContext,
|
||||
markDynamicCalls: Boolean,
|
||||
|
||||
+4
@@ -36,4 +36,8 @@ public abstract class DiagnosticFactoryWithPsiElement<E extends PsiElement, D ex
|
||||
protected boolean isValid(ParametrizedDiagnostic<E> diagnostic) {
|
||||
return positioningStrategy.isValid(diagnostic.getPsiElement());
|
||||
}
|
||||
|
||||
public PositioningStrategy<? super E> getPositioningStrategy() {
|
||||
return positioningStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class JvmIrCodegenFactory(private val phaseConfig: PhaseConfig) : CodegenFactory
|
||||
)
|
||||
}
|
||||
|
||||
internal fun doGenerateFilesInternal(
|
||||
fun doGenerateFilesInternal(
|
||||
state: GenerationState,
|
||||
irModuleFragment: IrModuleFragment,
|
||||
symbolTable: SymbolTable,
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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.test.util
|
||||
|
||||
import java.util.*
|
||||
|
||||
fun interface Condition<in T> {
|
||||
operator fun invoke(value: T): Boolean
|
||||
}
|
||||
|
||||
object Conditions {
|
||||
private val TRUE = Condition<Any?> { true }
|
||||
private val FALSE = Condition<Any?> { false }
|
||||
|
||||
fun <T> alwaysTrue(): Condition<T> {
|
||||
return TRUE
|
||||
}
|
||||
|
||||
fun <T> alwaysFalse(): Condition<T> {
|
||||
return FALSE
|
||||
}
|
||||
|
||||
fun <T> notNull(): Condition<T> {
|
||||
return Condition { it != null }
|
||||
}
|
||||
|
||||
fun <T> constant(value: Boolean): Condition<T> {
|
||||
return if (value) alwaysTrue() else alwaysFalse()
|
||||
}
|
||||
|
||||
fun <T> instanceOf(clazz: Class<*>): Condition<T> {
|
||||
return Condition { t -> clazz.isInstance(t) }
|
||||
}
|
||||
|
||||
fun <T> notInstanceOf(clazz: Class<*>): Condition<T> {
|
||||
return Condition { t -> !clazz.isInstance(t) }
|
||||
}
|
||||
|
||||
fun assignableTo(clazz: Class<*>): Condition<Class<*>> {
|
||||
return Condition { t -> clazz.isAssignableFrom(t) }
|
||||
}
|
||||
|
||||
fun <T> instanceOf(vararg clazz: Class<*>): Condition<T> {
|
||||
return Condition { t ->
|
||||
for (aClass in clazz) {
|
||||
if (aClass.isInstance(t)) return@Condition true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> `is`(option: T): Condition<T> {
|
||||
return equalTo(option)
|
||||
}
|
||||
|
||||
fun <T> equalTo(option: Any?): Condition<T> {
|
||||
return Condition { t -> t == option }
|
||||
}
|
||||
|
||||
fun <T> notEqualTo(option: Any?): Condition<T> {
|
||||
return Condition { t -> t != option }
|
||||
}
|
||||
|
||||
fun <T> oneOf(vararg options: T): Condition<T> {
|
||||
return oneOf(options.toList())
|
||||
}
|
||||
|
||||
fun <T> oneOf(options: Collection<T>): Condition<T> {
|
||||
return Condition { t -> options.contains(t) }
|
||||
}
|
||||
|
||||
fun <T> not(c: Condition<T>): Condition<T> {
|
||||
if (c === alwaysTrue<Any>()) return alwaysFalse()
|
||||
if (c === alwaysFalse<Any>()) return alwaysTrue()
|
||||
return if (c is Not<*>) {
|
||||
(c as Not<T>).c as Condition<T>
|
||||
} else Not(c)
|
||||
}
|
||||
|
||||
fun <T> and(c1: Condition<T>, c2: Condition<T>): Condition<T> {
|
||||
if (c1 === alwaysTrue<Any>() || c2 === alwaysFalse<Any>()) return c2
|
||||
return if (c2 === alwaysTrue<Any>() || c1 === alwaysFalse<Any>()) c1 else And(c1, c2)
|
||||
}
|
||||
|
||||
fun <T> or(c1: Condition<T>, c2: Condition<T>): Condition<T> {
|
||||
if (c1 === alwaysFalse<Any>() || c2 === alwaysTrue<Any>()) return c2
|
||||
return if (c2 === alwaysFalse<Any>() || c1 === alwaysTrue<Any>()) c1 else Or(c1, c2)
|
||||
}
|
||||
|
||||
fun <A, B> compose(func: (A) -> B, condition: Condition<B>): Condition<A> {
|
||||
return Condition { condition.invoke(func(it)) }
|
||||
}
|
||||
|
||||
fun <T> cached(c: Condition<T>): Condition<T> {
|
||||
return SoftRefCache(c)
|
||||
}
|
||||
|
||||
private class Not<T>(val c: Condition<T>) : Condition<T> {
|
||||
override fun invoke(value: T): Boolean {
|
||||
return !c.invoke(value)
|
||||
}
|
||||
}
|
||||
|
||||
private class And<T>(val c1: Condition<T>, val c2: Condition<T>) : Condition<T> {
|
||||
override fun invoke(value: T): Boolean {
|
||||
return c1.invoke(value) && c2.invoke(value)
|
||||
}
|
||||
}
|
||||
|
||||
private class Or<T>(val c1: Condition<T>, val c2: Condition<T>) : Condition<T> {
|
||||
override fun invoke(value: T): Boolean {
|
||||
return c1.invoke(value) || c2.invoke(value)
|
||||
}
|
||||
}
|
||||
|
||||
private class SoftRefCache<T>(private val myCondition: Condition<T>) : Condition<T> {
|
||||
private val myCache: WeakHashMap<T, Boolean> = WeakHashMap();
|
||||
|
||||
override fun invoke(value: T): Boolean {
|
||||
return myCache.computeIfAbsent(value) { myCondition(value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infix fun <T> Condition<T>.and(other: Condition<T>): Condition<T> {
|
||||
return Conditions.and(this, other)
|
||||
}
|
||||
|
||||
infix fun <T> Condition<T>.or(other: Condition<T>): Condition<T> {
|
||||
return Conditions.or(this, other)
|
||||
}
|
||||
|
||||
operator fun <T> Condition<T>.not(): Condition<T> {
|
||||
return Conditions.not(this)
|
||||
}
|
||||
|
||||
fun <T> Condition<T>.cached(): Condition<T> {
|
||||
return Conditions.cached(this)
|
||||
}
|
||||
+12
@@ -7,3 +7,15 @@ package org.jetbrains.kotlin.test.util
|
||||
|
||||
fun Iterable<*>.joinToArrayString(): String = joinToString(separator = ", ", prefix = "[", postfix = "]")
|
||||
fun Array<*>.joinToArrayString(): String = joinToString(separator = ", ", prefix = "[", postfix = "]")
|
||||
|
||||
private const val DEFAULT_LINE_SEPARATOR = "\n"
|
||||
|
||||
fun String.trimTrailingWhitespacesAndAddNewlineAtEOF(): String =
|
||||
this.trimTrailingWhitespaces().let { result -> if (result.endsWith("\n")) result else result + "\n" }
|
||||
|
||||
fun String.trimTrailingWhitespaces(): String =
|
||||
this.split('\n').joinToString(separator = "\n") { it.trimEnd() }
|
||||
|
||||
fun String.convertLineSeparators(separator: String = DEFAULT_LINE_SEPARATOR): String {
|
||||
return replace(Regex.fromLiteral("\r\n|\r|\n"), separator)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import org.jetbrains.kotlin.ideaExt.idea
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testApi(project(":compiler:fir:entrypoint"))
|
||||
testApi(project(":compiler:cli"))
|
||||
testImplementation(project(":compiler:ir.tree.impl"))
|
||||
testImplementation(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
|
||||
testCompileOnly(project(":kotlin-reflect-api"))
|
||||
testRuntimeOnly(project(":kotlin-reflect"))
|
||||
testRuntimeOnly(project(":core:descriptors.runtime"))
|
||||
|
||||
testImplementation(projectTests(":generators:test-generator"))
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.7.0"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testImplementation("org.junit.platform:junit-platform-commons:1.7.0")
|
||||
testApi(projectTests(":compiler:test-infrastructure"))
|
||||
testImplementation(projectTests(":compiler:test-infrastructure-utils"))
|
||||
|
||||
testImplementation(intellijDep()) {
|
||||
// This dependency is needed only for FileComparisonFailure
|
||||
includeJars("idea_rt", rootProject = rootProject)
|
||||
isTransitive = false
|
||||
}
|
||||
|
||||
// This is needed only for using FileComparisonFailure, which relies on JUnit 3 classes
|
||||
testRuntimeOnly(commonDep("junit:junit"))
|
||||
testRuntimeOnly(intellijDep()) {
|
||||
includeJars("jna", rootProject = rootProject)
|
||||
}
|
||||
testRuntimeOnly(toolsJar())
|
||||
}
|
||||
|
||||
val generationRoot = projectDir.resolve("tests-gen")
|
||||
|
||||
sourceSets {
|
||||
"main" { none() }
|
||||
"test" {
|
||||
projectDefault()
|
||||
this.java.srcDir(generationRoot.name)
|
||||
}
|
||||
}
|
||||
|
||||
if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
|
||||
apply(plugin = "idea")
|
||||
idea {
|
||||
this.module.generatedSourceDirs.add(generationRoot)
|
||||
}
|
||||
}
|
||||
|
||||
projectTest(parallel = true) {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
jvmArgs!!.removeIf { it.contains("-Xmx") }
|
||||
maxHeapSize = "3g"
|
||||
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
testsJar()
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.test.backend
|
||||
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
|
||||
import org.jetbrains.kotlin.test.model.BackendKinds
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import org.jetbrains.kotlin.test.util.joinToArrayString
|
||||
|
||||
class BlackBoxCodegenSuppressor(testServices: TestServices) : AfterAnalysisChecker(testServices) {
|
||||
override val directives: List<DirectivesContainer>
|
||||
get() = listOf(CodegenTestDirectives)
|
||||
|
||||
override fun suppressIfNeeded(failedAssertions: List<AssertionError>): List<AssertionError> {
|
||||
val moduleStructure = testServices.moduleStructure
|
||||
val ignoredBackends = moduleStructure.modules.flatMap { it.directives[CodegenTestDirectives.IGNORE_BACKEND] }
|
||||
if (ignoredBackends.isEmpty()) return failedAssertions
|
||||
val targetBackends = moduleStructure.modules.flatMap { it.targetBackends }
|
||||
val matchedBackend = ignoredBackends.intersect(targetBackends)
|
||||
if (ignoredBackends.contains(TargetBackend.ANY)) {
|
||||
return processAssertions(failedAssertions)
|
||||
}
|
||||
if (matchedBackend.isNotEmpty()) {
|
||||
return processAssertions(failedAssertions, "for ${matchedBackend.joinToArrayString()}")
|
||||
}
|
||||
return failedAssertions
|
||||
|
||||
}
|
||||
|
||||
private fun processAssertions(failedAssertions: List<AssertionError>, additionalMessage: String = ""): List<AssertionError> {
|
||||
return if (failedAssertions.isNotEmpty()) emptyList()
|
||||
else {
|
||||
val message = buildString {
|
||||
append("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive")
|
||||
if (additionalMessage.isNotEmpty()) {
|
||||
append(" ")
|
||||
append(additionalMessage)
|
||||
}
|
||||
}
|
||||
listOf(AssertionError(message))
|
||||
}
|
||||
}
|
||||
|
||||
private val TestModule.targetBackends: List<TargetBackend>
|
||||
get() = when (backendKind) {
|
||||
BackendKinds.ClassicBackend -> when {
|
||||
targetPlatform.isJvm() -> listOf(TargetBackend.JVM, TargetBackend.JVM_OLD)
|
||||
targetPlatform.isJs() -> listOf(TargetBackend.JS)
|
||||
else -> emptyList()
|
||||
}
|
||||
BackendKinds.IrBackend -> when {
|
||||
targetPlatform.isJvm() -> listOf(TargetBackend.JVM_IR)
|
||||
targetPlatform.isJs() -> listOf(TargetBackend.JS_IR)
|
||||
else -> emptyList()
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.test.backend.classic
|
||||
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
abstract class ClassicBackendFacade<A : ResultingArtifact.Binary<A>>(
|
||||
testServices: TestServices,
|
||||
binaryKind: BinaryKind<A>
|
||||
) : BackendFacade<ClassicBackendInput, A>(testServices, BackendKinds.ClassicBackend, binaryKind)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.test.backend.classic
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.test.model.BackendKinds
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
|
||||
// Old backend (JVM and JS)
|
||||
data class ClassicBackendInput(
|
||||
val psiFiles: Collection<KtFile>,
|
||||
val bindingContext: BindingContext,
|
||||
val moduleDescriptor: ModuleDescriptor,
|
||||
val project: Project,
|
||||
val languageVersionSettings: LanguageVersionSettings
|
||||
) : ResultingArtifact.BackendInput<ClassicBackendInput>() {
|
||||
override val kind: BackendKinds.ClassicBackend
|
||||
get() = BackendKinds.ClassicBackend
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.test.backend.classic
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.DefaultCodegenFactory
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.test.model.ArtifactKinds
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||
|
||||
class ClassicJvmBackendFacade(
|
||||
testServices: TestServices
|
||||
) : ClassicBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
|
||||
override fun transform(
|
||||
module: TestModule,
|
||||
inputArtifact: ClassicBackendInput
|
||||
): BinaryArtifacts.Jvm {
|
||||
val compilerConfiguration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val (psiFiles, bindingContext, moduleDescriptor, project, languageVersionSettings) = inputArtifact
|
||||
// TODO: add configuring classBuilderFactory
|
||||
val generationState = GenerationState.Builder(
|
||||
project,
|
||||
ClassBuilderFactories.TEST,
|
||||
moduleDescriptor,
|
||||
bindingContext,
|
||||
psiFiles.toList(),
|
||||
compilerConfiguration
|
||||
).codegenFactory(DefaultCodegenFactory).build()
|
||||
|
||||
KotlinCodegenFacade.compileCorrectFiles(generationState)
|
||||
|
||||
return BinaryArtifacts.Jvm(generationState.factory)
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.test.backend.handlers
|
||||
|
||||
import org.jetbrains.kotlin.test.model.ArtifactKinds
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifactHandler
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
abstract class JvmBinaryArtifactHandler(
|
||||
testServices: TestServices
|
||||
) : BinaryArtifactHandler<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm)
|
||||
|
||||
abstract class JsBinaryArtifactHandler(
|
||||
testServices: TestServices
|
||||
) : BinaryArtifactHandler<BinaryArtifacts.Js>(testServices, ArtifactKinds.Js)
|
||||
|
||||
abstract class NativeBinaryArtifactHandler(
|
||||
testServices: TestServices
|
||||
) : BinaryArtifactHandler<BinaryArtifacts.Native>(testServices, ArtifactKinds.Native)
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.test.backend.handlers
|
||||
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberDeclarationsToGenerate
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
||||
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil.getFileClassInfoNoResolve
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
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.addToStdlib.runIf
|
||||
import java.lang.reflect.Method
|
||||
import java.net.URLClassLoader
|
||||
|
||||
class JvmBoxRunner(
|
||||
testServices: TestServices
|
||||
) : JvmBinaryArtifactHandler(testServices) {
|
||||
companion object {
|
||||
private val BOX_IN_SEPARATE_PROCESS_PORT = System.getProperty("kotlin.test.box.in.separate.process.port")
|
||||
}
|
||||
|
||||
private var boxMethodFound = false
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||
if (!boxMethodFound) {
|
||||
assertions.fail { "Can't find box methods" }
|
||||
}
|
||||
}
|
||||
|
||||
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
|
||||
val ktFiles = info.classFileFactory.inputFiles
|
||||
val classLoader = createClassLoader(module, info.classFileFactory)
|
||||
for (ktFile in ktFiles) {
|
||||
val className = ktFile.getFacadeFqName() ?: continue
|
||||
val clazz = classLoader.getGeneratedClass(className)
|
||||
val method = clazz.getBoxMethodOrNull() ?: continue
|
||||
boxMethodFound = true
|
||||
callBoxMethodAndCheckResult(classLoader, clazz, method, unexpectedBehaviour = false)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private fun callBoxMethodAndCheckResult(
|
||||
classLoader: URLClassLoader,
|
||||
clazz: Class<*>?,
|
||||
method: Method,
|
||||
unexpectedBehaviour: Boolean
|
||||
) {
|
||||
val result = if (BOX_IN_SEPARATE_PROCESS_PORT != null) {
|
||||
TODO()
|
||||
// result = invokeBoxInSeparateProcess(classLoader, aClass)
|
||||
} else {
|
||||
val savedClassLoader = Thread.currentThread().contextClassLoader
|
||||
if (savedClassLoader !== classLoader) {
|
||||
// otherwise the test infrastructure used in the test may conflict with the one from the context classloader
|
||||
Thread.currentThread().contextClassLoader = classLoader
|
||||
}
|
||||
try {
|
||||
method.invoke(null) as String
|
||||
} finally {
|
||||
if (savedClassLoader !== classLoader) {
|
||||
Thread.currentThread().contextClassLoader = savedClassLoader
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unexpectedBehaviour) {
|
||||
TestCase.assertNotSame("OK", result)
|
||||
} else {
|
||||
assertions.assertEquals("OK", result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createClassLoader(module: TestModule, classFileFactory: ClassFileFactory): GeneratedClassLoader {
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val urls = configuration.jvmClasspathRoots.map { it.toURI().toURL() }
|
||||
val classLoader = URLClassLoader(urls.toTypedArray())
|
||||
return GeneratedClassLoader(classFileFactory, classLoader)
|
||||
}
|
||||
|
||||
private fun KtFile.getFacadeFqName(): String? {
|
||||
return runIf(getMemberDeclarationsToGenerate(this).isNotEmpty()) {
|
||||
getFileClassInfoNoResolve(this).facadeClassFqName.asString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassLoader.getGeneratedClass(className: String): Class<*> {
|
||||
try {
|
||||
return loadClass(className)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
assertions.fail { "No class file was generated for: $className" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun Class<*>.getBoxMethodOrNull(): Method? {
|
||||
return try {
|
||||
getMethod("box")
|
||||
} catch (e: NoSuchMethodException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.test.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
abstract class IrBackendFacade<A : ResultingArtifact.Binary<A>>(
|
||||
testServices: TestServices,
|
||||
binaryKind: BinaryKind<A>
|
||||
) : BackendFacade<IrBackendInput, A>(testServices, BackendKinds.IrBackend, binaryKind)
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.test.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
|
||||
import org.jetbrains.kotlin.backend.jvm.MetadataSerializerFactory
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.linkage.IrProvider
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
|
||||
import org.jetbrains.kotlin.test.model.BackendKinds
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
|
||||
// IR backend (JVM, JS, Native)
|
||||
data class IrBackendInput(
|
||||
val state: GenerationState,
|
||||
val irModuleFragment: IrModuleFragment,
|
||||
val symbolTable: SymbolTable,
|
||||
val sourceManager: PsiSourceManager,
|
||||
val phaseConfig: PhaseConfig,
|
||||
val irProviders: List<IrProvider>,
|
||||
val extensions: JvmGeneratorExtensions,
|
||||
val serializerFactory: MetadataSerializerFactory
|
||||
) : ResultingArtifact.BackendInput<IrBackendInput>() {
|
||||
override val kind: BackendKinds.IrBackend
|
||||
get() = BackendKinds.IrBackend
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.test.backend.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class JvmIrBackendFacade(
|
||||
testServices: TestServices
|
||||
) : IrBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
|
||||
override fun transform(
|
||||
module: TestModule,
|
||||
inputArtifact: IrBackendInput
|
||||
): BinaryArtifacts.Jvm {
|
||||
val (state, irModuleFragment, symbolTable, sourceManager, phaseConfig, irProviders, extensions, serializerFactory) = inputArtifact
|
||||
|
||||
val codegenFactory = state.codegenFactory as JvmIrCodegenFactory
|
||||
codegenFactory.doGenerateFilesInternal(
|
||||
state,
|
||||
irModuleFragment,
|
||||
symbolTable,
|
||||
sourceManager,
|
||||
phaseConfig,
|
||||
irProviders,
|
||||
extensions,
|
||||
serializerFactory
|
||||
)
|
||||
state.factory.done()
|
||||
|
||||
return BinaryArtifacts.Jvm(state.factory)
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.test.builders
|
||||
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.fir.PrivateForInline
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.test.services.DefaultsDsl
|
||||
import org.jetbrains.kotlin.test.services.DefaultsProvider
|
||||
import org.jetbrains.kotlin.test.model.BackendKind
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKind
|
||||
|
||||
@DefaultsDsl
|
||||
class DefaultsProviderBuilder {
|
||||
lateinit var backend: BackendKind<*>
|
||||
lateinit var frontend: FrontendKind<*>
|
||||
lateinit var targetPlatform: TargetPlatform
|
||||
lateinit var dependencyKind: DependencyKind
|
||||
|
||||
@PrivateForInline
|
||||
var languageVersionSettings: LanguageVersionSettings? = null
|
||||
|
||||
@PrivateForInline
|
||||
var languageVersionSettingsBuilder: LanguageVersionSettingsBuilder? = null
|
||||
|
||||
@OptIn(PrivateForInline::class)
|
||||
inline fun languageSettings(init: LanguageVersionSettingsBuilder.() -> Unit) {
|
||||
languageVersionSettings = LanguageVersionSettingsBuilder().apply(init).also {
|
||||
languageVersionSettingsBuilder = it
|
||||
}.build()
|
||||
}
|
||||
|
||||
@OptIn(PrivateForInline::class)
|
||||
fun build(): DefaultsProvider {
|
||||
return DefaultsProvider(
|
||||
backend,
|
||||
frontend,
|
||||
languageVersionSettings ?: LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE),
|
||||
languageVersionSettingsBuilder ?: LanguageVersionSettingsBuilder(),
|
||||
targetPlatform,
|
||||
dependencyKind
|
||||
)
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.test.builders
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.model.*
|
||||
|
||||
class RegisteredDirectivesBuilder {
|
||||
private val simpleDirectives: MutableList<SimpleDirective> = mutableListOf()
|
||||
private val stringDirectives: MutableMap<StringDirective, List<String>> = mutableMapOf()
|
||||
private val valueDirectives: MutableMap<ValueDirective<*>, List<Any>> = mutableMapOf()
|
||||
|
||||
operator fun SimpleDirective.unaryPlus() {
|
||||
simpleDirectives += this
|
||||
}
|
||||
|
||||
infix fun StringDirective.with(value: String) {
|
||||
with(listOf(value))
|
||||
}
|
||||
|
||||
infix fun StringDirective.with(values: List<String>) {
|
||||
stringDirectives.putWithExistsCheck(this, values)
|
||||
}
|
||||
|
||||
infix fun <T : Any> ValueDirective<T>.with(value: T) {
|
||||
with(listOf(value))
|
||||
}
|
||||
|
||||
infix fun <T : Any> ValueDirective<T>.with(values: List<T>) {
|
||||
valueDirectives.putWithExistsCheck(this, values)
|
||||
}
|
||||
|
||||
private fun <K : Directive, V> MutableMap<K, V>.putWithExistsCheck(key: K, value: V) {
|
||||
val alreadyRegistered = put(key, value)
|
||||
if (alreadyRegistered != null) {
|
||||
error("Default values for $key directive already registered")
|
||||
}
|
||||
}
|
||||
|
||||
fun build(): RegisteredDirectives {
|
||||
return RegisteredDirectivesImpl(simpleDirectives, stringDirectives, valueDirectives)
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.test.builders
|
||||
|
||||
import org.jetbrains.kotlin.test.TestConfiguration
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.impl.TestConfigurationImpl
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
|
||||
typealias Constructor<T> = (TestServices) -> T
|
||||
|
||||
@DefaultsDsl
|
||||
class TestConfigurationBuilder {
|
||||
val defaultsProviderBuilder: DefaultsProviderBuilder = DefaultsProviderBuilder()
|
||||
lateinit var assertions: Assertions
|
||||
|
||||
private val facades: MutableList<Constructor<AbstractTestFacade<*, *>>> = mutableListOf()
|
||||
|
||||
private val handlers: MutableList<Constructor<AnalysisHandler<*>>> = mutableListOf()
|
||||
|
||||
private val sourcePreprocessors: MutableList<Constructor<SourceFilePreprocessor>> = mutableListOf()
|
||||
private val additionalMetaInfoProcessors: MutableList<Constructor<AdditionalMetaInfoProcessor>> = mutableListOf()
|
||||
private val environmentConfigurators: MutableList<Constructor<EnvironmentConfigurator>> = mutableListOf()
|
||||
|
||||
private val additionalSourceProviders: MutableList<Constructor<AdditionalSourceProvider>> = mutableListOf()
|
||||
|
||||
private val metaTestConfigurators: MutableList<Constructor<MetaTestConfigurator>> = mutableListOf()
|
||||
private val afterAnalysisCheckers: MutableList<Constructor<AfterAnalysisChecker>> = mutableListOf()
|
||||
|
||||
private var metaInfoHandlerEnabled: Boolean = false
|
||||
|
||||
private val directives: MutableList<DirectivesContainer> = mutableListOf()
|
||||
val defaultRegisteredDirectivesBuilder: RegisteredDirectivesBuilder = RegisteredDirectivesBuilder()
|
||||
|
||||
private val configurationsByTestDataCondition: MutableList<Pair<Regex, TestConfigurationBuilder.() -> Unit>> = mutableListOf()
|
||||
|
||||
fun forTestsMatching(pattern: String, configuration: TestConfigurationBuilder.() -> Unit) {
|
||||
val regex = pattern.toMatchingRegexString().toRegex()
|
||||
forTestsMatching(regex, configuration)
|
||||
}
|
||||
|
||||
infix fun String.or(other: String): String {
|
||||
return """$this|$other"""
|
||||
}
|
||||
|
||||
private fun String.toMatchingRegexString(): String = """^${replace("*", ".*")}$"""
|
||||
|
||||
fun forTestsMatching(pattern: Regex, configuration: TestConfigurationBuilder.() -> Unit) {
|
||||
configurationsByTestDataCondition += pattern to configuration
|
||||
}
|
||||
|
||||
inline fun globalDefaults(init: DefaultsProviderBuilder.() -> Unit) {
|
||||
defaultsProviderBuilder.apply(init)
|
||||
}
|
||||
|
||||
fun unregisterAllFacades() {
|
||||
facades.clear()
|
||||
}
|
||||
|
||||
fun useFrontendFacades(vararg constructor: Constructor<FrontendFacade<*>>) {
|
||||
facades += constructor
|
||||
}
|
||||
|
||||
fun useBackendFacades(vararg constructor: Constructor<BackendFacade<*, *>>) {
|
||||
facades += constructor
|
||||
}
|
||||
|
||||
fun useFrontend2BackendConverters(vararg constructor: Constructor<Frontend2BackendConverter<*, *>>) {
|
||||
facades += constructor
|
||||
}
|
||||
|
||||
fun useFrontendHandlers(vararg constructor: Constructor<FrontendOutputHandler<*>>) {
|
||||
handlers += constructor
|
||||
}
|
||||
|
||||
fun useBackendHandlers(vararg constructor: Constructor<BackendInputHandler<*>>) {
|
||||
handlers += constructor
|
||||
}
|
||||
|
||||
fun useArtifactsHandlers(vararg constructor: Constructor<BinaryArtifactHandler<*>>) {
|
||||
handlers += constructor
|
||||
}
|
||||
|
||||
fun useSourcePreprocessor(vararg preprocessors: Constructor<SourceFilePreprocessor>) {
|
||||
sourcePreprocessors += preprocessors
|
||||
}
|
||||
|
||||
fun useDirectives(vararg directives: DirectivesContainer) {
|
||||
this.directives += directives
|
||||
}
|
||||
|
||||
fun useConfigurators(vararg environmentConfigurators: Constructor<EnvironmentConfigurator>) {
|
||||
this.environmentConfigurators += environmentConfigurators
|
||||
}
|
||||
|
||||
fun useMetaInfoProcessors(vararg updaters: Constructor<AdditionalMetaInfoProcessor>) {
|
||||
additionalMetaInfoProcessors += updaters
|
||||
}
|
||||
|
||||
fun useAdditionalSourceProviders(vararg providers: Constructor<AdditionalSourceProvider>) {
|
||||
additionalSourceProviders += providers
|
||||
}
|
||||
|
||||
fun useMetaTestConfigurators(vararg configurators: Constructor<MetaTestConfigurator>) {
|
||||
metaTestConfigurators += configurators
|
||||
}
|
||||
|
||||
fun useAfterAnalysisCheckers(vararg checkers: Constructor<AfterAnalysisChecker>) {
|
||||
afterAnalysisCheckers += checkers
|
||||
}
|
||||
|
||||
inline fun defaultDirectives(init: RegisteredDirectivesBuilder.() -> Unit) {
|
||||
defaultRegisteredDirectivesBuilder.apply(init)
|
||||
}
|
||||
|
||||
fun enableMetaInfoHandler() {
|
||||
metaInfoHandlerEnabled = true
|
||||
}
|
||||
|
||||
fun build(testDataPath: String): TestConfiguration {
|
||||
for ((regex, configuration) in configurationsByTestDataCondition) {
|
||||
if (regex.matches(testDataPath)) {
|
||||
this.configuration()
|
||||
}
|
||||
}
|
||||
return TestConfigurationImpl(
|
||||
defaultsProviderBuilder.build(),
|
||||
assertions,
|
||||
facades,
|
||||
handlers,
|
||||
sourcePreprocessors,
|
||||
additionalMetaInfoProcessors,
|
||||
environmentConfigurators,
|
||||
additionalSourceProviders,
|
||||
metaTestConfigurators,
|
||||
afterAnalysisCheckers,
|
||||
metaInfoHandlerEnabled,
|
||||
directives,
|
||||
defaultRegisteredDirectivesBuilder.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun testConfiguration(testDataPath: String, init: TestConfigurationBuilder.() -> Unit): TestConfiguration {
|
||||
return TestConfigurationBuilder().apply(init).build(testDataPath)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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.test.builders
|
||||
|
||||
@DslMarker
|
||||
annotation class TestConfigurationDslMarker()
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* 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.test.builders
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.test.builders
|
||||
|
||||
import org.jetbrains.kotlin.test.TestRunner
|
||||
|
||||
inline fun testRunner(testDataPath: String, crossinline init: TestConfigurationBuilder.() -> Unit): TestRunner {
|
||||
return TestRunner(testConfiguration(testDataPath, init))
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.test.directives
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
|
||||
object AdditionalFilesDirectives : SimpleDirectivesContainer() {
|
||||
val CHECK_TYPE by directive(
|
||||
description = """
|
||||
Adds util functions for type checking
|
||||
See file ./compiler/testData/diagnostics/helpers/types/checkType.kt
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val WITH_COROUTINES by directive(
|
||||
description = """
|
||||
Adds util functions for checking coroutines
|
||||
See file ./compiler/testData/diagnostics/helpers/coroutines/CoroutineHelpers.kt
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val CHECK_STATE_MACHINE by directive(
|
||||
description = """
|
||||
Adds util functions for checking state machines
|
||||
May be enabled only with $WITH_COROUTINES directive
|
||||
See file ./compiler/testData/diagnostics/helpers/coroutines/StateMachineChecker.kt
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val CHECK_TAIL_CALL_OPTIMIZATION by directive(
|
||||
description = """
|
||||
Adds util functions for checking tail call optimizations
|
||||
May be enabled only with $WITH_COROUTINES directive
|
||||
See file ./compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.test.directives
|
||||
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
|
||||
object CodegenTestDirectives : SimpleDirectivesContainer() {
|
||||
val IGNORE_BACKEND by enumDirective<TargetBackend>(
|
||||
description = "Ignore failures of test on target backend"
|
||||
)
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.test.directives
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.USE_JAVAC
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
|
||||
object DiagnosticsDirectives : SimpleDirectivesContainer() {
|
||||
val WITH_NEW_INFERENCE by directive(
|
||||
description = "Enables rendering different diagnostics for old and new inference"
|
||||
)
|
||||
|
||||
val DIAGNOSTICS by stringDirective(
|
||||
description = """
|
||||
Enables or disables rendering of specific diagnostics.
|
||||
Syntax:
|
||||
Must be '[+-]DIAGNOSTIC_FACTORY_NAME'
|
||||
where '+' means 'include'
|
||||
'-' means 'exclude'
|
||||
'+' May be used in case if some diagnostic was disabled by default in test runner
|
||||
and it should be enabled in specific test
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val SKIP_TXT by directive(
|
||||
description = "Disables handler which dumps declarations to testName.txt"
|
||||
)
|
||||
|
||||
val NI_EXPECTED_FILE by directive(
|
||||
description = "Create separate .ni.txt file for declarations dump with new inference enabled"
|
||||
)
|
||||
|
||||
val SKIP_JAVAC by directive(
|
||||
description = "Skip this test if $USE_JAVAC enabled"
|
||||
)
|
||||
|
||||
val JAVAC_EXPECTED_FILE by directive(
|
||||
description = "Dump descriptors to .javac.txt file if $USE_JAVAC enabled"
|
||||
)
|
||||
|
||||
val MARK_DYNAMIC_CALLS by directive(
|
||||
description = """
|
||||
Render debug info about dynamic calls
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.test.directives
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
|
||||
object FirDiagnosticsDirectives : SimpleDirectivesContainer() {
|
||||
val DUMP_CFG by directive(
|
||||
description = """
|
||||
Dumps control flow graphs of all declarations to `testName.dot` file
|
||||
This directive may be applied only to all modules
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val FIR_DUMP by directive(
|
||||
description = """
|
||||
Dumps resulting fir to `testName.fir` file
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val FIR_IDENTICAL by directive(
|
||||
description = "Contents of fir test data file and FE 1.0 are identical"
|
||||
)
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.test.directives
|
||||
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
|
||||
object JsEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
|
||||
val MODULE_KIND by enumDirective<ModuleKind>("Specifies kind of js module")
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.test.directives
|
||||
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
|
||||
object JvmEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
|
||||
val JVM_TARGET by enumDirective<JvmTarget>(
|
||||
description = "Target bytecode version",
|
||||
additionalParser = JvmTarget.Companion::fromString
|
||||
)
|
||||
|
||||
val JDK_KIND by enumDirective<TestJdkKind>("JDK used in tests")
|
||||
val FULL_JDK by directive("Add full java standard library to classpath")
|
||||
val STDLIB_JDK8 by directive("Add Java 8 stdlib to classpath")
|
||||
|
||||
val WITH_RUNTIME by directive(
|
||||
description = """
|
||||
Add Kotlin stdlib to classpath
|
||||
This directive is deprecated, use WITH_STDLIB instead
|
||||
""".trimIndent()
|
||||
)
|
||||
val WITH_STDLIB by directive("Add Kotlin runtime to classpath")
|
||||
val WITH_REFLECT by directive("Add Kotlin reflect to classpath")
|
||||
val NO_RUNTIME by directive("Don't add any runtime libs to classpath")
|
||||
|
||||
val ANDROID_ANNOTATIONS by directive("Add android annotations to classpath")
|
||||
|
||||
val USE_PSI_CLASS_FILES_READING by directive("Use a slower (PSI-based) class files reading implementation")
|
||||
val USE_JAVAC by directive("Enable javac integration")
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.test.frontend.classic
|
||||
|
||||
import org.jetbrains.kotlin.test.backend.classic.ClassicBackendInput
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
|
||||
class ClassicFrontend2ClassicBackendConverter(
|
||||
testServices: TestServices
|
||||
) : Frontend2BackendConverter<ClassicFrontendOutputArtifact, ClassicBackendInput>(
|
||||
testServices,
|
||||
FrontendKinds.ClassicFrontend,
|
||||
BackendKinds.ClassicBackend
|
||||
) {
|
||||
override fun transform(
|
||||
module: TestModule,
|
||||
inputArtifact: ClassicFrontendOutputArtifact
|
||||
): ClassicBackendInput {
|
||||
val (psiFiles, analysisResults, project, languageVersionSettings) = inputArtifact
|
||||
return ClassicBackendInput(
|
||||
psiFiles.values,
|
||||
analysisResults.bindingContext,
|
||||
analysisResults.moduleDescriptor,
|
||||
project,
|
||||
languageVersionSettings
|
||||
)
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.test.frontend.classic
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
|
||||
import org.jetbrains.kotlin.backend.common.ir.BuiltinSymbolsBase
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmNameProvider
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.DescriptorMetadataSerializer
|
||||
import org.jetbrains.kotlin.backend.jvm.jvmPhases
|
||||
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
|
||||
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
|
||||
import org.jetbrains.kotlin.idea.MainFunctionDetector
|
||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.EmptyLoggingContext
|
||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrLinker
|
||||
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
|
||||
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
|
||||
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
|
||||
import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.TypeTranslator
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
|
||||
import org.jetbrains.kotlin.test.model.BackendKinds
|
||||
import org.jetbrains.kotlin.test.model.Frontend2BackendConverter
|
||||
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
|
||||
|
||||
class ClassicFrontend2IrConverter(
|
||||
testServices: TestServices
|
||||
) : Frontend2BackendConverter<ClassicFrontendOutputArtifact, IrBackendInput>(
|
||||
testServices,
|
||||
FrontendKinds.ClassicFrontend,
|
||||
BackendKinds.IrBackend
|
||||
) {
|
||||
override fun transform(
|
||||
module: TestModule,
|
||||
inputArtifact: ClassicFrontendOutputArtifact
|
||||
): IrBackendInput {
|
||||
val (psiFiles, analysisResult, project, languageVersionSettings) = inputArtifact
|
||||
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
|
||||
val files = psiFiles.values.toList()
|
||||
val phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jvmPhases)
|
||||
val state = GenerationState.Builder(
|
||||
project, ClassBuilderFactories.TEST, analysisResult.moduleDescriptor, analysisResult.bindingContext,
|
||||
files, configuration
|
||||
).codegenFactory(
|
||||
JvmIrCodegenFactory(phaseConfig)
|
||||
).isIrBackend(true).build()
|
||||
|
||||
val extensions = JvmGeneratorExtensions()
|
||||
val mangler = JvmManglerDesc(MainFunctionDetector(state.bindingContext, state.languageVersionSettings))
|
||||
val psi2ir = Psi2IrTranslator(state.languageVersionSettings, Psi2IrConfiguration())
|
||||
val symbolTable = SymbolTable(JvmIdSignatureDescriptor(mangler), IrFactoryImpl, JvmNameProvider)
|
||||
val psi2irContext = psi2ir.createGeneratorContext(state.module, state.bindingContext, symbolTable, extensions)
|
||||
val pluginExtensions = IrGenerationExtension.getInstances(state.project)
|
||||
val functionFactory = IrFunctionFactory(psi2irContext.irBuiltIns, symbolTable)
|
||||
psi2irContext.irBuiltIns.functionFactory = functionFactory
|
||||
|
||||
val stubGenerator = DeclarationStubGenerator(
|
||||
psi2irContext.moduleDescriptor, symbolTable, psi2irContext.irBuiltIns.languageVersionSettings, extensions
|
||||
)
|
||||
val frontEndContext = object : TranslationPluginContext {
|
||||
override val moduleDescriptor: ModuleDescriptor
|
||||
get() = psi2irContext.moduleDescriptor
|
||||
override val bindingContext: BindingContext
|
||||
get() = psi2irContext.bindingContext
|
||||
override val symbolTable: ReferenceSymbolTable
|
||||
get() = symbolTable
|
||||
override val typeTranslator: TypeTranslator
|
||||
get() = psi2irContext.typeTranslator
|
||||
override val irBuiltIns: IrBuiltIns
|
||||
get() = psi2irContext.irBuiltIns
|
||||
}
|
||||
val irLinker = JvmIrLinker(
|
||||
psi2irContext.moduleDescriptor,
|
||||
EmptyLoggingContext,
|
||||
psi2irContext.irBuiltIns,
|
||||
symbolTable,
|
||||
functionFactory,
|
||||
frontEndContext,
|
||||
stubGenerator,
|
||||
mangler
|
||||
)
|
||||
|
||||
val pluginContext by lazy {
|
||||
psi2irContext.run {
|
||||
val symbols = BuiltinSymbolsBase(irBuiltIns, moduleDescriptor.builtIns, symbolTable.lazyWrapper)
|
||||
IrPluginContextImpl(
|
||||
moduleDescriptor, bindingContext, languageVersionSettings, symbolTable, typeTranslator, irBuiltIns, irLinker, symbols
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (extension in pluginExtensions) {
|
||||
psi2ir.addPostprocessingStep { moduleFragment ->
|
||||
val old = stubGenerator.unboundSymbolGeneration
|
||||
try {
|
||||
stubGenerator.unboundSymbolGeneration = true
|
||||
extension.generate(moduleFragment, pluginContext)
|
||||
} finally {
|
||||
stubGenerator.unboundSymbolGeneration = old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dependencies = psi2irContext.moduleDescriptor.allDependencyModules.map {
|
||||
val kotlinLibrary = (it.getCapability(KlibModuleOrigin.CAPABILITY) as? DeserializedKlibModuleOrigin)?.library
|
||||
irLinker.deserializeIrModuleHeader(it, kotlinLibrary)
|
||||
}
|
||||
val irProviders = listOf(irLinker)
|
||||
|
||||
val irModuleFragment = psi2ir.generateModuleFragment(
|
||||
psi2irContext,
|
||||
files,
|
||||
irProviders,
|
||||
pluginExtensions,
|
||||
expectDescriptorToSymbol = null
|
||||
)
|
||||
irLinker.postProcess()
|
||||
|
||||
stubGenerator.unboundSymbolGeneration = true
|
||||
|
||||
// We need to compile all files we reference in Klibs
|
||||
irModuleFragment.files.addAll(dependencies.flatMap { it.files })
|
||||
|
||||
return IrBackendInput(
|
||||
state,
|
||||
irModuleFragment,
|
||||
symbolTable,
|
||||
psi2irContext.sourceManager,
|
||||
phaseConfig,
|
||||
irProviders,
|
||||
extensions,
|
||||
::DescriptorMetadataSerializer
|
||||
)
|
||||
}
|
||||
}
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.test.frontend.classic
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
|
||||
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys.JVM_TARGET
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.context.ProjectContext
|
||||
import org.jetbrains.kotlin.context.withModule
|
||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava
|
||||
import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
|
||||
import org.jetbrains.kotlin.js.config.JsConfig
|
||||
import org.jetbrains.kotlin.load.java.lazy.SingleModuleClassResolver
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.isCommon
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
|
||||
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
|
||||
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.*
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import java.io.File
|
||||
|
||||
class ClassicFrontendFacade(
|
||||
testServices: TestServices
|
||||
) : FrontendFacade<ClassicFrontendOutputArtifact>(testServices, FrontendKinds.ClassicFrontend) {
|
||||
override val additionalServices: List<ServiceRegistrationData>
|
||||
get() = listOf(service(::ModuleDescriptorProvider))
|
||||
|
||||
override fun analyze(module: TestModule): ClassicFrontendOutputArtifact {
|
||||
val dependencyProvider = testServices.dependencyProvider
|
||||
val moduleDescriptorProvider = testServices.moduleDescriptorProvider
|
||||
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
|
||||
val packagePartProviderFactory = compilerConfigurationProvider.getPackagePartProviderFactory(module)
|
||||
val project = compilerConfigurationProvider.getProject(module)
|
||||
val configuration = compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
|
||||
val ktFilesMap = testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).toMutableMap()
|
||||
val languageVersionSettings = module.languageVersionSettings
|
||||
|
||||
val sourceDependencies = module.dependencies.filter { it.kind == DependencyKind.Source }
|
||||
val dependentDescriptors = sourceDependencies.map {
|
||||
val testModule = dependencyProvider.getTestModule(it.moduleName)
|
||||
moduleDescriptorProvider.getModuleDescriptor(testModule)
|
||||
}
|
||||
|
||||
var hasCommonModules = false
|
||||
sourceDependencies.forEach {
|
||||
val dependencyModule = dependencyProvider.getTestModule(it.moduleName)
|
||||
if (dependencyModule.targetPlatform.isCommon()) {
|
||||
val artifact = dependencyProvider.getArtifact(dependencyModule, FrontendKinds.ClassicFrontend)
|
||||
/*
|
||||
* We need create KtFiles again with new project because otherwise we can access to some caches using
|
||||
* old project as key which may leads to missing services in core environment
|
||||
*/
|
||||
ktFilesMap.putAll(testServices.sourceFileProvider.getKtFilesForSourceFiles(artifact.allKtFiles.keys, project))
|
||||
hasCommonModules = true
|
||||
}
|
||||
}
|
||||
|
||||
val ktFiles = ktFilesMap.values.toList()
|
||||
setupJavacIfNeeded(module, ktFiles, configuration)
|
||||
val analysisResult = performResolve(
|
||||
module,
|
||||
project,
|
||||
configuration,
|
||||
packagePartProviderFactory,
|
||||
ktFiles,
|
||||
languageVersionSettings,
|
||||
dependentDescriptors,
|
||||
hasCommonModules
|
||||
)
|
||||
moduleDescriptorProvider.replaceModuleDescriptorForModule(module, analysisResult.moduleDescriptor)
|
||||
return ClassicFrontendOutputArtifact(
|
||||
ktFilesMap,
|
||||
analysisResult,
|
||||
project,
|
||||
languageVersionSettings
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupJavacIfNeeded(
|
||||
module: TestModule,
|
||||
ktFiles: List<KtFile>,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
if (JvmEnvironmentConfigurationDirectives.USE_JAVAC !in module.directives) return
|
||||
val mockJdk = runIf(JvmEnvironmentConfigurationDirectives.FULL_JDK !in module.directives) {
|
||||
File(KtTestUtil.getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/rt.jar")
|
||||
}
|
||||
testServices.compilerConfigurationProvider.registerJavacForModule(module, ktFiles, mockJdk)
|
||||
configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
|
||||
}
|
||||
|
||||
private fun performResolve(
|
||||
module: TestModule,
|
||||
project: Project,
|
||||
configuration: CompilerConfiguration,
|
||||
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
|
||||
files: List<KtFile>,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
dependentDescriptors: List<ModuleDescriptorImpl>,
|
||||
hasCommonModules: Boolean
|
||||
): AnalysisResult {
|
||||
val targetPlatform = module.targetPlatform
|
||||
return when {
|
||||
targetPlatform.isJvm() -> performJvmModuleResolve(
|
||||
module,
|
||||
project,
|
||||
configuration,
|
||||
packagePartProviderFactory,
|
||||
files,
|
||||
dependentDescriptors,
|
||||
hasCommonModules
|
||||
)
|
||||
targetPlatform.isJs() -> performJsModuleResolve(project, configuration, files, dependentDescriptors)
|
||||
targetPlatform.isNative() -> TODO()
|
||||
targetPlatform.isCommon() -> performCommonModuleResolve(module, files, languageVersionSettings)
|
||||
else -> error("Should not be here")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun performJvmModuleResolve(
|
||||
module: TestModule,
|
||||
project: Project,
|
||||
configuration: CompilerConfiguration,
|
||||
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
|
||||
files: List<KtFile>,
|
||||
dependentDescriptors: List<ModuleDescriptorImpl>,
|
||||
hasCommonModules: Boolean
|
||||
): AnalysisResult {
|
||||
val moduleTrace = NoScopeRecordCliBindingTrace()
|
||||
if (!hasCommonModules) {
|
||||
return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
|
||||
project,
|
||||
files,
|
||||
moduleTrace,
|
||||
configuration.copy(),
|
||||
packagePartProviderFactory,
|
||||
explicitModuleDependencyList = dependentDescriptors
|
||||
)
|
||||
}
|
||||
|
||||
val projectContext = ProjectContext(project, "test project context")
|
||||
val storageManager = projectContext.storageManager
|
||||
|
||||
val builtIns = JvmBuiltIns(storageManager, JvmBuiltIns.Kind.FROM_CLASS_LOADER)
|
||||
val moduleDescriptor = ModuleDescriptorImpl(Name.special("<${module.name}>"), storageManager, builtIns, module.targetPlatform)
|
||||
val dependencies = buildList {
|
||||
add(moduleDescriptor)
|
||||
add(moduleDescriptor.builtIns.builtInsModule)
|
||||
addAll(dependentDescriptors)
|
||||
}
|
||||
moduleDescriptor.setDependencies(dependencies)
|
||||
|
||||
val moduleContentScope = GlobalSearchScope.allScope(project)
|
||||
val moduleClassResolver = SingleModuleClassResolver()
|
||||
val moduleContext = projectContext.withModule(moduleDescriptor)
|
||||
val jvmTarget = configuration[JVM_TARGET] ?: JvmTarget.DEFAULT
|
||||
val container = createContainerForLazyResolveWithJava(
|
||||
JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget), // TODO(dsavvinov): do not pass JvmTarget around
|
||||
moduleContext,
|
||||
moduleTrace,
|
||||
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
|
||||
moduleContentScope,
|
||||
moduleClassResolver,
|
||||
CompilerEnvironment, LookupTracker.DO_NOTHING,
|
||||
ExpectActualTracker.DoNothing,
|
||||
packagePartProviderFactory(moduleContentScope),
|
||||
module.languageVersionSettings,
|
||||
useBuiltInsProvider = true
|
||||
)
|
||||
|
||||
container.initJvmBuiltInsForTopDownAnalysis()
|
||||
moduleClassResolver.resolver = container.get()
|
||||
|
||||
moduleDescriptor.initialize(
|
||||
CompositePackageFragmentProvider(
|
||||
listOf(
|
||||
container.get<KotlinCodeAnalyzer>().packageFragmentProvider,
|
||||
container.get<JavaDescriptorResolver>().packageFragmentProvider
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
container.get<LazyTopDownAnalyzer>().analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
|
||||
|
||||
return AnalysisResult.success(moduleTrace.bindingContext, moduleDescriptor)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun performJsModuleResolve(
|
||||
project: Project,
|
||||
configuration: CompilerConfiguration,
|
||||
files: List<KtFile>,
|
||||
dependentDescriptors: List<ModuleDescriptorImpl>
|
||||
): AnalysisResult {
|
||||
val jsConfig = JsConfig(project, configuration)
|
||||
val dependentDescriptorsIncludingLibraries = buildList {
|
||||
addAll(dependentDescriptors)
|
||||
addAll(jsConfig.moduleDescriptors)
|
||||
}
|
||||
return TopDownAnalyzerFacadeForJS.analyzeFiles(
|
||||
files,
|
||||
project,
|
||||
configuration,
|
||||
moduleDescriptors = dependentDescriptorsIncludingLibraries,
|
||||
friendModuleDescriptors = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
private fun performCommonModuleResolve(
|
||||
module: TestModule,
|
||||
files: List<KtFile>,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
): AnalysisResult {
|
||||
return CommonResolverForModuleFactory.analyzeFiles(
|
||||
files,
|
||||
Name.special("<${module.name}>"),
|
||||
dependOnBuiltIns = true,
|
||||
languageVersionSettings,
|
||||
module.targetPlatform,
|
||||
// TODO: add dependency manager
|
||||
) { _ ->
|
||||
// TODO
|
||||
MetadataPartProvider.Empty
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.test.frontend.classic
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
|
||||
data class ClassicFrontendOutputArtifact(
|
||||
val allKtFiles: Map<TestFile, KtFile>,
|
||||
val analysisResult: AnalysisResult,
|
||||
val project: Project,
|
||||
val languageVersionSettings: LanguageVersionSettings
|
||||
) : ResultingArtifact.FrontendOutput<ClassicFrontendOutputArtifact>() {
|
||||
override val kind: FrontendKinds.ClassicFrontend
|
||||
get() = FrontendKinds.ClassicFrontend
|
||||
|
||||
val ktFiles: Map<TestFile, KtFile> = allKtFiles.filterKeys { !it.isAdditional }
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.test.frontend.classic
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.test.services.TestService
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.assertions
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
|
||||
class ModuleDescriptorProvider(
|
||||
private val testServices: TestServices
|
||||
) : TestService {
|
||||
private val moduleDescriptorByModule = mutableMapOf<TestModule, ModuleDescriptorImpl>()
|
||||
|
||||
fun getModuleDescriptor(testModule: TestModule): ModuleDescriptorImpl {
|
||||
return moduleDescriptorByModule[testModule] ?: testServices.assertions.fail {
|
||||
"Module descriptor for module ${testModule.name} not found"
|
||||
}
|
||||
}
|
||||
|
||||
fun replaceModuleDescriptorForModule(testModule: TestModule, moduleDescriptor: ModuleDescriptor) {
|
||||
require(moduleDescriptor is ModuleDescriptorImpl)
|
||||
moduleDescriptorByModule[testModule] = moduleDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.moduleDescriptorProvider: ModuleDescriptorProvider by TestServices.testServiceAccessor()
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.test.frontend.classic.handlers
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.asJava.getJvmSignatureDiagnostics
|
||||
import org.jetbrains.kotlin.checkers.diagnostics.SyntaxErrorDiagnostic
|
||||
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
|
||||
import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration
|
||||
import org.jetbrains.kotlin.codeMetaInfo.model.CodeMetaInfo
|
||||
import org.jetbrains.kotlin.codeMetaInfo.model.DiagnosticCodeMetaInfo
|
||||
import org.jetbrains.kotlin.codeMetaInfo.model.ParsedCodeMetaInfo
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.platform.isCommon
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
|
||||
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.MARK_DYNAMIC_CALLS
|
||||
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import java.util.*
|
||||
|
||||
class ClassicDiagnosticsHandler(testServices: TestServices) : ClassicFrontendAnalysisHandler(testServices) {
|
||||
override val directivesContainers: List<DirectivesContainer> =
|
||||
listOf(DiagnosticsDirectives)
|
||||
|
||||
override val additionalServices: List<ServiceRegistrationData> =
|
||||
listOf(service(::DiagnosticsService))
|
||||
|
||||
private val globalMetadataInfoHandler: GlobalMetadataInfoHandler
|
||||
get() = testServices.globalMetadataInfoHandler
|
||||
|
||||
private val diagnosticsService: DiagnosticsService
|
||||
get() = testServices.diagnosticsService
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) {
|
||||
var allDiagnostics = info.analysisResult.bindingContext.diagnostics + computeJvmSignatureDiagnostics(info)
|
||||
if (AdditionalFilesDirectives.CHECK_TYPE in module.directives) {
|
||||
allDiagnostics = allDiagnostics.filter { it.factory.name != Errors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name }
|
||||
}
|
||||
if (LanguageSettingsDirectives.API_VERSION in module.directives) {
|
||||
allDiagnostics = allDiagnostics.filter { it.factory.name != Errors.NEWER_VERSION_IN_SINCE_KOTLIN.name }
|
||||
}
|
||||
|
||||
val diagnosticsPerFile = allDiagnostics.groupBy { it.psiFile }
|
||||
|
||||
val withNewInferenceModeEnabled = testServices.withNewInferenceModeEnabled()
|
||||
|
||||
val configuration = DiagnosticsRenderingConfiguration(
|
||||
platform = null,
|
||||
withNewInference = info.languageVersionSettings.supportsFeature(LanguageFeature.NewInference),
|
||||
languageVersionSettings = info.languageVersionSettings,
|
||||
skipDebugInfoDiagnostics = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
.getBoolean(JVMConfigurationKeys.IR)
|
||||
)
|
||||
|
||||
for ((file, ktFile) in info.ktFiles) {
|
||||
val diagnostics = diagnosticsPerFile[ktFile] ?: emptyList()
|
||||
for (diagnostic in diagnostics) {
|
||||
if (!diagnostic.isValid) continue
|
||||
if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name)) continue
|
||||
globalMetadataInfoHandler.addMetadataInfosForFile(
|
||||
file,
|
||||
diagnostic.toMetaInfo(
|
||||
module,
|
||||
file,
|
||||
configuration.withNewInference,
|
||||
withNewInferenceModeEnabled
|
||||
)
|
||||
)
|
||||
}
|
||||
for (errorElement in AnalyzingUtils.getSyntaxErrorRanges(ktFile)) {
|
||||
globalMetadataInfoHandler.addMetadataInfosForFile(
|
||||
file,
|
||||
SyntaxErrorDiagnostic(errorElement).toMetaInfo(
|
||||
module,
|
||||
file,
|
||||
configuration.withNewInference,
|
||||
withNewInferenceModeEnabled
|
||||
)
|
||||
)
|
||||
}
|
||||
processDebugInfoDiagnostics(configuration, module, file, ktFile, info, withNewInferenceModeEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeJvmSignatureDiagnostics(info: ClassicFrontendOutputArtifact): Set<Diagnostic> {
|
||||
if (testServices.moduleStructure.modules.any { !it.targetPlatform.isJvm() }) return emptySet()
|
||||
val bindingContext = info.analysisResult.bindingContext
|
||||
val project = info.project
|
||||
val jvmSignatureDiagnostics = HashSet<Diagnostic>()
|
||||
for (ktFile in info.ktFiles.values) {
|
||||
val declarations = PsiTreeUtil.findChildrenOfType(ktFile, KtDeclaration::class.java)
|
||||
for (declaration in declarations) {
|
||||
val diagnostics = getJvmSignatureDiagnostics(
|
||||
declaration,
|
||||
bindingContext.diagnostics,
|
||||
GlobalSearchScope.allScope(project)
|
||||
) ?: continue
|
||||
jvmSignatureDiagnostics.addAll(diagnostics.forElement(declaration))
|
||||
}
|
||||
}
|
||||
return jvmSignatureDiagnostics
|
||||
}
|
||||
|
||||
private fun Diagnostic.toMetaInfo(
|
||||
module: TestModule,
|
||||
file: TestFile,
|
||||
newInferenceEnabled: Boolean,
|
||||
withNewInferenceModeEnabled: Boolean
|
||||
): List<DiagnosticCodeMetaInfo> = textRanges.map { range ->
|
||||
val metaInfo = DiagnosticCodeMetaInfo(range, ClassicMetaInfoUtils.renderDiagnosticNoArgs, this)
|
||||
if (withNewInferenceModeEnabled) {
|
||||
metaInfo.attributes += if (newInferenceEnabled) OldNewInferenceMetaInfoProcessor.NI else OldNewInferenceMetaInfoProcessor.OI
|
||||
}
|
||||
if (file !in module.files) {
|
||||
val targetPlatform = module.targetPlatform
|
||||
metaInfo.attributes += when {
|
||||
targetPlatform.isJvm() -> "JVM"
|
||||
targetPlatform.isJs() -> "JS"
|
||||
targetPlatform.isNative() -> "NATIVE"
|
||||
targetPlatform.isCommon() -> "COMMON"
|
||||
else -> error("Should not be here")
|
||||
}
|
||||
}
|
||||
val existing = globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo)
|
||||
if (existing.any { it.description != null }) {
|
||||
metaInfo.replaceRenderConfiguration(ClassicMetaInfoUtils.renderDiagnosticWithArgs)
|
||||
}
|
||||
metaInfo
|
||||
}
|
||||
|
||||
private fun processDebugInfoDiagnostics(
|
||||
configuration: DiagnosticsRenderingConfiguration,
|
||||
module: TestModule,
|
||||
file: TestFile,
|
||||
ktFile: KtFile,
|
||||
info: ClassicFrontendOutputArtifact,
|
||||
withNewInferenceModeEnabled: Boolean
|
||||
) {
|
||||
val diagnosedRanges = globalMetadataInfoHandler.getExistingMetaInfosForFile(file)
|
||||
.groupBy(
|
||||
keySelector = { it.start..it.end },
|
||||
valueTransform = { it.tag }
|
||||
)
|
||||
.mapValues { (_, it) -> it.toMutableSet() }
|
||||
val debugAnnotations = CheckerTestUtil.getDebugInfoDiagnostics(
|
||||
ktFile,
|
||||
info.analysisResult.bindingContext,
|
||||
markDynamicCalls = MARK_DYNAMIC_CALLS in module.directives,
|
||||
dynamicCallDescriptors = mutableListOf(),
|
||||
configuration,
|
||||
dataFlowValueFactory = DataFlowValueFactoryImpl(info.languageVersionSettings),
|
||||
info.analysisResult.moduleDescriptor as ModuleDescriptorImpl,
|
||||
diagnosedRanges = diagnosedRanges
|
||||
)
|
||||
debugAnnotations.mapNotNull { debugAnnotation ->
|
||||
if (!diagnosticsService.shouldRenderDiagnostic(module, debugAnnotation.diagnostic.factory.name)) return@mapNotNull null
|
||||
globalMetadataInfoHandler.addMetadataInfosForFile(
|
||||
file,
|
||||
debugAnnotation.diagnostic.toMetaInfo(module, file, configuration.withNewInference, withNewInferenceModeEnabled)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
}
|
||||
|
||||
class OldNewInferenceMetaInfoProcessor(testServices: TestServices) : AdditionalMetaInfoProcessor(testServices) {
|
||||
companion object {
|
||||
const val OI = "OI"
|
||||
const val NI = "NI"
|
||||
}
|
||||
|
||||
override fun processMetaInfos(module: TestModule, file: TestFile) {
|
||||
/*
|
||||
* Rules for OI/NI attribute:
|
||||
* ┌──────────┬──────┬──────┬──────────┐
|
||||
* │ │ OI │ NI │ nothing │ <- reported
|
||||
* ├──────────┼──────┼──────┼──────────┤
|
||||
* │ nothing │ both │ both │ nothing │
|
||||
* │ OI │ OI │ both │ OI │
|
||||
* │ NI │ both │ NI │ NI │
|
||||
* │ both │ both │ both │ opposite │ <- OI if NI enabled in test and vice versa
|
||||
* └──────────┴──────┴──────┴──────────┘
|
||||
* ^ existed
|
||||
*/
|
||||
if (!testServices.withNewInferenceModeEnabled()) return
|
||||
val newInferenceEnabled = module.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)
|
||||
val (currentFlag, otherFlag) = when (newInferenceEnabled) {
|
||||
true -> NI to OI
|
||||
false -> OI to NI
|
||||
}
|
||||
val matchedExistedInfos = mutableSetOf<ParsedCodeMetaInfo>()
|
||||
val matchedReportedInfos = mutableSetOf<CodeMetaInfo>()
|
||||
val allReportedInfos = globalMetadataInfoHandler.getReportedMetaInfosForFile(file)
|
||||
for ((_, reportedInfos) in allReportedInfos.groupBy { Triple(it.start, it.end, it.tag) }) {
|
||||
val existedInfos = globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, reportedInfos.first())
|
||||
for ((reportedInfo, existedInfo) in reportedInfos.zip(existedInfos)) {
|
||||
matchedExistedInfos += existedInfo
|
||||
matchedReportedInfos += reportedInfo
|
||||
if (currentFlag !in reportedInfo.attributes) continue
|
||||
if (currentFlag in existedInfo.attributes) continue
|
||||
reportedInfo.attributes.remove(currentFlag)
|
||||
}
|
||||
}
|
||||
|
||||
if (allReportedInfos.size != matchedReportedInfos.size) {
|
||||
for (info in allReportedInfos) {
|
||||
if (info !in matchedReportedInfos) {
|
||||
info.attributes.remove(currentFlag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val allExistedInfos = globalMetadataInfoHandler.getExistingMetaInfosForFile(file)
|
||||
if (allExistedInfos.size == matchedExistedInfos.size) return
|
||||
|
||||
val newInfos = allExistedInfos.mapNotNull {
|
||||
if (it in matchedExistedInfos) return@mapNotNull null
|
||||
if (currentFlag in it.attributes) return@mapNotNull null
|
||||
it.copy().apply {
|
||||
if (otherFlag !in attributes) {
|
||||
attributes += otherFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
globalMetadataInfoHandler.addMetadataInfosForFile(file, newInfos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestServices.withNewInferenceModeEnabled(): Boolean {
|
||||
return DiagnosticsDirectives.WITH_NEW_INFERENCE in moduleStructure.allDirectives
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.test.frontend.classic.handlers
|
||||
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.model.FrontendOutputHandler
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
abstract class ClassicFrontendAnalysisHandler(
|
||||
testServices: TestServices
|
||||
) : FrontendOutputHandler<ClassicFrontendOutputArtifact>(testServices, FrontendKinds.ClassicFrontend)
|
||||
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.test.frontend.classic.handlers
|
||||
|
||||
import org.jetbrains.kotlin.codeMetaInfo.renderConfigurations.DiagnosticCodeMetaInfoRenderConfiguration
|
||||
|
||||
object ClassicMetaInfoUtils {
|
||||
val renderDiagnosticNoArgs = DiagnosticCodeMetaInfoRenderConfiguration().apply { renderParams = false }
|
||||
val renderDiagnosticWithArgs = DiagnosticCodeMetaInfoRenderConfiguration().apply { renderParams = true }
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.test.frontend.classic.handlers
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
|
||||
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
|
||||
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.isJavaFile
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import org.jetbrains.kotlin.test.util.DescriptorValidator
|
||||
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
|
||||
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE
|
||||
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumper
|
||||
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumperImpl
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.util.function.Predicate
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class DeclarationsDumpHandler(
|
||||
testServices: TestServices
|
||||
) : ClassicFrontendAnalysisHandler(testServices) {
|
||||
companion object {
|
||||
private val NAMES_OF_CHECK_TYPE_HELPER = listOf("checkSubtype", "CheckTypeInv", "_", "checkType").map { Name.identifier(it) }
|
||||
|
||||
private val JAVA_PACKAGE_PATTERN = Pattern.compile("^\\s*package [.\\w\\d]*", Pattern.MULTILINE)
|
||||
}
|
||||
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(DiagnosticsDirectives)
|
||||
|
||||
private val dumper: MultiModuleInfoDumper = MultiModuleInfoDumperImpl(moduleHeaderTemplate = "// -- Module: <%s> --")
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||
if (dumper.isEmpty()) return
|
||||
val resultDump = dumper.generateResultingDump()
|
||||
val testDataFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
val allDirectives = testServices.moduleStructure.allDirectives
|
||||
val prefix = when {
|
||||
DiagnosticsDirectives.NI_EXPECTED_FILE in allDirectives &&
|
||||
testServices.moduleStructure.modules.any { it.languageVersionSettings.supportsFeature(LanguageFeature.NewInference) } -> ".ni"
|
||||
|
||||
JvmEnvironmentConfigurationDirectives.USE_JAVAC in allDirectives
|
||||
&& DiagnosticsDirectives.JAVAC_EXPECTED_FILE in allDirectives -> ".javac"
|
||||
|
||||
else -> ""
|
||||
}
|
||||
val expectedFileName = "${testDataFile.nameWithoutExtension}$prefix.txt"
|
||||
val expectedFile = testDataFile.parentFile.resolve(expectedFileName)
|
||||
assertions.assertEqualsToFile(expectedFile, resultDump)
|
||||
}
|
||||
|
||||
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) {
|
||||
if (DiagnosticsDirectives.SKIP_TXT in module.directives) return
|
||||
val moduleDescriptor = info.analysisResult.moduleDescriptor
|
||||
val checkTypeEnabled = AdditionalFilesDirectives.CHECK_TYPE in module.directives
|
||||
val comparator = RecursiveDescriptorComparator(
|
||||
createdAffectedPackagesConfiguration(module.files, info.ktFiles, moduleDescriptor, checkTypeEnabled)
|
||||
)
|
||||
val packages = listOf(FqName.ROOT)
|
||||
val textByPackage = packages.keysToMap { StringBuilder() }
|
||||
|
||||
for ((packageName, packageText) in textByPackage.entries) {
|
||||
val aPackage = moduleDescriptor.getPackage(packageName)
|
||||
assertions.assertFalse(aPackage.isEmpty())
|
||||
|
||||
val actualSerialized = comparator.serializeRecursively(aPackage)
|
||||
packageText.append(actualSerialized)
|
||||
}
|
||||
val allPackagesText = textByPackage.values.joinToString("\n")
|
||||
dumper.builderForModule(module).appendLine(allPackagesText)
|
||||
}
|
||||
|
||||
private fun createdAffectedPackagesConfiguration(
|
||||
testFiles: List<TestFile>,
|
||||
ktFiles: Map<TestFile, KtFile>,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
checkTypeEnabled: Boolean
|
||||
): RecursiveDescriptorComparator.Configuration {
|
||||
val packagesNames = testFiles.mapNotNullTo(mutableSetOf()) {
|
||||
val ktFile = ktFiles[it]
|
||||
when {
|
||||
ktFile != null -> ktFile.packageFqName.pathSegments().firstOrNull() ?: SpecialNames.ROOT_PACKAGE
|
||||
it.isJavaFile -> getJavaFilePackage(it)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
val stepIntoFilter = Predicate<DeclarationDescriptor> { descriptor ->
|
||||
val module = DescriptorUtils.getContainingModuleOrNull(descriptor)
|
||||
if (module != moduleDescriptor) return@Predicate false
|
||||
|
||||
if (descriptor is PackageViewDescriptor) {
|
||||
val fqName = descriptor.fqName
|
||||
return@Predicate fqName.isRoot || fqName.pathSegments().first() in packagesNames
|
||||
}
|
||||
|
||||
if (checkTypeEnabled && descriptor.name in NAMES_OF_CHECK_TYPE_HELPER) return@Predicate false
|
||||
|
||||
true
|
||||
}
|
||||
return RECURSIVE.filterRecursion(stepIntoFilter)
|
||||
.withValidationStrategy(DescriptorValidator.ValidationVisitor.errorTypesAllowed())
|
||||
.checkFunctionContracts(true)
|
||||
}
|
||||
|
||||
private fun getJavaFilePackage(testFile: TestFile): Name {
|
||||
val matcher = JAVA_PACKAGE_PATTERN.matcher(testFile.originalContent)
|
||||
|
||||
if (matcher.find()) {
|
||||
return testFile.originalContent
|
||||
.substring(matcher.start(), matcher.end())
|
||||
.split(" ")
|
||||
.last()
|
||||
.filter { !it.isWhitespace() }
|
||||
.let { Name.identifier(it.split(".").first()) }
|
||||
}
|
||||
|
||||
return SpecialNames.ROOT_PACKAGE
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.test.frontend.classic.handlers
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.services.MetaTestConfigurator
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
|
||||
class DiagnosticTestWithJavacSkipConfigurator(testServices: TestServices) : MetaTestConfigurator(testServices) {
|
||||
override val directives: List<DirectivesContainer>
|
||||
get() = listOf(DiagnosticsDirectives)
|
||||
|
||||
override fun shouldSkipTest(): Boolean {
|
||||
return DiagnosticsDirectives.SKIP_JAVAC in testServices.moduleStructure.allDirectives
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.test.frontend.classic.handlers
|
||||
|
||||
import org.jetbrains.kotlin.codeMetaInfo.clearTextFromDiagnosticMarkup
|
||||
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
|
||||
import org.jetbrains.kotlin.test.runners.AbstractFirDiagnosticTest
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.assertions
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import org.jetbrains.kotlin.test.utils.firTestDataFile
|
||||
import java.io.File
|
||||
|
||||
class FirTestDataConsistencyHandler(testServices: TestServices) : AfterAnalysisChecker(testServices) {
|
||||
override val directives: List<DirectivesContainer>
|
||||
get() = listOf(FirDiagnosticsDirectives)
|
||||
|
||||
override fun check(failedAssertions: List<AssertionError>) {
|
||||
val moduleStructure = testServices.moduleStructure
|
||||
val testData = moduleStructure.originalTestDataFiles.first()
|
||||
if (testData.extension == "kts") return
|
||||
if (FirDiagnosticsDirectives.FIR_IDENTICAL in moduleStructure.allDirectives) return
|
||||
val firTestData = testData.firTestDataFile
|
||||
if (!firTestData.exists()) {
|
||||
runFirTestAndGeneratedTestData(testData, firTestData)
|
||||
return
|
||||
}
|
||||
val originalFileContent = clearTextFromDiagnosticMarkup(testData.readText())
|
||||
val firFileContent = clearTextFromDiagnosticMarkup(firTestData.readText())
|
||||
testServices.assertions.assertEquals(originalFileContent, firFileContent) {
|
||||
"Original and fir test data aren't identical. " +
|
||||
"Please, add changes from ${testData.name} to ${firTestData.name}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun runFirTestAndGeneratedTestData(testData: File, firTestData: File) {
|
||||
firTestData.writeText(clearTextFromDiagnosticMarkup(testData.readText()))
|
||||
val test = object : AbstractFirDiagnosticTest() {}
|
||||
test.runTest(firTestData.absolutePath)
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.test.frontend.fir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||
import org.jetbrains.kotlin.backend.jvm.jvmPhases
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirMetadataSerializer
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
|
||||
import org.jetbrains.kotlin.ir.util.generateTypicalIrProviderList
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
|
||||
import org.jetbrains.kotlin.test.model.BackendKinds
|
||||
import org.jetbrains.kotlin.test.model.Frontend2BackendConverter
|
||||
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
|
||||
|
||||
class Fir2IrResultsConverter(
|
||||
testServices: TestServices
|
||||
) : Frontend2BackendConverter<FirOutputArtifact, IrBackendInput>(
|
||||
testServices,
|
||||
FrontendKinds.FIR,
|
||||
BackendKinds.IrBackend
|
||||
) {
|
||||
override fun transform(
|
||||
module: TestModule,
|
||||
inputArtifact: FirOutputArtifact
|
||||
): IrBackendInput {
|
||||
val extensions = JvmGeneratorExtensions()
|
||||
val (irModuleFragment, symbolTable, sourceManager, components) = inputArtifact.firAnalyzerFacade.convertToIr(extensions)
|
||||
val dummyBindingContext = NoScopeRecordCliBindingTrace().bindingContext
|
||||
|
||||
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
|
||||
val configuration = compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
|
||||
val phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jvmPhases)
|
||||
val codegenFactory = JvmIrCodegenFactory(phaseConfig)
|
||||
|
||||
// TODO: handle fir from light tree
|
||||
val ktFiles = inputArtifact.firFiles.values.mapNotNull { it.psi as KtFile? }
|
||||
|
||||
// Create and initialize the module and its dependencies
|
||||
val project = compilerConfigurationProvider.getProject(module)
|
||||
val container = TopDownAnalyzerFacadeForJVM.createContainer(
|
||||
project, ktFiles, NoScopeRecordCliBindingTrace(), configuration,
|
||||
compilerConfigurationProvider.getPackagePartProviderFactory(module),
|
||||
::FileBasedDeclarationProviderFactory, CompilerEnvironment,
|
||||
TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles), emptyList()
|
||||
)
|
||||
|
||||
val generationState = GenerationState.Builder(
|
||||
project, ClassBuilderFactories.BINARIES,
|
||||
container.get(), dummyBindingContext, ktFiles,
|
||||
configuration
|
||||
).codegenFactory(
|
||||
codegenFactory
|
||||
).isIrBackend(
|
||||
true
|
||||
).jvmBackendClassResolver(
|
||||
FirJvmBackendClassResolver(components)
|
||||
).build()
|
||||
|
||||
irModuleFragment.irBuiltins.functionFactory = IrFunctionFactory(irModuleFragment.irBuiltins, symbolTable)
|
||||
val irProviders = generateTypicalIrProviderList(
|
||||
irModuleFragment.descriptor, irModuleFragment.irBuiltins, symbolTable, extensions = extensions
|
||||
)
|
||||
|
||||
return IrBackendInput(
|
||||
generationState,
|
||||
irModuleFragment,
|
||||
symbolTable,
|
||||
sourceManager,
|
||||
phaseConfig,
|
||||
irProviders,
|
||||
extensions,
|
||||
) { context, irClass, _, serializationBindings, parent ->
|
||||
FirMetadataSerializer(inputArtifact.session, context, irClass, serializationBindings, components, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.test.frontend.fir
|
||||
|
||||
import org.jetbrains.kotlin.test.ExceptionFromTestError
|
||||
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
|
||||
class FirFailingTestSuppressor(testServices: TestServices) : AfterAnalysisChecker(testServices) {
|
||||
override fun suppressIfNeeded(failedAssertions: List<AssertionError>): List<AssertionError> {
|
||||
val testFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
val failFile = testFile.parentFile.resolve("${testFile.nameWithoutExtension}.fail")
|
||||
val exceptionFromFir = failedAssertions.firstOrNull { it is ExceptionFromTestError }
|
||||
return when {
|
||||
failFile.exists() && exceptionFromFir != null -> emptyList()
|
||||
failFile.exists() && exceptionFromFir == null -> {
|
||||
failedAssertions + AssertionError("Fail file exists but no exception was throw. Please remove ${failFile.name}")
|
||||
}
|
||||
else -> failedAssertions
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.test.frontend.fir
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
|
||||
import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.fir.session.FirJvmModuleInfo
|
||||
import org.jetbrains.kotlin.fir.session.FirSessionFactory
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
|
||||
class FirFrontendFacade(
|
||||
testServices: TestServices
|
||||
) : FrontendFacade<FirOutputArtifact>(testServices, FrontendKinds.FIR) {
|
||||
override val additionalServices: List<ServiceRegistrationData>
|
||||
get() = listOf(service(::FirModuleInfoProvider))
|
||||
|
||||
override fun analyze(module: TestModule): FirOutputArtifact {
|
||||
val moduleInfoProvider = testServices.firModuleInfoProvider
|
||||
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
|
||||
// TODO: add configurable parser
|
||||
|
||||
val project = compilerConfigurationProvider.getProject(module)
|
||||
|
||||
PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java)
|
||||
|
||||
val ktFiles = testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).values
|
||||
|
||||
val sessionProvider = moduleInfoProvider.firSessionProvider
|
||||
|
||||
val languageVersionSettings = module.languageVersionSettings
|
||||
val builtinsModuleInfo = moduleInfoProvider.builtinsModuleInfoForModule(module)
|
||||
val packagePartProviderFactory = compilerConfigurationProvider.getPackagePartProviderFactory(module)
|
||||
|
||||
createSessionForBuiltins(builtinsModuleInfo, sessionProvider, project, packagePartProviderFactory)
|
||||
createSessionForBinaryDependencies(module, sessionProvider, project, packagePartProviderFactory)
|
||||
|
||||
val sourcesScope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles)
|
||||
val sourcesModuleInfo = moduleInfoProvider.convertToFirModuleInfo(module)
|
||||
val session = FirSessionFactory.createJavaModuleBasedSession(
|
||||
sourcesModuleInfo,
|
||||
sessionProvider,
|
||||
sourcesScope,
|
||||
project,
|
||||
languageVersionSettings = languageVersionSettings
|
||||
)
|
||||
|
||||
val firAnalyzerFacade = FirAnalyzerFacade(session, languageVersionSettings, ktFiles)
|
||||
val firFiles = firAnalyzerFacade.runResolution()
|
||||
val filesMap = firFiles.mapNotNull { firFile ->
|
||||
val testFile = module.files.firstOrNull { it.name == firFile.name } ?: return@mapNotNull null
|
||||
testFile to firFile
|
||||
}.toMap()
|
||||
|
||||
return FirOutputArtifact(session, filesMap, firAnalyzerFacade)
|
||||
}
|
||||
|
||||
private fun createSessionForBuiltins(
|
||||
builtinsModuleInfo: FirJvmModuleInfo,
|
||||
sessionProvider: FirProjectSessionProvider,
|
||||
project: Project,
|
||||
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
|
||||
) {
|
||||
//For BuiltIns, registered in sessionProvider automatically
|
||||
val allProjectScope = GlobalSearchScope.allScope(project)
|
||||
|
||||
FirSessionFactory.createLibrarySession(
|
||||
builtinsModuleInfo, sessionProvider, allProjectScope, project,
|
||||
packagePartProviderFactory(allProjectScope)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createSessionForBinaryDependencies(
|
||||
module: TestModule,
|
||||
sessionProvider: FirProjectSessionProvider,
|
||||
project: Project,
|
||||
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
|
||||
) {
|
||||
val librariesScope = ProjectScope.getLibrariesScope(project)
|
||||
val librariesModuleInfo = FirJvmModuleInfo.createForLibraries(module.name)
|
||||
FirSessionFactory.createLibrarySession(
|
||||
librariesModuleInfo,
|
||||
sessionProvider,
|
||||
librariesScope,
|
||||
project,
|
||||
packagePartProviderFactory(librariesScope)
|
||||
)
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.test.frontend.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
|
||||
import org.jetbrains.kotlin.fir.session.FirJvmModuleInfo
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.test.services.TestService
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.dependencyProvider
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
|
||||
class FirModuleInfoProvider(
|
||||
private val testServices: TestServices
|
||||
) : TestService {
|
||||
val firSessionProvider = FirProjectSessionProvider()
|
||||
|
||||
private val builtinsByModule: MutableMap<TestModule, FirJvmModuleInfo> = mutableMapOf()
|
||||
private val firModuleInfoByModule: MutableMap<TestModule, FirJvmModuleInfo> = mutableMapOf()
|
||||
|
||||
fun convertToFirModuleInfo(module: TestModule): FirJvmModuleInfo {
|
||||
return firModuleInfoByModule.getOrPut(module) {
|
||||
val dependencies = mutableListOf(builtinsModuleInfoForModule(module))
|
||||
module.dependencies.mapTo(dependencies) {
|
||||
convertToFirModuleInfo(testServices.dependencyProvider.getTestModule(it.moduleName))
|
||||
}
|
||||
FirJvmModuleInfo(
|
||||
module.name,
|
||||
dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun builtinsModuleInfoForModule(module: TestModule): FirJvmModuleInfo {
|
||||
return builtinsByModule.getOrPut(module) {
|
||||
FirJvmModuleInfo(Name.special("<built-ins>"), emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.firModuleInfoProvider: FirModuleInfoProvider by TestServices.testServiceAccessor()
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.test.frontend.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
|
||||
data class FirOutputArtifact(
|
||||
val session: FirSession,
|
||||
val allFirFiles: Map<TestFile, FirFile>,
|
||||
val firAnalyzerFacade: FirAnalyzerFacade
|
||||
) : ResultingArtifact.FrontendOutput<FirOutputArtifact>() {
|
||||
override val kind: FrontendKinds.FIR
|
||||
get() = FrontendKinds.FIR
|
||||
|
||||
val firFiles: Map<TestFile, FirFile> = allFirFiles.filterKeys { !it.isAdditional }
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.test.frontend.fir.handlers
|
||||
|
||||
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.model.FrontendOutputHandler
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import java.io.File
|
||||
|
||||
abstract class FirAnalysisHandler(
|
||||
testServices: TestServices
|
||||
) : FrontendOutputHandler<FirOutputArtifact>(testServices, FrontendKinds.FIR) {
|
||||
protected val File.nameWithoutFirExtension: String
|
||||
get() = nameWithoutExtension.removeSuffix(".fir")
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.test.frontend.fir.handlers
|
||||
|
||||
import org.jetbrains.kotlin.fir.AbstractFirDiagnosticsTest
|
||||
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class FirCfgConsistencyHandler(testServices: TestServices) : FirAnalysisHandler(testServices) {
|
||||
override fun processModule(module: TestModule, info: FirOutputArtifact) {
|
||||
info.firFiles.values.forEach { it.accept(AbstractFirDiagnosticsTest.CfgConsistencyChecker) }
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.test.frontend.fir.handlers
|
||||
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.FirControlFlowGraphRenderVisitor
|
||||
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.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
|
||||
// TODO: adapt to multifile and multimodule tests
|
||||
class FirCfgDumpHandler(testServices: TestServices) : FirAnalysisHandler(testServices) {
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(FirDiagnosticsDirectives)
|
||||
|
||||
private val builder = StringBuilder()
|
||||
private var alreadyDumped: Boolean = false
|
||||
|
||||
override fun processModule(module: TestModule, info: FirOutputArtifact) {
|
||||
if (alreadyDumped || FirDiagnosticsDirectives.DUMP_CFG !in module.directives) return
|
||||
val file = info.firFiles.values.first()
|
||||
file.accept(FirControlFlowGraphRenderVisitor(builder))
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||
if (!alreadyDumped) return
|
||||
val testDataFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
val expectedFile = testDataFile.parentFile.resolve("${testDataFile.nameWithoutFirExtension}.dot")
|
||||
assertions.assertEqualsToFile(expectedFile, builder.toString())
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.test.frontend.fir.handlers
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.codeMetaInfo.model.CodeMetaInfo
|
||||
import org.jetbrains.kotlin.codeMetaInfo.renderConfigurations.AbstractCodeMetaInfoRenderConfiguration
|
||||
import org.jetbrains.kotlin.diagnostics.PositioningStrategy
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDefaultErrorMessages
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderer
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
|
||||
object FirMetaInfoUtils {
|
||||
val renderDiagnosticNoArgs = FirDiagnosticCodeMetaRenderConfiguration().apply { renderParams = false }
|
||||
val renderDiagnosticWithArgs = FirDiagnosticCodeMetaRenderConfiguration().apply { renderParams = true }
|
||||
}
|
||||
|
||||
class FirDiagnosticCodeMetaInfo(
|
||||
val diagnostic: FirDiagnostic<*>,
|
||||
renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration
|
||||
) : CodeMetaInfo {
|
||||
// TODO: this implementation is hacky and doesn't support proper ranges for light tree diagnostics
|
||||
private val textRangeFromClassicDiagnostic: TextRange? = run {
|
||||
val psi = diagnostic.element.psi ?: return@run null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val positioningStrategy = diagnostic.factory.positioningStrategy.psiStrategy as PositioningStrategy<PsiElement>
|
||||
positioningStrategy.mark(psi).first()
|
||||
}
|
||||
|
||||
override var renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration = renderConfiguration
|
||||
private set
|
||||
|
||||
override val start: Int
|
||||
get() = textRangeFromClassicDiagnostic?.startOffset ?: diagnostic.element.startOffset
|
||||
|
||||
override val end: Int
|
||||
get() = textRangeFromClassicDiagnostic?.endOffset ?: diagnostic.element.endOffset
|
||||
|
||||
override val tag: String
|
||||
get() = renderConfiguration.getTag(this)
|
||||
|
||||
override val attributes: MutableList<String> = mutableListOf()
|
||||
|
||||
override fun asString(): String = renderConfiguration.asString(this)
|
||||
|
||||
fun replaceRenderConfiguration(renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration) {
|
||||
this.renderConfiguration = renderConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
class FirDiagnosticCodeMetaRenderConfiguration(
|
||||
val renderSeverity: Boolean = false,
|
||||
) : AbstractCodeMetaInfoRenderConfiguration(renderParams = false) {
|
||||
private val crossPlatformLineBreak = """\r?\n""".toRegex()
|
||||
|
||||
override fun asString(codeMetaInfo: CodeMetaInfo): String {
|
||||
if (codeMetaInfo !is FirDiagnosticCodeMetaInfo) return ""
|
||||
return (getTag(codeMetaInfo)
|
||||
+ getPlatformsString(codeMetaInfo)
|
||||
+ getParamsString(codeMetaInfo))
|
||||
.replace(crossPlatformLineBreak, "")
|
||||
}
|
||||
|
||||
private fun getParamsString(codeMetaInfo: FirDiagnosticCodeMetaInfo): String {
|
||||
if (!renderParams) return ""
|
||||
val params = mutableListOf<String>()
|
||||
|
||||
val diagnostic = codeMetaInfo.diagnostic
|
||||
|
||||
val renderer = FirDefaultErrorMessages.getRendererForDiagnostic(diagnostic) as FirDiagnosticRenderer<FirDiagnostic<*>>
|
||||
params.add(renderer.render(diagnostic))
|
||||
|
||||
if (renderSeverity)
|
||||
params.add("severity='${diagnostic.severity}'")
|
||||
|
||||
params.add(getAdditionalParams(codeMetaInfo))
|
||||
|
||||
return "(\"${params.filter { it.isNotEmpty() }.joinToString("; ")}\")"
|
||||
}
|
||||
|
||||
fun getTag(codeMetaInfo: FirDiagnosticCodeMetaInfo): String {
|
||||
return codeMetaInfo.diagnostic.factory.name
|
||||
}
|
||||
}
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.test.frontend.fir.handlers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
|
||||
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(testServices) {
|
||||
companion object {
|
||||
private val allowedKindsForDebugInfo = setOf(
|
||||
FirRealSourceElementKind,
|
||||
FirFakeSourceElementKind.DesugaredCompoundAssignment,
|
||||
)
|
||||
}
|
||||
|
||||
private val globalMetadataInfoHandler: GlobalMetadataInfoHandler
|
||||
get() = testServices.globalMetadataInfoHandler
|
||||
|
||||
private val diagnosticsService: DiagnosticsService
|
||||
get() = testServices.diagnosticsService
|
||||
|
||||
override val directivesContainers: List<DirectivesContainer> =
|
||||
listOf(DiagnosticsDirectives)
|
||||
|
||||
override val additionalServices: List<ServiceRegistrationData> =
|
||||
listOf(service(::DiagnosticsService))
|
||||
|
||||
override fun processModule(module: TestModule, info: FirOutputArtifact) {
|
||||
val diagnosticsPerFile = info.firAnalyzerFacade.runCheckers()
|
||||
|
||||
for (file in module.files) {
|
||||
val firFile = info.firFiles[file] ?: continue
|
||||
val diagnostics = diagnosticsPerFile[firFile] ?: continue
|
||||
val diagnosticsMetadataInfos = diagnostics.mapNotNull { diagnostic ->
|
||||
if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name)) return@mapNotNull null
|
||||
// SYNTAX errors will be reported later
|
||||
if (diagnostic.factory == FirErrors.SYNTAX) return@mapNotNull null
|
||||
if (!diagnostic.isValid) return@mapNotNull null
|
||||
diagnostic.toMetaInfo(file)
|
||||
}
|
||||
globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos)
|
||||
collectSyntaxDiagnostics(file, firFile)
|
||||
collectDebugInfoDiagnostics(file, firFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirDiagnostic<*>.toMetaInfo(file: TestFile, forceRenderArguments: Boolean = false): FirDiagnosticCodeMetaInfo {
|
||||
val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs)
|
||||
val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo)
|
||||
.any { it.description != null }
|
||||
if (shouldRenderArguments) {
|
||||
metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs)
|
||||
}
|
||||
return metaInfo
|
||||
}
|
||||
|
||||
private fun collectSyntaxDiagnostics(testFile: TestFile, firFile: FirFile) {
|
||||
// TODO: support in light tree
|
||||
val psiFile = firFile.psi ?: return
|
||||
val metaInfos = AnalyzingUtils.getSyntaxErrorRanges(psiFile).map {
|
||||
FirErrors.SYNTAX.on(FirRealPsiSourceElement(it)).toMetaInfo(testFile)
|
||||
}
|
||||
globalMetadataInfoHandler.addMetadataInfosForFile(testFile, metaInfos)
|
||||
}
|
||||
|
||||
private fun collectDebugInfoDiagnostics(
|
||||
testFile: TestFile,
|
||||
firFile: FirFile,
|
||||
) {
|
||||
val result = mutableListOf<FirDiagnostic<*>>()
|
||||
val diagnosedRangesToDiagnosticNames = globalMetadataInfoHandler.getExistingMetaInfosForFile(testFile).groupBy(
|
||||
keySelector = { it.start..it.end },
|
||||
valueTransform = { it.tag }
|
||||
).mapValues { (_, it) -> it.toSet() }
|
||||
object : FirDefaultVisitorVoid() {
|
||||
override fun visitElement(element: FirElement) {
|
||||
if (element is FirExpression) {
|
||||
result.addIfNotNull(
|
||||
createExpressionTypeDiagnosticIfExpected(
|
||||
element, diagnosedRangesToDiagnosticNames
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(functionCall: FirFunctionCall) {
|
||||
result.addIfNotNull(
|
||||
createCallDiagnosticIfExpected(functionCall, functionCall.calleeReference, diagnosedRangesToDiagnosticNames)
|
||||
)
|
||||
|
||||
super.visitFunctionCall(functionCall)
|
||||
}
|
||||
}.let(firFile::accept)
|
||||
globalMetadataInfoHandler.addMetadataInfosForFile(testFile, result.map { it.toMetaInfo(testFile, forceRenderArguments = true) })
|
||||
}
|
||||
|
||||
fun createExpressionTypeDiagnosticIfExpected(
|
||||
element: FirExpression,
|
||||
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>
|
||||
): FirDiagnosticWithParameters1<FirSourceElement, String>? =
|
||||
DebugInfoDiagnosticFactory1.EXPRESSION_TYPE.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) {
|
||||
element.typeRef.renderAsString((element as? FirExpressionWithSmartcast)?.originalType)
|
||||
}
|
||||
|
||||
private fun FirTypeRef.renderAsString(originalTypeRef: FirTypeRef?): String {
|
||||
val type = coneTypeSafe<ConeKotlinType>() ?: return "Type is unknown"
|
||||
val rendered = type.renderForDebugInfo()
|
||||
val originalTypeRendered = originalTypeRef?.coneTypeSafe<ConeKotlinType>()?.renderForDebugInfo() ?: return rendered
|
||||
|
||||
return "$rendered & $originalTypeRendered"
|
||||
}
|
||||
|
||||
private fun createCallDiagnosticIfExpected(
|
||||
element: FirElement,
|
||||
reference: FirNamedReference,
|
||||
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>
|
||||
): FirDiagnosticWithParameters1<FirSourceElement, String>? =
|
||||
DebugInfoDiagnosticFactory1.CALL.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) {
|
||||
val resolvedSymbol = (reference as? FirResolvedNamedReference)?.resolvedSymbol
|
||||
val fqName = resolvedSymbol?.fqNameUnsafe()
|
||||
Renderers.renderCallInfo(fqName, getTypeOfCall(reference, resolvedSymbol))
|
||||
}
|
||||
|
||||
private inline fun DebugInfoDiagnosticFactory1.createDebugInfoDiagnostic(
|
||||
element: FirElement,
|
||||
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>,
|
||||
argument: () -> String,
|
||||
): FirDiagnosticWithParameters1<FirSourceElement, String>? {
|
||||
val sourceElement = element.source ?: return null
|
||||
if (sourceElement.kind !in allowedKindsForDebugInfo) return null
|
||||
// Lambda argument is always (?) duplicated by function literal
|
||||
// Block expression is always (?) duplicated by single block expression
|
||||
if (sourceElement.elementType == KtNodeTypes.LAMBDA_ARGUMENT || sourceElement.elementType == KtNodeTypes.BLOCK) return null
|
||||
if (diagnosedRangesToDiagnosticNames[sourceElement.startOffset..sourceElement.endOffset]?.contains(this.name) != true) return null
|
||||
|
||||
val argumentText = argument()
|
||||
return when (sourceElement) {
|
||||
is FirPsiSourceElement<*> -> FirPsiDiagnosticWithParameters1(
|
||||
sourceElement,
|
||||
argumentText,
|
||||
severity,
|
||||
FirDiagnosticFactory1(
|
||||
name,
|
||||
severity,
|
||||
)
|
||||
)
|
||||
is FirLightSourceElement -> FirLightDiagnosticWithParameters1(
|
||||
sourceElement,
|
||||
argumentText,
|
||||
severity,
|
||||
FirDiagnosticFactory1<FirSourceElement, PsiElement, String>(
|
||||
name,
|
||||
severity
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTypeOfCall(
|
||||
reference: FirNamedReference,
|
||||
resolvedSymbol: AbstractFirBasedSymbol<*>?
|
||||
): String {
|
||||
if (resolvedSymbol == null) return TypeOfCall.UNRESOLVED.nameToRender
|
||||
|
||||
if ((resolvedSymbol as? FirFunctionSymbol)?.callableId?.callableName == OperatorNameConventions.INVOKE
|
||||
&& reference.name != OperatorNameConventions.INVOKE
|
||||
) {
|
||||
return TypeOfCall.VARIABLE_THROUGH_INVOKE.nameToRender
|
||||
}
|
||||
|
||||
return when (val fir = resolvedSymbol.fir) {
|
||||
is FirProperty -> {
|
||||
TypeOfCall.PROPERTY_GETTER.nameToRender
|
||||
}
|
||||
is FirFunction<*> -> buildString {
|
||||
if (fir is FirCallableMemberDeclaration<*>) {
|
||||
if (fir.status.isInline) append("inline ")
|
||||
if (fir.status.isInfix) append("infix ")
|
||||
if (fir.status.isOperator) append("operator ")
|
||||
if (fir.receiverTypeRef != null) append("extension ")
|
||||
}
|
||||
append(TypeOfCall.FUNCTION.nameToRender)
|
||||
}
|
||||
else -> TypeOfCall.OTHER.nameToRender
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractFirBasedSymbol<*>.fqNameUnsafe(): FqNameUnsafe? = when (this) {
|
||||
is FirClassLikeSymbol<*> -> classId.asSingleFqName().toUnsafe()
|
||||
is FirCallableSymbol<*> -> callableId.asFqNameForDebugInfo().toUnsafe()
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.test.frontend.fir.handlers
|
||||
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
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.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumper
|
||||
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumperImpl
|
||||
|
||||
class FirDumpHandler(
|
||||
testServices: TestServices
|
||||
) : FirAnalysisHandler(testServices) {
|
||||
private val dumper: MultiModuleInfoDumper = MultiModuleInfoDumperImpl()
|
||||
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(FirDiagnosticsDirectives)
|
||||
|
||||
override fun processModule(module: TestModule, info: FirOutputArtifact) {
|
||||
if (FirDiagnosticsDirectives.FIR_DUMP !in module.directives) return
|
||||
val builderForModule = dumper.builderForModule(module)
|
||||
val firFiles = info.firFiles
|
||||
firFiles.values.forEach { builderForModule.append(it.render()) }
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||
if (dumper.isEmpty()) return
|
||||
// TODO: change according to multiple testdata files
|
||||
val testDataFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
val expectedFile = testDataFile.parentFile.resolve("${testDataFile.nameWithoutFirExtension}.fir.txt")
|
||||
val actualText = dumper.generateResultingDump()
|
||||
assertions.assertEqualsToFile(expectedFile, actualText, message = { "Content is not equal" })
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.test.frontend.fir.handlers
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.assertions
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import org.jetbrains.kotlin.test.utils.firTestDataFile
|
||||
import org.jetbrains.kotlin.test.utils.isFirTestData
|
||||
import org.jetbrains.kotlin.test.utils.originalTestDataFile
|
||||
import java.io.File
|
||||
|
||||
class FirIdenticalChecker(testServices: TestServices) : AfterAnalysisChecker(testServices) {
|
||||
override fun check(failedAssertions: List<AssertionError>) {
|
||||
if (failedAssertions.isNotEmpty()) return
|
||||
val file = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
if (file.isFirTestData) {
|
||||
addDirectiveToClassicFile(file)
|
||||
} else {
|
||||
removeFirFileIfExist(file)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDirectiveToClassicFile(firFile: File) {
|
||||
val classicFile = firFile.originalTestDataFile
|
||||
val classicFileContent = classicFile.readText()
|
||||
val firFileContent = firFile.readText()
|
||||
if (classicFileContent == firFileContent) {
|
||||
classicFile.writer().use {
|
||||
it.appendLine("// ${FirDiagnosticsDirectives.FIR_IDENTICAL.name}")
|
||||
it.append(classicFileContent)
|
||||
}
|
||||
firFile.delete()
|
||||
testServices.assertions.fail {
|
||||
"""
|
||||
Dumps via FIR & via old FE are the same.
|
||||
Deleted .fir.txt dump, added // FIR_IDENTICAL to test source
|
||||
Please re-run the test now
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeFirFileIfExist(classicFile: File) {
|
||||
val firFile = classicFile.firTestDataFile
|
||||
firFile.delete()
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.test.impl
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import org.jetbrains.kotlin.test.Constructor
|
||||
import org.jetbrains.kotlin.test.TestConfiguration
|
||||
import org.jetbrains.kotlin.test.directives.model.ComposedDirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.services.impl.ModuleStructureExtractorImpl
|
||||
import org.jetbrains.kotlin.test.utils.TestDisposable
|
||||
|
||||
class TestConfigurationImpl(
|
||||
defaultsProvider: DefaultsProvider,
|
||||
assertions: Assertions,
|
||||
|
||||
facades: List<Constructor<AbstractTestFacade<*, *>>>,
|
||||
|
||||
analysisHandlers: List<Constructor<AnalysisHandler<*>>>,
|
||||
|
||||
sourcePreprocessors: List<Constructor<SourceFilePreprocessor>>,
|
||||
additionalMetaInfoProcessors: List<Constructor<AdditionalMetaInfoProcessor>>,
|
||||
environmentConfigurators: List<Constructor<EnvironmentConfigurator>>,
|
||||
|
||||
additionalSourceProviders: List<Constructor<AdditionalSourceProvider>>,
|
||||
metaTestConfigurators: List<Constructor<MetaTestConfigurator>>,
|
||||
afterAnalysisCheckers: List<Constructor<AfterAnalysisChecker>>,
|
||||
|
||||
override val metaInfoHandlerEnabled: Boolean,
|
||||
|
||||
directives: List<DirectivesContainer>,
|
||||
override val defaultRegisteredDirectives: RegisteredDirectives
|
||||
) : TestConfiguration() {
|
||||
override val rootDisposable: Disposable = TestDisposable()
|
||||
override val testServices: TestServices = TestServices()
|
||||
|
||||
private val allDirectives = directives.toMutableSet()
|
||||
override val directives: DirectivesContainer by lazy {
|
||||
when (allDirectives.size) {
|
||||
0 -> DirectivesContainer.Empty
|
||||
1 -> allDirectives.single()
|
||||
else -> ComposedDirectivesContainer(allDirectives)
|
||||
}
|
||||
}
|
||||
|
||||
override val moduleStructureExtractor: ModuleStructureExtractor = ModuleStructureExtractorImpl(
|
||||
testServices,
|
||||
additionalSourceProviders.map { it.invoke(testServices) }.also {
|
||||
it.flatMapTo(allDirectives) { provider -> provider.directives }
|
||||
}
|
||||
)
|
||||
|
||||
override val metaTestConfigurators: List<MetaTestConfigurator> = metaTestConfigurators.map {
|
||||
it.invoke(testServices).also { configurator ->
|
||||
allDirectives += configurator.directives
|
||||
}
|
||||
}
|
||||
|
||||
override val afterAnalysisCheckers: List<AfterAnalysisChecker> = afterAnalysisCheckers.map {
|
||||
it.invoke(testServices).also { checker ->
|
||||
allDirectives += checker.directives
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
testServices.apply {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
val sourceFilePreprocessors = sourcePreprocessors.map { it.invoke(this@apply) }
|
||||
val sourceFileProvider = SourceFileProviderImpl(sourceFilePreprocessors)
|
||||
register(SourceFileProvider::class, sourceFileProvider)
|
||||
|
||||
val configurators = environmentConfigurators.map { it.invoke(this) }
|
||||
configurators.flatMapTo(allDirectives) { it.directivesContainers }
|
||||
for (configurator in configurators) {
|
||||
configurator.additionalServices.forEach { register(it) }
|
||||
}
|
||||
val environmentProvider = CompilerConfigurationProviderImpl(
|
||||
rootDisposable,
|
||||
configurators
|
||||
)
|
||||
register(CompilerConfigurationProvider::class, environmentProvider)
|
||||
|
||||
register(Assertions::class, assertions)
|
||||
register(DefaultsProvider::class, defaultsProvider)
|
||||
|
||||
register(DefaultRegisteredDirectivesProvider::class, DefaultRegisteredDirectivesProvider(defaultRegisteredDirectives))
|
||||
|
||||
val metaInfoProcessors = additionalMetaInfoProcessors.map { it.invoke(this) }
|
||||
register(GlobalMetadataInfoHandler::class, GlobalMetadataInfoHandler(this, metaInfoProcessors))
|
||||
}
|
||||
}
|
||||
|
||||
private val facades: Map<TestArtifactKind<*>, Map<TestArtifactKind<*>, AbstractTestFacade<*, *>>> =
|
||||
facades
|
||||
.map { it.invoke(testServices) }
|
||||
.groupBy { it.inputKind }
|
||||
.mapValues { (frontendKind, converters) ->
|
||||
converters.groupBy { it.outputKind }.mapValues {
|
||||
it.value.singleOrNull() ?: manyFacadesError("converters", "$frontendKind -> ${it.key}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val analysisHandlers: Map<TestArtifactKind<*>, List<AnalysisHandler<*>>> =
|
||||
analysisHandlers.map { it.invoke(testServices).also(this::registerDirectivesAndServices) }
|
||||
.groupBy { it.artifactKind }
|
||||
.withDefault { emptyList() }
|
||||
|
||||
private fun manyFacadesError(name: String, kinds: String): Nothing {
|
||||
error("Too many $name passed for $kinds configuration")
|
||||
}
|
||||
|
||||
private fun registerDirectivesAndServices(handler: AnalysisHandler<*>) {
|
||||
allDirectives += handler.directivesContainers
|
||||
testServices.register(handler.additionalServices)
|
||||
}
|
||||
|
||||
init {
|
||||
testServices.apply {
|
||||
this@TestConfigurationImpl.facades.values.forEach { it.values.forEach { facade -> register(facade.additionalServices) } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun <I : ResultingArtifact<I>, O : ResultingArtifact<O>> getFacade(
|
||||
inputKind: TestArtifactKind<I>,
|
||||
outputKind: TestArtifactKind<O>
|
||||
): AbstractTestFacade<I, O> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return facades[inputKind]?.get(outputKind) as AbstractTestFacade<I, O>?
|
||||
?: facadeNotFoundError(inputKind, outputKind)
|
||||
}
|
||||
|
||||
private fun facadeNotFoundError(from: Any, to: Any): Nothing {
|
||||
error("Facade for converting '$from' to '$to' not found")
|
||||
}
|
||||
|
||||
override fun <A : ResultingArtifact<A>> getHandlers(artifactKind: TestArtifactKind<A>): List<AnalysisHandler<A>> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return analysisHandlers.getValue(artifactKind) as List<AnalysisHandler<A>>
|
||||
}
|
||||
|
||||
override fun getAllHandlers(): List<AnalysisHandler<*>> {
|
||||
return analysisHandlers.values.flatten()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.test.model
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
||||
|
||||
object BinaryArtifacts {
|
||||
class Jvm(val classFileFactory: ClassFileFactory) : ResultingArtifact.Binary<Jvm>() {
|
||||
override val kind: BinaryKind<Jvm>
|
||||
get() = ArtifactKinds.Jvm
|
||||
}
|
||||
|
||||
class Js : ResultingArtifact.Binary<Js>() {
|
||||
override val kind: BinaryKind<Js>
|
||||
get() = ArtifactKinds.Js
|
||||
}
|
||||
|
||||
class Native : ResultingArtifact.Binary<Native>() {
|
||||
override val kind: BinaryKind<Native>
|
||||
get() = ArtifactKinds.Native
|
||||
}
|
||||
|
||||
class KLib : ResultingArtifact.Binary<KLib>() {
|
||||
override val kind: BinaryKind<KLib>
|
||||
get() = ArtifactKinds.KLib
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.test.model
|
||||
|
||||
import org.jetbrains.kotlin.test.backend.classic.ClassicBackendInput
|
||||
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
|
||||
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
|
||||
|
||||
object FrontendKinds {
|
||||
object ClassicFrontend : FrontendKind<ClassicFrontendOutputArtifact>("ClassicFrontend")
|
||||
object FIR : FrontendKind<FirOutputArtifact>("FIR")
|
||||
|
||||
fun fromString(string: String): FrontendKind<*>? {
|
||||
return when (string) {
|
||||
"ClassicFrontend" -> ClassicFrontend
|
||||
"FIR" -> FIR
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object BackendKinds {
|
||||
object ClassicBackend : BackendKind<ClassicBackendInput>("ClassicBackend")
|
||||
object IrBackend : BackendKind<IrBackendInput>("IrBackend")
|
||||
|
||||
fun fromString(string: String): BackendKind<*>? {
|
||||
return when (string) {
|
||||
"ClassicBackend" -> ClassicBackend
|
||||
"IrBackend" -> IrBackend
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ArtifactKinds {
|
||||
object Jvm : BinaryKind<BinaryArtifacts.Jvm>("JVM")
|
||||
object Js : BinaryKind<BinaryArtifacts.Js>("JS")
|
||||
object Native : BinaryKind<BinaryArtifacts.Native>("Native")
|
||||
object KLib : BinaryKind<BinaryArtifacts.KLib>("KLib")
|
||||
|
||||
fun fromString(string: String): BinaryKind<*>? {
|
||||
return when (string) {
|
||||
"Jvm" -> Jvm
|
||||
"Js" -> Js
|
||||
"Native" -> Native
|
||||
"KLib" -> KLib
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.test.preprocessors
|
||||
|
||||
import org.jetbrains.kotlin.codeMetaInfo.clearTextFromDiagnosticMarkup
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.services.SourceFilePreprocessor
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class MetaInfosCleanupPreprocessor(testServices: TestServices) : SourceFilePreprocessor(testServices) {
|
||||
override fun process(file: TestFile, content: String): String {
|
||||
return clearTextFromDiagnosticMarkup(content)
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.test.services
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import java.io.File
|
||||
|
||||
class AdditionalDiagnosticsSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
||||
companion object {
|
||||
private const val HELPERS_PATH = "./compiler/testData/diagnostics/helpers"
|
||||
private const val CHECK_TYPE_PATH = "$HELPERS_PATH/types/checkType.kt"
|
||||
}
|
||||
|
||||
override val directives: List<DirectivesContainer> =
|
||||
listOf(AdditionalFilesDirectives)
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
|
||||
return buildList {
|
||||
if (containsDirective(globalDirectives, module, AdditionalFilesDirectives.CHECK_TYPE)) {
|
||||
add(File(CHECK_TYPE_PATH).toTestFile())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.test.services
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import java.io.File
|
||||
|
||||
abstract class CompilerConfigurationProvider : TestService {
|
||||
abstract val testRootDisposable: Disposable
|
||||
|
||||
protected abstract fun getKotlinCoreEnvironment(module: TestModule): KotlinCoreEnvironment
|
||||
|
||||
fun getProject(module: TestModule): Project {
|
||||
return getKotlinCoreEnvironment(module).project
|
||||
}
|
||||
|
||||
fun getPackagePartProviderFactory(module: TestModule): (GlobalSearchScope) -> JvmPackagePartProvider {
|
||||
return getKotlinCoreEnvironment(module)::createPackagePartProvider
|
||||
}
|
||||
|
||||
fun getCompilerConfiguration(module: TestModule): CompilerConfiguration {
|
||||
return getKotlinCoreEnvironment(module).configuration
|
||||
}
|
||||
|
||||
fun registerJavacForModule(module: TestModule, ktFiles: List<KtFile>, mockJdk: File?) {
|
||||
val environment = getKotlinCoreEnvironment(module)
|
||||
val bootClasspath = mockJdk?.let { listOf(it) }
|
||||
environment.registerJavac(kotlinFiles = ktFiles, bootClasspath = bootClasspath)
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.compilerConfigurationProvider: CompilerConfigurationProvider by TestServices.testServiceAccessor()
|
||||
|
||||
class CompilerConfigurationProviderImpl(
|
||||
override val testRootDisposable: Disposable,
|
||||
val configurators: List<EnvironmentConfigurator>
|
||||
) : CompilerConfigurationProvider() {
|
||||
private val cache: MutableMap<TestModule, KotlinCoreEnvironment> = mutableMapOf()
|
||||
|
||||
override fun getKotlinCoreEnvironment(module: TestModule): KotlinCoreEnvironment {
|
||||
return cache.getOrPut(module) {
|
||||
val projectEnv = KotlinCoreEnvironment.createProjectEnvironmentForTests(testRootDisposable, CompilerConfiguration())
|
||||
KotlinCoreEnvironment.createForTests(
|
||||
projectEnv,
|
||||
createCompilerConfiguration(module, projectEnv.project),
|
||||
EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCompilerConfiguration(module: TestModule, project: MockProject): CompilerConfiguration {
|
||||
val configuration = CompilerConfiguration()
|
||||
configuration[CommonConfigurationKeys.MODULE_NAME] = module.name
|
||||
|
||||
configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] = object : MessageCollector {
|
||||
override fun clear() {}
|
||||
|
||||
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) {
|
||||
if (severity == CompilerMessageSeverity.ERROR) {
|
||||
val prefix = if (location == null) "" else "(" + location.path + ":" + location.line + ":" + location.column + ") "
|
||||
throw AssertionError(prefix + message)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasErrors(): Boolean = false
|
||||
}
|
||||
configuration.languageVersionSettings = module.languageVersionSettings
|
||||
|
||||
configurators.forEach { it.configureCompilerConfiguration(configuration, module, project) }
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
private operator fun <T : Any> CompilerConfiguration.set(key: CompilerConfigurationKey<T>, value: T) {
|
||||
put(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
val TestModule.javaFiles: List<TestFile>
|
||||
get() = files.filter { it.isJavaFile }
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.test.services
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.CHECK_STATE_MACHINE
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.CHECK_TAIL_CALL_OPTIMIZATION
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.WITH_COROUTINES
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import java.io.File
|
||||
|
||||
class CoroutineHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
||||
companion object {
|
||||
private const val HELPERS_PATH = "./compiler/testData/diagnostics/helpers/coroutines"
|
||||
private const val COROUTINE_HELPERS_PATH = "$HELPERS_PATH/CoroutineHelpers.kt"
|
||||
private const val STATE_MACHINE_CHECKER_PATH = "$HELPERS_PATH/StateMachineChecker.kt"
|
||||
private const val TAIL_CALL_OPTIMIZATION_CHECKER_PATH = "$HELPERS_PATH/TailCallOptimizationChecker.kt"
|
||||
}
|
||||
|
||||
override val directives: List<DirectivesContainer> =
|
||||
listOf(AdditionalFilesDirectives)
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
|
||||
if (WITH_COROUTINES !in module.directives) return emptyList()
|
||||
return buildList {
|
||||
add(File(COROUTINE_HELPERS_PATH).toTestFile())
|
||||
if (CHECK_STATE_MACHINE in module.directives) {
|
||||
add(File(STATE_MACHINE_CHECKER_PATH).toTestFile())
|
||||
}
|
||||
if (CHECK_TAIL_CALL_OPTIMIZATION in module.directives) {
|
||||
add(File(TAIL_CALL_OPTIMIZATION_CHECKER_PATH).toTestFile())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.test.services
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.util.*
|
||||
|
||||
class DiagnosticsService(val testServices: TestServices) : TestService {
|
||||
private val conditionsPerModule: MutableMap<TestModule, Condition<String>> = mutableMapOf()
|
||||
private val globalDefinedDiagnostics by lazy {
|
||||
testServices.moduleStructure.allDirectives[DiagnosticsDirectives.DIAGNOSTICS]
|
||||
}
|
||||
|
||||
fun shouldRenderDiagnostic(module: TestModule, name: String): Boolean {
|
||||
val condition = conditionsPerModule.getOrPut(module) {
|
||||
computeDiagnosticConditionForModule(module)
|
||||
}
|
||||
return condition(name)
|
||||
}
|
||||
|
||||
private fun computeDiagnosticConditionForModule(module: TestModule): Condition<String> {
|
||||
val diagnosticsInDirective = module.directives[DiagnosticsDirectives.DIAGNOSTICS] + globalDefinedDiagnostics
|
||||
val enabledNames = mutableSetOf<String>()
|
||||
val disabledNames = mutableSetOf<String>()
|
||||
for (diagnosticInDirective in diagnosticsInDirective) {
|
||||
val enabled = when {
|
||||
diagnosticInDirective.startsWith("+") -> true
|
||||
diagnosticInDirective.startsWith("-") -> false
|
||||
else -> error("Incorrect diagnostics directive syntax. See reference:\n${DiagnosticsDirectives.DIAGNOSTICS.description}")
|
||||
}
|
||||
val name = diagnosticInDirective.substring(1)
|
||||
val collection = if (enabled) enabledNames else disabledNames
|
||||
collection += name
|
||||
}
|
||||
if (disabledNames.isEmpty()) return Conditions.alwaysTrue()
|
||||
var condition = !Conditions.oneOf(disabledNames)
|
||||
if (enabledNames.isNotEmpty()) {
|
||||
condition = condition or Conditions.oneOf(enabledNames)
|
||||
}
|
||||
return condition.cached()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val TestServices.diagnosticsService: DiagnosticsService by TestServices.testServiceAccessor()
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.test.services
|
||||
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure
|
||||
import org.jetbrains.kotlin.test.util.convertLineSeparators
|
||||
import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF
|
||||
import org.jetbrains.kotlin.utils.rethrow
|
||||
import org.junit.jupiter.api.function.Executable
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import org.junit.jupiter.api.Assertions as JUnit5PlatformAssertions
|
||||
|
||||
object JUnit5Assertions : Assertions() {
|
||||
override fun assertEqualsToFile(expectedFile: File, actual: String, sanitizer: (String) -> String, message: () -> String) {
|
||||
try {
|
||||
val actualText = actual.trim { it <= ' ' }.convertLineSeparators().trimTrailingWhitespacesAndAddNewlineAtEOF()
|
||||
if (!expectedFile.exists()) {
|
||||
expectedFile.writeText(actualText)
|
||||
org.junit.jupiter.api.fail("Expected data file did not exist. Generating: $expectedFile")
|
||||
}
|
||||
val expected = expectedFile.readText().convertLineSeparators()
|
||||
val expectedText = expected.trim { it <= ' ' }.trimTrailingWhitespacesAndAddNewlineAtEOF()
|
||||
if (sanitizer.invoke(expectedText) != sanitizer.invoke(actualText)) {
|
||||
throw FileComparisonFailure(
|
||||
"${message()}: ${expectedFile.name}",
|
||||
expected, actual, expectedFile.absolutePath
|
||||
)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw rethrow(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun assertEquals(expected: Any?, actual: Any?, message: (() -> String)?) {
|
||||
JUnit5PlatformAssertions.assertEquals(expected, actual, message?.invoke())
|
||||
}
|
||||
|
||||
override fun assertNotEquals(expected: Any?, actual: Any?, message: (() -> String)?) {
|
||||
JUnit5PlatformAssertions.assertNotEquals(expected, actual, message?.invoke())
|
||||
}
|
||||
|
||||
override fun assertTrue(value: Boolean, message: (() -> String)?) {
|
||||
JUnit5PlatformAssertions.assertTrue(value, message?.invoke())
|
||||
}
|
||||
|
||||
override fun assertFalse(value: Boolean, message: (() -> String)?) {
|
||||
JUnit5PlatformAssertions.assertFalse(value, message?.invoke())
|
||||
}
|
||||
|
||||
override fun assertAll(exceptions: List<AssertionError>) {
|
||||
exceptions.singleOrNull()?.let { throw it }
|
||||
JUnit5PlatformAssertions.assertAll(exceptions.map { Executable { throw it } })
|
||||
}
|
||||
|
||||
override fun fail(message: () -> String): Nothing {
|
||||
org.junit.jupiter.api.fail(message)
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.test.services.configuration
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.config.JsConfig
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.util.joinToArrayString
|
||||
|
||||
class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(JsEnvironmentConfigurationDirectives)
|
||||
|
||||
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
|
||||
val moduleKinds = module.directives[JsEnvironmentConfigurationDirectives.MODULE_KIND]
|
||||
val moduleKind = when (moduleKinds.size) {
|
||||
0 -> ModuleKind.PLAIN
|
||||
1 -> moduleKinds.single()
|
||||
else -> error("Too many module kinds passed ${moduleKinds.joinToArrayString()}")
|
||||
}
|
||||
configuration.put(JSConfigurationKeys.MODULE_KIND, moduleKind)
|
||||
|
||||
configuration.put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB)
|
||||
}
|
||||
}
|
||||
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.test.services.configuration
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.BackendKinds
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.services.jvm.CompiledJarManager
|
||||
import org.jetbrains.kotlin.test.services.jvm.compiledJarManager
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import org.jetbrains.kotlin.test.util.joinToArrayString
|
||||
import java.io.File
|
||||
|
||||
class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(JvmEnvironmentConfigurationDirectives)
|
||||
|
||||
override val additionalServices: List<ServiceRegistrationData>
|
||||
get() = listOf(service(::CompiledJarManager))
|
||||
|
||||
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
|
||||
if (module.targetPlatform !in JvmPlatforms.allJvmPlatforms) return
|
||||
val registeredDirectives = module.allRegisteredDirectives
|
||||
val targets = registeredDirectives[JvmEnvironmentConfigurationDirectives.JVM_TARGET]
|
||||
when (targets.size) {
|
||||
0 -> {}
|
||||
1 -> configuration.put(JVMConfigurationKeys.JVM_TARGET, targets.single())
|
||||
else -> error("Too many jvm targets passed: ${targets.joinToArrayString()}")
|
||||
}
|
||||
|
||||
when (extractJdkKind(registeredDirectives)) {
|
||||
TestJdkKind.MOCK_JDK -> {
|
||||
configuration.addJvmClasspathRoot(KtTestUtil.findMockJdkRtJar())
|
||||
configuration.put(JVMConfigurationKeys.NO_JDK, true)
|
||||
}
|
||||
TestJdkKind.MODIFIED_MOCK_JDK -> {
|
||||
configuration.addJvmClasspathRoot(KtTestUtil.findMockJdkRtModified())
|
||||
configuration.put(JVMConfigurationKeys.NO_JDK, true)
|
||||
}
|
||||
TestJdkKind.FULL_JDK_6 -> {
|
||||
val jdk6 = System.getenv("JDK_16") ?: error("Environment variable JDK_16 is not set")
|
||||
configuration.put(JVMConfigurationKeys.JDK_HOME, File(jdk6))
|
||||
}
|
||||
TestJdkKind.FULL_JDK_9 -> {
|
||||
configuration.put(JVMConfigurationKeys.JDK_HOME, KtTestUtil.getJdk9Home())
|
||||
}
|
||||
TestJdkKind.FULL_JDK -> {
|
||||
if (SystemInfo.IS_AT_LEAST_JAVA9) {
|
||||
configuration.put(JVMConfigurationKeys.JDK_HOME, File(System.getProperty("java.home")))
|
||||
}
|
||||
}
|
||||
TestJdkKind.ANDROID_API -> {
|
||||
configuration.addJvmClasspathRoot(KtTestUtil.findAndroidApiJar())
|
||||
configuration.put(JVMConfigurationKeys.NO_JDK, true)
|
||||
}
|
||||
}
|
||||
|
||||
val configurationKind = extractConfigurationKind(registeredDirectives)
|
||||
|
||||
if (configurationKind.withRuntime) {
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTests())
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.scriptRuntimeJarForTests())
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.kotlinTestJarForTests())
|
||||
} else if (configurationKind.withMockRuntime) {
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.minimalRuntimeJarForTests())
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.scriptRuntimeJarForTests())
|
||||
}
|
||||
if (configurationKind.withReflection) {
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.reflectJarForTests())
|
||||
}
|
||||
configuration.addJvmClasspathRoot(KtTestUtil.getAnnotationsJar())
|
||||
|
||||
if (JvmEnvironmentConfigurationDirectives.STDLIB_JDK8 in module.directives) {
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTestsWithJdk8())
|
||||
}
|
||||
|
||||
if (JvmEnvironmentConfigurationDirectives.ANDROID_ANNOTATIONS in module.directives) {
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.androidAnnotationsForTests())
|
||||
}
|
||||
|
||||
val isIr = module.backendKind == BackendKinds.IrBackend
|
||||
configuration.put(JVMConfigurationKeys.IR, isIr)
|
||||
|
||||
module.javaFiles.takeIf { it.isNotEmpty() }?.let { javaFiles ->
|
||||
javaFiles.forEach { testServices.sourceFileProvider.getRealFileForSourceFile(it) }
|
||||
configuration.addJavaSourceRoot(testServices.sourceFileProvider.javaSourceDirectory)
|
||||
}
|
||||
|
||||
configuration.registerModuleDependencies(module)
|
||||
|
||||
if (JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING in module.directives) {
|
||||
configuration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractJdkKind(registeredDirectives: RegisteredDirectives): TestJdkKind {
|
||||
val fullJdkEnabled = JvmEnvironmentConfigurationDirectives.FULL_JDK in registeredDirectives
|
||||
val jdkKinds = registeredDirectives[JvmEnvironmentConfigurationDirectives.JDK_KIND]
|
||||
|
||||
if (fullJdkEnabled) {
|
||||
if (jdkKinds.isNotEmpty()) {
|
||||
error("FULL_JDK and JDK_KIND can not be used together")
|
||||
}
|
||||
return TestJdkKind.FULL_JDK
|
||||
}
|
||||
|
||||
return when (jdkKinds.size) {
|
||||
0 -> TestJdkKind.MOCK_JDK
|
||||
1 -> jdkKinds.single()
|
||||
else -> error("Too many jdk kinds passed: ${jdkKinds.joinToArrayString()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractConfigurationKind(registeredDirectives: RegisteredDirectives): ConfigurationKind {
|
||||
val withRuntime = JvmEnvironmentConfigurationDirectives.WITH_RUNTIME in registeredDirectives || JvmEnvironmentConfigurationDirectives.WITH_STDLIB in registeredDirectives
|
||||
val withReflect = JvmEnvironmentConfigurationDirectives.WITH_REFLECT in registeredDirectives
|
||||
val noRuntime = JvmEnvironmentConfigurationDirectives.NO_RUNTIME in registeredDirectives
|
||||
if (noRuntime && withRuntime) {
|
||||
error("NO_RUNTIME and WITH_RUNTIME can not be used together")
|
||||
}
|
||||
if (withReflect && !withRuntime) {
|
||||
error("WITH_REFLECT may be used only with WITH_RUNTIME")
|
||||
}
|
||||
return when {
|
||||
withRuntime && withReflect -> ConfigurationKind.ALL
|
||||
withRuntime -> ConfigurationKind.NO_KOTLIN_REFLECT
|
||||
noRuntime -> ConfigurationKind.JDK_NO_RUNTIME
|
||||
else -> ConfigurationKind.JDK_ONLY
|
||||
}
|
||||
}
|
||||
|
||||
private fun CompilerConfiguration.registerModuleDependencies(module: TestModule) {
|
||||
val dependencyProvider = testServices.dependencyProvider
|
||||
val modulesFromDependencies = module.dependencies
|
||||
.filter { it.kind == DependencyKind.Binary }
|
||||
.map { dependencyProvider.getTestModule(it.moduleName) }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?: return
|
||||
val jarManager = testServices.compiledJarManager
|
||||
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledJarForModule(it) }
|
||||
addJvmClasspathRoots(dependenciesClassPath)
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.test.services.configuration
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.script.loadScriptingPlugin
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.isKtsFile
|
||||
|
||||
class ScriptingEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
|
||||
if (module.files.any { it.isKtsFile }) {
|
||||
loadScriptingPlugin(configuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.test.services.fir
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
|
||||
import org.jetbrains.kotlin.test.services.MetaTestConfigurator
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.utils.firTestDataFile
|
||||
import java.io.File
|
||||
|
||||
class FirOldFrontendMetaConfigurator(testServices: TestServices) : MetaTestConfigurator(testServices) {
|
||||
override fun transformTestDataPath(testDataFileName: String): String {
|
||||
val originalFile = File(testDataFileName)
|
||||
val isFirIdentical = originalFile.useLines { it.first() == "// ${FirDiagnosticsDirectives.FIR_IDENTICAL.name}" }
|
||||
return if (isFirIdentical) {
|
||||
testDataFileName
|
||||
} else {
|
||||
val firFile = originalFile.firTestDataFile
|
||||
if (!firFile.exists()) {
|
||||
originalFile.copyTo(firFile)
|
||||
}
|
||||
firFile.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* 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.test.services.impl
|
||||
|
||||
import org.jetbrains.kotlin.platform.CommonPlatforms
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.js.JsPlatforms
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.platform.konan.NativePlatforms
|
||||
import org.jetbrains.kotlin.test.builders.LanguageVersionSettingsBuilder
|
||||
import org.jetbrains.kotlin.test.directives.ModuleStructureDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.ComposedRegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.util.joinToArrayString
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import java.io.File
|
||||
|
||||
class ModuleStructureExtractorImpl(
|
||||
testServices: TestServices,
|
||||
additionalSourceProviders: List<AdditionalSourceProvider>
|
||||
) : ModuleStructureExtractor(testServices, additionalSourceProviders) {
|
||||
companion object {
|
||||
private val allowedExtensionsForFiles = listOf(".kt", ".kts", ".java")
|
||||
private val moduleDirectiveRegex = """([\w-]+)(\((.*)\))?""".toRegex()
|
||||
}
|
||||
|
||||
override fun splitTestDataByModules(
|
||||
testDataFileName: String,
|
||||
directivesContainer: DirectivesContainer,
|
||||
): TestModuleStructure {
|
||||
val testDataFile = File(testDataFileName)
|
||||
val extractor = ModuleStructureExtractorWorker(listOf(testDataFile), directivesContainer)
|
||||
return extractor.splitTestDataByModules()
|
||||
}
|
||||
|
||||
private inner class ModuleStructureExtractorWorker constructor(
|
||||
private val testDataFiles: List<File>,
|
||||
private val directivesContainer: DirectivesContainer,
|
||||
) {
|
||||
private val assertions: Assertions
|
||||
get() = testServices.assertions
|
||||
|
||||
private val defaultsProvider: DefaultsProvider
|
||||
get() = testServices.defaultsProvider
|
||||
|
||||
private lateinit var currentTestDataFile: File
|
||||
|
||||
private val defaultFileName: String
|
||||
get() = currentTestDataFile.name
|
||||
|
||||
private val defaultModuleName: String
|
||||
get() = "main"
|
||||
|
||||
private var currentModuleName: String? = null
|
||||
private var currentModuleTargetPlatform: TargetPlatform? = null
|
||||
private var currentModuleFrontendKind: FrontendKind<*>? = null
|
||||
private var currentModuleBackendKind: BackendKind<*>? = null
|
||||
private var currentModuleLanguageVersionSettingsBuilder: LanguageVersionSettingsBuilder = initLanguageSettingsBuilder()
|
||||
private var dependenciesOfCurrentModule = mutableListOf<DependencyDescription>()
|
||||
private var filesOfCurrentModule = mutableListOf<TestFile>()
|
||||
|
||||
private var currentFileName: String? = null
|
||||
private var firstFileInModule: Boolean = true
|
||||
private var linesOfCurrentFile = mutableListOf<String>()
|
||||
private var startLineNumberOfCurrentFile = 0
|
||||
|
||||
private var directivesBuilder = RegisteredDirectivesParser(directivesContainer, assertions)
|
||||
|
||||
private var globalDirectives: RegisteredDirectives? = null
|
||||
|
||||
private val modules = mutableListOf<TestModule>()
|
||||
|
||||
private val moduleDirectiveBuilder = RegisteredDirectivesParser(ModuleStructureDirectives, assertions)
|
||||
|
||||
fun splitTestDataByModules(): TestModuleStructure {
|
||||
for (testDataFile in testDataFiles) {
|
||||
currentTestDataFile = testDataFile
|
||||
val lines = testDataFile.readLines()
|
||||
lines.forEachIndexed { lineNumber, line ->
|
||||
val rawDirective = RegisteredDirectivesParser.parseDirective(line)
|
||||
if (tryParseStructureDirective(rawDirective, lineNumber + 1)) {
|
||||
linesOfCurrentFile.add(line)
|
||||
return@forEachIndexed
|
||||
}
|
||||
tryParseRegularDirective(rawDirective)
|
||||
linesOfCurrentFile.add(line)
|
||||
}
|
||||
}
|
||||
finishModule()
|
||||
val sortedModules = sortModules(modules)
|
||||
checkCycles(modules)
|
||||
return TestModuleStructureImpl(sortedModules, testDataFiles)
|
||||
}
|
||||
|
||||
private fun sortModules(modules: List<TestModule>): List<TestModule> {
|
||||
val moduleByName = modules.groupBy { it.name }.mapValues { (name, modules) ->
|
||||
modules.singleOrNull() ?: error("Duplicated modules with name $name")
|
||||
}
|
||||
return DFS.topologicalOrder(modules) { module ->
|
||||
module.dependencies.map {
|
||||
val moduleName = it.moduleName
|
||||
moduleByName[moduleName] ?: error("Module \"$moduleName\" not found while observing dependencies of \"${module.name}\"")
|
||||
}
|
||||
}.asReversed()
|
||||
}
|
||||
|
||||
private fun checkCycles(modules: List<TestModule>) {
|
||||
val visited = mutableSetOf<String>()
|
||||
for (module in modules) {
|
||||
val moduleName = module.name
|
||||
visited.add(moduleName)
|
||||
for (dependency in module.dependencies) {
|
||||
val dependencyName = dependency.moduleName
|
||||
if (dependencyName == moduleName) {
|
||||
error("Module $moduleName has dependency to itself")
|
||||
}
|
||||
if (dependencyName !in visited) {
|
||||
error("There is cycle in modules dependencies. See modules: $dependencyName, $moduleName")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* returns [true] means that passed directive was module directive and line is processed
|
||||
*/
|
||||
private fun tryParseStructureDirective(rawDirective: RegisteredDirectivesParser.RawDirective?, lineNumber: Int): Boolean {
|
||||
if (rawDirective == null) return false
|
||||
val (directive, values) = moduleDirectiveBuilder.convertToRegisteredDirective(rawDirective) ?: return false
|
||||
when (directive) {
|
||||
ModuleStructureDirectives.MODULE -> {
|
||||
/*
|
||||
* There was previous module, so we should save it
|
||||
*/
|
||||
if (currentModuleName != null) {
|
||||
finishModule()
|
||||
} else {
|
||||
finishGlobalDirectives()
|
||||
}
|
||||
val (moduleName, dependencies) = splitRawModuleStringToNameAndDependencies(values.joinToString(separator = " "))
|
||||
currentModuleName = moduleName
|
||||
dependencies.mapTo(dependenciesOfCurrentModule) { name ->
|
||||
val kind = defaultsProvider.defaultDependencyKind
|
||||
DependencyDescription(name, kind, DependencyRelation.Dependency)
|
||||
}
|
||||
}
|
||||
ModuleStructureDirectives.DEPENDENCY,
|
||||
ModuleStructureDirectives.DEPENDS_ON -> {
|
||||
val name = values.first() as String
|
||||
val kind = values.getOrNull(1)?.let { valueOfOrNull(it as String) } ?: defaultsProvider.defaultDependencyKind
|
||||
val relation = when (directive) {
|
||||
ModuleStructureDirectives.DEPENDENCY -> DependencyRelation.Dependency
|
||||
ModuleStructureDirectives.DEPENDS_ON -> DependencyRelation.DependsOn
|
||||
else -> error("Should not be here")
|
||||
}
|
||||
dependenciesOfCurrentModule.add(DependencyDescription(name, kind, relation))
|
||||
}
|
||||
ModuleStructureDirectives.TARGET_FRONTEND -> {
|
||||
val name = values.singleOrNull() as? String? ?: assertions.fail {
|
||||
"Target frontend specified incorrectly\nUsage: ${directive.description}"
|
||||
}
|
||||
currentModuleFrontendKind = FrontendKinds.fromString(name) ?: assertions.fail {
|
||||
"Unknown frontend: $name"
|
||||
}
|
||||
}
|
||||
ModuleStructureDirectives.TARGET_BACKEND_KIND -> {
|
||||
val name = values.singleOrNull() as? String ?: assertions.fail {
|
||||
"Target backend specified incorrectly\nUsage: ${directive.description}"
|
||||
}
|
||||
currentModuleBackendKind = BackendKinds.fromString(name) ?: assertions.fail {
|
||||
"Unknown backend: $name"
|
||||
}
|
||||
}
|
||||
ModuleStructureDirectives.FILE -> {
|
||||
if (currentFileName != null) {
|
||||
finishFile()
|
||||
} else {
|
||||
resetFileCaches()
|
||||
}
|
||||
currentFileName = (values.first() as String).also(::validateFileName)
|
||||
startLineNumberOfCurrentFile = lineNumber
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun splitRawModuleStringToNameAndDependencies(moduleDirectiveString: String): Pair<String, List<String>> {
|
||||
val matchResult = moduleDirectiveRegex.matchEntire(moduleDirectiveString)
|
||||
?: error("\"$moduleDirectiveString\" doesn't matches with pattern \"moduleName(dep1, dep2)\"")
|
||||
val (name, _, dependencies) = matchResult.destructured
|
||||
if (dependencies.isBlank()) return name to emptyList()
|
||||
return name to dependencies.split(" ")
|
||||
}
|
||||
|
||||
private fun finishGlobalDirectives() {
|
||||
globalDirectives = directivesBuilder.build()
|
||||
resetModuleCaches()
|
||||
resetFileCaches()
|
||||
}
|
||||
|
||||
private fun finishModule() {
|
||||
finishFile()
|
||||
val moduleDirectives = directivesBuilder.build() + testServices.defaultDirectives + globalDirectives
|
||||
currentModuleLanguageVersionSettingsBuilder.configureUsingDirectives(moduleDirectives)
|
||||
val moduleName = currentModuleName ?: defaultModuleName
|
||||
val testModule = TestModule(
|
||||
name = moduleName,
|
||||
targetPlatform = currentModuleTargetPlatform ?: parseModulePlatformByName(moduleName) ?: defaultsProvider.defaultPlatform,
|
||||
frontendKind = currentModuleFrontendKind ?: defaultsProvider.defaultFrontend,
|
||||
backendKind = currentModuleBackendKind ?: defaultsProvider.defaultBackend,
|
||||
files = filesOfCurrentModule,
|
||||
dependencies = dependenciesOfCurrentModule,
|
||||
directives = moduleDirectives,
|
||||
languageVersionSettings = currentModuleLanguageVersionSettingsBuilder.build()
|
||||
)
|
||||
modules += testModule
|
||||
additionalSourceProviders.flatMapTo(filesOfCurrentModule) { additionalSourceProvider ->
|
||||
additionalSourceProvider.produceAdditionalFiles(
|
||||
globalDirectives ?: RegisteredDirectives.Empty,
|
||||
testModule
|
||||
).also { additionalFiles ->
|
||||
require(additionalFiles.all { it.isAdditional }) {
|
||||
"Files produced by ${additionalSourceProvider::class.qualifiedName} should have flag `isAdditional = true`"
|
||||
}
|
||||
}
|
||||
}
|
||||
firstFileInModule = true
|
||||
resetModuleCaches()
|
||||
}
|
||||
|
||||
private fun parseModulePlatformByName(moduleName: String): TargetPlatform? {
|
||||
val nameSuffix = moduleName.substringAfterLast("-", "").toUpperCase()
|
||||
return when {
|
||||
nameSuffix == "COMMON" -> CommonPlatforms.defaultCommonPlatform
|
||||
nameSuffix == "JVM" -> JvmPlatforms.unspecifiedJvmPlatform // TODO(dsavvinov): determine JvmTarget precisely
|
||||
nameSuffix == "JS" -> JsPlatforms.defaultJsPlatform
|
||||
nameSuffix == "NATIVE" -> NativePlatforms.unspecifiedNativePlatform
|
||||
nameSuffix.isEmpty() -> null // TODO(dsavvinov): this leads to 'null'-platform in ModuleDescriptor
|
||||
else -> throw IllegalStateException("Can't determine platform by name $nameSuffix")
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishFile() {
|
||||
val filename = currentFileName ?: defaultFileName
|
||||
if (filesOfCurrentModule.any { it.name == filename }) {
|
||||
error("File with name \"$filename\" already defined in module ${currentModuleName ?: defaultModuleName}")
|
||||
}
|
||||
filesOfCurrentModule.add(
|
||||
TestFile(
|
||||
relativePath = filename,
|
||||
originalContent = linesOfCurrentFile.joinToString(separator = System.lineSeparator(), postfix = System.lineSeparator()),
|
||||
originalFile = currentTestDataFile,
|
||||
startLineNumberInOriginalFile = startLineNumberOfCurrentFile,
|
||||
isAdditional = false
|
||||
)
|
||||
)
|
||||
firstFileInModule = false
|
||||
resetFileCaches()
|
||||
}
|
||||
|
||||
private fun resetModuleCaches() {
|
||||
firstFileInModule = true
|
||||
currentModuleName = null
|
||||
currentModuleTargetPlatform = null
|
||||
currentModuleFrontendKind = null
|
||||
currentModuleBackendKind = null
|
||||
currentModuleLanguageVersionSettingsBuilder = initLanguageSettingsBuilder()
|
||||
filesOfCurrentModule = mutableListOf()
|
||||
dependenciesOfCurrentModule = mutableListOf()
|
||||
directivesBuilder = RegisteredDirectivesParser(directivesContainer, assertions)
|
||||
}
|
||||
|
||||
private fun resetFileCaches() {
|
||||
if (!firstFileInModule) {
|
||||
linesOfCurrentFile = mutableListOf()
|
||||
}
|
||||
currentFileName = null
|
||||
startLineNumberOfCurrentFile = 0
|
||||
}
|
||||
|
||||
private fun tryParseRegularDirective(rawDirective: RegisteredDirectivesParser.RawDirective?) {
|
||||
if (rawDirective == null) return
|
||||
val parsedDirective = directivesBuilder.convertToRegisteredDirective(rawDirective) ?: return
|
||||
directivesBuilder.addParsedDirective(parsedDirective)
|
||||
}
|
||||
|
||||
private fun validateFileName(fileName: String) {
|
||||
if (!allowedExtensionsForFiles.any { fileName.endsWith(it) }) {
|
||||
assertions.fail {
|
||||
"Filename $fileName is not valid. Allowed extensions: ${allowedExtensionsForFiles.joinToArrayString()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initLanguageSettingsBuilder(): LanguageVersionSettingsBuilder {
|
||||
return defaultsProvider.newLanguageSettingsBuilder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun RegisteredDirectives.plus(other: RegisteredDirectives?): RegisteredDirectives {
|
||||
return when {
|
||||
other == null -> this
|
||||
other.isEmpty() -> this
|
||||
this.isEmpty() -> other
|
||||
else -> ComposedRegisteredDirectives(this, other)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Enum<T>> valueOfOrNull(value: String): T? {
|
||||
for (enumValue in enumValues<T>()) {
|
||||
if (enumValue.name == value) {
|
||||
return enumValue
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.test.services.impl
|
||||
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.js.JsPlatforms
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.platform.konan.NativePlatforms
|
||||
import org.jetbrains.kotlin.test.directives.model.ComposedRegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.BinaryKind
|
||||
import org.jetbrains.kotlin.test.model.ArtifactKinds
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestModuleStructure
|
||||
import java.io.File
|
||||
|
||||
class TestModuleStructureImpl(
|
||||
override val modules: List<TestModule>,
|
||||
override val originalTestDataFiles: List<File>
|
||||
) : TestModuleStructure() {
|
||||
override val allDirectives: RegisteredDirectives = ComposedRegisteredDirectives(modules.map { it.directives })
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private val targetArtifactsByModule: Map<String, List<BinaryKind<*>>> = buildMap {
|
||||
for (module in modules) {
|
||||
val result = mutableListOf<BinaryKind<*>>()
|
||||
for (dependency in module.dependencies) {
|
||||
if (dependency.kind == DependencyKind.KLib) {
|
||||
result += ArtifactKinds.KLib
|
||||
}
|
||||
}
|
||||
module.targetPlatform.toArtifactKind()?.let { result += it }
|
||||
put(module.name, result)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTargetArtifactKinds(module: TestModule): List<BinaryKind<*>> {
|
||||
return targetArtifactsByModule[module.name] ?: emptyList()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return buildString {
|
||||
modules.forEach {
|
||||
appendLine(it)
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun TargetPlatform.toArtifactKind(): BinaryKind<*>? = when (this) {
|
||||
in JvmPlatforms.allJvmPlatforms -> ArtifactKinds.Jvm
|
||||
in JsPlatforms.allJsPlatforms -> ArtifactKinds.Js
|
||||
in NativePlatforms.allNativePlatforms -> ArtifactKinds.Native
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.test.services.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAll
|
||||
import org.jetbrains.kotlin.test.model.ArtifactKinds
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestService
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||
import org.jetbrains.kotlin.test.services.dependencyProvider
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
class CompiledJarManager(val testServices: TestServices) : TestService {
|
||||
private val jarCache = mutableMapOf<TestModule, File>()
|
||||
|
||||
fun getCompiledJarForModule(module: TestModule): File {
|
||||
return jarCache.getOrPut(module) {
|
||||
val outputDir = Files.createTempDirectory("module_${module.name}").toFile()
|
||||
val classFileFactory = testServices.dependencyProvider.getArtifact(module, ArtifactKinds.Jvm).classFileFactory
|
||||
val outputFileCollection = SimpleOutputFileCollection(classFileFactory.currentOutput)
|
||||
val messageCollector = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
outputFileCollection.writeAll(outputDir, messageCollector, reportOutputFiles = false)
|
||||
classFileFactory.releaseGeneratedOutput()
|
||||
outputDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.compiledJarManager: CompiledJarManager by TestServices.testServiceAccessor()
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.test.utils
|
||||
|
||||
import java.io.File
|
||||
|
||||
private const val FIR_KT = ".fir.kt"
|
||||
private const val KT = ".kt"
|
||||
|
||||
val File.isFirTestData: Boolean
|
||||
get() = name.endsWith(FIR_KT)
|
||||
|
||||
val File.originalTestDataFile: File
|
||||
get() = if (isFirTestData) {
|
||||
parentFile.resolve("${name.removeSuffix(FIR_KT)}$KT")
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
val File.firTestDataFile: File
|
||||
get() = if (isFirTestData) {
|
||||
this
|
||||
} else {
|
||||
parentFile.resolve("${name.removeSuffix(KT)}$FIR_KT")
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.test.utils
|
||||
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
|
||||
abstract class MultiModuleInfoDumper {
|
||||
abstract fun builderForModule(module: TestModule): StringBuilder
|
||||
abstract fun generateResultingDump(): String
|
||||
abstract fun isEmpty(): Boolean
|
||||
}
|
||||
|
||||
// TODO: consider about tests with multiple testdata files
|
||||
class MultiModuleInfoDumperImpl(private val moduleHeaderTemplate: String = "Module: %s") : MultiModuleInfoDumper() {
|
||||
private val builderByModule = LinkedHashMap<TestModule, StringBuilder>()
|
||||
|
||||
override fun builderForModule(module: TestModule): StringBuilder {
|
||||
return builderByModule.getOrPut(module, ::StringBuilder)
|
||||
}
|
||||
|
||||
override fun generateResultingDump(): String {
|
||||
builderByModule.values.singleOrNull()?.let { return it.toString() }
|
||||
return buildString {
|
||||
for ((module, builder) in builderByModule) {
|
||||
appendLine(String.format(moduleHeaderTemplate, module.name))
|
||||
append(builder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return builderByModule.isEmpty()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.test.utils
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
|
||||
class TestDisposable : Disposable {
|
||||
@Volatile
|
||||
var isDisposed = false
|
||||
private set
|
||||
|
||||
override fun dispose() {
|
||||
isDisposed = true
|
||||
}
|
||||
}
|
||||
@@ -295,7 +295,7 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
|
||||
firFiles.forEach { it.accept(CfgConsistencyChecker) }
|
||||
}
|
||||
|
||||
private object CfgConsistencyChecker : FirVisitorVoid() {
|
||||
object CfgConsistencyChecker : FirVisitorVoid() {
|
||||
override fun visitElement(element: FirElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
|
||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
||||
import org.jetbrains.kotlin.test.util.JetTestUtilsKt;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.test.util.StringUtilsKt;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.junit.Assert;
|
||||
|
||||
@@ -291,7 +291,7 @@ public class KotlinTestUtils {
|
||||
|
||||
public static void assertEqualsToFile(@NotNull String message, @NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
|
||||
try {
|
||||
String actualText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
||||
String actualText = StringUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
||||
|
||||
if (!expectedFile.exists()) {
|
||||
FileUtil.writeToFile(expectedFile, actualText);
|
||||
@@ -299,7 +299,7 @@ public class KotlinTestUtils {
|
||||
}
|
||||
String expected = FileUtil.loadFile(expectedFile, CharsetToolkit.UTF8, true);
|
||||
|
||||
String expectedText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
|
||||
String expectedText = StringUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
|
||||
|
||||
if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) {
|
||||
throw new FileComparisonFailure(message + ": " + expectedFile.getName(),
|
||||
|
||||
@@ -24,14 +24,6 @@ import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPackageDirective
|
||||
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
|
||||
|
||||
fun String.trimTrailingWhitespaces(): String =
|
||||
this.split('\n').joinToString(separator = "\n") { it.trimEnd() }
|
||||
|
||||
fun String.trimTrailingWhitespacesAndAddNewlineAtEOF(): String =
|
||||
this.trimTrailingWhitespaces().let {
|
||||
result -> if (result.endsWith("\n")) result else result + "\n"
|
||||
}
|
||||
|
||||
fun PsiFile.findElementByCommentPrefix(commentText: String): PsiElement? =
|
||||
findElementsByCommentPrefix(commentText).keys.singleOrNull()
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ abstract class JsPlatform : SimplePlatform("JS") {
|
||||
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
object JsPlatforms {
|
||||
private object DefaultSimpleJsPlatform : JsPlatform()
|
||||
object DefaultSimpleJsPlatform : JsPlatform()
|
||||
|
||||
@Deprecated(
|
||||
message = "Should be accessed only by compatibility layer, other clients should use 'defaultJsPlatform'",
|
||||
|
||||
+2
-1
@@ -327,7 +327,8 @@ include ":compiler:fir:cones",
|
||||
":compiler:fir:analysis-tests"
|
||||
|
||||
include ":compiler:test-infrastructure",
|
||||
":compiler:test-infrastructure-utils"
|
||||
":compiler:test-infrastructure-utils",
|
||||
":compiler:tests-common-new"
|
||||
|
||||
include ":idea:idea-frontend-fir:idea-fir-low-level-api"
|
||||
include ":idea:idea-fir-performance-tests"
|
||||
|
||||
Reference in New Issue
Block a user