diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index a250c145bc4..a95847df0bd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -37,7 +37,6 @@ import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier; -import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker; import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible; import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData; import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo; @@ -568,7 +567,7 @@ public interface Errors { DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory2>>>> HEADER_CLASS_MEMBERS_ARE_NOT_IMPLEMENTED = + List>>>> HEADER_CLASS_MEMBERS_ARE_NOT_IMPLEMENTED = DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 IMPL_MISSING = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt index 94e3baeefc4..5ca74664895 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/PlatformIncompatibilityDiagnosticRenderer.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.diagnostics.rendering -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible @@ -44,9 +43,9 @@ class PlatformIncompatibilityDiagnosticRenderer( class IncompatibleHeaderImplClassScopesRenderer( private val mode: MultiplatformDiagnosticRenderingMode -) : DiagnosticParameterRenderer>>>> { +) : DiagnosticParameterRenderer>>>> { override fun render( - obj: List>>>, + obj: List>>>, renderingContext: RenderingContext ): String { if (obj.isEmpty()) return "" @@ -110,7 +109,7 @@ private fun StringBuilder.renderIncompatibilityInformation( } private fun StringBuilder.renderIncompatibleClassScopes( - unimplemented: List>>>, + unimplemented: List>>>, indent: String, context: RenderingContext, mode: MultiplatformDiagnosticRenderingMode diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index 343f33e8e5d..35d7848f4e5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension; import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; +import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker; import org.jetbrains.kotlin.resolve.checkers.PublishedApiUsageChecker; @@ -35,8 +36,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_CLASS_NOT_ALLOWED; -import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_OBJECT_NOT_ALLOWED; +import static org.jetbrains.kotlin.diagnostics.Errors.*; import static org.jetbrains.kotlin.lexer.KtTokens.*; public class ModifiersChecker { @@ -160,6 +160,7 @@ public class ModifiersChecker { checkObjectInsideInnerClass(modifierListOwner, descriptor); checkTypeParametersModifiers(modifierListOwner); checkModifierListCommon(modifierListOwner, descriptor); + checkIllegalHeader(modifierListOwner, descriptor); } private void checkObjectInsideInnerClass(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) { @@ -195,6 +196,16 @@ public class ModifiersChecker { } } + private void checkIllegalHeader(@NotNull KtModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { + // Most cases are already handled by ModifierCheckerCore, only check nested classes here + KtModifierList modifierList = modifierListOwner.getModifierList(); + PsiElement keyword = modifierList != null ? modifierList.getModifier(HEADER_KEYWORD) : null; + if (keyword != null && + descriptor instanceof ClassDescriptor && descriptor.getContainingDeclaration() instanceof ClassDescriptor) { + trace.report(WRONG_MODIFIER_TARGET.on(keyword, KtTokens.HEADER_KEYWORD, "nested class")); + } + } + private void checkNestedClassAllowed(@NotNull KtModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) { if (modifierListOwner.hasModifier(INNER_KEYWORD)) return; if (modifierListOwner instanceof KtClass && !(modifierListOwner instanceof KtEnumEntry)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt index ce8893cae81..17edee1f752 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt @@ -35,7 +35,6 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Compatible import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible import org.jetbrains.kotlin.resolve.descriptorUtil.classId -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope @@ -64,9 +63,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker { val checkImpl = !languageVersionSettings.getFlag(AnalysisFlag.multiPlatformDoNotCheckImpl) if (descriptor.isHeader) { - if (declaration.hasModifier(KtTokens.HEADER_KEYWORD)) { - checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticHolder, descriptor.module, checkImpl) - } + checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticHolder, descriptor.module, checkImpl) } else { checkImplementationHasHeaderDeclaration(declaration, descriptor, diagnosticHolder, checkImpl) @@ -81,7 +78,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker { checkImpl: Boolean ) { // Only look for implementations of top level members; class members will be handled as a part of that header class - if (descriptor is CallableMemberDescriptor && descriptor.containingDeclaration !is PackageFragmentDescriptor) return + if (descriptor.containingDeclaration !is PackageFragmentDescriptor) return val compatibility = findImplForHeader(descriptor, platformModule, checkImpl) ?: return @@ -180,12 +177,11 @@ object HeaderImplDeclarationChecker : DeclarationChecker { // This should ideally be handled by CallableMemberDescriptor.Kind, but default constructors have kind DECLARATION and non-empty source. // Their source is the containing KtClass instance though, as opposed to explicit constructors, whose source is KtConstructor - private fun CallableMemberDescriptor.isExplicitImplDeclaration(): Boolean = - if (this is ConstructorDescriptor) { - DescriptorToSourceUtils.getSourceFromDescriptor(this) is KtConstructor<*> - } - else { - kind == CallableMemberDescriptor.Kind.DECLARATION + private fun MemberDescriptor.isExplicitImplDeclaration(): Boolean = + when (this) { + is ConstructorDescriptor -> DescriptorToSourceUtils.getSourceFromDescriptor(this) is KtConstructor<*> + is CallableMemberDescriptor -> kind == CallableMemberDescriptor.Kind.DECLARATION + else -> true } private fun findHeaderForImpl(impl: MemberDescriptor, commonModule: ModuleDescriptor): Map>? { @@ -196,7 +192,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker { is ClassDescriptor -> { // TODO: replace with 'singleOrNull' as soon as multi-module diagnostic tests are refactored val headerClass = findHeaderForImpl(container, commonModule)?.values?.firstOrNull()?.firstOrNull() as? ClassDescriptor - headerClass?.getMembers(impl.name).orEmpty() + headerClass?.getMembers(impl.name)?.filterIsInstance().orEmpty() } is PackageFragmentDescriptor -> impl.findNamesakesFromModule(commonModule) else -> return null // do not report anything for incorrect code, e.g. 'impl' local function @@ -317,7 +313,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker { object Supertypes : Incompatible("some supertypes are missing in the implementation") class ClassScopes( - val unimplemented: List>>> + val unimplemented: List>>> ) : Incompatible("some members are not implemented") object EnumEntries : Incompatible("some entries from header enum are missing in the impl enum") @@ -472,7 +468,8 @@ object HeaderImplDeclarationChecker : DeclarationChecker { } private fun areCompatibleClassifiers(a: ClassDescriptor, other: ClassifierDescriptor, checkImpl: Boolean): Compatibility { - assert(a.fqNameUnsafe == other.fqNameUnsafe) { "This function should be invoked only for declarations with the same name: $a, $other" } + // Can't check FQ names here because nested header class may be implemented via impl typealias's expansion with the other FQ name + assert(a.name == other.name) { "This function should be invoked only for declarations with the same name: $a, $other" } var implTypealias = false val b = when (other) { @@ -515,6 +512,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker { return Compatible } + private fun areCompatibleClassScopes( a: ClassDescriptor, b: ClassDescriptor, @@ -522,19 +520,30 @@ object HeaderImplDeclarationChecker : DeclarationChecker { platformModule: ModuleDescriptor, substitutor: Substitutor ): Compatibility { - val unimplemented = arrayListOf>>>() + val unimplemented = arrayListOf>>>() val bMembersByName = b.getMembers().groupBy { it.name } outer@ for (aMember in a.getMembers()) { - if (!aMember.kind.isReal) continue + if (aMember is CallableMemberDescriptor && !aMember.kind.isReal) continue - val mapping = bMembersByName[aMember.name].orEmpty().keysToMap { bMember -> - areCompatibleCallables(aMember, bMember, checkImpl, platformModule, substitutor) + val bMembers = bMembersByName[aMember.name]?.filter { bMember -> + aMember is CallableMemberDescriptor && bMember is CallableMemberDescriptor || + aMember is ClassDescriptor && bMember is ClassDescriptor + }.orEmpty() + + val mapping = bMembers.keysToMap { bMember -> + when (aMember) { + is CallableMemberDescriptor -> + areCompatibleCallables(aMember, bMember as CallableMemberDescriptor, checkImpl, platformModule, substitutor) + is ClassDescriptor -> + areCompatibleClassifiers(aMember, bMember as ClassDescriptor, checkImpl) + else -> throw UnsupportedOperationException("Unsupported declaration: $aMember ($bMembers)") + } } if (mapping.values.any { it == Compatible }) continue - val incompatibilityMap = mutableMapOf>() + val incompatibilityMap = mutableMapOf>() for ((descriptor, compatibility) in mapping) { when (compatibility) { Compatible -> continue@outer @@ -561,10 +570,13 @@ object HeaderImplDeclarationChecker : DeclarationChecker { return Incompatible.ClassScopes(unimplemented) } - private fun ClassDescriptor.getMembers(name: Name? = null): Collection { + private fun ClassDescriptor.getMembers(name: Name? = null): Collection { val nameFilter = if (name != null) { it -> it == name } else MemberScope.ALL_NAME_FILTER - return defaultType.memberScope.getDescriptorsFiltered(nameFilter = nameFilter).filterIsInstance() + - constructors.filter { nameFilter(it.name) } + return defaultType.memberScope + .getDescriptorsFiltered(nameFilter = nameFilter) + .filterIsInstance() + .filterNot(DescriptorUtils::isEnumEntry) + .plus(constructors.filter { nameFilter(it.name) }) } private inline fun equalBy(first: T, second: T, selector: (T) -> K): Boolean = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java index 0d1dbd46b48..9ca67bc9e4a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassDescriptor.java @@ -165,9 +165,11 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes this.isInner = modifierList != null && modifierList.hasModifier(INNER_KEYWORD) && !isIllegalInner(this); this.isData = modifierList != null && modifierList.hasModifier(KtTokens.DATA_KEYWORD); - this.isHeader = modifierList != null && modifierList.hasModifier(KtTokens.HEADER_KEYWORD); this.isImpl = modifierList != null && modifierList.hasModifier(KtTokens.IMPL_KEYWORD); + this.isHeader = modifierList != null && modifierList.hasModifier(KtTokens.HEADER_KEYWORD) || + containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).isHeader(); + // Annotation entries are taken from both own annotations (if any) and object literal annotations (if any) List annotationEntries = new ArrayList<>(); if (classOrObject != null && classOrObject.getParent() instanceof KtObjectLiteralExpression) { diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/additionalEntriesInImpl.txt b/compiler/testData/diagnostics/tests/multiplatform/enum/additionalEntriesInImpl.txt index 314fa0790c5..0a9600148d1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/additionalEntriesInImpl.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/additionalEntriesInImpl.txt @@ -2,11 +2,11 @@ package public final header enum class Bar : kotlin.Enum { - enum entry X + header enum entry X - enum entry Y + header enum entry Y - enum entry Z + header enum entry Z public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int @@ -22,9 +22,9 @@ public final header enum class Bar : kotlin.Enum { } public final header enum class Foo : kotlin.Enum { - enum entry A + header enum entry A - enum entry B + header enum entry B public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.txt b/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.txt index f10856c7b5c..541b95b4a41 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.txt @@ -1,9 +1,9 @@ package public final header enum class En : kotlin.Enum { - enum entry E1 + header enum entry E1 - enum entry E2 + header enum entry E2 private constructor En(/*0*/ x: kotlin.Int) private constructor En(/*0*/ s: kotlin.String) @@ -21,7 +21,7 @@ public final header enum class En : kotlin.Enum { } public final header enum class En2 : kotlin.Enum { - enum entry E1 + header enum entry E1 public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/differentEntryOrder.txt b/compiler/testData/diagnostics/tests/multiplatform/enum/differentEntryOrder.txt index 7793ae89178..fa3c9899ff4 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/differentEntryOrder.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/differentEntryOrder.txt @@ -2,11 +2,11 @@ package public final header enum class Bar : kotlin.Enum { - enum entry X + header enum entry X - enum entry Y + header enum entry Y - enum entry Z + header enum entry Z public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int @@ -22,9 +22,9 @@ public final header enum class Bar : kotlin.Enum { } public final header enum class Foo : kotlin.Enum { - enum entry A + header enum entry A - enum entry B + header enum entry B public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.kt index 646c15f6daa..15752d70a8a 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.kt @@ -5,7 +5,7 @@ header enum class En { E1, E2 { - fun foo() = "" + fun foo() = "" }, E3 { }; } diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.txt b/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.txt index 347f516f7e4..5152048b9b1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/enumEntryWithBody.txt @@ -1,11 +1,11 @@ package public final header enum class En : kotlin.Enum { - enum entry E1 + header enum entry E1 - enum entry E2 + header enum entry E2 - enum entry E3 + header enum entry E3 public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.txt b/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.txt index bbe83822682..0bbcaccdaf5 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/simpleEnum.txt @@ -2,11 +2,11 @@ package public final header enum class Foo : kotlin.Enum { - enum entry ENTRY1 + header enum entry ENTRY1 - enum entry ENTRY2 + header enum entry ENTRY2 - enum entry ENTRY3 + header enum entry ENTRY3 public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/classKinds.txt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/classKinds.txt index 3f6873cca72..be85f31bea5 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/classKinds.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/classKinds.txt @@ -17,7 +17,7 @@ public final header class Class { } public final header enum class En : kotlin.Enum { - enum entry ENTRY + header enum entry ENTRY public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt index 1e247b58d61..2c4dd10c7da 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt @@ -3,23 +3,29 @@ // FILE: common.kt header class OuterClass { - header class NestedClass { - header class DeepNested { - header class Another + class NestedClass { + class DeepNested { + class Another { + fun f(s: String) + val p: Int + } } } - header inner class InnerClass + inner class InnerClass { + fun f(x: Int) + val p: String + } - header companion object + companion object } header class OuterClassWithNamedCompanion { - header companion object Factory + companion object Factory } header object OuterObject { - header object NestedObject + object NestedObject } // MODULE: m2-jvm(m1-common) @@ -28,11 +34,17 @@ header object OuterObject { impl class OuterClass { impl class NestedClass { impl class DeepNested { - impl class Another + impl class Another { + impl fun f(s: String) {} + impl val p: Int = 42 + } } } - impl inner class InnerClass + impl inner class InnerClass { + impl fun f(x: Int) {} + impl val p: String = "" + } impl companion object } diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt index d4507fbf8f9..a83195df542 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt @@ -15,7 +15,9 @@ public final header class OuterClass { public final header inner class InnerClass { public constructor InnerClass() + public header final val p: kotlin.String public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final header fun f(/*0*/ x: kotlin.Int): kotlin.Unit public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @@ -34,7 +36,9 @@ public final header class OuterClass { public final header class Another { public constructor Another() + public header final val p: kotlin.Int public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final header fun f(/*0*/ s: kotlin.String): kotlin.Unit public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @@ -86,7 +90,9 @@ public final impl class OuterClass { public final impl inner class InnerClass { public constructor InnerClass() + public impl final val p: kotlin.String = "" public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final impl fun f(/*0*/ x: kotlin.Int): kotlin.Unit public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } @@ -105,7 +111,9 @@ public final impl class OuterClass { public final impl class Another { public constructor Another() + public impl final val p: kotlin.Int = 42 public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final impl fun f(/*0*/ s: kotlin.String): kotlin.Unit public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.kt new file mode 100644 index 00000000000..b0381ebabcd --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.kt @@ -0,0 +1,38 @@ +// !LANGUAGE: +MultiPlatformProjects +// MODULE: m1-common +// FILE: common.kt + +header class B { + class N { + fun body() {} + header fun extraHeader() + } +} + +header class C { + header class N + header enum class E + header inner class I +} + +header class D { + class N +} + +// MODULE: m1-jvm(m1-common) +// FILE: jvm.kt + +impl class B { + impl class N { + impl fun body() {} + impl fun extraHeader() {} + } +} + +impl class C { + impl class N + impl enum class E + impl inner class I +} + +impl class D diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.txt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.txt new file mode 100644 index 00000000000..42afe52215b --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.txt @@ -0,0 +1,132 @@ +// -- Module: -- +package + +public final header class B { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final header class N { + public constructor N() + public final header fun body(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final header fun extraHeader(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final header class C { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final header enum class E : kotlin.Enum { + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: C.E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): C.E + public final /*synthesized*/ fun values(): kotlin.Array + } + + public final header inner class I { + public constructor I() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final header class N { + public constructor N() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final header class D { + public constructor D() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final header class N { + public constructor N() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + + +// -- Module: -- +package + +public final impl class B { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final impl class N { + public constructor N() + public final impl fun body(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final impl fun extraHeader(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final impl class C { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final impl enum class E : kotlin.Enum { + private constructor E() + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: C.E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit + public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class! + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): C.E + public final /*synthesized*/ fun values(): kotlin.Array + } + + public final impl inner class I { + public constructor I() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final impl class N { + public constructor N() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final impl class D { + public constructor D() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/modifierApplicability.kt b/compiler/testData/diagnostics/tests/multiplatform/modifierApplicability.kt index 1ccb53f3f8b..4eaef1e6e8d 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/modifierApplicability.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/modifierApplicability.kt @@ -5,7 +5,7 @@ header typealias Foo = String class Outer header constructor() { - header class Nested + header class Nested header init {} diff --git a/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/common.kt b/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/common.kt index 6ab70aa0599..b2aff6acf18 100644 --- a/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/common.kt +++ b/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/common.kt @@ -1,3 +1,3 @@ header class ByTypeAlias { - header interface Nested + interface Nested } diff --git a/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/output.txt b/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/output.txt index 630f7bdebf8..b08950d503d 100644 --- a/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/output.txt +++ b/compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/output.txt @@ -3,8 +3,5 @@ Exit code: OK Output: -- JVM -- -Exit code: COMPILATION_ERROR +Exit code: OK Output: -compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/common.kt:2:22: error: 'header' interface 'Nested' has no implementation in module - header interface Nested - ^ diff --git a/compiler/testData/multiplatform/incompatibleNestedClasses/common.kt b/compiler/testData/multiplatform/incompatibleNestedClasses/common.kt index 0e2231f7e08..aaa50f224f4 100644 --- a/compiler/testData/multiplatform/incompatibleNestedClasses/common.kt +++ b/compiler/testData/multiplatform/incompatibleNestedClasses/common.kt @@ -1,19 +1,19 @@ header class O1 { - header class N1 - header interface N2 - header object N3 + class N1 + interface N2 + object N3 } header class O2 { - header class N2 - header inner class I2 + class N2 + inner class I2 } header class O3 { - header object Companion - header companion object Factory + object Companion + companion object Factory } header class O4 { - header companion object + companion object } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 8a89959ecad..ae351734eb6 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -13782,6 +13782,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("nestedClassesWithErrors.kt") + public void testNestedClassesWithErrors() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClassesWithErrors.kt"); + doTest(fileName); + } + @TestMetadata("noImplKeywordOnMember.kt") public void testNoImplKeywordOnMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/noImplKeywordOnMember.kt"); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt index 433e2fd56db..2c1891f7633 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/PlatformHeaderAnnotator.kt @@ -29,11 +29,12 @@ import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor import org.jetbrains.kotlin.idea.core.toDescriptor import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker -import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.diagnostics.SimpleDiagnostics val ModuleDescriptor.sourceKind: SourceKind @@ -55,7 +56,7 @@ val ModuleDescriptor.allImplementingCompatibleModules class PlatformHeaderAnnotator : Annotator { override fun annotate(element: PsiElement, holder: AnnotationHolder) { val declaration = element as? KtDeclaration ?: return - if (!declaration.hasModifier(KtTokens.HEADER_KEYWORD)) return + if (!isHeaderDeclaration(declaration)) return if (TargetPlatformDetector.getPlatform(declaration.containingKtFile) !is TargetPlatform.Default) return @@ -78,4 +79,9 @@ class PlatformHeaderAnnotator : Annotator { KotlinPsiChecker().annotateElement(declaration, holder, SimpleDiagnostics(filteredList)) } + + private fun isHeaderDeclaration(declaration: KtDeclaration): Boolean { + return declaration.hasModifier(KtTokens.HEADER_KEYWORD) || + declaration is KtClassOrObject && KtPsiUtil.getOutermostClassOrObject(declaration)?.hasModifier(KtTokens.HEADER_KEYWORD) == true + } } diff --git a/idea/testData/multiModuleHighlighting/multiplatform/nestedClassWithoutImpl/common/common.kt b/idea/testData/multiModuleHighlighting/multiplatform/nestedClassWithoutImpl/common/common.kt new file mode 100644 index 00000000000..595768499b6 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/multiplatform/nestedClassWithoutImpl/common/common.kt @@ -0,0 +1,15 @@ +package a + +header class A { + class Nested +} + +header class B { + class Nested { + fun foo(s: String) + } +} + +header class C { + header inner class Inner +} diff --git a/idea/testData/multiModuleHighlighting/multiplatform/nestedClassWithoutImpl/jvm/jvm.kt b/idea/testData/multiModuleHighlighting/multiplatform/nestedClassWithoutImpl/jvm/jvm.kt new file mode 100644 index 00000000000..092dc9c8c56 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/multiplatform/nestedClassWithoutImpl/jvm/jvm.kt @@ -0,0 +1,11 @@ +package a + +impl class A + +impl class B { + impl class Nested +} + +impl class C { + impl inner class Inner +} diff --git a/idea/testData/multiModuleQuickFix/enum/header/header.kt b/idea/testData/multiModuleQuickFix/enum/header/header.kt index bcbd2c7d99e..14895c4061b 100644 --- a/idea/testData/multiModuleQuickFix/enum/header/header.kt +++ b/idea/testData/multiModuleQuickFix/enum/header/header.kt @@ -6,12 +6,4 @@ header enum class MyEnum { LAST; val num: Int - - companion object { - fun byNum(num: Int): MyEnum = when (num) { - 1 -> FIRST - 2 -> SECOND - else -> LAST - } - } -} \ No newline at end of file +} diff --git a/idea/testData/multiModuleQuickFix/enum/header/header.kt.after b/idea/testData/multiModuleQuickFix/enum/header/header.kt.after index 95e3a9d3b2a..1807a1d27f1 100644 --- a/idea/testData/multiModuleQuickFix/enum/header/header.kt.after +++ b/idea/testData/multiModuleQuickFix/enum/header/header.kt.after @@ -6,12 +6,4 @@ header enum class MyEnum { LAST; val num: Int - - companion object { - fun byNum(num: Int): MyEnum = when (num) { - 1 -> FIRST - 2 -> SECOND - else -> LAST - } - } -} \ No newline at end of file +} diff --git a/idea/testData/multiModuleQuickFix/enum/js/MyEnum.kt.after b/idea/testData/multiModuleQuickFix/enum/js/MyEnum.kt.after index 627f78ffede..0d82f7c6aa3 100644 --- a/idea/testData/multiModuleQuickFix/enum/js/MyEnum.kt.after +++ b/idea/testData/multiModuleQuickFix/enum/js/MyEnum.kt.after @@ -4,14 +4,7 @@ impl enum class MyEnum { SECOND, LAST; - companion object { - fun byNum(num: Int): MyEnum = when (num) { - 1 -> FIRST - 2 -> SECOND - else -> LAST - } - } - impl val num: Int get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/nested/header/header.kt b/idea/testData/multiModuleQuickFix/nested/header/header.kt index 6bb4f70a8c2..752697e70bf 100644 --- a/idea/testData/multiModuleQuickFix/nested/header/header.kt +++ b/idea/testData/multiModuleQuickFix/nested/header/header.kt @@ -4,10 +4,10 @@ header class WithNested { fun foo(): Int class Nested { - fun bar() = "Nested" + fun bar() } inner class Inner { - fun baz() = "Inner" + fun baz() } -} \ No newline at end of file +} diff --git a/idea/testData/multiModuleQuickFix/nested/header/header.kt.after b/idea/testData/multiModuleQuickFix/nested/header/header.kt.after index 8603817fd0f..af4cd9208c6 100644 --- a/idea/testData/multiModuleQuickFix/nested/header/header.kt.after +++ b/idea/testData/multiModuleQuickFix/nested/header/header.kt.after @@ -4,10 +4,10 @@ header class WithNested { fun foo(): Int class Nested { - fun bar() = "Nested" + fun bar() } inner class Inner { - fun baz() = "Inner" + fun baz() } -} \ No newline at end of file +} diff --git a/idea/testData/multiModuleQuickFix/nested/jvm/WithNested.kt.after b/idea/testData/multiModuleQuickFix/nested/jvm/WithNested.kt.after index 4f5d843101c..cc8028c1032 100644 --- a/idea/testData/multiModuleQuickFix/nested/jvm/WithNested.kt.after +++ b/idea/testData/multiModuleQuickFix/nested/jvm/WithNested.kt.after @@ -2,11 +2,11 @@ impl class WithNested { class Nested { - fun bar() = "Nested" + fun bar() } inner class Inner { - fun baz() = "Inner" + fun baz() } impl fun foo(): Int { diff --git a/idea/testData/multiModuleQuickFix/sealed/header/header.kt b/idea/testData/multiModuleQuickFix/sealed/header/header.kt index 15944e8eefd..fc330e0546a 100644 --- a/idea/testData/multiModuleQuickFix/sealed/header/header.kt +++ b/idea/testData/multiModuleQuickFix/sealed/header/header.kt @@ -1,7 +1,7 @@ // "Create header class implementation for platform JS" "true" header sealed class Sealed { - object Obj : Sealed() + object Obj : Sealed - class Klass(val x: Int) : Sealed() + class Klass(x: Int) : Sealed } \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/sealed/header/header.kt.after b/idea/testData/multiModuleQuickFix/sealed/header/header.kt.after index 2fe1c88ee10..bd7b89684ab 100644 --- a/idea/testData/multiModuleQuickFix/sealed/header/header.kt.after +++ b/idea/testData/multiModuleQuickFix/sealed/header/header.kt.after @@ -1,7 +1,7 @@ // "Create header class implementation for platform JS" "true" header sealed class Sealed { - object Obj : Sealed() + object Obj : Sealed - class Klass(val x: Int) : Sealed() + class Klass(x: Int) : Sealed } \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/sealed/js/Sealed.kt.after b/idea/testData/multiModuleQuickFix/sealed/js/Sealed.kt.after index 3a1683353e2..1e4dcfec5b2 100644 --- a/idea/testData/multiModuleQuickFix/sealed/js/Sealed.kt.after +++ b/idea/testData/multiModuleQuickFix/sealed/js/Sealed.kt.after @@ -1,6 +1,6 @@ // Sealed: to be implemented impl sealed class Sealed { - object Obj : Sealed() + object Obj : Sealed - class Klass(val x: Int) : Sealed() + class Klass(x: Int) : Sealed } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt index 9096edf509f..43a0f259470 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt @@ -284,5 +284,9 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() { } }) } + + fun testNestedClassWithoutImpl() { + doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]) + } } } diff --git a/libraries/stdlib/common/src/kotlin/TextH.kt b/libraries/stdlib/common/src/kotlin/TextH.kt index c58829b48d4..f0f8020cad4 100644 --- a/libraries/stdlib/common/src/kotlin/TextH.kt +++ b/libraries/stdlib/common/src/kotlin/TextH.kt @@ -58,7 +58,7 @@ header class Regex { fun split(input: CharSequence): List fun split(input: CharSequence, limit: Int): List - header companion object { + companion object { fun fromLiteral(literal: String): Regex fun escape(literal: String): String fun escapeReplacement(literal: String): String @@ -200,4 +200,4 @@ header fun String.toFloatOrNull(): Float? @PublishedApi internal header fun checkRadix(radix: Int): Int -internal header fun digitOf(char: Char, radix: Int): Int \ No newline at end of file +internal header fun digitOf(char: Char, radix: Int): Int