diff --git a/compiler/fir/resolve/testData/resolve/diagnostics/infixFunctions.diagnostics.txt b/compiler/fir/resolve/testData/resolve/diagnostics/infixFunctions.diagnostics.txt deleted file mode 100644 index b7a97bbcb4e..00000000000 --- a/compiler/fir/resolve/testData/resolve/diagnostics/infixFunctions.diagnostics.txt +++ /dev/null @@ -1,5 +0,0 @@ -: infix fun Int.foo(x: Int, y: Int) {} - -: infix fun Int.bar() {} - -: infix fun baz(x: Int, y: Int) {} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/diagnostics/infixFunctions.kt b/compiler/fir/resolve/testData/resolve/diagnostics/infixFunctions.kt index a5098ffdb42..21e7601f977 100644 --- a/compiler/fir/resolve/testData/resolve/diagnostics/infixFunctions.kt +++ b/compiler/fir/resolve/testData/resolve/diagnostics/infixFunctions.kt @@ -1,10 +1,10 @@ infix fun Int.good(x: Int) {} -infix fun Int.foo(x: Int, y: Int) {} +infix fun Int.foo(x: Int, y: Int) {} -infix fun Int.bar() {} +infix fun Int.bar() {} -infix fun baz(x: Int, y: Int) {} +infix fun baz(x: Int, y: Int) {} infix class A diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveTestCase.kt b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveTestCase.kt index b01513747f2..8e3f7eb695a 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveTestCase.kt +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveTestCase.kt @@ -25,8 +25,7 @@ abstract class AbstractFirResolveTestCase : AbstractFirResolveWithSessionTestCas return createEnvironmentWithMockJdk(configurationKind) } - private fun doCreateAndProcessFir(ktFiles: List): List { - + protected fun doCreateAndProcessFir(ktFiles: List): List { val scope = GlobalSearchScope.filesScope(project, ktFiles.mapNotNull { it.virtualFile }) .uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project)) val session = createSession(environment, scope) @@ -48,26 +47,25 @@ abstract class AbstractFirResolveTestCase : AbstractFirResolveWithSessionTestCas } } - protected fun processInputFile(path: String): List { + protected fun generateKtFiles(path: String): List { val file = File(path) val allFiles = listOf(file) + file.parentFile.listFiles { sibling -> sibling.name.removePrefix(file.nameWithoutExtension).removeSuffix(file.extension).matches("\\.[0-9]+\\.".toRegex()) } - val ktFiles = - allFiles.map { - val text = KotlinTestUtils.doLoadFile(it) - it.name to text - } - .sortedBy { (_, text) -> - KotlinTestUtils.parseDirectives(text)["analyzePriority"]?.toInt() - } - .map { (name, text) -> - KotlinTestUtils.createFile(name, text, project) - } + return allFiles.map { + val text = KotlinTestUtils.doLoadFile(it) + it.name to text + }.sortedBy { (_, text) -> + KotlinTestUtils.parseDirectives(text)["analyzePriority"]?.toInt() + }.map { (name, text) -> + KotlinTestUtils.createFile(name, text, project) + } + } - return doCreateAndProcessFir(ktFiles) + protected fun processInputFile(path: String): List { + return doCreateAndProcessFir(generateKtFiles(path)) } open fun doTest(path: String) { diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveWithDiagnosticsTestCase.kt b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveWithDiagnosticsTestCase.kt index e251f782fed..13943b9531d 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveWithDiagnosticsTestCase.kt +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/AbstractFirResolveWithDiagnosticsTestCase.kt @@ -5,40 +5,184 @@ package org.jetbrains.kotlin.fir +import com.intellij.openapi.util.TextRange +import org.jetbrains.kotlin.checkers.DiagnosedRange +import org.jetbrains.kotlin.checkers.DiagnosticDiffCallbacks +import org.jetbrains.kotlin.checkers.TestCheckerUtil +import org.jetbrains.kotlin.checkers.diagnostics.ActualDiagnostic +import org.jetbrains.kotlin.checkers.diagnostics.PositionalTextDiagnostic +import org.jetbrains.kotlin.checkers.diagnostics.TextDiagnostic +import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil +import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.resolve.diagnostics.* +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnostic +import org.jetbrains.kotlin.fir.resolve.diagnostics.collectors.AbstractDiagnosticCollector import org.jetbrains.kotlin.fir.resolve.diagnostics.collectors.ParallelDiagnosticsCollector -import org.jetbrains.kotlin.fir.resolve.diagnostics.collectors.SimpleDiagnosticsCollector import org.jetbrains.kotlin.fir.resolve.diagnostics.collectors.components.DeclarationCheckersDiagnosticComponent +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TestFiles import java.io.File abstract class AbstractFirResolveWithDiagnosticsTestCase : AbstractFirResolveTestCase() { override fun doTest(path: String) { - val firFiles = processInputFile(path) + val file = File(path) + val expectedText = KotlinTestUtils.doLoadFile(file) + val testFiles = createTestFiles(file, expectedText) + val firFiles = doCreateAndProcessFir(testFiles.mapNotNull { it.ktFile }) checkFir(path, firFiles) - checkDiagnostics(path, firFiles) + checkDiagnostics(file, testFiles, firFiles) } - private fun checkDiagnostics(path: String, firFiles: List) { + private fun createCollector(): AbstractDiagnosticCollector { // val collector = SimpleDiagnosticsCollector() val collector = ParallelDiagnosticsCollector(4) collector.initializeComponents(DeclarationCheckersDiagnosticComponent(collector)) - val diagnostics = mutableListOf() - for (file in firFiles) { - diagnostics += collector.collectDiagnostics(file) + return collector + } + + private fun checkDiagnostics(file: File, testFiles: List, firFiles: List) { + val collector = createCollector() + val actualText = StringBuilder() + for ((testFile, firFile) in testFiles zip firFiles) { + val coneDiagnostics = collector.collectDiagnostics(firFile) + testFile.getActualText(coneDiagnostics, actualText) } - val expectedPath = path.replace(".kt", ".diagnostics.txt") - val expectedFile = File(expectedPath) - if (diagnostics.isEmpty()) { - assertFalse("There is no diagnostics but expected file exists", expectedFile.exists()) - return + KotlinTestUtils.assertEqualsToFile(file, actualText.toString()) + } + + private fun createTestFiles(file: File, expectedText: String?): List { + return TestFiles.createTestFiles(file.name, expectedText, object : TestFiles.TestFileFactory { + override fun createFile(module: Nothing?, fileName: String, text: String, directives: MutableMap): TestFile { + return TestFile(fileName, text, directives) + } + + override fun createModule(name: String, dependencies: MutableList, friends: MutableList): Nothing? { + return null + } + }) + } + + + private inner class TestFile( + fileName: String, + textWithMarkers: String, + val directives: Map + ) { + private val diagnosedRanges: MutableList = mutableListOf() + private val diagnosedRangesToDiagnosticNames: MutableMap> = mutableMapOf() + val ktFile: KtFile? by lazy { + if (fileName.endsWith(".java")) { + null + } else { + TestCheckerUtil.createCheckAndReturnPsiFile(fileName, clearText, project) + } + } + val actualDiagnostics: MutableList = mutableListOf() + val clearText: String + val expectedText: String + + init { + if (fileName.endsWith(".java")) { + clearText = textWithMarkers + expectedText = clearText + } else { + expectedText = textWithMarkers + clearText = CheckerTestUtil.parseDiagnosedRanges(addExtracts(expectedText), diagnosedRanges, diagnosedRangesToDiagnosticNames) + } } - val actual = diagnostics.joinToString("\n\n") { - val text = (it.source as FirPsiSourceElement).psi.text - "<${it.diagnostic.factory.name}>: $text" + fun addExtracts(text: String): String { + // TODO + return text } - KotlinTestUtils.assertEqualsToFile(expectedFile, actual) + + fun getActualText( + coneDiagnostics: Iterable, + actualText: StringBuilder + ): Boolean { + val ktFile = this.ktFile + if (ktFile == null) { + // TODO: check java files too + actualText.append(this.clearText) + return true + } + + if (ktFile.name.endsWith("CoroutineUtil.kt") && ktFile.packageFqName == FqName("helpers")) return true + + // TODO: report JVM signature diagnostics also for implementing modules + + val ok = booleanArrayOf(true) + val diagnostics = coneDiagnostics.toActualDiagnostic() + val filteredDiagnostics = diagnostics // TODO + + actualDiagnostics.addAll(filteredDiagnostics) + + val uncheckedDiagnostics = mutableListOf() + + val diagnosticToExpectedDiagnostic = + CheckerTestUtil.diagnosticsDiff(diagnosedRanges, filteredDiagnostics, object : DiagnosticDiffCallbacks { + override fun missingDiagnostic(diagnostic: TextDiagnostic, expectedStart: Int, expectedEnd: Int) { + val message = "Missing " + diagnostic.description + PsiDiagnosticUtils.atLocation( + ktFile, + TextRange(expectedStart, expectedEnd) + ) + System.err.println(message) + ok[0] = false + } + + override fun wrongParametersDiagnostic( + expectedDiagnostic: TextDiagnostic, + actualDiagnostic: TextDiagnostic, + start: Int, + end: Int + ) { + val message = "Parameters of diagnostic not equal at position " + + PsiDiagnosticUtils.atLocation(ktFile, TextRange(start, end)) + + ". Expected: ${expectedDiagnostic.asString()}, actual: $actualDiagnostic" + System.err.println(message) + ok[0] = false + } + + override fun unexpectedDiagnostic(diagnostic: TextDiagnostic, actualStart: Int, actualEnd: Int) { + val message = "Unexpected ${diagnostic.description}${PsiDiagnosticUtils.atLocation( + ktFile, + TextRange(actualStart, actualEnd) + )}" + System.err.println(message) + ok[0] = false + } + + fun updateUncheckedDiagnostics(diagnostic: TextDiagnostic, start: Int, end: Int) { + uncheckedDiagnostics.add(PositionalTextDiagnostic(diagnostic, start, end)) + } + }) + + actualText.append( + CheckerTestUtil.addDiagnosticMarkersToText( + ktFile, + filteredDiagnostics, + diagnosticToExpectedDiagnostic, + { file -> file.text }, + uncheckedDiagnostics, + false, + false + ) + ) + + stripExtras(actualText) + + return ok[0] + } + + private fun stripExtras(text: StringBuilder): StringBuilder { + // TODO + return text + } + } + + private fun Iterable.toActualDiagnostic(): Collection { + return map { ActualDiagnostic(it.diagnostic, null, true) } } } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt index 027462ded08..9fa385959f6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/checkers/utils/CheckerTestUtil.kt @@ -148,7 +148,7 @@ object CheckerTestUtil { languageVersionSettings: LanguageVersionSettings?, dataFlowValueFactory: DataFlowValueFactory?, moduleDescriptor: ModuleDescriptorImpl?, - diagnosedRanges: MutableMap>? + diagnosedRanges: Map>? ): List { val debugAnnotations = mutableListOf()