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:
Alexander Udalov
2016-12-15 18:51:58 +03:00
parent 56d4ff0cad
commit da6c3c3231
21 changed files with 455 additions and 11 deletions
@@ -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);
@@ -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 }
}
}
@@ -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
@@ -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(