diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library1/a1.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library1/a1.kt new file mode 100644 index 00000000000..9bf2d35bded --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library1/a1.kt @@ -0,0 +1,9 @@ +package a + +class A { + inner class Inner +} + +class AA { + inner class Inner +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library1/b1.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library1/b1.kt new file mode 100644 index 00000000000..284e4c1af29 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library1/b1.kt @@ -0,0 +1,9 @@ +package b + +import a.A +import a.AA + +interface B1 { + fun produceA(): A.Inner + fun produceAA(): AA.Inner +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library2/a2.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library2/a2.kt new file mode 100644 index 00000000000..0f8359b44ef --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library2/a2.kt @@ -0,0 +1,9 @@ +package a + +class A { + inner class Inner +} + +class AA { + inner class Inner +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library2/b2.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library2/b2.kt new file mode 100644 index 00000000000..bbbf0213b89 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/library2/b2.kt @@ -0,0 +1,9 @@ +package b + +import a.A +import a.AA + +interface B2 { + fun consumeA(a: A.Inner) + fun consumeAA(a: AA.Inner) +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/output.txt new file mode 100644 index 00000000000..8d0140057b2 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/output.txt @@ -0,0 +1,7 @@ +compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/source.kt:7:17: error: type mismatch: inferred type is A.Inner but A.Inner was expected + b2.consumeA(b1.produceA()) + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/source.kt:11:18: error: type mismatch: inferred type is AA.Inner but AA.Inner was expected + b2.consumeAA(b1.produceAA()) + ^ +COMPILATION_ERROR \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/source.kt new file mode 100644 index 00000000000..e2679bfb912 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencyConflictingLibraries/source.kt @@ -0,0 +1,12 @@ +package c + +import b.B1 +import b.B2 + +fun testA(b1: B1, b2: B2) { + b2.consumeA(b1.produceA()) +} + +fun testAA(b1: B1, b2: B2) { + b2.consumeAA(b1.produceAA()) +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/library/a.kt new file mode 100644 index 00000000000..0ba6a65f47f --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/library/a.kt @@ -0,0 +1,3 @@ +package a + +interface A diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/library/b.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/library/b.kt new file mode 100644 index 00000000000..253a275fa1f --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/library/b.kt @@ -0,0 +1,7 @@ +package b + +import a.A + +interface B { + fun foo(): A +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/output.txt new file mode 100644 index 00000000000..1e6e2903286 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/output.txt @@ -0,0 +1,4 @@ +compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/source.kt:10:21: error: type mismatch: inferred type is A but String was expected + val x: String = b.foo() + ^ +COMPILATION_ERROR \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/source.kt new file mode 100644 index 00000000000..5cf11b3c769 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingDependencySimple/source.kt @@ -0,0 +1,11 @@ +package c + +import b.B + +fun bar(b: B) { + // Implicit usage of (unavailable) a.A, return value is not used. It should still be an error as in Java + b.foo() + + // Return value is used but the type is incorrect, also an error + val x: String = b.foo() +} diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/missingEnumReferencedInAnnotationArgument/missingEnumReferencedInAnnotationArgument.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/missingEnumReferencedInAnnotationArgument/missingEnumReferencedInAnnotationArgument.txt index fe09641b246..8f45601d63d 100644 --- a/compiler/testData/compileKotlinAgainstCustomBinaries/missingEnumReferencedInAnnotationArgument/missingEnumReferencedInAnnotationArgument.txt +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/missingEnumReferencedInAnnotationArgument/missingEnumReferencedInAnnotationArgument.txt @@ -1,8 +1,8 @@ package test public final annotation class Anno : kotlin.Annotation { - public constructor Anno(/*0*/ e: [ERROR : test.E]) - public final val e: [ERROR : test.E] + public constructor Anno(/*0*/ e: test.E) + public final val e: test.E } @test.Anno(e = Unresolved enum entry: test/E.ENTRY) public open class Class { diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java index d9dc8abb27b..2ae9e7c3d45 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.java @@ -290,6 +290,19 @@ public class CompileKotlinAgainstCustomBinariesTest extends TestCaseWithTmpdir { doTestBrokenKotlinLibrary("library", "test/Super.class"); } + public void testMissingDependencySimple() throws Exception { + doTestBrokenKotlinLibrary("library", "a/A.class", "a/A$Inner.class"); + } + + public void testMissingDependencyConflictingLibraries() throws Exception { + File library1 = copyJarFileWithoutEntry(compileLibrary("library1"), + "a/A.class", "a/A$Inner.class", "a/AA.class", "a/AA$Inner.class"); + File library2 = copyJarFileWithoutEntry(compileLibrary("library2"), + "a/A.class", "a/A$Inner.class", "a/AA.class", "a/AA$Inner.class"); + Pair output = compileKotlin("source.kt", tmpdir, library1, library2); + KotlinTestUtils.assertEqualsToFile(new File(getTestDataDirectory(), "output.txt"), normalizeOutput(output)); + } + /*test source mapping generation when source info is absent*/ public void testInlineFunWithoutDebugInfo() throws Exception { compileKotlin("sourceInline.kt", tmpdir); diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedType.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedType.kt index 4b69ceede5a..9f7676b7cd9 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedType.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/DeserializedType.kt @@ -88,6 +88,4 @@ class DeserializedType( } private class PossiblyInnerTypeCapabilityImpl(override val possiblyInnerType: PossiblyInnerType?) : PossiblyInnerTypeCapability - - fun getPresentableText(): String = typeDeserializer.presentableTextForErrorType(typeProto) } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/NotFoundClasses.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/NotFoundClasses.kt new file mode 100644 index 00000000000..8bbe046603c --- /dev/null +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/NotFoundClasses.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.serialization.deserialization + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorBase +import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.serialization.ProtoBuf +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.types.ClassTypeConstructorImpl +import org.jetbrains.kotlin.types.TypeConstructor +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.utils.toReadOnlyList + +class NotFoundClasses(private val storageManager: StorageManager, private val module: ModuleDescriptor) { + /** + * @param typeParametersCount list of numbers of type parameters in this class and all its outer classes, starting from this class + */ + private data class ClassRequest(val classId: ClassId, val typeParametersCount: List) + + private val packageFragments = storageManager.createMemoizedFunction { fqName -> + EmptyPackageFragmentDescriptor(module, fqName) + } + + private val classes = storageManager.createMemoizedFunction { request -> + val (classId, typeParametersCount) = request + + if (classId.isLocal) { + throw UnsupportedOperationException("Unresolved local class: $classId") + } + + val container = + if (classId.isNestedClass) get(classId.outerClassId, typeParametersCount.drop(1)) + else packageFragments(classId.packageFqName) + + // Treat a class with a nested ClassId as inner for simplicity, otherwise the outer type cannot have generic arguments + val isInner = classId.isNestedClass + + MockClassDescriptor(storageManager, container, classId.shortClassName, isInner, typeParametersCount.firstOrNull() ?: 0) + } + + class MockClassDescriptor( + storageManager: StorageManager, + container: DeclarationDescriptor, + name: Name, + private val isInner: Boolean, + numberOfDeclaredTypeParameters: Int + ) : ClassDescriptorBase(storageManager, container, name, SourceElement.NO_SOURCE) { + private val typeParameters = (1..numberOfDeclaredTypeParameters).map { index -> + TypeParameterDescriptorImpl.createWithDefaultBound( + this, Annotations.EMPTY, false, Variance.INVARIANT, Name.identifier("T$index"), index + ) + } + + private val typeConstructor = ClassTypeConstructorImpl( + this, Annotations.EMPTY, /* isFinal = */ true, typeParameters, setOf(module.builtIns.anyType) + ) + + override fun getKind() = ClassKind.CLASS + override fun getModality() = Modality.FINAL + override fun getVisibility() = Visibilities.PUBLIC + override fun getTypeConstructor() = typeConstructor + override fun getDeclaredTypeParameters() = typeParameters + override fun isInner() = isInner + + override fun isCompanionObject() = false + override fun isData() = false + override fun getAnnotations() = Annotations.EMPTY + + override fun getUnsubstitutedMemberScope() = MemberScope.Empty + override fun getStaticScope() = MemberScope.Empty + override fun getConstructors(): Collection = emptySet() + override fun getUnsubstitutedPrimaryConstructor(): ConstructorDescriptor? = null + override fun getCompanionObjectDescriptor(): ClassDescriptor? = null + + override fun toString() = "class $name (not found)" + } + + // We create different ClassDescriptor instances for types with the same ClassId but different number of type arguments. + // (This may happen when a class with the same FQ name is instantiated with different type arguments in different modules.) + // It's better than creating just one descriptor because otherwise would fail in multiple places where it's asserted that + // the number of type arguments in a type must be equal to the number of the type parameters of the class + private fun get(classId: ClassId, typeParametersCount: List): ClassDescriptor { + return classes(ClassRequest(classId, typeParametersCount)) + } + + fun get(proto: ProtoBuf.Type, nameResolver: NameResolver, typeTable: TypeTable): TypeConstructor { + val classId = nameResolver.getClassId(proto.className) + val typeParametersCount = generateSequence(proto) { it.outerType(typeTable) }.map { it.argumentCount }.toMutableList() + val classNestingLevel = generateSequence(classId) { if (it.isNestedClass) it.outerClassId else null }.count() + while (typeParametersCount.size < classNestingLevel) { + typeParametersCount.add(0) + } + return get(classId, typeParametersCount.toReadOnlyList()).typeConstructor + } +} diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt index c2b54cae945..8020edc7be1 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt @@ -71,10 +71,13 @@ class TypeDeserializer( fun typeConstructor(proto: ProtoBuf.Type): TypeConstructor = when { - proto.hasClassName() -> + proto.hasClassName() -> { classDescriptors(proto.className)?.typeConstructor + ?: c.components.notFoundClasses.get(proto, c.nameResolver, c.typeTable) + } proto.hasTypeParameter() -> typeParameterTypeConstructor(proto.typeParameter) + ?: ErrorUtils.createErrorType("Unknown type parameter ${proto.typeParameter}").constructor proto.hasTypeParameterName() -> { val container = c.containingDeclaration val typeParameters = when (container) { @@ -86,20 +89,8 @@ class TypeDeserializer( val parameter = typeParameters.find { it.name.asString() == name } parameter?.typeConstructor ?: ErrorUtils.createErrorType("Deserialized type parameter $name in $container").constructor } - else -> - null - } ?: ErrorUtils.createErrorType(presentableTextForErrorType(proto)).constructor - - internal fun presentableTextForErrorType(proto: ProtoBuf.Type): String = when { - proto.hasClassName() -> - c.nameResolver.getClassId(proto.className).asSingleFqName().asString() - proto.hasTypeParameter() -> - "Unknown type parameter ${proto.typeParameter}" - proto.hasTypeParameterName() -> - "Unknown type parameter ${c.nameResolver.getString(proto.typeParameterName)}" - else -> - "Unknown type" - } + else -> ErrorUtils.createErrorType("Unknown type").constructor + } private fun typeParameterTypeConstructor(typeParameterId: Int): TypeConstructor? = typeParameterDescriptors().get(typeParameterId)?.typeConstructor ?: diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt index 76cec4ba2af..26af1a93ffe 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/context.kt @@ -41,6 +41,7 @@ class DeserializationComponents( val additionalSupertypes: AdditionalSupertypes = AdditionalSupertypes.None ) { val classDeserializer: ClassDeserializer = ClassDeserializer(this) + val notFoundClasses: NotFoundClasses = NotFoundClasses(storageManager, moduleDescriptor) fun deserializeClass(classId: ClassId): ClassDescriptor? = classDeserializer.deserializeClass(classId) diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt index 3ebfbd23b62..e8aee48bb8c 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt @@ -25,8 +25,8 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.NonReportingOverrideStrategy -import org.jetbrains.kotlin.resolve.OverridingStrategy import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.StaticScopeForKotlinEnum @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.serialization.deserialization.* import org.jetbrains.kotlin.types.AbstractClassTypeConstructor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructor -import org.jetbrains.kotlin.types.upperIfFlexible import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.singletonOrEmptyList import org.jetbrains.kotlin.utils.toReadOnlyList @@ -154,24 +153,19 @@ class DeserializedClassDescriptor( } override fun computeSupertypes(): Collection { - val result = ArrayList(classProto.supertypeCount) - val unresolved = ArrayList(0) + val result = classProto.supertypes(c.typeTable).map { supertypeProto -> + c.typeDeserializer.type(supertypeProto) + } + c.components.additionalSupertypes.forClass(this@DeserializedClassDescriptor) - for (supertypeProto in classProto.supertypes(c.typeTable)) { - val supertype = c.typeDeserializer.type(supertypeProto) - if (supertype.isError) { - unresolved.add(supertype.upperIfFlexible() as? DeserializedType ?: error("Not a deserialized type: $supertype")) - } - else { - result.add(supertype) - } + val unresolved = result.mapNotNull { supertype -> + supertype.constructor.declarationDescriptor as? NotFoundClasses.MockClassDescriptor } - result.addAll(c.components.additionalSupertypes.forClass(this@DeserializedClassDescriptor)) - if (unresolved.isNotEmpty()) { c.components.errorReporter.reportIncompleteHierarchy( - this@DeserializedClassDescriptor, unresolved.map(DeserializedType::getPresentableText)) + this@DeserializedClassDescriptor, + unresolved.map { it.classId.asSingleFqName().asString() } + ) } return result.toReadOnlyList()