Report incomplete hierarchy error

This is the case when you reference a Java class in Kotlin whose superclass is
not resolved. Previously this fact was swallowed by LazyJavaClassDescriptor
leading to mysterious compilation errors

 #KT-5129 Fixed
This commit is contained in:
Alexander Udalov
2015-02-09 17:28:02 +03:00
parent 28d8c8906a
commit 4128655893
12 changed files with 111 additions and 23 deletions
@@ -104,12 +104,16 @@ public final class AnalyzerWithCompilerReport {
private void reportIncompleteHierarchies() {
assert analysisResult != null;
Collection<ClassDescriptor> incompletes = analysisResult.getBindingContext().getKeys(BindingContext.INCOMPLETE_HIERARCHY);
if (!incompletes.isEmpty()) {
BindingContext bindingContext = analysisResult.getBindingContext();
Collection<ClassDescriptor> classes = bindingContext.getKeys(TraceBasedErrorReporter.INCOMPLETE_HIERARCHY);
if (!classes.isEmpty()) {
StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n");
for (ClassDescriptor incomplete : incompletes) {
String fqName = DescriptorUtils.getFqName(incomplete).asString();
message.append(" ").append(fqName).append("\n");
for (ClassDescriptor descriptor : classes) {
String fqName = DescriptorUtils.getFqName(descriptor).asString();
List<String> unresolved = bindingContext.get(TraceBasedErrorReporter.INCOMPLETE_HIERARCHY, descriptor);
assert unresolved != null && !unresolved.isEmpty() :
"Incomplete hierarchy should be reported with names of unresolved superclasses: " + fqName;
message.append(" ").append(fqName).append(", unresolved: ").append(unresolved).append("\n");
}
messageCollectorWrapper.report(CompilerMessageSeverity.ERROR, message.toString(), CompilerMessageLocation.NO_LOCATION);
}
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass;
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
import org.jetbrains.kotlin.resolve.BindingTrace;
@@ -28,11 +29,13 @@ import org.jetbrains.kotlin.util.slicedMap.Slices;
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
import javax.inject.Inject;
import java.util.List;
public class TraceBasedErrorReporter implements ErrorReporter {
private static final Logger LOG = Logger.getInstance(TraceBasedErrorReporter.class);
public static final WritableSlice<VirtualFileKotlinClass, Integer> ABI_VERSION_ERRORS = Slices.createCollectiveSlice();
public static final WritableSlice<ClassDescriptor, List<String>> INCOMPLETE_HIERARCHY = Slices.createCollectiveSlice();
private BindingTrace trace;
@@ -46,6 +49,11 @@ public class TraceBasedErrorReporter implements ErrorReporter {
trace.record(ABI_VERSION_ERRORS, (VirtualFileKotlinClass) kotlinClass, actualVersion);
}
@Override
public void reportIncompleteHierarchy(@NotNull ClassDescriptor descriptor, @NotNull List<String> unresolvedSuperClasses) {
trace.record(INCOMPLETE_HIERARCHY, descriptor, unresolvedSuperClasses);
}
@Override
public void reportCannotInferVisibility(@NotNull CallableMemberDescriptor descriptor) {
OverrideResolver.createCannotInferVisibilityReporter(trace).invoke(descriptor);
@@ -232,8 +232,6 @@ public interface BindingContext {
WritableSlice<JetFile, PackageFragmentDescriptor> FILE_TO_PACKAGE_FRAGMENT = Slices.createSimpleSlice();
WritableSlice<FqName, Collection<JetFile>> PACKAGE_TO_FILES = Slices.createSimpleSlice();
WritableSlice<ClassDescriptor, Boolean> INCOMPLETE_HIERARCHY = Slices.createCollectiveSetSlice();
@SuppressWarnings("UnusedDeclaration")
@Deprecated // This field is needed only for the side effects of its initializer
Void _static_initializer = BasicWritableSlice.initSliceDebugNames(BindingContext.class);
@@ -0,0 +1,4 @@
package test;
public class Sub extends Super {
}
@@ -0,0 +1,7 @@
package test;
public class Super {
public String foo() {
return "!";
}
}
@@ -0,0 +1,3 @@
ERROR: compiler/testData/compileKotlinAgainstCustomBinaries/incompleteHierarchyInJava/source.kt: (5, 22) Unresolved reference: foo
ERROR: The following classes have incomplete hierarchies:
test.Sub, unresolved: [Super]
@@ -0,0 +1,5 @@
import test.Sub
class SubSub : Sub()
fun bar() = SubSub().foo()
@@ -17,10 +17,11 @@
package org.jetbrains.kotlin.cli;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Charsets;
import kotlin.io.IoPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.cli.common.CLICompiler;
import org.jetbrains.kotlin.cli.common.ExitCode;
@@ -33,7 +34,10 @@ import org.jetbrains.kotlin.utils.UtilsPackage;
import org.junit.Rule;
import org.junit.rules.TestName;
import java.io.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
public class CliBaseTest {
@@ -46,7 +50,7 @@ public class CliBaseTest {
public final TestName testName = new TestName();
@NotNull
private static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLICompiler<?> compiler, @NotNull List<String> args) {
public static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLICompiler<?> compiler, @NotNull List<String> args) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream origErr = System.err;
try {
@@ -88,7 +92,7 @@ public class CliBaseTest {
@NotNull final String testDataDir,
@NotNull final String tempDir
) throws IOException {
List<String> lines = FileUtil.loadLines(new FileInputStream(argsFilePath));
List<String> lines = IoPackage.readLines(new File(argsFilePath), Charsets.UTF_8);
return ContainerUtil.mapNotNull(lines, new Function<String, String>() {
@Override
@@ -18,11 +18,15 @@ package org.jetbrains.kotlin.jvm.compiler;
import com.google.common.collect.Iterables;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.cli.CliBaseTest;
import org.jetbrains.kotlin.cli.common.ExitCode;
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport;
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorPlainTextToStream;
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles;
import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.kotlin.config.CompilerConfiguration;
@@ -181,4 +185,30 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir {
assertEquals("There should be no diagnostics", 0, Iterables.size(bindingContext.getDiagnostics()));
}
public void testIncompleteHierarchyInJava() throws Exception {
// This test compiles a Java library of two classes (Super and Sub), then deletes Super.class and attempts to compile a Kotlin
// source against this broken library. The expected result is an "incomplete hierarchy" error message from the compiler
JetTestUtils.compileJavaFiles(
Arrays.asList(
new File(getTestDataDirectory() + "/library/test/Super.java"),
new File(getTestDataDirectory() + "/library/test/Sub.java")
),
Arrays.asList("-d", tmpdir.getPath())
);
File superClassFile = new File(tmpdir + "/test/Super.class");
assert superClassFile.delete() : "Can't delete " + superClassFile;
File source = new File(getTestDataDirectory(), "source.kt");
Pair<String, ExitCode> pair = CliBaseTest.executeCompilerGrabOutput(new K2JVMCompiler(), Arrays.asList(
source.getPath(),
"-classpath", tmpdir.getPath(),
"-d", tmpdir.getPath()
));
JetTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), pair.first);
}
}