!RENDER_DIAGNOSTICS_FULL_TEXT directive

Compares diagnostics rendered as text with corresponding .diag.txt file
This commit is contained in:
Dmitry Petrov
2020-02-06 12:35:47 +03:00
parent 4e6c3b6b43
commit a1326d3ef5
6 changed files with 99 additions and 28 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.cli.common.messages; package org.jetbrains.kotlin.cli.common.messages;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.utils.fileUtils.FileUtilsKt; import org.jetbrains.kotlin.utils.fileUtils.FileUtilsKt;
@@ -69,6 +70,23 @@ public interface MessageRenderer {
} }
}; };
MessageRenderer SYSTEM_INDEPENDENT_RELATIVE_PATHS = new PlainTextMessageRenderer() {
private final File cwd = new File(".").getAbsoluteFile();
@Nullable
@Override
protected String getPath(@NotNull CompilerMessageLocation location) {
return FileUtil.toSystemIndependentName(
FileUtilsKt.descendantRelativeTo(new File(location.getPath()), cwd).getPath()
);
}
@Override
public String getName() {
return "SystemIndependentRelativePath";
}
};
MessageRenderer GRADLE_STYLE = new GradleStyleMessageRenderer(); MessageRenderer GRADLE_STYLE = new GradleStyleMessageRenderer();
String renderPreamble(); String renderPreamble();
@@ -0,0 +1,6 @@
/inlineCycle.kt:8:5: error: the 'inlineFun2' invocation is a part of inline cycle
inlineFun2(p)
^
/inlineCycle.kt:13:5: error: the 'inlineFun1' invocation is a part of inline cycle
inlineFun1(p)
^
@@ -1,3 +1,5 @@
// !RENDER_DIAGNOSTICS_FULL_TEXT
inline fun inlineFun1(p: () -> Unit) { inline fun inlineFun1(p: () -> Unit) {
p() p()
<!INLINE_CALL_CYCLE, INLINE_CALL_CYCLE!>inlineFun2(p)<!> <!INLINE_CALL_CYCLE, INLINE_CALL_CYCLE!>inlineFun2(p)<!>
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.TestsCompilerError
import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.*
@@ -61,7 +62,9 @@ import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE_ALL import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE_ALL
import org.jetbrains.kotlin.utils.keysToMap import org.jetbrains.kotlin.utils.keysToMap
import org.junit.Assert import org.junit.Assert
import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.PrintStream
import java.util.* import java.util.*
import java.util.function.Predicate import java.util.function.Predicate
import java.util.regex.Pattern import java.util.regex.Pattern
@@ -169,6 +172,15 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
// main checks // main checks
var ok = true var ok = true
val diagnosticsFullTextByteArrayStream = ByteArrayOutputStream()
val diagnosticsFullTextPrintStream = PrintStream(diagnosticsFullTextByteArrayStream)
var shouldCheckDiagnosticsFullText = false
val diagnosticsFullTextCollector =
GroupingMessageCollector(
PrintingMessageCollector(diagnosticsFullTextPrintStream, MessageRenderer.SYSTEM_INDEPENDENT_RELATIVE_PATHS, true),
false
)
val actualText = StringBuilder() val actualText = StringBuilder()
for (testFile in files) { for (testFile in files) {
val module = testFile.module val module = testFile.module
@@ -183,14 +195,20 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
} }
val moduleDescriptor = modules[module]!! val moduleDescriptor = modules[module]!!
val moduleBindingContext = moduleBindings[module]!!
ok = ok and testFile.getActualText( ok = ok and testFile.getActualText(
moduleBindings[module]!!, moduleBindingContext,
implementingModulesBindings, implementingModulesBindings,
actualText, actualText,
shouldSkipJvmSignatureDiagnostics(groupedByModule) || isCommonModule, shouldSkipJvmSignatureDiagnostics(groupedByModule) || isCommonModule,
languageVersionSettingsByModule[module]!!, languageVersionSettingsByModule[module]!!,
moduleDescriptor moduleDescriptor
) )
if (testFile.renderDiagnosticsFullText) {
shouldCheckDiagnosticsFullText = true
AnalyzerWithCompilerReport.reportDiagnostics(moduleBindingContext.diagnostics, diagnosticsFullTextCollector)
}
} }
var exceptionFromDynamicCallDescriptorsValidation: Throwable? = null var exceptionFromDynamicCallDescriptorsValidation: Throwable? = null
@@ -201,6 +219,15 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
exceptionFromDynamicCallDescriptorsValidation = e exceptionFromDynamicCallDescriptorsValidation = e
} }
if (shouldCheckDiagnosticsFullText) {
diagnosticsFullTextCollector.flush()
diagnosticsFullTextPrintStream.flush()
KotlinTestUtils.assertEqualsToFile(
File(FileUtil.getNameWithoutExtension(testDataFile.absolutePath) + ".diag.txt"),
String(diagnosticsFullTextByteArrayStream.toByteArray())
)
}
KotlinTestUtils.assertEqualsToFile(getExpectedDiagnosticsFile(testDataFile), actualText.cleanupInferenceDiagnostics()) { s -> KotlinTestUtils.assertEqualsToFile(getExpectedDiagnosticsFile(testDataFile), actualText.cleanupInferenceDiagnostics()) { s ->
s.replace("COROUTINES_PACKAGE", coroutinesPackage) s.replace("COROUTINES_PACKAGE", coroutinesPackage)
} }
@@ -218,6 +245,22 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
checkOriginalAndFirTestdataIdentity(testDataFile) checkOriginalAndFirTestdataIdentity(testDataFile)
} }
private class DiagnosticsFullTextMessageCollector : MessageCollector {
override fun clear() {
TODO("Not yet implemented")
}
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
TODO("Not yet implemented")
}
override fun hasErrors(): Boolean {
TODO("Not yet implemented")
}
}
private fun checkOriginalAndFirTestdataIdentity(testDataFile: File) { private fun checkOriginalAndFirTestdataIdentity(testDataFile: File) {
val firTestDataFile = File(testDataFile.absolutePath.replace(".kt", ".fir.kt")) val firTestDataFile = File(testDataFile.absolutePath.replace(".kt", ".fir.kt"))
if (!firTestDataFile.exists()) return if (!firTestDataFile.exists()) return
@@ -157,6 +157,7 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
val withNewInferenceDirective: Boolean val withNewInferenceDirective: Boolean
val newInferenceEnabled: Boolean val newInferenceEnabled: Boolean
val renderDiagnosticMessages: Boolean val renderDiagnosticMessages: Boolean
val renderDiagnosticsFullText: Boolean
init { init {
this.whatDiagnosticsToConsider = parseDiagnosticFilterDirective(directives, declareCheckType) this.whatDiagnosticsToConsider = parseDiagnosticFilterDirective(directives, declareCheckType)
@@ -166,8 +167,8 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
this.declareFlexibleType = EXPLICIT_FLEXIBLE_TYPES_DIRECTIVE in directives this.declareFlexibleType = EXPLICIT_FLEXIBLE_TYPES_DIRECTIVE in directives
this.markDynamicCalls = MARK_DYNAMIC_CALLS_DIRECTIVE in directives this.markDynamicCalls = MARK_DYNAMIC_CALLS_DIRECTIVE in directives
this.withNewInferenceDirective = WITH_NEW_INFERENCE_DIRECTIVE in directives this.withNewInferenceDirective = WITH_NEW_INFERENCE_DIRECTIVE in directives
this.newInferenceEnabled = customLanguageVersionSettings?.supportsFeature(LanguageFeature.NewInference) ?: this.newInferenceEnabled =
shouldUseNewInferenceForTests() customLanguageVersionSettings?.supportsFeature(LanguageFeature.NewInference) ?: shouldUseNewInferenceForTests()
if (fileName.endsWith(".java")) { if (fileName.endsWith(".java")) {
// TODO: check there are no syntax errors in .java sources // TODO: check there are no syntax errors in .java sources
this.createKtFile = lazyOf(null) this.createKtFile = lazyOf(null)
@@ -175,10 +176,12 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
this.expectedText = this.clearText this.expectedText = this.clearText
} else { } else {
this.expectedText = textWithMarkers this.expectedText = textWithMarkers
this.clearText = CheckerTestUtil.parseDiagnosedRanges(addExtras(expectedText), diagnosedRanges, diagnosedRangesToDiagnosticNames) this.clearText =
CheckerTestUtil.parseDiagnosedRanges(addExtras(expectedText), diagnosedRanges, diagnosedRangesToDiagnosticNames)
this.createKtFile = lazy { TestCheckerUtil.createCheckAndReturnPsiFile(fileName, clearText, project) } this.createKtFile = lazy { TestCheckerUtil.createCheckAndReturnPsiFile(fileName, clearText, project) }
} }
this.renderDiagnosticMessages = RENDER_DIAGNOSTICS_MESSAGES in directives this.renderDiagnosticMessages = RENDER_DIAGNOSTICS_MESSAGES in directives
this.renderDiagnosticsFullText = RENDER_DIAGNOSTICS_FULL_TEXT in directives
} }
val ktFile: KtFile? by createKtFile val ktFile: KtFile? by createKtFile
@@ -210,14 +213,14 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
private fun addImports(text: String, imports: String): String { private fun addImports(text: String, imports: String): String {
var result = text var result = text
val pattern = Pattern.compile("^package [\\.\\w\\d]*\n", Pattern.MULTILINE) val pattern = Pattern.compile("^package [.\\w\\d]*\n", Pattern.MULTILINE)
val matcher = pattern.matcher(result) val matcher = pattern.matcher(result)
if (matcher.find()) { result = if (matcher.find()) {
// add imports after the package directive // add imports after the package directive
result = result.substring(0, matcher.end()) + imports + result.substring(matcher.end()) result.substring(0, matcher.end()) + imports + result.substring(matcher.end())
} else { } else {
// add imports at the beginning // add imports at the beginning
result = imports + result imports + result
} }
return result return result
} }
@@ -264,10 +267,9 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
moduleDescriptor, moduleDescriptor,
this.diagnosedRangesToDiagnosticNames this.diagnosedRangesToDiagnosticNames
) )
val filteredDiagnostics = ContainerUtil.filter( val filteredDiagnostics = ContainerUtil.filter(diagnostics + jvmSignatureDiagnostics) {
diagnostics + jvmSignatureDiagnostics, whatDiagnosticsToConsider.value(it.diagnostic)
{ whatDiagnosticsToConsider.value(it.diagnostic) } }
)
actualDiagnostics.addAll(filteredDiagnostics) actualDiagnostics.addAll(filteredDiagnostics)
@@ -320,11 +322,9 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
fun updateUncheckedDiagnostics(diagnostic: TextDiagnostic, start: Int, end: Int) { fun updateUncheckedDiagnostics(diagnostic: TextDiagnostic, start: Int, end: Int) {
diagnostic.enhanceInferenceCompatibility(invertedInferenceCompatibilityOfTest) diagnostic.enhanceInferenceCompatibility(invertedInferenceCompatibilityOfTest)
uncheckedDiagnostics.add(PositionalTextDiagnostic(diagnostic, start, end uncheckedDiagnostics.add(PositionalTextDiagnostic(diagnostic, start, end))
) }
) })
}
})
actualText.append( actualText.append(
CheckerTestUtil.addDiagnosticMarkersToText( CheckerTestUtil.addDiagnosticMarkersToText(
@@ -370,7 +370,7 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
companion object { companion object {
private const val HELPERS_PATH = "./compiler/testData/diagnostics/helpers" private const val HELPERS_PATH = "./compiler/testData/diagnostics/helpers"
val DIAGNOSTICS_DIRECTIVE = "DIAGNOSTICS" val DIAGNOSTICS_DIRECTIVE = "DIAGNOSTICS"
val DIAGNOSTICS_PATTERN: Pattern = Pattern.compile("([\\+\\-!])(\\w+)\\s*") val DIAGNOSTICS_PATTERN: Pattern = Pattern.compile("([+\\-!])(\\w+)\\s*")
val DIAGNOSTICS_TO_INCLUDE_ANYWAY: Set<DiagnosticFactory<*>> = setOf( val DIAGNOSTICS_TO_INCLUDE_ANYWAY: Set<DiagnosticFactory<*>> = setOf(
Errors.UNRESOLVED_REFERENCE, Errors.UNRESOLVED_REFERENCE,
Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER, Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER,
@@ -406,6 +406,8 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
val RENDER_DIAGNOSTICS_MESSAGES = "RENDER_DIAGNOSTICS_MESSAGES" val RENDER_DIAGNOSTICS_MESSAGES = "RENDER_DIAGNOSTICS_MESSAGES"
val RENDER_DIAGNOSTICS_FULL_TEXT = "RENDER_DIAGNOSTICS_FULL_TEXT"
val DIAGNOSTIC_IN_TESTDATA_PATTERN = Regex("<!>|<!(.*?(\\(\".*?\"\\)|\\(\\))??)+(?<!<)!>") val DIAGNOSTIC_IN_TESTDATA_PATTERN = Regex("<!>|<!(.*?(\\(\".*?\"\\)|\\(\\))??)+(?<!<)!>")
fun parseDiagnosticFilterDirective( fun parseDiagnosticFilterDirective(
@@ -435,12 +437,12 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
if (!matcher.find()) { if (!matcher.find()) {
Assert.fail( Assert.fail(
"Wrong syntax in the '// !$DIAGNOSTICS_DIRECTIVE: ...' directive:\n" + "Wrong syntax in the '// !$DIAGNOSTICS_DIRECTIVE: ...' directive:\n" +
"found: '$directives'\n" + "found: '$directives'\n" +
"Must be '([+-!]DIAGNOSTIC_FACTORY_NAME|ERROR|WARNING|INFO)+'\n" + "Must be '([+-!]DIAGNOSTIC_FACTORY_NAME|ERROR|WARNING|INFO)+'\n" +
"where '+' means 'include'\n" + "where '+' means 'include'\n" +
" '-' means 'exclude'\n" + " '-' means 'exclude'\n" +
" '!' means 'exclude everything but this'\n" + " '!' means 'exclude everything but this'\n" +
"directives are applied in the order of appearance, i.e. !FOO +BAR means include only FOO and BAR" "directives are applied in the order of appearance, i.e. !FOO +BAR means include only FOO and BAR"
) )
} }
@@ -461,7 +463,7 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
if (!first) { if (!first) {
Assert.fail( Assert.fail(
"'$operation$name' appears in a position rather than the first one, " + "'$operation$name' appears in a position rather than the first one, " +
"which effectively cancels all the previous filters in this directive" "which effectively cancels all the previous filters in this directive"
) )
} }
condition = newCondition condition = newCondition
@@ -59,7 +59,7 @@ import org.jetbrains.kotlin.types.AbstractTypeBindingTest
import org.jetbrains.kotlin.visualizer.fir.AbstractFirVisualizer import org.jetbrains.kotlin.visualizer.fir.AbstractFirVisualizer
import org.jetbrains.kotlin.visualizer.psi.AbstractPsiVisualizer import org.jetbrains.kotlin.visualizer.psi.AbstractPsiVisualizer
fun main(args: Array<String>) { fun main() {
System.setProperty("java.awt.headless", "true") System.setProperty("java.awt.headless", "true")
val excludedFirTestdataPattern = "^(.+)\\.fir\\.kts?\$" val excludedFirTestdataPattern = "^(.+)\\.fir\\.kts?\$"
@@ -127,11 +127,11 @@ fun main(args: Array<String>) {
} }
testClass<AbstractDiagnosticsTestWithOldJvmBackend> { testClass<AbstractDiagnosticsTestWithOldJvmBackend> {
model("diagnostics/testsWithJvmBackend") model("diagnostics/testsWithJvmBackend", targetBackend = TargetBackend.JVM)
} }
testClass<AbstractDiagnosticsTestWithJvmIrBackend> { testClass<AbstractDiagnosticsTestWithJvmIrBackend> {
model("diagnostics/testsWithJvmBackend") model("diagnostics/testsWithJvmBackend", targetBackend = TargetBackend.JVM_IR)
} }
testClass<AbstractMultiPlatformIntegrationTest> { testClass<AbstractMultiPlatformIntegrationTest> {