[Test] Check that FIR tree does not contain stub types and type variable types after resolution
This commit is contained in:
committed by
Space Team
parent
20045ac0dc
commit
097a86deb5
@@ -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)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+8
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
-75
@@ -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) {}
|
||||
}
|
||||
+150
@@ -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) {}
|
||||
}
|
||||
+1
-1
@@ -95,7 +95,7 @@ abstract class AbstractFirNativeDiagnosticsTest : AbstractDiagnosticsNativeTestB
|
||||
::FirDumpHandler,
|
||||
::FirCfgDumpHandler,
|
||||
::FirCfgConsistencyHandler,
|
||||
::FirNoImplicitTypesHandler,
|
||||
::FirResolvedTypesVerifier,
|
||||
::FirScopeDumpHandler,
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ fun TestConfigurationBuilder.baseFirDiagnosticTestConfiguration(
|
||||
::FirDumpHandler,
|
||||
::FirCfgDumpHandler,
|
||||
::FirCfgConsistencyHandler,
|
||||
::FirNoImplicitTypesHandler,
|
||||
::FirResolvedTypesVerifier,
|
||||
::FirScopeDumpHandler,
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ abstract class AbstractFirForeignAnnotationsTestBase(kind: ForeignAnnotationsTes
|
||||
::FirDumpHandler,
|
||||
::FirCfgDumpHandler,
|
||||
::FirCfgConsistencyHandler,
|
||||
::FirNoImplicitTypesHandler,
|
||||
::FirResolvedTypesVerifier,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user