Support header/impl enum classes
- prohibit constructors for header enum classes - prohibit bodies for header enum entries - all entries from header enum must be present in impl enum
This commit is contained in:
@@ -512,6 +512,8 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtParameter> HEADER_DECLARATION_WITH_DEFAULT_PARAMETER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtConstructorDelegationCall> HEADER_CLASS_CONSTRUCTOR_DELEGATION_CALL = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtParameter> HEADER_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtConstructor<?>> HEADER_ENUM_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtEnumEntry> HEADER_ENUM_ENTRY_WITH_BODY = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtExpression> HEADER_PROPERTY_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtTypeAlias> IMPL_TYPE_ALIAS_NOT_TO_CLASS = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
+2
@@ -265,6 +265,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(HEADER_DECLARATION_WITH_DEFAULT_PARAMETER, "Header declaration cannot have parameters with default values");
|
||||
MAP.put(HEADER_CLASS_CONSTRUCTOR_DELEGATION_CALL, "Explicit delegation call for constructor of a header class is not allowed");
|
||||
MAP.put(HEADER_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER, "Header class constructor cannot have a property parameter");
|
||||
MAP.put(HEADER_ENUM_CONSTRUCTOR, "Header enum class cannot have a constructor");
|
||||
MAP.put(HEADER_ENUM_ENTRY_WITH_BODY, "Header enum entry cannot have a body");
|
||||
MAP.put(HEADER_PROPERTY_INITIALIZER, "Header property cannot have an initializer");
|
||||
|
||||
MAP.put(IMPL_TYPE_ALIAS_NOT_TO_CLASS, "Right-hand side of 'impl' type alias should be a class, not another type alias");
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveAbstractMembers
|
||||
@@ -274,6 +273,10 @@ class DeclarationsChecker(
|
||||
trace.report(HEADER_DECLARATION_WITH_BODY.on(declaration))
|
||||
}
|
||||
|
||||
if (constructorDescriptor.containingDeclaration.kind == ClassKind.ENUM_CLASS) {
|
||||
trace.report(HEADER_ENUM_CONSTRUCTOR.on(declaration))
|
||||
}
|
||||
|
||||
if (declaration is KtPrimaryConstructor && !DescriptorUtils.isAnnotationClass(constructorDescriptor.constructedClass)) {
|
||||
for (parameter in declaration.valueParameters) {
|
||||
if (parameter.hasValOrVar()) {
|
||||
@@ -895,15 +898,20 @@ class DeclarationsChecker(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkEnumEntry(enumEntry: KtEnumEntry, classDescriptor: ClassDescriptor) {
|
||||
val declaration = classDescriptor.containingDeclaration
|
||||
if (DescriptorUtils.isEnumClass(declaration)) {
|
||||
if (!enumEntry.hasInitializer() && !hasDefaultConstructor(declaration as ClassDescriptor)) {
|
||||
private fun checkEnumEntry(enumEntry: KtEnumEntry, enumEntryClass: ClassDescriptor) {
|
||||
val enumClass = enumEntryClass.containingDeclaration as ClassDescriptor
|
||||
if (DescriptorUtils.isEnumClass(enumClass)) {
|
||||
if (enumClass.isHeader) {
|
||||
if (enumEntry.getBody() != null) {
|
||||
trace.report(HEADER_ENUM_ENTRY_WITH_BODY.on(enumEntry))
|
||||
}
|
||||
}
|
||||
else if (!enumEntry.hasInitializer() && !hasDefaultConstructor(enumClass)) {
|
||||
trace.report(ENUM_ENTRY_SHOULD_BE_INITIALIZED.on(enumEntry))
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(DescriptorUtils.isInterface(declaration)) { "Enum entry should be declared in enum class: " + classDescriptor }
|
||||
assert(DescriptorUtils.isInterface(enumClass)) { "Enum entry should be declared in enum class: " + enumEntryClass }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
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
|
||||
@@ -212,6 +213,8 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
||||
unimplemented: List<Pair<CallableMemberDescriptor, Map<Incompatible, Collection<CallableMemberDescriptor>>>>
|
||||
) : Incompatible("some members are not implemented", unimplemented)
|
||||
|
||||
object EnumEntries : Incompatible("some entries from header enum are missing in the impl enum")
|
||||
|
||||
// Common
|
||||
|
||||
object Modality : Incompatible("modality is different")
|
||||
@@ -390,7 +393,16 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
||||
unimplemented.add(aMember to incompatibilityMap)
|
||||
}
|
||||
|
||||
// TODO: check static scope, enum entries
|
||||
if (a.kind == ClassKind.ENUM_CLASS) {
|
||||
fun ClassDescriptor.enumEntries() =
|
||||
unsubstitutedMemberScope.getDescriptorsFiltered().filter(DescriptorUtils::isEnumEntry).map { it.name }
|
||||
val aEntries = a.enumEntries()
|
||||
val bEntries = b.enumEntries()
|
||||
|
||||
if (!bEntries.containsAll(aEntries)) return Incompatible.EnumEntries
|
||||
}
|
||||
|
||||
// TODO: check static scope?
|
||||
|
||||
if (unimplemented.isEmpty()) return Compatible
|
||||
|
||||
|
||||
+7
-3
@@ -334,9 +334,13 @@ open class LazyClassMemberScope(
|
||||
val classOrObject = declarationProvider.correspondingClassOrObject ?: return null
|
||||
|
||||
val hasPrimaryConstructor = classOrObject.hasExplicitPrimaryConstructor()
|
||||
if (DescriptorUtils.isInterface(thisDescriptor) && !hasPrimaryConstructor) return null
|
||||
|
||||
if (thisDescriptor.isHeader && thisDescriptor.kind == ClassKind.OBJECT) return null
|
||||
if (!hasPrimaryConstructor) {
|
||||
when (thisDescriptor.kind) {
|
||||
ClassKind.INTERFACE -> return null
|
||||
ClassKind.OBJECT, ClassKind.ENUM_CLASS -> if (thisDescriptor.isHeader) return null
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
if (DescriptorUtils.canHaveDeclaredConstructors(thisDescriptor) || hasPrimaryConstructor) {
|
||||
val constructor = c.functionDescriptorResolver.resolvePrimaryConstructorDescriptor(
|
||||
|
||||
Reference in New Issue
Block a user