KT-650 Prohibit creating class without constructor
This commit is contained in:
@@ -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");
|
||||
|
||||
+9
-7
@@ -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));
|
||||
|
||||
+1
-1
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user