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:
+9
-5
@@ -104,12 +104,16 @@ public final class AnalyzerWithCompilerReport {
|
|||||||
|
|
||||||
private void reportIncompleteHierarchies() {
|
private void reportIncompleteHierarchies() {
|
||||||
assert analysisResult != null;
|
assert analysisResult != null;
|
||||||
Collection<ClassDescriptor> incompletes = analysisResult.getBindingContext().getKeys(BindingContext.INCOMPLETE_HIERARCHY);
|
BindingContext bindingContext = analysisResult.getBindingContext();
|
||||||
if (!incompletes.isEmpty()) {
|
Collection<ClassDescriptor> classes = bindingContext.getKeys(TraceBasedErrorReporter.INCOMPLETE_HIERARCHY);
|
||||||
|
if (!classes.isEmpty()) {
|
||||||
StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n");
|
StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n");
|
||||||
for (ClassDescriptor incomplete : incompletes) {
|
for (ClassDescriptor descriptor : classes) {
|
||||||
String fqName = DescriptorUtils.getFqName(incomplete).asString();
|
String fqName = DescriptorUtils.getFqName(descriptor).asString();
|
||||||
message.append(" ").append(fqName).append("\n");
|
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);
|
messageCollectorWrapper.report(CompilerMessageSeverity.ERROR, message.toString(), CompilerMessageLocation.NO_LOCATION);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
|
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.KotlinJvmBinaryClass;
|
||||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
|
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass;
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
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 org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class TraceBasedErrorReporter implements ErrorReporter {
|
public class TraceBasedErrorReporter implements ErrorReporter {
|
||||||
private static final Logger LOG = Logger.getInstance(TraceBasedErrorReporter.class);
|
private static final Logger LOG = Logger.getInstance(TraceBasedErrorReporter.class);
|
||||||
|
|
||||||
public static final WritableSlice<VirtualFileKotlinClass, Integer> ABI_VERSION_ERRORS = Slices.createCollectiveSlice();
|
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;
|
private BindingTrace trace;
|
||||||
|
|
||||||
@@ -46,6 +49,11 @@ public class TraceBasedErrorReporter implements ErrorReporter {
|
|||||||
trace.record(ABI_VERSION_ERRORS, (VirtualFileKotlinClass) kotlinClass, actualVersion);
|
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
|
@Override
|
||||||
public void reportCannotInferVisibility(@NotNull CallableMemberDescriptor descriptor) {
|
public void reportCannotInferVisibility(@NotNull CallableMemberDescriptor descriptor) {
|
||||||
OverrideResolver.createCannotInferVisibilityReporter(trace).invoke(descriptor);
|
OverrideResolver.createCannotInferVisibilityReporter(trace).invoke(descriptor);
|
||||||
|
|||||||
@@ -232,8 +232,6 @@ public interface BindingContext {
|
|||||||
WritableSlice<JetFile, PackageFragmentDescriptor> FILE_TO_PACKAGE_FRAGMENT = Slices.createSimpleSlice();
|
WritableSlice<JetFile, PackageFragmentDescriptor> FILE_TO_PACKAGE_FRAGMENT = Slices.createSimpleSlice();
|
||||||
WritableSlice<FqName, Collection<JetFile>> PACKAGE_TO_FILES = Slices.createSimpleSlice();
|
WritableSlice<FqName, Collection<JetFile>> PACKAGE_TO_FILES = Slices.createSimpleSlice();
|
||||||
|
|
||||||
WritableSlice<ClassDescriptor, Boolean> INCOMPLETE_HIERARCHY = Slices.createCollectiveSetSlice();
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
@Deprecated // This field is needed only for the side effects of its initializer
|
@Deprecated // This field is needed only for the side effects of its initializer
|
||||||
Void _static_initializer = BasicWritableSlice.initSliceDebugNames(BindingContext.class);
|
Void _static_initializer = BasicWritableSlice.initSliceDebugNames(BindingContext.class);
|
||||||
|
|||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public class Sub extends Super {
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public class Super {
|
||||||
|
public String foo() {
|
||||||
|
return "!";
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -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]
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
import test.Sub
|
||||||
|
|
||||||
|
class SubSub : Sub()
|
||||||
|
|
||||||
|
fun bar() = SubSub().foo()
|
||||||
@@ -17,10 +17,11 @@
|
|||||||
package org.jetbrains.kotlin.cli;
|
package org.jetbrains.kotlin.cli;
|
||||||
|
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
|
||||||
import com.intellij.util.ArrayUtil;
|
import com.intellij.util.ArrayUtil;
|
||||||
import com.intellij.util.Function;
|
import com.intellij.util.Function;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
|
import kotlin.Charsets;
|
||||||
|
import kotlin.io.IoPackage;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.cli.common.CLICompiler;
|
import org.jetbrains.kotlin.cli.common.CLICompiler;
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||||
@@ -33,7 +34,10 @@ import org.jetbrains.kotlin.utils.UtilsPackage;
|
|||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.rules.TestName;
|
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;
|
import java.util.List;
|
||||||
|
|
||||||
public class CliBaseTest {
|
public class CliBaseTest {
|
||||||
@@ -46,7 +50,7 @@ public class CliBaseTest {
|
|||||||
public final TestName testName = new TestName();
|
public final TestName testName = new TestName();
|
||||||
|
|
||||||
@NotNull
|
@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();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
PrintStream origErr = System.err;
|
PrintStream origErr = System.err;
|
||||||
try {
|
try {
|
||||||
@@ -88,7 +92,7 @@ public class CliBaseTest {
|
|||||||
@NotNull final String testDataDir,
|
@NotNull final String testDataDir,
|
||||||
@NotNull final String tempDir
|
@NotNull final String tempDir
|
||||||
) throws IOException {
|
) 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>() {
|
return ContainerUtil.mapNotNull(lines, new Function<String, String>() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+30
@@ -18,11 +18,15 @@ package org.jetbrains.kotlin.jvm.compiler;
|
|||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.util.Pair;
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult;
|
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.AnalyzerWithCompilerReport;
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorPlainTextToStream;
|
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.EnvironmentConfigFiles;
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment;
|
import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment;
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
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()));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -19,11 +19,16 @@ package org.jetbrains.kotlin.load.java.components;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
|
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.KotlinJvmBinaryClass;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface ErrorReporter {
|
public interface ErrorReporter {
|
||||||
void reportIncompatibleAbiVersion(@NotNull KotlinJvmBinaryClass kotlinClass, int actualVersion);
|
void reportIncompatibleAbiVersion(@NotNull KotlinJvmBinaryClass kotlinClass, int actualVersion);
|
||||||
|
|
||||||
|
void reportIncompleteHierarchy(@NotNull ClassDescriptor descriptor, @NotNull List<String> unresolvedSuperClasses);
|
||||||
|
|
||||||
void reportCannotInferVisibility(@NotNull CallableMemberDescriptor descriptor);
|
void reportCannotInferVisibility(@NotNull CallableMemberDescriptor descriptor);
|
||||||
|
|
||||||
void reportLoadingError(@NotNull String message, @Nullable Exception exception);
|
void reportLoadingError(@NotNull String message, @Nullable Exception exception);
|
||||||
|
|||||||
+27
-12
@@ -30,10 +30,13 @@ import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
|
|||||||
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
||||||
import org.jetbrains.kotlin.resolve.scopes.InnerClassesScopeWrapper
|
import org.jetbrains.kotlin.resolve.scopes.InnerClassesScopeWrapper
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.utils.*
|
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.types.AbstractClassTypeConstructor
|
import org.jetbrains.kotlin.types.AbstractClassTypeConstructor
|
||||||
|
import java.util.ArrayList
|
||||||
|
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaType
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
|
||||||
|
|
||||||
class LazyJavaClassDescriptor(
|
class LazyJavaClassDescriptor(
|
||||||
private val outerC: LazyJavaResolverContext,
|
private val outerC: LazyJavaResolverContext,
|
||||||
@@ -106,23 +109,35 @@ class LazyJavaClassDescriptor(
|
|||||||
jClass.getTypeParameters().map {
|
jClass.getTypeParameters().map {
|
||||||
p ->
|
p ->
|
||||||
c.typeParameterResolver.resolveTypeParameter(p)
|
c.typeParameterResolver.resolveTypeParameter(p)
|
||||||
?: throw AssertionError("Parameter $p surely belongs to class ${jClass}, so it must be resolved")
|
?: throw AssertionError("Parameter $p surely belongs to class $jClass, so it must be resolved")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getParameters(): List<TypeParameterDescriptor> = parameters()
|
override fun getParameters(): List<TypeParameterDescriptor> = parameters()
|
||||||
|
|
||||||
private val supertypes = c.storageManager.createLazyValue<Collection<JetType>> {
|
private val supertypes = c.storageManager.createLazyValue<Collection<JetType>> {
|
||||||
jClass.getSupertypes().stream()
|
val javaTypes = jClass.getSupertypes()
|
||||||
.map {
|
val result = ArrayList<JetType>(javaTypes.size())
|
||||||
supertype ->
|
val incomplete = ArrayList<JavaType>(0)
|
||||||
c.typeResolver.transformJavaType(supertype, TypeUsage.SUPERTYPE.toAttributes())
|
|
||||||
}
|
for (javaType in javaTypes) {
|
||||||
.filter { supertype -> !supertype.isError() && !KotlinBuiltIns.isAnyOrNullableAny(supertype) }
|
val jetType = c.typeResolver.transformJavaType(javaType, TypeUsage.SUPERTYPE.toAttributes())
|
||||||
.toList()
|
if (jetType.isError()) {
|
||||||
.ifEmpty {
|
incomplete.add(javaType)
|
||||||
listOf(KotlinBuiltIns.getInstance().getAnyType())
|
continue
|
||||||
}
|
}
|
||||||
|
if (!KotlinBuiltIns.isAnyOrNullableAny(jetType)) {
|
||||||
|
result.add(jetType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incomplete.isNotEmpty()) {
|
||||||
|
c.errorReporter.reportIncompleteHierarchy(getDeclarationDescriptor(), incomplete.map { javaType ->
|
||||||
|
(javaType as JavaClassifierType).getPresentableText()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isNotEmpty()) result.toReadOnlyList() else listOf(KotlinBuiltIns.getInstance().getAnyType())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSupertypes(): Collection<JetType> = supertypes()
|
override fun getSupertypes(): Collection<JetType> = supertypes()
|
||||||
|
|||||||
+5
@@ -20,12 +20,17 @@ import org.jetbrains.kotlin.load.java.components.ErrorReporter
|
|||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
|
||||||
class LoggingErrorReporter(private val log: Logger) : ErrorReporter {
|
class LoggingErrorReporter(private val log: Logger) : ErrorReporter {
|
||||||
override fun reportLoadingError(message: String, exception: Exception?) {
|
override fun reportLoadingError(message: String, exception: Exception?) {
|
||||||
log.error(message, exception)
|
log.error(message, exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun reportIncompleteHierarchy(descriptor: ClassDescriptor, unresolvedSuperClasses: List<String>) {
|
||||||
|
log.error("Incomplete hierarchy for $descriptor. Super classes are not found: $unresolvedSuperClasses")
|
||||||
|
}
|
||||||
|
|
||||||
override fun reportCannotInferVisibility(descriptor: CallableMemberDescriptor) {
|
override fun reportCannotInferVisibility(descriptor: CallableMemberDescriptor) {
|
||||||
log.error("Could not infer visibility for $descriptor")
|
log.error("Could not infer visibility for $descriptor")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user