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
This commit is contained in:
+41
-11
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages;
|
|||||||
import org.jetbrains.kotlin.load.java.JavaBindingContext;
|
import org.jetbrains.kotlin.load.java.JavaBindingContext;
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||||
import org.jetbrains.kotlin.load.java.components.TraceBasedErrorReporter;
|
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.psi.JetFile;
|
||||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
|
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
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.resolve.jvm.JvmClassName;
|
||||||
import org.jetbrains.kotlin.utils.UtilsPackage;
|
import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -72,7 +74,11 @@ public final class AnalyzerWithCompilerReport {
|
|||||||
messageCollector = new MessageSeverityCollector(collector);
|
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;
|
if (!diagnostic.isValid()) return false;
|
||||||
|
|
||||||
String render;
|
String render;
|
||||||
@@ -83,6 +89,12 @@ public final class AnalyzerWithCompilerReport {
|
|||||||
render = DefaultErrorMessages.render(diagnostic);
|
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();
|
PsiFile file = diagnostic.getPsiFile();
|
||||||
messageCollector.report(
|
messageCollector.report(
|
||||||
convertSeverity(diagnostic.getSeverity()),
|
convertSeverity(diagnostic.getSeverity()),
|
||||||
@@ -137,15 +149,23 @@ public final class AnalyzerWithCompilerReport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportAbiVersionErrors() {
|
@NotNull
|
||||||
|
private List<AbiVersionErrorData> getAbiVersionErrors() {
|
||||||
assert analysisResult != null;
|
assert analysisResult != null;
|
||||||
BindingContext bindingContext = analysisResult.getBindingContext();
|
BindingContext bindingContext = analysisResult.getBindingContext();
|
||||||
|
|
||||||
Collection<String> errorClasses = bindingContext.getKeys(TraceBasedErrorReporter.ABI_VERSION_ERRORS);
|
Collection<String> errorClasses = bindingContext.getKeys(TraceBasedErrorReporter.ABI_VERSION_ERRORS);
|
||||||
|
List<AbiVersionErrorData> result = new ArrayList<AbiVersionErrorData>(errorClasses.size());
|
||||||
for (String kotlinClass : errorClasses) {
|
for (String kotlinClass : errorClasses) {
|
||||||
TraceBasedErrorReporter.AbiVersionErrorData data = bindingContext.get(TraceBasedErrorReporter.ABI_VERSION_ERRORS, kotlinClass);
|
result.add(bindingContext.get(TraceBasedErrorReporter.ABI_VERSION_ERRORS, kotlinClass));
|
||||||
assert data != null;
|
}
|
||||||
String path = toSystemDependentName(kotlinClass);
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reportAbiVersionErrors(@NotNull List<AbiVersionErrorData> errors) {
|
||||||
|
for (AbiVersionErrorData data : errors) {
|
||||||
|
String path = toSystemDependentName(data.getFilePath());
|
||||||
messageCollector.report(
|
messageCollector.report(
|
||||||
CompilerMessageSeverity.ERROR,
|
CompilerMessageSeverity.ERROR,
|
||||||
"Class '" + JvmClassName.byClassId(data.getClassId()) + "' was compiled with an incompatible version of Kotlin. " +
|
"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;
|
boolean hasErrors = false;
|
||||||
for (Diagnostic diagnostic : sortedDiagnostics(diagnostics.all())) {
|
for (Diagnostic diagnostic : sortedDiagnostics(diagnostics.all())) {
|
||||||
hasErrors |= reportDiagnostic(diagnostic, messageCollector);
|
hasErrors |= reportDiagnostic(diagnostic, messageCollector, incompatibleFilesFound);
|
||||||
}
|
}
|
||||||
return hasErrors;
|
return hasErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean reportDiagnostics(@NotNull Diagnostics diagnostics, @NotNull MessageCollector collector) {
|
||||||
|
return reportDiagnostics(diagnostics, collector, false);
|
||||||
|
}
|
||||||
|
|
||||||
private void reportSyntaxErrors(@NotNull Collection<JetFile> files) {
|
private void reportSyntaxErrors(@NotNull Collection<JetFile> files) {
|
||||||
for (JetFile file : files) {
|
for (JetFile file : files) {
|
||||||
reportSyntaxErrors(file, messageCollector);
|
reportSyntaxErrors(file, messageCollector);
|
||||||
@@ -194,7 +222,7 @@ public final class AnalyzerWithCompilerReport {
|
|||||||
|
|
||||||
private <E extends PsiElement> void reportDiagnostic(E element, DiagnosticFactory0<E> factory, String message) {
|
private <E extends PsiElement> void reportDiagnostic(E element, DiagnosticFactory0<E> factory, String message) {
|
||||||
MyDiagnostic<?> diagnostic = new MyDiagnostic<E>(element, factory, message);
|
MyDiagnostic<?> diagnostic = new MyDiagnostic<E>(element, factory, message);
|
||||||
AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, messageCollector);
|
AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, messageCollector, false);
|
||||||
if (element.getTextRange().getStartOffset() != file.getTextRange().getEndOffset()) {
|
if (element.getTextRange().getStartOffset() != file.getTextRange().getEndOffset()) {
|
||||||
allErrorsAtEof = false;
|
allErrorsAtEof = false;
|
||||||
}
|
}
|
||||||
@@ -225,10 +253,12 @@ public final class AnalyzerWithCompilerReport {
|
|||||||
|
|
||||||
public void analyzeAndReport(@NotNull Collection<JetFile> files, @NotNull Function0<AnalysisResult> analyzer) {
|
public void analyzeAndReport(@NotNull Collection<JetFile> files, @NotNull Function0<AnalysisResult> analyzer) {
|
||||||
analysisResult = analyzer.invoke();
|
analysisResult = analyzer.invoke();
|
||||||
reportAbiVersionErrors();
|
|
||||||
reportSyntaxErrors(files);
|
reportSyntaxErrors(files);
|
||||||
//noinspection ConstantConditions
|
List<AbiVersionErrorData> abiVersionErrors = getAbiVersionErrors();
|
||||||
reportDiagnostics(analysisResult.getBindingContext().getDiagnostics(), messageCollector);
|
reportDiagnostics(analysisResult.getBindingContext().getDiagnostics(), messageCollector, !abiVersionErrors.isEmpty());
|
||||||
|
if (hasErrors()) {
|
||||||
|
reportAbiVersionErrors(abiVersionErrors);
|
||||||
|
}
|
||||||
reportIncompleteHierarchies();
|
reportIncompleteHierarchies();
|
||||||
reportAlternativeSignatureErrors();
|
reportAlternativeSignatureErrors();
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -39,11 +39,12 @@ public class TraceBasedErrorReporter(private val trace: BindingTrace) : ErrorRep
|
|||||||
|
|
||||||
public data class AbiVersionErrorData(
|
public data class AbiVersionErrorData(
|
||||||
public val actualVersion: BinaryVersion,
|
public val actualVersion: BinaryVersion,
|
||||||
|
public val filePath: String,
|
||||||
public val classId: ClassId
|
public val classId: ClassId
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun reportIncompatibleAbiVersion(classId: ClassId, filePath: String, actualVersion: BinaryVersion) {
|
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<String>) {
|
override fun reportIncompleteHierarchy(descriptor: ClassDescriptor, unresolvedSuperClasses: List<String>) {
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,7 @@
|
|||||||
import wrong.*
|
import wrong.*
|
||||||
|
|
||||||
fun foo(x: ClassWithWrongAbiVersion) {
|
fun foo(x: ClassWithWrongAbiVersion) {
|
||||||
bar()
|
bar()
|
||||||
}
|
|
||||||
|
1.set(2, 3)
|
||||||
|
}
|
||||||
|
|||||||
+9
-3
@@ -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 <K, V> kotlin.MutableMap<kotlin.Int, kotlin.Int>.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/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/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
|
COMPILATION_ERROR
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
$TESTDATA_DIR$/wrongAbiVersionNoErrors.kt
|
||||||
|
-classpath
|
||||||
|
$TESTDATA_DIR$/wrongAbiVersionLib/bin
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -223,6 +223,12 @@ public class KotlincExecutableTestGenerated extends AbstractKotlincExecutableTes
|
|||||||
doJvmTest(fileName);
|
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")
|
@TestMetadata("wrongArgument.args")
|
||||||
public void testWrongArgument() throws Exception {
|
public void testWrongArgument() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/wrongArgument.args");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cli/jvm/wrongArgument.args");
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ public class K2JvmCliTest extends CliBaseTest {
|
|||||||
executeCompilerCompareOutputJVM();
|
executeCompilerCompareOutputJVM();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void wrongAbiVersionNoErrors() throws Exception {
|
||||||
|
executeCompilerCompareOutputJVM();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nonExistingClassPathAndAnnotationsPath() throws Exception {
|
public void nonExistingClassPathAndAnnotationsPath() throws Exception {
|
||||||
executeCompilerCompareOutputJVM();
|
executeCompilerCompareOutputJVM();
|
||||||
|
|||||||
Reference in New Issue
Block a user