From 2b269b26522dbc6183adee8d972431c2f12d5860 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 15 Sep 2015 19:33:42 +0300 Subject: [PATCH] Improve ABI version error reporting in the compiler - only report ABI errors if there's at least one other error - append additional helpful information to the "unresolved reference" error --- .../messages/AnalyzerWithCompilerReport.java | 52 +++++++++++++++---- .../components/TraceBasedErrorReporter.kt | 3 +- compiler/testData/cli/jvm/wrongAbiVersion.kt | 6 ++- compiler/testData/cli/jvm/wrongAbiVersion.out | 12 +++-- .../cli/jvm/wrongAbiVersionNoErrors.args | 5 ++ .../cli/jvm/wrongAbiVersionNoErrors.kt | 7 +++ .../cli/jvm/wrongAbiVersionNoErrors.out | 1 + .../cli/KotlincExecutableTestGenerated.java | 6 +++ .../kotlin/cli/jvm/K2JvmCliTest.java | 5 ++ 9 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 compiler/testData/cli/jvm/wrongAbiVersionNoErrors.args create mode 100644 compiler/testData/cli/jvm/wrongAbiVersionNoErrors.kt create mode 100644 compiler/testData/cli/jvm/wrongAbiVersionNoErrors.out diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.java b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.java index 7b257bafddd..1379d341a20 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.java @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages; import org.jetbrains.kotlin.load.java.JavaBindingContext; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.components.TraceBasedErrorReporter; +import org.jetbrains.kotlin.load.java.components.TraceBasedErrorReporter.AbiVersionErrorData; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.resolve.AnalyzingUtils; import org.jetbrains.kotlin.resolve.BindingContext; @@ -42,6 +43,7 @@ import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics; import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.utils.UtilsPackage; +import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -72,7 +74,11 @@ public final class AnalyzerWithCompilerReport { messageCollector = new MessageSeverityCollector(collector); } - private static boolean reportDiagnostic(@NotNull Diagnostic diagnostic, @NotNull MessageCollector messageCollector) { + private static boolean reportDiagnostic( + @NotNull Diagnostic diagnostic, + @NotNull MessageCollector messageCollector, + boolean incompatibleFilesFound + ) { if (!diagnostic.isValid()) return false; String render; @@ -83,6 +89,12 @@ public final class AnalyzerWithCompilerReport { render = DefaultErrorMessages.render(diagnostic); } + if (incompatibleFilesFound && Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) { + render += "\n(note: this may be caused by the fact that some classes compiled with an incompatible version of Kotlin " + + "were found in the classpath. Such classes cannot be loaded properly by this version of Kotlin compiler. " + + "See below for more information)"; + } + PsiFile file = diagnostic.getPsiFile(); messageCollector.report( convertSeverity(diagnostic.getSeverity()), @@ -137,15 +149,23 @@ public final class AnalyzerWithCompilerReport { } } - private void reportAbiVersionErrors() { + @NotNull + private List getAbiVersionErrors() { assert analysisResult != null; BindingContext bindingContext = analysisResult.getBindingContext(); Collection errorClasses = bindingContext.getKeys(TraceBasedErrorReporter.ABI_VERSION_ERRORS); + List result = new ArrayList(errorClasses.size()); for (String kotlinClass : errorClasses) { - TraceBasedErrorReporter.AbiVersionErrorData data = bindingContext.get(TraceBasedErrorReporter.ABI_VERSION_ERRORS, kotlinClass); - assert data != null; - String path = toSystemDependentName(kotlinClass); + result.add(bindingContext.get(TraceBasedErrorReporter.ABI_VERSION_ERRORS, kotlinClass)); + } + + return result; + } + + private void reportAbiVersionErrors(@NotNull List errors) { + for (AbiVersionErrorData data : errors) { + String path = toSystemDependentName(data.getFilePath()); messageCollector.report( CompilerMessageSeverity.ERROR, "Class '" + JvmClassName.byClassId(data.getClassId()) + "' was compiled with an incompatible version of Kotlin. " + @@ -155,14 +175,22 @@ public final class AnalyzerWithCompilerReport { } } - public static boolean reportDiagnostics(@NotNull Diagnostics diagnostics, @NotNull MessageCollector messageCollector) { + private static boolean reportDiagnostics( + @NotNull Diagnostics diagnostics, + @NotNull MessageCollector messageCollector, + boolean incompatibleFilesFound + ) { boolean hasErrors = false; for (Diagnostic diagnostic : sortedDiagnostics(diagnostics.all())) { - hasErrors |= reportDiagnostic(diagnostic, messageCollector); + hasErrors |= reportDiagnostic(diagnostic, messageCollector, incompatibleFilesFound); } return hasErrors; } + public static boolean reportDiagnostics(@NotNull Diagnostics diagnostics, @NotNull MessageCollector collector) { + return reportDiagnostics(diagnostics, collector, false); + } + private void reportSyntaxErrors(@NotNull Collection files) { for (JetFile file : files) { reportSyntaxErrors(file, messageCollector); @@ -194,7 +222,7 @@ public final class AnalyzerWithCompilerReport { private void reportDiagnostic(E element, DiagnosticFactory0 factory, String message) { MyDiagnostic diagnostic = new MyDiagnostic(element, factory, message); - AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, messageCollector); + AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, messageCollector, false); if (element.getTextRange().getStartOffset() != file.getTextRange().getEndOffset()) { allErrorsAtEof = false; } @@ -225,10 +253,12 @@ public final class AnalyzerWithCompilerReport { public void analyzeAndReport(@NotNull Collection files, @NotNull Function0 analyzer) { analysisResult = analyzer.invoke(); - reportAbiVersionErrors(); reportSyntaxErrors(files); - //noinspection ConstantConditions - reportDiagnostics(analysisResult.getBindingContext().getDiagnostics(), messageCollector); + List abiVersionErrors = getAbiVersionErrors(); + reportDiagnostics(analysisResult.getBindingContext().getDiagnostics(), messageCollector, !abiVersionErrors.isEmpty()); + if (hasErrors()) { + reportAbiVersionErrors(abiVersionErrors); + } reportIncompleteHierarchies(); reportAlternativeSignatureErrors(); } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/TraceBasedErrorReporter.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/TraceBasedErrorReporter.kt index 15989706c20..e16e1b06fb3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/TraceBasedErrorReporter.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/TraceBasedErrorReporter.kt @@ -39,11 +39,12 @@ public class TraceBasedErrorReporter(private val trace: BindingTrace) : ErrorRep public data class AbiVersionErrorData( public val actualVersion: BinaryVersion, + public val filePath: String, public val classId: ClassId ) override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: BinaryVersion) { - trace.record(ABI_VERSION_ERRORS, filePath, AbiVersionErrorData(actualVersion, classId)) + trace.record(ABI_VERSION_ERRORS, filePath, AbiVersionErrorData(actualVersion, filePath, classId)) } override fun reportIncompleteHierarchy(descriptor: ClassDescriptor, unresolvedSuperClasses: List) { diff --git a/compiler/testData/cli/jvm/wrongAbiVersion.kt b/compiler/testData/cli/jvm/wrongAbiVersion.kt index 3ba290b1244..60a787af9f6 100644 --- a/compiler/testData/cli/jvm/wrongAbiVersion.kt +++ b/compiler/testData/cli/jvm/wrongAbiVersion.kt @@ -1,5 +1,7 @@ import wrong.* fun foo(x: ClassWithWrongAbiVersion) { - bar() -} \ No newline at end of file + bar() + + 1.set(2, 3) +} diff --git a/compiler/testData/cli/jvm/wrongAbiVersion.out b/compiler/testData/cli/jvm/wrongAbiVersion.out index 552859abfe0..d1e7f34210f 100644 --- a/compiler/testData/cli/jvm/wrongAbiVersion.out +++ b/compiler/testData/cli/jvm/wrongAbiVersion.out @@ -1,6 +1,12 @@ +compiler/testData/cli/jvm/wrongAbiVersion.kt:4:5: error: unresolved reference: bar +(note: this may be caused by the fact that some classes compiled with an incompatible version of Kotlin were found in the classpath. Such classes cannot be loaded properly by this version of Kotlin compiler. See below for more information) + bar() + ^ +compiler/testData/cli/jvm/wrongAbiVersion.kt:6:7: error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch: +public fun kotlin.MutableMap.set(key: kotlin.Int, value: kotlin.Int): kotlin.Int? defined in kotlin +(note: this may be caused by the fact that some classes compiled with an incompatible version of Kotlin were found in the classpath. Such classes cannot be loaded properly by this version of Kotlin compiler. See below for more information) + 1.set(2, 3) + ^ compiler/testData/cli/jvm/wrongAbiVersionLib/bin/ClassWithWrongAbiVersion.class: error: class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. Its ABI version is unknown, expected ABI version is $ABI_VERSION$ compiler/testData/cli/jvm/wrongAbiVersionLib/bin/wrong/WrongPackage.class: error: class 'wrong/WrongPackage' was compiled with an incompatible version of Kotlin. Its ABI version is unknown, expected ABI version is $ABI_VERSION$ -compiler/testData/cli/jvm/wrongAbiVersion.kt:4:3: error: unresolved reference: bar - bar() - ^ COMPILATION_ERROR diff --git a/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.args b/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.args new file mode 100644 index 00000000000..93dddf89c8f --- /dev/null +++ b/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.args @@ -0,0 +1,5 @@ +$TESTDATA_DIR$/wrongAbiVersionNoErrors.kt +-classpath +$TESTDATA_DIR$/wrongAbiVersionLib/bin +-d +$TEMP_DIR$ diff --git a/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.kt b/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.kt new file mode 100644 index 00000000000..8dc96a5d49b --- /dev/null +++ b/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.kt @@ -0,0 +1,7 @@ +// This should compile despite the fact that there are usages of symbols with the wrong ABI version! + +import wrong.ClassWithInnerLambda + +fun happy(): Int { + return 2 + 2 +} diff --git a/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.out b/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.out new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/cli/jvm/wrongAbiVersionNoErrors.out @@ -0,0 +1 @@ +OK diff --git a/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java index 51c5f0e4e6e..e05d7028713 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cli/KotlincExecutableTestGenerated.java @@ -223,6 +223,12 @@ public class KotlincExecutableTestGenerated extends AbstractKotlincExecutableTes doJvmTest(fileName); } + @TestMetadata("wrongAbiVersionNoErrors.args") + public void testWrongAbiVersionNoErrors() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/wrongAbiVersionNoErrors.args"); + doJvmTest(fileName); + } + @TestMetadata("wrongArgument.args") public void testWrongArgument() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/wrongArgument.args"); diff --git a/compiler/tests/org/jetbrains/kotlin/cli/jvm/K2JvmCliTest.java b/compiler/tests/org/jetbrains/kotlin/cli/jvm/K2JvmCliTest.java index 3438a627af9..462ed6af197 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/jvm/K2JvmCliTest.java +++ b/compiler/tests/org/jetbrains/kotlin/cli/jvm/K2JvmCliTest.java @@ -35,6 +35,11 @@ public class K2JvmCliTest extends CliBaseTest { executeCompilerCompareOutputJVM(); } + @Test + public void wrongAbiVersionNoErrors() throws Exception { + executeCompilerCompareOutputJVM(); + } + @Test public void nonExistingClassPathAndAnnotationsPath() throws Exception { executeCompilerCompareOutputJVM();