[FIR] Add old FE-like diagnostic tests

This commit is contained in:
Dmitriy Novozhilov
2019-10-29 12:49:47 +03:00
parent 8b900624d3
commit f72fa87583
5 changed files with 178 additions and 41 deletions
@@ -1,5 +0,0 @@
<INAPPLICABLE_INFIX_MODIFIER>: infix fun Int.foo(x: Int, y: Int) {}
<INAPPLICABLE_INFIX_MODIFIER>: infix fun Int.bar() {}
<INAPPLICABLE_INFIX_MODIFIER>: infix fun baz(x: Int, y: Int) {}
@@ -1,10 +1,10 @@
infix fun Int.good(x: Int) {}
infix fun Int.foo(x: Int, y: Int) {}
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun Int.foo(x: Int, y: Int) {}<!>
infix fun Int.bar() {}
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun Int.bar() {}<!>
infix fun baz(x: Int, y: Int) {}
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun baz(x: Int, y: Int) {}<!>
infix class A
@@ -25,8 +25,7 @@ abstract class AbstractFirResolveTestCase : AbstractFirResolveWithSessionTestCas
return createEnvironmentWithMockJdk(configurationKind)
}
private fun doCreateAndProcessFir(ktFiles: List<KtFile>): List<FirFile> {
protected fun doCreateAndProcessFir(ktFiles: List<KtFile>): List<FirFile> {
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<FirFile> {
protected fun generateKtFiles(path: String): List<KtFile> {
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<FirFile> {
return doCreateAndProcessFir(generateKtFiles(path))
}
open fun doTest(path: String) {
@@ -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<FirFile>) {
private fun createCollector(): AbstractDiagnosticCollector {
// val collector = SimpleDiagnosticsCollector()
val collector = ParallelDiagnosticsCollector(4)
collector.initializeComponents(DeclarationCheckersDiagnosticComponent(collector))
val diagnostics = mutableListOf<ConeDiagnostic>()
for (file in firFiles) {
diagnostics += collector.collectDiagnostics(file)
return collector
}
private fun checkDiagnostics(file: File, testFiles: List<TestFile>, firFiles: List<FirFile>) {
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<TestFile> {
return TestFiles.createTestFiles(file.name, expectedText, object : TestFiles.TestFileFactory<Nothing?, TestFile> {
override fun createFile(module: Nothing?, fileName: String, text: String, directives: MutableMap<String, String>): TestFile {
return TestFile(fileName, text, directives)
}
override fun createModule(name: String, dependencies: MutableList<String>, friends: MutableList<String>): Nothing? {
return null
}
})
}
private inner class TestFile(
fileName: String,
textWithMarkers: String,
val directives: Map<String, String>
) {
private val diagnosedRanges: MutableList<DiagnosedRange> = mutableListOf()
private val diagnosedRangesToDiagnosticNames: MutableMap<IntRange, MutableSet<String>> = mutableMapOf()
val ktFile: KtFile? by lazy {
if (fileName.endsWith(".java")) {
null
} else {
TestCheckerUtil.createCheckAndReturnPsiFile(fileName, clearText, project)
}
}
val actualDiagnostics: MutableList<ActualDiagnostic> = 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<ConeDiagnostic>,
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<PositionalTextDiagnostic>()
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<ConeDiagnostic>.toActualDiagnostic(): Collection<ActualDiagnostic> {
return map { ActualDiagnostic(it.diagnostic, null, true) }
}
}
@@ -148,7 +148,7 @@ object CheckerTestUtil {
languageVersionSettings: LanguageVersionSettings?,
dataFlowValueFactory: DataFlowValueFactory?,
moduleDescriptor: ModuleDescriptorImpl?,
diagnosedRanges: MutableMap<IntRange, MutableSet<String>>?
diagnosedRanges: Map<IntRange, MutableSet<String>>?
): List<ActualDiagnostic> {
val debugAnnotations = mutableListOf<ActualDiagnostic>()