[Test] Check that FIR tree does not contain stub types and type variable types after resolution

This commit is contained in:
Dmitriy Novozhilov
2022-10-11 13:34:48 +03:00
committed by Space Team
parent 20045ac0dc
commit 097a86deb5
12 changed files with 181 additions and 89 deletions
@@ -49,7 +49,12 @@ abstract class Assertions {
return collection.joinToString("\n")
}
abstract fun assertAll(exceptions: List<Throwable>)
abstract fun failAll(exceptions: List<Throwable>)
abstract fun assertAll(conditions: List<() -> Unit>)
fun assertAll(vararg conditions: () -> Unit) {
assertAll(conditions.toList())
}
abstract fun fail(message: () -> String): Nothing
}
@@ -119,7 +119,7 @@ class TestRunner(private val testConfiguration: TestConfiguration) {
filteredFailedAssertions.firstIsInstanceOrNull<WrappedException.FromFacade>()?.let {
throw it
}
services.assertions.assertAll(filteredFailedAssertions)
services.assertions.failAll(filteredFailedAssertions)
}
/*
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.test.directives
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability.Global
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirResolvedTypesVerifier
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirScopeDumpHandler
object FirDiagnosticsDirectives : SimpleDirectivesContainer() {
@@ -65,4 +66,11 @@ object FirDiagnosticsDirectives : SimpleDirectivesContainer() {
val ENABLE_PLUGIN_PHASES by directive(
description = "Enable plugin phases"
)
val IGNORE_LEAKED_INTERNAL_TYPES by stringDirective(
description = """
Ignore failures in ${FirResolvedTypesVerifier::class}.
Directive must contain description of ignoring in argument
""".trimIndent()
)
}
@@ -1,75 +0,0 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.test.frontend.fir.handlers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.expressions.FirLoop
import org.jetbrains.kotlin.fir.expressions.FirLoopJump
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
class FirNoImplicitTypesHandler(testServices: TestServices) : FirAnalysisHandler(testServices, failureDisablesNextSteps = true) {
override fun processModule(module: TestModule, info: FirOutputArtifact) {
val visitor = Visitor()
for (firFile in info.firFiles.values) {
firFile.acceptChildren(visitor, firFile)
}
if (visitor.detectedImplicitTypesParents.isNotEmpty()) {
assertions.fail {
buildString {
val count = visitor.detectedImplicitTypesParents.size
if (count == 1) {
appendLine("One implicit type was found:")
} else {
appendLine("$count implicit types were found:")
}
val types = visitor.detectedImplicitTypesParents.joinToString(separator = "\n") {
" - Type in ${it.render()}"
}
append(types)
}
}
}
}
private inner class Visitor : FirDefaultVisitor<Unit, FirElement>() {
val detectedImplicitTypesParents = mutableSetOf<FirElement>()
val visitedLoopTargets = mutableSetOf<FirLoop>()
override fun visitElement(element: FirElement, data: FirElement) {
element.acceptChildren(this, element)
}
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: FirElement) {
super.visitResolvedTypeRef(resolvedTypeRef, data)
resolvedTypeRef.delegatedTypeRef?.let { visitElement(it, data) }
}
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef, data: FirElement) {
super.visitErrorTypeRef(errorTypeRef, data)
errorTypeRef.delegatedTypeRef?.let { visitElement(it, data) }
}
override fun visitLoopJump(loopJump: FirLoopJump, data: FirElement) {
super.visitLoopJump(loopJump, data)
if (visitedLoopTargets.add(loopJump.target.labeledElement)) {
visitElement(loopJump.target.labeledElement, data)
}
}
override fun visitImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef, data: FirElement) {
detectedImplicitTypesParents += data
}
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -0,0 +1,150 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.test.frontend.fir.handlers
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.diagnostics.ConeAmbiguousSuper
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
import org.jetbrains.kotlin.fir.expressions.FirErrorLoop
import org.jetbrains.kotlin.fir.expressions.FirLoop
import org.jetbrains.kotlin.fir.expressions.FirLoopJump
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeFunctionExpectedError
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.IGNORE_LEAKED_INTERNAL_TYPES
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
class FirResolvedTypesVerifier(testServices: TestServices) : FirAnalysisHandler(testServices, failureDisablesNextSteps = true) {
override val directiveContainers: List<DirectivesContainer>
get() = listOf(FirDiagnosticsDirectives)
override fun processModule(module: TestModule, info: FirOutputArtifact) {
val visitor = Visitor()
for (firFile in info.firFiles.values) {
firFile.acceptChildren(visitor, firFile)
}
val ignored = IGNORE_LEAKED_INTERNAL_TYPES in module.directives
try {
assertions.assertAll(
{ visitor.detectedImplicitTypesParents.check("implicit") },
{ visitor.detectedTypeVariableTypesParents.check("type variable") },
{ visitor.detectedStubTypesParents.check("stub") },
)
} catch (e: AssertionError) {
if (ignored) {
return
} else {
throw e
}
}
if (ignored) {
assertions.fail { "There is no leaked internal types in test. Please remove $IGNORE_LEAKED_INTERNAL_TYPES directive" }
}
}
private fun Collection<FirElement>.check(typeName: String) {
assertions.assertTrue(this.isEmpty()) {
buildString {
val count = size
if (count == 1) {
appendLine("One $typeName type was found:")
} else {
appendLine("$count $typeName types were found:")
}
val types = joinToString(separator = "\n") {
" - Type in ${it.render()}"
}
append(types)
}
}
}
private inner class Visitor : FirDefaultVisitor<Unit, FirElement>() {
val detectedImplicitTypesParents = mutableSetOf<FirElement>()
val detectedTypeVariableTypesParents = mutableSetOf<FirElement>()
val detectedStubTypesParents = mutableSetOf<FirElement>()
override fun visitElement(element: FirElement, data: FirElement) {
if (element is FirDiagnosticHolder) {
for (coneType in element.diagnostic.coneTypes()) {
checkElementWithConeType(element, coneType)
}
}
element.acceptChildren(this, element)
}
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: FirElement) {
visitElement(resolvedTypeRef, data)
checkElementWithConeType(resolvedTypeRef, resolvedTypeRef.type)
resolvedTypeRef.delegatedTypeRef?.let { visitElement(it, data) }
}
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef, data: FirElement) {
visitElement(errorTypeRef, data)
errorTypeRef.delegatedTypeRef?.let { visitElement(it, data) }
checkElementWithConeType(errorTypeRef, errorTypeRef.type)
}
override fun visitLoopJump(loopJump: FirLoopJump, data: FirElement) {
visitElement(loopJump, data)
if (loopJump.target.labeledElement is FirErrorLoop) {
visitElement(loopJump.target.labeledElement, data)
}
}
override fun visitImplicitTypeRef(implicitTypeRef: FirImplicitTypeRef, data: FirElement) {
detectedImplicitTypesParents += data
}
// --------------------------------------------------------------------------------------------
private fun checkElementWithConeType(element: FirElement, type: ConeKotlinType) {
when (checkConeType(type)) {
ConeTypeStatus.TypeVariableFound -> detectedTypeVariableTypesParents += element
ConeTypeStatus.StubFound -> detectedStubTypesParents += element
null -> {}
}
}
private fun checkConeType(type: ConeKotlinType): ConeTypeStatus? {
var typeVariableFound = false
var stubTypeFound = false
type.contains {
when (it) {
is ConeTypeVariableType -> typeVariableFound = true
is ConeStubType -> stubTypeFound = true
else -> {}
}
false
}
return when {
stubTypeFound -> ConeTypeStatus.StubFound
typeVariableFound -> ConeTypeStatus.TypeVariableFound
else -> null
}
}
private fun ConeDiagnostic.coneTypes(): List<ConeKotlinType> = when (this) {
is ConeAmbiguousSuper -> candidateTypes
is ConeFunctionExpectedError -> listOf(type)
else -> emptyList()
}
}
private enum class ConeTypeStatus {
TypeVariableFound, StubFound
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -95,7 +95,7 @@ abstract class AbstractFirNativeDiagnosticsTest : AbstractDiagnosticsNativeTestB
::FirDumpHandler,
::FirCfgDumpHandler,
::FirCfgConsistencyHandler,
::FirNoImplicitTypesHandler,
::FirResolvedTypesVerifier,
::FirScopeDumpHandler,
)
}
@@ -89,7 +89,7 @@ fun TestConfigurationBuilder.baseFirDiagnosticTestConfiguration(
::FirDumpHandler,
::FirCfgDumpHandler,
::FirCfgConsistencyHandler,
::FirNoImplicitTypesHandler,
::FirResolvedTypesVerifier,
::FirScopeDumpHandler,
)
}
@@ -35,7 +35,7 @@ abstract class AbstractFirForeignAnnotationsTestBase(kind: ForeignAnnotationsTes
::FirDumpHandler,
::FirCfgDumpHandler,
::FirCfgConsistencyHandler,
::FirNoImplicitTypesHandler,
::FirResolvedTypesVerifier,
)
}
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.test.frontend.fir.FirMetaInfoDiffSuppressor
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirCfgDumpHandler
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDumpHandler
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirNoImplicitTypesHandler
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirResolvedTypesVerifier
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirScopeDumpHandler
import org.jetbrains.kotlin.test.model.*
@@ -56,7 +56,7 @@ open class AbstractFirBlackBoxCodegenTest : AbstractJvmBlackBoxCodegenTestBase<F
::FirDumpHandler,
::FirScopeDumpHandler,
::FirCfgDumpHandler,
::FirNoImplicitTypesHandler,
::FirResolvedTypesVerifier,
)
}
@@ -58,11 +58,15 @@ object JUnit5Assertions : AssertionsService() {
JUnit5PlatformAssertions.assertFalse(value, message)
}
override fun assertAll(exceptions: List<Throwable>) {
override fun failAll(exceptions: List<Throwable>) {
exceptions.singleOrNull()?.let { throw it }
JUnit5PlatformAssertions.assertAll(exceptions.sortedWith(FileComparisonFailureFirst).map { Executable { throw it } })
}
override fun assertAll(conditions: List<() -> Unit>) {
JUnit5PlatformAssertions.assertAll(conditions.map { Executable { it() } })
}
override fun assertNotNull(value: Any?, message: (() -> String)?) {
JUnit5PlatformAssertions.assertNotNull(value, message)
}
@@ -40,10 +40,14 @@ object JUnit4Assertions : Assertions() {
KtUsefulTestCase.assertSameElements(message?.invoke() ?: "", expected, actual)
}
override fun assertAll(exceptions: List<Throwable>) {
override fun failAll(exceptions: List<Throwable>) {
exceptions.forEach { throw it }
}
override fun assertAll(conditions: List<() -> Unit>) {
conditions.forEach { it.invoke() }
}
override fun fail(message: () -> String): Nothing {
throw AssertionError(message.invoke())
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.js.test.ir
import com.intellij.testFramework.TestDataFile
import org.jetbrains.kotlin.js.test.AbstractJsBlackBoxCodegenTestBase
import org.jetbrains.kotlin.js.test.JsAdditionalSourceProvider
import org.jetbrains.kotlin.js.test.converters.JsIrBackendFacade
@@ -22,14 +21,12 @@ import org.jetbrains.kotlin.test.directives.*
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2IrConverter
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.frontend.classic.handlers.ClassicDiagnosticsHandler
import org.jetbrains.kotlin.test.frontend.fir.Fir2IrResultsConverter
import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.frontend.fir.handlers.*
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest
import org.jetbrains.kotlin.test.runners.codegen.commonClassicFrontendHandlersForCodegenTest
import org.jetbrains.kotlin.test.services.JsLibraryProvider
import org.jetbrains.kotlin.test.services.MetaTestConfigurator
import org.jetbrains.kotlin.test.services.TestServices
@@ -38,7 +35,6 @@ import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurato
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.utils.isDirectiveDefined
import java.io.File
import java.lang.Boolean.getBoolean
abstract class AbstractJsIrTest(
@@ -246,7 +242,7 @@ open class AbstractFirJsTest : AbstractKotlinCompilerWithTargetBackendTest(Targe
::FirDumpHandler,
::FirCfgDumpHandler,
::FirCfgConsistencyHandler,
::FirNoImplicitTypesHandler,
::FirResolvedTypesVerifier,
)
}