Treat nested class of header class as header

Note that the quick fix to implement header class works incorrectly when
that class has nested classes at the moment; this should be fixed
separately

 #KT-15494 Fixed
 #KT-18573 Fixed
This commit is contained in:
Alexander Udalov
2017-07-21 18:22:32 +03:00
parent 56b507d141
commit d1cff41ce0
35 changed files with 348 additions and 119 deletions
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier; 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.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible;
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData; import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo; import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo;
@@ -568,7 +567,7 @@ public interface Errors {
DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory2<KtDeclaration, ClassDescriptor, DiagnosticFactory2<KtDeclaration, ClassDescriptor,
List<Pair<CallableMemberDescriptor, Map<Incompatible, Collection<CallableMemberDescriptor>>>>> HEADER_CLASS_MEMBERS_ARE_NOT_IMPLEMENTED = List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>> HEADER_CLASS_MEMBERS_ARE_NOT_IMPLEMENTED =
DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtDeclaration> IMPL_MISSING = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0<KtDeclaration> IMPL_MISSING = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.diagnostics.rendering package org.jetbrains.kotlin.diagnostics.rendering
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible
@@ -44,9 +43,9 @@ class PlatformIncompatibilityDiagnosticRenderer(
class IncompatibleHeaderImplClassScopesRenderer( class IncompatibleHeaderImplClassScopesRenderer(
private val mode: MultiplatformDiagnosticRenderingMode private val mode: MultiplatformDiagnosticRenderingMode
) : DiagnosticParameterRenderer<List<Pair<CallableMemberDescriptor, Map<Incompatible, Collection<CallableMemberDescriptor>>>>> { ) : DiagnosticParameterRenderer<List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>> {
override fun render( override fun render(
obj: List<Pair<CallableMemberDescriptor, Map<Incompatible, Collection<CallableMemberDescriptor>>>>, obj: List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>,
renderingContext: RenderingContext renderingContext: RenderingContext
): String { ): String {
if (obj.isEmpty()) return "" if (obj.isEmpty()) return ""
@@ -110,7 +109,7 @@ private fun StringBuilder.renderIncompatibilityInformation(
} }
private fun StringBuilder.renderIncompatibleClassScopes( private fun StringBuilder.renderIncompatibleClassScopes(
unimplemented: List<Pair<CallableMemberDescriptor, Map<Incompatible, Collection<CallableMemberDescriptor>>>>, unimplemented: List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>,
indent: String, indent: String,
context: RenderingContext, context: RenderingContext,
mode: MultiplatformDiagnosticRenderingMode mode: MultiplatformDiagnosticRenderingMode
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension; import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension;
import org.jetbrains.kotlin.lexer.KtKeywordToken; import org.jetbrains.kotlin.lexer.KtKeywordToken;
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken; import org.jetbrains.kotlin.lexer.KtModifierKeywordToken;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker; import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker;
import org.jetbrains.kotlin.resolve.checkers.PublishedApiUsageChecker; import org.jetbrains.kotlin.resolve.checkers.PublishedApiUsageChecker;
@@ -35,8 +36,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_CLASS_NOT_ALLOWED; import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_OBJECT_NOT_ALLOWED;
import static org.jetbrains.kotlin.lexer.KtTokens.*; import static org.jetbrains.kotlin.lexer.KtTokens.*;
public class ModifiersChecker { public class ModifiersChecker {
@@ -160,6 +160,7 @@ public class ModifiersChecker {
checkObjectInsideInnerClass(modifierListOwner, descriptor); checkObjectInsideInnerClass(modifierListOwner, descriptor);
checkTypeParametersModifiers(modifierListOwner); checkTypeParametersModifiers(modifierListOwner);
checkModifierListCommon(modifierListOwner, descriptor); checkModifierListCommon(modifierListOwner, descriptor);
checkIllegalHeader(modifierListOwner, descriptor);
} }
private void checkObjectInsideInnerClass(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor 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) { private void checkNestedClassAllowed(@NotNull KtModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
if (modifierListOwner.hasModifier(INNER_KEYWORD)) return; if (modifierListOwner.hasModifier(INNER_KEYWORD)) return;
if (modifierListOwner instanceof KtClass && !(modifierListOwner instanceof KtEnumEntry)) { if (modifierListOwner instanceof KtClass && !(modifierListOwner instanceof KtEnumEntry)) {
@@ -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.Compatible
import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible
import org.jetbrains.kotlin.resolve.descriptorUtil.classId 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.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -64,9 +63,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
val checkImpl = !languageVersionSettings.getFlag(AnalysisFlag.multiPlatformDoNotCheckImpl) val checkImpl = !languageVersionSettings.getFlag(AnalysisFlag.multiPlatformDoNotCheckImpl)
if (descriptor.isHeader) { if (descriptor.isHeader) {
if (declaration.hasModifier(KtTokens.HEADER_KEYWORD)) { checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticHolder, descriptor.module, checkImpl)
checkHeaderDeclarationHasImplementation(declaration, descriptor, diagnosticHolder, descriptor.module, checkImpl)
}
} }
else { else {
checkImplementationHasHeaderDeclaration(declaration, descriptor, diagnosticHolder, checkImpl) checkImplementationHasHeaderDeclaration(declaration, descriptor, diagnosticHolder, checkImpl)
@@ -81,7 +78,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
checkImpl: Boolean checkImpl: Boolean
) { ) {
// Only look for implementations of top level members; class members will be handled as a part of that header class // 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 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. // 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 // Their source is the containing KtClass instance though, as opposed to explicit constructors, whose source is KtConstructor
private fun CallableMemberDescriptor.isExplicitImplDeclaration(): Boolean = private fun MemberDescriptor.isExplicitImplDeclaration(): Boolean =
if (this is ConstructorDescriptor) { when (this) {
DescriptorToSourceUtils.getSourceFromDescriptor(this) is KtConstructor<*> is ConstructorDescriptor -> DescriptorToSourceUtils.getSourceFromDescriptor(this) is KtConstructor<*>
} is CallableMemberDescriptor -> kind == CallableMemberDescriptor.Kind.DECLARATION
else { else -> true
kind == CallableMemberDescriptor.Kind.DECLARATION
} }
private fun findHeaderForImpl(impl: MemberDescriptor, commonModule: ModuleDescriptor): Map<Compatibility, List<MemberDescriptor>>? { private fun findHeaderForImpl(impl: MemberDescriptor, commonModule: ModuleDescriptor): Map<Compatibility, List<MemberDescriptor>>? {
@@ -196,7 +192,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
is ClassDescriptor -> { is ClassDescriptor -> {
// TODO: replace with 'singleOrNull' as soon as multi-module diagnostic tests are refactored // TODO: replace with 'singleOrNull' as soon as multi-module diagnostic tests are refactored
val headerClass = findHeaderForImpl(container, commonModule)?.values?.firstOrNull()?.firstOrNull() as? ClassDescriptor val headerClass = findHeaderForImpl(container, commonModule)?.values?.firstOrNull()?.firstOrNull() as? ClassDescriptor
headerClass?.getMembers(impl.name).orEmpty() headerClass?.getMembers(impl.name)?.filterIsInstance<CallableMemberDescriptor>().orEmpty()
} }
is PackageFragmentDescriptor -> impl.findNamesakesFromModule(commonModule) is PackageFragmentDescriptor -> impl.findNamesakesFromModule(commonModule)
else -> return null // do not report anything for incorrect code, e.g. 'impl' local function 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") object Supertypes : Incompatible("some supertypes are missing in the implementation")
class ClassScopes( class ClassScopes(
val unimplemented: List<Pair<CallableMemberDescriptor, Map<Incompatible, Collection<CallableMemberDescriptor>>>> val unimplemented: List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>
) : Incompatible("some members are not implemented") ) : Incompatible("some members are not implemented")
object EnumEntries : Incompatible("some entries from header enum are missing in the impl enum") 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 { 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 var implTypealias = false
val b = when (other) { val b = when (other) {
@@ -515,6 +512,7 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
return Compatible return Compatible
} }
private fun areCompatibleClassScopes( private fun areCompatibleClassScopes(
a: ClassDescriptor, a: ClassDescriptor,
b: ClassDescriptor, b: ClassDescriptor,
@@ -522,19 +520,30 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
platformModule: ModuleDescriptor, platformModule: ModuleDescriptor,
substitutor: Substitutor substitutor: Substitutor
): Compatibility { ): Compatibility {
val unimplemented = arrayListOf<Pair<CallableMemberDescriptor, Map<Incompatible, MutableCollection<CallableMemberDescriptor>>>>() val unimplemented = arrayListOf<Pair<MemberDescriptor, Map<Incompatible, MutableCollection<MemberDescriptor>>>>()
val bMembersByName = b.getMembers().groupBy { it.name } val bMembersByName = b.getMembers().groupBy { it.name }
outer@ for (aMember in a.getMembers()) { 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 -> val bMembers = bMembersByName[aMember.name]?.filter { bMember ->
areCompatibleCallables(aMember, bMember, checkImpl, platformModule, substitutor) 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 if (mapping.values.any { it == Compatible }) continue
val incompatibilityMap = mutableMapOf<Incompatible, MutableCollection<CallableMemberDescriptor>>() val incompatibilityMap = mutableMapOf<Incompatible, MutableCollection<MemberDescriptor>>()
for ((descriptor, compatibility) in mapping) { for ((descriptor, compatibility) in mapping) {
when (compatibility) { when (compatibility) {
Compatible -> continue@outer Compatible -> continue@outer
@@ -561,10 +570,13 @@ object HeaderImplDeclarationChecker : DeclarationChecker {
return Incompatible.ClassScopes(unimplemented) return Incompatible.ClassScopes(unimplemented)
} }
private fun ClassDescriptor.getMembers(name: Name? = null): Collection<CallableMemberDescriptor> { private fun ClassDescriptor.getMembers(name: Name? = null): Collection<MemberDescriptor> {
val nameFilter = if (name != null) { it -> it == name } else MemberScope.ALL_NAME_FILTER val nameFilter = if (name != null) { it -> it == name } else MemberScope.ALL_NAME_FILTER
return defaultType.memberScope.getDescriptorsFiltered(nameFilter = nameFilter).filterIsInstance<CallableMemberDescriptor>() + return defaultType.memberScope
constructors.filter { nameFilter(it.name) } .getDescriptorsFiltered(nameFilter = nameFilter)
.filterIsInstance<MemberDescriptor>()
.filterNot(DescriptorUtils::isEnumEntry)
.plus(constructors.filter { nameFilter(it.name) })
} }
private inline fun <T, K> equalBy(first: T, second: T, selector: (T) -> K): Boolean = private inline fun <T, K> equalBy(first: T, second: T, selector: (T) -> K): Boolean =
@@ -165,9 +165,11 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
this.isInner = modifierList != null && modifierList.hasModifier(INNER_KEYWORD) && !isIllegalInner(this); this.isInner = modifierList != null && modifierList.hasModifier(INNER_KEYWORD) && !isIllegalInner(this);
this.isData = modifierList != null && modifierList.hasModifier(KtTokens.DATA_KEYWORD); 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.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) // Annotation entries are taken from both own annotations (if any) and object literal annotations (if any)
List<KtAnnotationEntry> annotationEntries = new ArrayList<>(); List<KtAnnotationEntry> annotationEntries = new ArrayList<>();
if (classOrObject != null && classOrObject.getParent() instanceof KtObjectLiteralExpression) { if (classOrObject != null && classOrObject.getParent() instanceof KtObjectLiteralExpression) {
@@ -2,11 +2,11 @@
package package
public final header enum class Bar : kotlin.Enum<Bar> { public final header enum class Bar : kotlin.Enum<Bar> {
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 name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -22,9 +22,9 @@ public final header enum class Bar : kotlin.Enum<Bar> {
} }
public final header enum class Foo : kotlin.Enum<Foo> { public final header enum class Foo : kotlin.Enum<Foo> {
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 name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -1,9 +1,9 @@
package package
public final header enum class En : kotlin.Enum<En> { public final header enum class En : kotlin.Enum<En> {
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*/ x: kotlin.Int)
private constructor En(/*0*/ s: kotlin.String) private constructor En(/*0*/ s: kotlin.String)
@@ -21,7 +21,7 @@ public final header enum class En : kotlin.Enum<En> {
} }
public final header enum class En2 : kotlin.Enum<En2> { public final header enum class En2 : kotlin.Enum<En2> {
enum entry E1 header enum entry E1
public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -2,11 +2,11 @@
package package
public final header enum class Bar : kotlin.Enum<Bar> { public final header enum class Bar : kotlin.Enum<Bar> {
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 name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -22,9 +22,9 @@ public final header enum class Bar : kotlin.Enum<Bar> {
} }
public final header enum class Foo : kotlin.Enum<Foo> { public final header enum class Foo : kotlin.Enum<Foo> {
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 name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -5,7 +5,7 @@
header enum class En { header enum class En {
E1, E1,
<!HEADER_ENUM_ENTRY_WITH_BODY!>E2 { <!HEADER_ENUM_ENTRY_WITH_BODY!>E2 {
fun foo() = "" <!HEADER_DECLARATION_WITH_BODY!>fun foo()<!> = ""
},<!> },<!>
<!HEADER_ENUM_ENTRY_WITH_BODY!>E3 { };<!> <!HEADER_ENUM_ENTRY_WITH_BODY!>E3 { };<!>
} }
@@ -1,11 +1,11 @@
package package
public final header enum class En : kotlin.Enum<En> { public final header enum class En : kotlin.Enum<En> {
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 name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -2,11 +2,11 @@
package package
public final header enum class Foo : kotlin.Enum<Foo> { public final header enum class Foo : kotlin.Enum<Foo> {
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 name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -17,7 +17,7 @@ public final header class Class {
} }
public final header enum class En : kotlin.Enum<En> { public final header enum class En : kotlin.Enum<En> {
enum entry ENTRY header enum entry ENTRY
public final override /*1*/ /*fake_override*/ val name: kotlin.String public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -3,23 +3,29 @@
// FILE: common.kt // FILE: common.kt
header class OuterClass { header class OuterClass {
header class NestedClass { class NestedClass {
header class DeepNested { class DeepNested {
header class Another 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 class OuterClassWithNamedCompanion {
header companion object Factory companion object Factory
} }
header object OuterObject { header object OuterObject {
header object NestedObject object NestedObject
} }
// MODULE: m2-jvm(m1-common) // MODULE: m2-jvm(m1-common)
@@ -28,11 +34,17 @@ header object OuterObject {
impl class OuterClass { impl class OuterClass {
impl class NestedClass { impl class NestedClass {
impl class DeepNested { 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 impl companion object
} }
@@ -15,7 +15,9 @@ public final header class OuterClass {
public final header inner class InnerClass { public final header inner class InnerClass {
public constructor 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 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 hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String 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 final header class Another {
public constructor 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 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 hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String 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 final impl inner class InnerClass {
public constructor 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 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 hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String 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 final impl class Another {
public constructor 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 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 hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
} }
@@ -0,0 +1,38 @@
// !LANGUAGE: +MultiPlatformProjects
// MODULE: m1-common
// FILE: common.kt
header class B {
class N {
<!HEADER_DECLARATION_WITH_BODY, JVM:HEADER_DECLARATION_WITH_BODY!>fun body()<!> {}
<!WRONG_MODIFIER_TARGET, JVM:WRONG_MODIFIER_TARGET!>header<!> fun extraHeader()
}
}
header class C {
<!WRONG_MODIFIER_TARGET, JVM:WRONG_MODIFIER_TARGET!>header<!> class N
<!WRONG_MODIFIER_TARGET, JVM:WRONG_MODIFIER_TARGET!>header<!> enum class E
<!WRONG_MODIFIER_TARGET, JVM:WRONG_MODIFIER_TARGET!>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 <!HEADER_CLASS_MEMBERS_ARE_NOT_IMPLEMENTED!>D<!>
@@ -0,0 +1,132 @@
// -- Module: <m1-common> --
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<C.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
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<C.E>
}
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: <m1-jvm> --
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<C.E> {
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<C.E!>!
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<C.E>
}
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
}
@@ -5,7 +5,7 @@
<!WRONG_MODIFIER_TARGET!>header<!> typealias Foo = String <!WRONG_MODIFIER_TARGET!>header<!> typealias Foo = String
class Outer <!WRONG_MODIFIER_TARGET!>header<!> constructor() { class Outer <!WRONG_MODIFIER_TARGET!>header<!> constructor() {
header class Nested <!WRONG_MODIFIER_TARGET!>header<!> class Nested
<!WRONG_MODIFIER_TARGET!>header<!> init {} <!WRONG_MODIFIER_TARGET!>header<!> init {}
@@ -1,3 +1,3 @@
header class ByTypeAlias { header class ByTypeAlias {
header interface Nested interface Nested
} }
@@ -3,8 +3,5 @@ Exit code: OK
Output: Output:
-- JVM -- -- JVM --
Exit code: COMPILATION_ERROR Exit code: OK
Output: Output:
compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/common.kt:2:22: error: 'header' interface 'Nested' has no implementation in module
header interface Nested
^
@@ -1,19 +1,19 @@
header class O1 { header class O1 {
header class N1 class N1
header interface N2 interface N2
header object N3 object N3
} }
header class O2 { header class O2 {
header class N2 class N2
header inner class I2 inner class I2
} }
header class O3 { header class O3 {
header object Companion object Companion
header companion object Factory companion object Factory
} }
header class O4 { header class O4 {
header companion object companion object
} }
@@ -13782,6 +13782,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); 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") @TestMetadata("noImplKeywordOnMember.kt")
public void testNoImplKeywordOnMember() throws Exception { public void testNoImplKeywordOnMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/noImplKeywordOnMember.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/noImplKeywordOnMember.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.core.toDescriptor
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.resolve.diagnostics.SimpleDiagnostics import org.jetbrains.kotlin.resolve.diagnostics.SimpleDiagnostics
val ModuleDescriptor.sourceKind: SourceKind val ModuleDescriptor.sourceKind: SourceKind
@@ -55,7 +56,7 @@ val ModuleDescriptor.allImplementingCompatibleModules
class PlatformHeaderAnnotator : Annotator { class PlatformHeaderAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) { override fun annotate(element: PsiElement, holder: AnnotationHolder) {
val declaration = element as? KtDeclaration ?: return 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 if (TargetPlatformDetector.getPlatform(declaration.containingKtFile) !is TargetPlatform.Default) return
@@ -78,4 +79,9 @@ class PlatformHeaderAnnotator : Annotator {
KotlinPsiChecker().annotateElement(declaration, holder, SimpleDiagnostics(filteredList)) 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
}
} }
@@ -0,0 +1,15 @@
package a
header class A {
class Nested
}
header class B {
class Nested {
fun foo(s: String)
}
}
header class C {
<error>header</error> inner class Inner
}
@@ -0,0 +1,11 @@
package a
impl class <error>A</error>
impl class B {
impl class <error>Nested</error>
}
impl class C {
impl inner class Inner
}
@@ -6,12 +6,4 @@ header enum class <caret>MyEnum {
LAST; LAST;
val num: Int val num: Int
companion object {
fun byNum(num: Int): MyEnum = when (num) {
1 -> FIRST
2 -> SECOND
else -> LAST
}
}
} }
@@ -6,12 +6,4 @@ header enum class MyEnum {
LAST; LAST;
val num: Int val num: Int
companion object {
fun byNum(num: Int): MyEnum = when (num) {
1 -> FIRST
2 -> SECOND
else -> LAST
}
}
} }
+1 -8
View File
@@ -4,14 +4,7 @@ impl enum class MyEnum {
SECOND, SECOND,
LAST; LAST;
companion object {
fun byNum(num: Int): MyEnum = when (num) {
1 -> FIRST
2 -> SECOND
else -> LAST
}
}
impl val num: Int impl val num: Int
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates. get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }
+2 -2
View File
@@ -4,10 +4,10 @@ header class <caret>WithNested {
fun foo(): Int fun foo(): Int
class Nested { class Nested {
fun bar() = "Nested" fun bar()
} }
inner class Inner { inner class Inner {
fun baz() = "Inner" fun baz()
} }
} }
@@ -4,10 +4,10 @@ header class WithNested {
fun foo(): Int fun foo(): Int
class Nested { class Nested {
fun bar() = "Nested" fun bar()
} }
inner class Inner { inner class Inner {
fun baz() = "Inner" fun baz()
} }
} }
@@ -2,11 +2,11 @@
impl class WithNested { impl class WithNested {
class Nested { class Nested {
fun bar() = "Nested" fun bar()
} }
inner class Inner { inner class Inner {
fun baz() = "Inner" fun baz()
} }
impl fun foo(): Int { impl fun foo(): Int {
+2 -2
View File
@@ -1,7 +1,7 @@
// "Create header class implementation for platform JS" "true" // "Create header class implementation for platform JS" "true"
header sealed class <caret>Sealed { header sealed class <caret>Sealed {
object Obj : Sealed() object Obj : Sealed
class Klass(val x: Int) : Sealed() class Klass(x: Int) : Sealed
} }
@@ -1,7 +1,7 @@
// "Create header class implementation for platform JS" "true" // "Create header class implementation for platform JS" "true"
header sealed class Sealed { header sealed class Sealed {
object Obj : Sealed() object Obj : Sealed
class Klass(val x: Int) : Sealed() class Klass(x: Int) : Sealed
} }
@@ -1,6 +1,6 @@
// Sealed: to be implemented // Sealed: to be implemented
impl sealed class Sealed { impl sealed class Sealed {
object Obj : Sealed() object Obj : Sealed
class Klass(val x: Int) : Sealed() class Klass(x: Int) : Sealed
} }
@@ -284,5 +284,9 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
} }
}) })
} }
fun testNestedClassWithoutImpl() {
doMultiPlatformTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
}
} }
} }
+1 -1
View File
@@ -58,7 +58,7 @@ header class Regex {
fun split(input: CharSequence): List<String> fun split(input: CharSequence): List<String>
fun split(input: CharSequence, limit: Int): List<String> fun split(input: CharSequence, limit: Int): List<String>
header companion object { companion object {
fun fromLiteral(literal: String): Regex fun fromLiteral(literal: String): Regex
fun escape(literal: String): String fun escape(literal: String): String
fun escapeReplacement(literal: String): String fun escapeReplacement(literal: String): String