Introduction of sealed classes
Sealed classes can be derived only by their own inner classes or objects. Their constructors cannot be called explicitly, so compiler knows all their descendants. Incompatible modifier checks (final, abstract). Impossible with interface, object, enum. A pack of tests provided.
This commit is contained in:
@@ -185,6 +185,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<JetModifierListOwner> OPEN_MODIFIER_IN_TRAIT = DiagnosticFactory0
|
||||
.create(WARNING, modifierSetPosition(JetTokens.OPEN_KEYWORD));
|
||||
DiagnosticFactory0<JetModifierListOwner> TRAIT_CAN_NOT_BE_FINAL = DiagnosticFactory0.create(ERROR, FINAL_MODIFIER);
|
||||
DiagnosticFactory0<JetModifierListOwner> TRAIT_CAN_NOT_BE_SEALED = DiagnosticFactory0.create(ERROR, SEALED_MODIFIER);
|
||||
|
||||
DiagnosticFactory0<JetDeclaration> CONSTRUCTOR_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
@@ -203,6 +204,8 @@ public interface Errors {
|
||||
.create(ERROR, modifierSetPosition(JetTokens.OPEN_KEYWORD));
|
||||
DiagnosticFactory0<JetModifierListOwner> ABSTRACT_MODIFIER_IN_ENUM = DiagnosticFactory0
|
||||
.create(ERROR, modifierSetPosition(JetTokens.ABSTRACT_KEYWORD));
|
||||
DiagnosticFactory0<JetModifierListOwner> SEALED_MODIFIER_IN_ENUM = DiagnosticFactory0
|
||||
.create(ERROR, modifierSetPosition(JetTokens.SEALED_KEYWORD));
|
||||
|
||||
DiagnosticFactory0<PsiElement> CLASS_IN_SUPERTYPE_FOR_ENUM = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetTypeParameterList> TYPE_PARAMETERS_IN_ENUM = DiagnosticFactory0.create(ERROR);
|
||||
@@ -214,6 +217,17 @@ public interface Errors {
|
||||
DiagnosticFactory1<JetEnumEntry, ClassDescriptor> ENUM_ENTRY_AFTER_ENUM_MEMBER = DiagnosticFactory1.create(WARNING, DECLARATION_NAME);
|
||||
DiagnosticFactory0<JetCallExpression> ENUM_CLASS_CONSTRUCTOR_CALL = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
// Sealed-specific
|
||||
DiagnosticFactory0<JetModifierListOwner> OPEN_MODIFIER_IN_SEALED = DiagnosticFactory0
|
||||
.create(ERROR, modifierSetPosition(JetTokens.OPEN_KEYWORD));
|
||||
DiagnosticFactory0<JetModifierListOwner> FINAL_MODIFIER_IN_SEALED = DiagnosticFactory0
|
||||
.create(ERROR, modifierSetPosition(JetTokens.FINAL_KEYWORD));
|
||||
DiagnosticFactory0<JetModifierListOwner> ABSTRACT_MODIFIER_IN_SEALED = DiagnosticFactory0
|
||||
.create(WARNING, modifierSetPosition(JetTokens.ABSTRACT_KEYWORD));
|
||||
DiagnosticFactory0<JetCallExpression> SEALED_CLASS_CONSTRUCTOR_CALL = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetTypeReference> SEALED_SUPERTYPE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetTypeReference> SEALED_SUPERTYPE_IN_LOCAL_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
// Companion objects
|
||||
|
||||
DiagnosticFactory0<JetObjectDeclaration> MANY_COMPANION_OBJECTS = DiagnosticFactory0.create(ERROR, COMPANION_OBJECT);
|
||||
|
||||
@@ -208,6 +208,8 @@ public object PositioningStrategies {
|
||||
|
||||
public val FINAL_MODIFIER: PositioningStrategy<JetModifierListOwner> = modifierSetPosition(JetTokens.FINAL_KEYWORD)
|
||||
|
||||
public val SEALED_MODIFIER: PositioningStrategy<JetModifierListOwner> = modifierSetPosition(JetTokens.SEALED_KEYWORD)
|
||||
|
||||
public val VARIANCE_MODIFIER: PositioningStrategy<JetModifierListOwner> = modifierSetPosition(JetTokens.IN_KEYWORD, JetTokens.OUT_KEYWORD)
|
||||
|
||||
public val FOR_REDECLARATION: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
|
||||
|
||||
+8
@@ -141,11 +141,16 @@ public class DefaultErrorMessages {
|
||||
MAP.put(REDUNDANT_MODIFIER, "Modifier ''{0}'' is redundant because ''{1}'' is present", TO_STRING, TO_STRING);
|
||||
MAP.put(ABSTRACT_MODIFIER_IN_TRAIT, "Modifier ''abstract'' is redundant in interface");
|
||||
MAP.put(OPEN_MODIFIER_IN_TRAIT, "Modifier ''open'' is redundant in interface");
|
||||
MAP.put(OPEN_MODIFIER_IN_SEALED, "Modifier ''open'' is not applicable for sealed class");
|
||||
MAP.put(OPEN_MODIFIER_IN_ENUM, "Modifier ''open'' is not applicable for enum class");
|
||||
MAP.put(ABSTRACT_MODIFIER_IN_ENUM, "Modifier ''abstract'' is not applicable for enum class");
|
||||
MAP.put(ABSTRACT_MODIFIER_IN_SEALED, "Modifier ''abstract'' is redundant for sealed class");
|
||||
MAP.put(SEALED_MODIFIER_IN_ENUM, "Modifier ''sealed'' is not applicable for enum class");
|
||||
MAP.put(FINAL_MODIFIER_IN_SEALED, "Modifier ''final'' is not applicable for sealed class");
|
||||
MAP.put(ILLEGAL_ENUM_ANNOTATION, "Annotation ''enum'' is only applicable for class");
|
||||
MAP.put(REDUNDANT_MODIFIER_IN_GETTER, "Visibility modifiers are redundant in getter");
|
||||
MAP.put(TRAIT_CAN_NOT_BE_FINAL, "Interface cannot be final");
|
||||
MAP.put(TRAIT_CAN_NOT_BE_SEALED, "Interface cannot be sealed");
|
||||
MAP.put(TYPE_PARAMETERS_IN_ENUM, "Enum class cannot have type parameters");
|
||||
MAP.put(TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM,
|
||||
"Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly"); // TODO: message
|
||||
@@ -292,6 +297,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(ENUM_ENTRY_USES_DEPRECATED_SUPER_CONSTRUCTOR, "Enum entry ''{0}'' uses deprecated super constructor syntax, use ENTRY(arguments) instead", NAME);
|
||||
MAP.put(ENUM_ENTRY_AFTER_ENUM_MEMBER, "Enum entry ''{0}'' is not allowed after a member", NAME);
|
||||
MAP.put(ENUM_CLASS_CONSTRUCTOR_CALL, "Enum types cannot be instantiated");
|
||||
MAP.put(SEALED_CLASS_CONSTRUCTOR_CALL, "Sealed types cannot be instantiated");
|
||||
|
||||
MAP.put(DELEGATION_IN_TRAIT, "Interfaces cannot use delegation");
|
||||
MAP.put(DELEGATION_NOT_TO_TRAIT, "Only interfaces can be delegated to");
|
||||
@@ -430,6 +436,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(TRAIT_WITH_SUPERCLASS, "An interface cannot inherit from a class");
|
||||
MAP.put(SUPERTYPE_APPEARS_TWICE, "A supertype appears twice");
|
||||
MAP.put(FINAL_SUPERTYPE, "This type is final, so it cannot be inherited from");
|
||||
MAP.put(SEALED_SUPERTYPE, "This type is sealed, so it can be inherited by only its own nested classes or objects");
|
||||
MAP.put(SEALED_SUPERTYPE_IN_LOCAL_CLASS, "Local class cannot extend a sealed class");
|
||||
MAP.put(SINGLETON_IN_SUPERTYPE, "Cannot inherit from a singleton");
|
||||
|
||||
MAP.put(CYCLIC_CONSTRUCTOR_DELEGATION_CALL, "There's a cycle in the delegation calls chain");
|
||||
|
||||
@@ -157,6 +157,7 @@ public interface JetTokens {
|
||||
JetModifierKeywordToken REIFIED_KEYWORD = JetModifierKeywordToken.softKeywordModifier("reified");
|
||||
JetModifierKeywordToken DYNAMIC_KEYWORD = JetModifierKeywordToken.softKeywordModifier("dynamic");
|
||||
JetModifierKeywordToken COMPANION_KEYWORD = JetModifierKeywordToken.softKeywordModifier("companion");
|
||||
JetModifierKeywordToken SEALED_KEYWORD = JetModifierKeywordToken.softKeywordModifier("sealed");
|
||||
|
||||
JetKeywordToken FINALLY_KEYWORD = JetKeywordToken.softKeyword("finally");
|
||||
JetModifierKeywordToken FINAL_KEYWORD = JetModifierKeywordToken.softKeywordModifier("final");
|
||||
@@ -174,7 +175,7 @@ public interface JetTokens {
|
||||
SET_KEYWORD, ABSTRACT_KEYWORD, ENUM_KEYWORD, OPEN_KEYWORD, INNER_KEYWORD, ANNOTATION_KEYWORD,
|
||||
OVERRIDE_KEYWORD, PRIVATE_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD, PROTECTED_KEYWORD,
|
||||
CATCH_KEYWORD, FINALLY_KEYWORD, OUT_KEYWORD, FINAL_KEYWORD, VARARG_KEYWORD, REIFIED_KEYWORD,
|
||||
DYNAMIC_KEYWORD, COMPANION_KEYWORD, CONSTRUCTOR_KEYWORD, INIT_KEYWORD
|
||||
DYNAMIC_KEYWORD, COMPANION_KEYWORD, CONSTRUCTOR_KEYWORD, INIT_KEYWORD, SEALED_KEYWORD
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -186,7 +187,7 @@ public interface JetTokens {
|
||||
new JetModifierKeywordToken[] {
|
||||
ABSTRACT_KEYWORD, ENUM_KEYWORD, OPEN_KEYWORD, INNER_KEYWORD, ANNOTATION_KEYWORD, OVERRIDE_KEYWORD, PRIVATE_KEYWORD,
|
||||
PUBLIC_KEYWORD, INTERNAL_KEYWORD, PROTECTED_KEYWORD, OUT_KEYWORD, IN_KEYWORD, FINAL_KEYWORD, VARARG_KEYWORD,
|
||||
REIFIED_KEYWORD, COMPANION_KEYWORD
|
||||
REIFIED_KEYWORD, COMPANION_KEYWORD, SEALED_KEYWORD
|
||||
};
|
||||
|
||||
TokenSet MODIFIER_KEYWORDS = TokenSet.create(MODIFIER_KEYWORDS_ARRAY);
|
||||
|
||||
@@ -394,15 +394,28 @@ public class BodyResolver {
|
||||
trace.report(SUPERTYPES_FOR_ANNOTATION_CLASS.on(jetClass.getDelegationSpecifierList()));
|
||||
}
|
||||
|
||||
Set<TypeConstructor> parentEnum =
|
||||
jetClass instanceof JetEnumEntry
|
||||
? Collections.singleton(((ClassDescriptor) descriptor.getContainingDeclaration()).getTypeConstructor())
|
||||
: Collections.<TypeConstructor>emptySet();
|
||||
Set<TypeConstructor> parentEnumOrSealed;
|
||||
if (jetClass instanceof JetEnumEntry) {
|
||||
parentEnumOrSealed = Collections.singleton(((ClassDescriptor) descriptor.getContainingDeclaration()).getTypeConstructor());
|
||||
}
|
||||
else {
|
||||
parentEnumOrSealed = Collections.emptySet();
|
||||
ClassDescriptor currentDescriptor = descriptor;
|
||||
while (currentDescriptor.getContainingDeclaration() instanceof ClassDescriptor) {
|
||||
currentDescriptor = (ClassDescriptor) currentDescriptor.getContainingDeclaration();
|
||||
if (currentDescriptor.getModality() == Modality.SEALED) {
|
||||
if (parentEnumOrSealed.isEmpty()) {
|
||||
parentEnumOrSealed = new HashSet<TypeConstructor>();
|
||||
}
|
||||
parentEnumOrSealed.add(currentDescriptor.getTypeConstructor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (primaryConstructorDelegationCall[0] != null && primaryConstructor != null) {
|
||||
recordConstructorDelegationCall(trace, primaryConstructor, primaryConstructorDelegationCall[0]);
|
||||
}
|
||||
checkSupertypeList(descriptor, supertypes, parentEnum);
|
||||
checkSupertypeList(descriptor, supertypes, parentEnumOrSealed);
|
||||
}
|
||||
|
||||
private static void recordConstructorDelegationCall(
|
||||
@@ -464,7 +477,21 @@ public class BodyResolver {
|
||||
trace.report(SINGLETON_IN_SUPERTYPE.on(typeReference));
|
||||
}
|
||||
else if (constructor.isFinal() && !allowedFinalSupertypes.contains(constructor)) {
|
||||
trace.report(FINAL_SUPERTYPE.on(typeReference));
|
||||
if (classDescriptor.getModality() == Modality.SEALED) {
|
||||
DeclarationDescriptor containingDescriptor = supertypeOwner.getContainingDeclaration();
|
||||
while (containingDescriptor != null && containingDescriptor != classDescriptor) {
|
||||
containingDescriptor = containingDescriptor.getContainingDeclaration();
|
||||
}
|
||||
if (containingDescriptor == null) {
|
||||
trace.report(SEALED_SUPERTYPE.on(typeReference));
|
||||
}
|
||||
else {
|
||||
trace.report(SEALED_SUPERTYPE_IN_LOCAL_CLASS.on(typeReference));
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace.report(FINAL_SUPERTYPE.on(typeReference));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +265,9 @@ public class DeclarationsChecker {
|
||||
trace.report(LOCAL_ENUM_NOT_ALLOWED.on(aClass, classDescriptor));
|
||||
}
|
||||
}
|
||||
else if (aClass.hasModifier(JetTokens.SEALED_KEYWORD)) {
|
||||
checkSealedModifiers(aClass);
|
||||
}
|
||||
else if (aClass instanceof JetEnumEntry) {
|
||||
checkEnumEntry((JetEnumEntry) aClass, classDescriptor);
|
||||
}
|
||||
@@ -319,6 +322,9 @@ public class DeclarationsChecker {
|
||||
if (modifierList.hasModifier(JetTokens.FINAL_KEYWORD)) {
|
||||
trace.report(Errors.TRAIT_CAN_NOT_BE_FINAL.on(aClass));
|
||||
}
|
||||
if (modifierList.hasModifier(JetTokens.SEALED_KEYWORD)) {
|
||||
trace.report(Errors.TRAIT_CAN_NOT_BE_SEALED.on(aClass));
|
||||
}
|
||||
if (modifierList.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
|
||||
trace.report(Errors.ABSTRACT_MODIFIER_IN_TRAIT.on(aClass));
|
||||
}
|
||||
@@ -550,6 +556,21 @@ public class DeclarationsChecker {
|
||||
if (aClass.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
|
||||
trace.report(ABSTRACT_MODIFIER_IN_ENUM.on(aClass));
|
||||
}
|
||||
if (aClass.hasModifier(JetTokens.SEALED_KEYWORD)) {
|
||||
trace.report(SEALED_MODIFIER_IN_ENUM.on(aClass));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSealedModifiers(JetClass aClass) {
|
||||
if (aClass.hasModifier(JetTokens.OPEN_KEYWORD)) {
|
||||
trace.report(OPEN_MODIFIER_IN_SEALED.on(aClass));
|
||||
}
|
||||
if (aClass.hasModifier(JetTokens.FINAL_KEYWORD)) {
|
||||
trace.report(FINAL_MODIFIER_IN_SEALED.on(aClass));
|
||||
}
|
||||
if (aClass.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
|
||||
trace.report(ABSTRACT_MODIFIER_IN_SEALED.on(aClass));
|
||||
}
|
||||
}
|
||||
|
||||
// Temporary
|
||||
|
||||
@@ -45,7 +45,7 @@ import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
|
||||
|
||||
public class ModifiersChecker {
|
||||
private static final Collection<JetModifierKeywordToken> MODALITY_MODIFIERS =
|
||||
Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD);
|
||||
Lists.newArrayList(ABSTRACT_KEYWORD, OPEN_KEYWORD, FINAL_KEYWORD, OVERRIDE_KEYWORD, SEALED_KEYWORD);
|
||||
|
||||
private static final Collection<JetModifierKeywordToken> VISIBILITY_MODIFIERS =
|
||||
Lists.newArrayList(PRIVATE_KEYWORD, PROTECTED_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD);
|
||||
@@ -370,6 +370,9 @@ public class ModifiersChecker {
|
||||
boolean hasAbstractModifier = modifierList.hasModifier(ABSTRACT_KEYWORD);
|
||||
boolean hasOverrideModifier = modifierList.hasModifier(OVERRIDE_KEYWORD);
|
||||
|
||||
if (modifierList.hasModifier(SEALED_KEYWORD)) {
|
||||
return Modality.SEALED;
|
||||
}
|
||||
if (modifierList.hasModifier(OPEN_KEYWORD)) {
|
||||
if (hasAbstractModifier || defaultModality == Modality.ABSTRACT) {
|
||||
return Modality.ABSTRACT;
|
||||
|
||||
+7
-2
@@ -211,13 +211,18 @@ public class CallExpressionResolver {
|
||||
return TypeInfoFactoryPackage.noTypeInfo(context);
|
||||
}
|
||||
if (functionDescriptor instanceof ConstructorDescriptor) {
|
||||
if (DescriptorUtils.isAnnotationClass(functionDescriptor.getContainingDeclaration())
|
||||
DeclarationDescriptor containingDescriptor = functionDescriptor.getContainingDeclaration();
|
||||
if (DescriptorUtils.isAnnotationClass(containingDescriptor)
|
||||
&& !canInstantiateAnnotationClass(callExpression)) {
|
||||
context.trace.report(ANNOTATION_CLASS_CONSTRUCTOR_CALL.on(callExpression));
|
||||
}
|
||||
if (DescriptorUtils.isEnumClass(functionDescriptor.getContainingDeclaration())) {
|
||||
if (DescriptorUtils.isEnumClass(containingDescriptor)) {
|
||||
context.trace.report(ENUM_CLASS_CONSTRUCTOR_CALL.on(callExpression));
|
||||
}
|
||||
if (containingDescriptor instanceof ClassDescriptor
|
||||
&& ((ClassDescriptor) containingDescriptor).getModality() == Modality.SEALED) {
|
||||
context.trace.report(SEALED_CLASS_CONSTRUCTOR_CALL.on(callExpression));
|
||||
}
|
||||
}
|
||||
|
||||
JetType type = functionDescriptor.getReturnType();
|
||||
|
||||
Reference in New Issue
Block a user