diff --git a/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/test/Assertions.kt b/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/test/Assertions.kt index 8f3f4261ff5..191a568411c 100644 --- a/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/test/Assertions.kt +++ b/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/test/Assertions.kt @@ -49,7 +49,12 @@ abstract class Assertions { return collection.joinToString("\n") } - abstract fun assertAll(exceptions: List) + abstract fun failAll(exceptions: List) + abstract fun assertAll(conditions: List<() -> Unit>) + + fun assertAll(vararg conditions: () -> Unit) { + assertAll(conditions.toList()) + } abstract fun fail(message: () -> String): Nothing } diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt index 148a9a7a573..c54cd6207df 100644 --- a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/TestRunner.kt @@ -119,7 +119,7 @@ class TestRunner(private val testConfiguration: TestConfiguration) { filteredFailedAssertions.firstIsInstanceOrNull()?.let { throw it } - services.assertions.assertAll(filteredFailedAssertions) + services.assertions.failAll(filteredFailedAssertions) } /* diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt index 11dabaeada6..8af6c9eeec2 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt @@ -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() + ) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirNoImplicitTypesHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirNoImplicitTypesHandler.kt deleted file mode 100644 index 0250bc55c9b..00000000000 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirNoImplicitTypesHandler.kt +++ /dev/null @@ -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() { - val detectedImplicitTypesParents = mutableSetOf() - val visitedLoopTargets = mutableSetOf() - - 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) {} -} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirResolvedTypesVerifier.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirResolvedTypesVerifier.kt new file mode 100644 index 00000000000..abb544c4690 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirResolvedTypesVerifier.kt @@ -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 + 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.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() { + val detectedImplicitTypesParents = mutableSetOf() + val detectedTypeVariableTypesParents = mutableSetOf() + val detectedStubTypesParents = mutableSetOf() + + 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 = when (this) { + is ConeAmbiguousSuper -> candidateTypes + is ConeFunctionExpectedError -> listOf(type) + else -> emptyList() + } + } + + private enum class ConeTypeStatus { + TypeVariableFound, StubFound + } + + override fun processAfterAllModules(someAssertionWasFailed: Boolean) {} +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsNativeTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsNativeTest.kt index 87fa649d23d..07da0d4b524 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsNativeTest.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractDiagnosticsNativeTest.kt @@ -95,7 +95,7 @@ abstract class AbstractFirNativeDiagnosticsTest : AbstractDiagnosticsNativeTestB ::FirDumpHandler, ::FirCfgDumpHandler, ::FirCfgConsistencyHandler, - ::FirNoImplicitTypesHandler, + ::FirResolvedTypesVerifier, ::FirScopeDumpHandler, ) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt index c1894324b6b..14082d0bde9 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt @@ -89,7 +89,7 @@ fun TestConfigurationBuilder.baseFirDiagnosticTestConfiguration( ::FirDumpHandler, ::FirCfgDumpHandler, ::FirCfgConsistencyHandler, - ::FirNoImplicitTypesHandler, + ::FirResolvedTypesVerifier, ::FirScopeDumpHandler, ) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirForeignAnnotationsTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirForeignAnnotationsTest.kt index 590359d225a..328b22bdf74 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirForeignAnnotationsTest.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirForeignAnnotationsTest.kt @@ -35,7 +35,7 @@ abstract class AbstractFirForeignAnnotationsTestBase(kind: ForeignAnnotationsTes ::FirDumpHandler, ::FirCfgDumpHandler, ::FirCfgConsistencyHandler, - ::FirNoImplicitTypesHandler, + ::FirResolvedTypesVerifier, ) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt index 31021970236..66306454d4e 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/codegen/AbstractFirBlackBoxCodegenTest.kt @@ -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) { + override fun failAll(exceptions: List) { 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) } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/util/JUnit4Assertions.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/test/util/JUnit4Assertions.kt index ab6845daf1a..08bd361b082 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/util/JUnit4Assertions.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/util/JUnit4Assertions.kt @@ -40,10 +40,14 @@ object JUnit4Assertions : Assertions() { KtUsefulTestCase.assertSameElements(message?.invoke() ?: "", expected, actual) } - override fun assertAll(exceptions: List) { + override fun failAll(exceptions: List) { exceptions.forEach { throw it } } + override fun assertAll(conditions: List<() -> Unit>) { + conditions.forEach { it.invoke() } + } + override fun fail(message: () -> String): Nothing { throw AssertionError(message.invoke()) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt index 8579638b7b9..ddcf1951fed 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt @@ -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, ) }