KT-650 Prohibit creating class without constructor

This commit is contained in:
svtk
2012-01-24 15:41:38 +04:00
parent ee5fcccda9
commit 70d0cd882b
43 changed files with 170 additions and 218 deletions
@@ -7,6 +7,7 @@ public enum ClassKind {
CLASS,
TRAIT,
ENUM_CLASS,
ENUM_ENTRY,
ANNOTATION_CLASS,
OBJECT
}
@@ -116,7 +116,6 @@ public interface Errors {
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> MUST_BE_INITIALIZED_OR_BE_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "Property must be initialized or be abstract");
DiagnosticWithParameterFactory<JetProperty, JetType> PROPERTY_INITIALIZER_IN_TRAIT = DiagnosticWithParameterFactory.create(ERROR, "Property initializers are not allowed in traits", DiagnosticParameters.TYPE);
SimpleDiagnosticFactory PROPERTY_INITIALIZER_NO_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR, "Initializer is not allowed here because this property has no backing field");
DiagnosticWithParameterFactory<JetProperty, JetClass> PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR = DiagnosticWithParameterFactory.create(ERROR, "Property initializers are not allowed when no primary constructor is present", DiagnosticParameters.CLASS);
PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, String, ClassDescriptor, JetClass> ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, String, ClassDescriptor, JetClass>(ERROR, "Abstract property {0} in non-abstract class {1}") {
@NotNull
protected DiagnosticWithPsiElement<JetModifierListOwner> on(@NotNull JetModifierListOwner elementToBlame, @NotNull TextRange textRangeToMark, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetClass aClass) {
@@ -141,19 +140,13 @@ public interface Errors {
SimpleDiagnosticFactory PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT = SimpleDiagnosticFactory.create(ERROR, "Projections are not allowed on type arguments of functions and properties"); // TODO : better positioning
SimpleDiagnosticFactory SUPERTYPE_NOT_INITIALIZED = SimpleDiagnosticFactory.create(ERROR, "This type has a constructor, and thus must be initialized here");
SimpleDiagnosticFactory SUPERTYPE_NOT_INITIALIZED_DEFAULT = SimpleDiagnosticFactory.create(ERROR, "Constructor invocation should be explicitly specified");
SimpleDiagnosticFactory SECONDARY_CONSTRUCTOR_BUT_NO_PRIMARY = SimpleDiagnosticFactory.create(ERROR, "A secondary constructor may appear only in a class that has a primary constructor");
SimpleDiagnosticFactory SECONDARY_CONSTRUCTOR_NO_INITIALIZER_LIST = SimpleDiagnosticFactory.create(ERROR, "Secondary constructors must have an initializer list");
SimpleDiagnosticFactory BY_IN_SECONDARY_CONSTRUCTOR = SimpleDiagnosticFactory.create(ERROR, "'by'-clause is only supported for primary constructors");
SimpleDiagnosticFactory INITIALIZER_WITH_NO_ARGUMENTS = SimpleDiagnosticFactory.create(ERROR, "Constructor arguments required");
SimpleDiagnosticFactory MANY_CALLS_TO_THIS = SimpleDiagnosticFactory.create(ERROR, "Only one call to 'this(...)' is allowed");
PsiElementOnlyDiagnosticFactory1<JetModifierList, CallableMemberDescriptor> NOTHING_TO_OVERRIDE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "{0} overrides nothing", DescriptorRenderer.TEXT);
PsiElementOnlyDiagnosticFactory1<JetClass, PropertyDescriptor> PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "This class must have a primary constructor, because property {0} has a backing field");
ParameterizedDiagnosticFactory1<JetClassOrObject> PRIMARY_CONSTRUCTOR_MISSING_SUPER_CONSTRUCTOR_CALL = new ParameterizedDiagnosticFactory1<JetClassOrObject>(ERROR, "Class {0} must have a constructor in order to be able to initialize supertypes") {
@Override
protected String makeMessageFor(@NotNull JetClassOrObject argument) {
return JetPsiUtil.safeName(argument.getName());
}
};
PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, CallableMemberDescriptor, CallableMemberDescriptor, DeclarationDescriptor> VIRTUAL_MEMBER_HIDDEN = PsiElementOnlyDiagnosticFactory3.create(ERROR, "''{0}'' hides ''{1}'' in class {2} and needs 'override' modifier", DescriptorRenderer.TEXT);
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, VariableDescriptor> UNINITIALIZED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable ''{0}'' must be initialized", NAME);
@@ -217,7 +210,7 @@ public interface Errors {
SimpleDiagnosticFactory CAST_NEVER_SUCCEEDS = SimpleDiagnosticFactory.create(WARNING, "This cast can never succeed");
DiagnosticWithParameterFactory<JetPropertyAccessor, JetType> WRONG_SETTER_PARAMETER_TYPE = DiagnosticWithParameterFactory.create(ERROR, "Setter parameter type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE);
DiagnosticWithParameterFactory<JetPropertyAccessor, JetType> WRONG_GETTER_RETURN_TYPE = DiagnosticWithParameterFactory.create(ERROR, "Getter return type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE);
ParameterizedDiagnosticFactory1<ClassifierDescriptor> NO_CLASS_OBJECT = ParameterizedDiagnosticFactory1.create(ERROR, "Classifier {0} does not have a class object", NAME);
ParameterizedDiagnosticFactory1<ClassifierDescriptor> NO_CLASS_OBJECT = ParameterizedDiagnosticFactory1.create(ERROR, "Please specify constructor invocation; classifier {0} does not have a class object", NAME);
SimpleDiagnosticFactory NO_GENERICS_IN_SUPERTYPE_SPECIFIER = SimpleDiagnosticFactory.create(ERROR, "Generic arguments of the base type must be specified");
SimpleDiagnosticFactory HAS_NEXT_PROPERTY_AND_FUNCTION_AMBIGUITY = SimpleDiagnosticFactory.create(ERROR, "An ambiguity between 'iterator().hasNext()' function and 'iterator().hasNext' property");
@@ -27,15 +27,17 @@ public class FunctionSignatureDiagnosticFactory extends DiagnosticFactoryWithMes
// primary constructor
JetClass klass = (JetClass) jetDeclaration;
PsiElement nameAsDeclaration = klass.getNameIdentifier();
PsiElement primaryConstructorParameterList = klass.getPrimaryConstructorParameterList();
if (nameAsDeclaration == null || primaryConstructorParameterList == null) {
if (nameAsDeclaration == null){
return klass.getTextRange();
} else {
return new TextRange(
nameAsDeclaration.getTextRange().getStartOffset(),
primaryConstructorParameterList.getTextRange().getEndOffset()
);
}
PsiElement primaryConstructorParameterList = klass.getPrimaryConstructorParameterList();
if (primaryConstructorParameterList == null) {
return nameAsDeclaration.getTextRange();
}
return new TextRange(
nameAsDeclaration.getTextRange().getStartOffset(),
primaryConstructorParameterList.getTextRange().getEndOffset()
);
} else {
// safe way
return jetDeclaration.getTextRange();
@@ -120,32 +120,26 @@ public class BodyResolver {
context.getTrace().report(SUPERTYPE_INITIALIZED_IN_TRAIT.on(node));
}
JetTypeReference typeReference = call.getTypeReference();
if (typeReference != null) {
if (descriptor.getUnsubstitutedPrimaryConstructor() != null) {
OverloadResolutionResults<FunctionDescriptor> results = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY).resolveCall(
context.getTrace(), scopeForConstructor,
CallMaker.makeCall(ReceiverDescriptor.NO_RECEIVER, null, call), NO_EXPECTED_TYPE);
if (results.isSuccess()) {
JetType supertype = results.getResultingDescriptor().getReturnType();
recordSupertype(typeReference, supertype);
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(supertype);
if (classDescriptor != null) {
if (classDescriptor.getKind() == ClassKind.TRAIT) {
context.getTrace().report(CONSTRUCTOR_IN_TRAIT.on(node));
}
}
}
else {
recordSupertype(typeReference, context.getTrace().getBindingContext().get(BindingContext.TYPE, typeReference));
if (typeReference == null) return;
if (descriptor.getUnsubstitutedPrimaryConstructor() == null) {
assert descriptor.getKind() == ClassKind.TRAIT;
return;
}
OverloadResolutionResults<FunctionDescriptor> results = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY).resolveCall(
context.getTrace(), scopeForConstructor,
CallMaker.makeCall(ReceiverDescriptor.NO_RECEIVER, null, call), NO_EXPECTED_TYPE);
if (results.isSuccess()) {
JetType supertype = results.getResultingDescriptor().getReturnType();
recordSupertype(typeReference, supertype);
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(supertype);
if (classDescriptor != null) {
if (classDescriptor.getKind() == ClassKind.TRAIT) {
context.getTrace().report(CONSTRUCTOR_IN_TRAIT.on(node));
}
}
else if (descriptor.getKind() != ClassKind.TRAIT) {
JetType supertype = context.getTrace().getBindingContext().get(BindingContext.TYPE, typeReference);
recordSupertype(typeReference, supertype);
assert valueArgumentList != null;
context.getTrace().report(PRIMARY_CONSTRUCTOR_MISSING_SUPER_CONSTRUCTOR_CALL.on(valueArgumentList, jetClass));
}
}
else {
recordSupertype(typeReference, context.getTrace().getBindingContext().get(BindingContext.TYPE, typeReference));
}
}
@@ -159,7 +153,18 @@ public class BodyResolver {
if (classDescriptor != null) {
if (descriptor.getKind() != ClassKind.TRAIT) {
if (classDescriptor.hasConstructors() && !ErrorUtils.isError(classDescriptor.getTypeConstructor()) && classDescriptor.getKind() != ClassKind.TRAIT) {
context.getTrace().report(SUPERTYPE_NOT_INITIALIZED.on(specifier));
boolean hasConstructorWithoutParams = false;
for (ConstructorDescriptor constructor : classDescriptor.getConstructors()) {
if (constructor.getValueParameters().isEmpty()) {
hasConstructorWithoutParams = true;
}
}
if (!hasConstructorWithoutParams) {
context.getTrace().report(SUPERTYPE_NOT_INITIALIZED.on(specifier));
}
else {
context.getTrace().report(SUPERTYPE_NOT_INITIALIZED_DEFAULT.on(specifier));
}
}
}
}
@@ -287,9 +292,7 @@ public class BodyResolver {
final CallResolver callResolver = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY); // TODO: dataFlowInfo
JetClass containingClass = PsiTreeUtil.getParentOfType(declaration, JetClass.class);
assert containingClass != null : "This must be guaranteed by the parser";
if (!containingClass.hasPrimaryConstructor()) {
if (classDescriptor.getUnsubstitutedPrimaryConstructor() == null) {
context.getTrace().report(SECONDARY_CONSTRUCTOR_BUT_NO_PRIMARY.on(declaration.getNameNode()));
}
else {
@@ -143,11 +143,12 @@ public class DeclarationResolver {
}
private void processPrimaryConstructor(MutableClassDescriptor classDescriptor, JetClass klass) {
if (!klass.hasPrimaryConstructor()) return;
if (classDescriptor.getKind() == ClassKind.TRAIT) {
// context.getTrace().getErrorHandler().genericError(klass.getPrimaryConstructorParameterList().getNode(), "A trait may not have a constructor");
context.getTrace().report(CONSTRUCTOR_IN_TRAIT.on(klass.getPrimaryConstructorParameterList()));
JetParameterList primaryConstructorParameterList = klass.getPrimaryConstructorParameterList();
if (primaryConstructorParameterList != null) {
context.getTrace().report(CONSTRUCTOR_IN_TRAIT.on(primaryConstructorParameterList));
}
if (!klass.hasPrimaryConstructor()) return;
}
// TODO : not all the parameters are real properties
@@ -34,8 +34,6 @@ public class DeclarationsChecker {
}
public void process() {
checkIfPrimaryConstructorIsNecessary();
Map<JetClass, MutableClassDescriptor> classes = context.getClasses();
for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
JetClass aClass = entry.getKey();
@@ -77,25 +75,6 @@ public class DeclarationsChecker {
}
private void checkIfPrimaryConstructorIsNecessary() {
for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
MutableClassDescriptor classDescriptor = entry.getValue();
JetClass jetClass = entry.getKey();
if (!context.completeAnalysisNeeded(jetClass)) return;
if (classDescriptor.getUnsubstitutedPrimaryConstructor() == null && !(classDescriptor.getKind() == ClassKind.TRAIT)) {
for (PropertyDescriptor propertyDescriptor : classDescriptor.getProperties()) {
if (context.getTrace().getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
PsiElement nameIdentifier = jetClass.getNameIdentifier();
if (nameIdentifier != null) {
context.getTrace().report(PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY.on(jetClass, nameIdentifier, propertyDescriptor));
}
break;
}
}
}
}
}
private void checkClass(JetClass aClass, MutableClassDescriptor classDescriptor) {
checkOpenMembers(aClass, classDescriptor);
checkTraitModifiers(aClass);
@@ -246,12 +225,6 @@ public class DeclarationsChecker {
else if (!backingFieldRequired) {
context.getTrace().report(PROPERTY_INITIALIZER_NO_BACKING_FIELD.on(initializer));
}
else if (classDescriptor != null && classDescriptor.getUnsubstitutedPrimaryConstructor() == null) {
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR.on(property, initializer, (JetClass) classElement));
}
}
protected void checkFunction(JetNamedFunction function, NamedFunctionDescriptor functionDescriptor) {
@@ -788,7 +788,7 @@ public class DescriptorResolver {
@Nullable
public ConstructorDescriptorImpl resolvePrimaryConstructorDescriptor(@NotNull JetScope scope, @NotNull ClassDescriptor classDescriptor, @NotNull JetClass classElement) {
if (!classElement.hasPrimaryConstructor()) return null;
if (classDescriptor.getKind() == ClassKind.ENUM_ENTRY && !classElement.hasPrimaryConstructor()) return null;
return createConstructorDescriptor(
scope,
classDescriptor,
@@ -105,7 +105,7 @@ public class TypeHierarchyResolver {
@Override
public void visitObjectDeclaration(JetObjectDeclaration declaration) {
final MutableClassDescriptor objectDescriptor = createClassDescriptorForObject(declaration, owner, outerScope);
final MutableClassDescriptor objectDescriptor = createClassDescriptorForObject(declaration, owner, outerScope, ClassKind.OBJECT);
context.getTrace().record(FQNAME_TO_CLASS_DESCRIPTOR, JetPsiUtil.getFQName(declaration), objectDescriptor);
}
@@ -114,32 +114,27 @@ public class TypeHierarchyResolver {
MutableClassDescriptor classObjectDescriptor = ((MutableClassDescriptor) owner).getClassObjectDescriptor();
assert classObjectDescriptor != null : enumEntry.getParent().getText();
if (enumEntry.getPrimaryConstructorParameterList() == null) {
MutableClassDescriptor classDescriptor = createClassDescriptorForObject(enumEntry, classObjectDescriptor, outerScopeForStatic);
context.getObjects().remove(enumEntry);
context.getClasses().put(enumEntry, classDescriptor);
}
else {
// TODO : Special kind for enum entry classes?
MutableClassDescriptor mutableClassDescriptor = new MutableClassDescriptor(context.getTrace(), classObjectDescriptor, outerScope, ClassKind.CLASS);
visitClassOrObject(
enumEntry,
(Map) context.getClasses(),
mutableClassDescriptor
);
classObjectDescriptor.addClassifierDescriptor(mutableClassDescriptor);
createClassDescriptorForObject(enumEntry, classObjectDescriptor, outerScopeForStatic, ClassKind.ENUM_ENTRY);
return;
}
MutableClassDescriptor mutableClassDescriptor = new MutableClassDescriptor(context.getTrace(), classObjectDescriptor, outerScope, ClassKind.ENUM_ENTRY);
visitClassOrObject(
enumEntry,
(Map) context.getClasses(),
mutableClassDescriptor
);
classObjectDescriptor.addClassifierDescriptor(mutableClassDescriptor);
}
private MutableClassDescriptor createClassDescriptorForObject(@NotNull JetClassOrObject declaration,
@NotNull NamespaceLike owner, JetScope scope) {
MutableClassDescriptor mutableClassDescriptor = new MutableClassDescriptor(context.getTrace(), owner, scope, ClassKind.OBJECT) {
private MutableClassDescriptor createClassDescriptorForObject(@NotNull JetClassOrObject declaration, @NotNull NamespaceLike owner, JetScope scope, ClassKind classKind) {
MutableClassDescriptor mutableClassDescriptor = new MutableClassDescriptor(context.getTrace(), owner, scope, classKind) {
@Override
public ClassObjectStatus setClassObjectDescriptor(@NotNull MutableClassDescriptor classObjectDescriptor) {
return ClassObjectStatus.NOT_ALLOWED;
}
};
visitClassOrObject(declaration, (Map) context.getObjects(), mutableClassDescriptor);
Map<JetClassOrObject, MutableClassDescriptor> map = classKind == ClassKind.OBJECT ? (Map) context.getObjects() : (Map) context.getClasses();
visitClassOrObject(declaration, map, mutableClassDescriptor);
createPrimaryConstructorForObject((JetDeclaration) declaration, mutableClassDescriptor);
owner.addObjectDescriptor(mutableClassDescriptor);
context.getTrace().record(BindingContext.CLASS, declaration, mutableClassDescriptor);
@@ -176,7 +171,7 @@ public class TypeHierarchyResolver {
public void visitClassObject(JetClassObject classObject) {
JetObjectDeclaration objectDeclaration = classObject.getObjectDeclaration();
if (objectDeclaration != null) {
NamespaceLike.ClassObjectStatus status = owner.setClassObjectDescriptor(createClassDescriptorForObject(objectDeclaration, owner, outerScopeForStatic));
NamespaceLike.ClassObjectStatus status = owner.setClassObjectDescriptor(createClassDescriptorForObject(objectDeclaration, owner, outerScopeForStatic, ClassKind.OBJECT));
switch (status) {
case DUPLICATE:
context.getTrace().report(MANY_CLASS_OBJECTS.on(classObject));
@@ -80,7 +80,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
if (classifier != null) {
context.trace.report(NO_CLASS_OBJECT.on(expression, classifier));
context.trace.record(REFERENCE_TARGET, expression, classifier);
return ErrorUtils.createErrorType("No class object in " + expression.getReferencedName());
return classifier.getDefaultType();
}
temporaryTrace.commit();
return result[0];