From 8c8f1eb4dd1bc85ddd50e7b64046d0e73df53cfb Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Wed, 4 May 2011 15:13:25 +0400 Subject: [PATCH] JET-6 Perform checks for primary and secondary constructors (In progress): class having a stateful property must have a constructor --- .../jet/lang/JetSemanticServices.java | 7 ++++++- .../lang/resolve/MutableClassDescriptor.java | 17 +++++++++++++++-- .../jet/lang/resolve/TopDownAnalyzer.java | 18 ++++++++++++++++++ .../jet/lang/resolve/WritableScopeImpl.java | 19 ++++++++++++++++++- .../jet/lang/types/JetStandardClasses.java | 2 +- idea/testData/checker/Constructors.jet | 2 +- idea/testData/checker/ForRangeConventions.jet | 4 ++-- idea/testData/checker/NamespaceQualified.jet | 2 +- idea/testData/checker/PrimaryConstructors.jet | 14 ++++++++++++++ 9 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 idea/testData/checker/PrimaryConstructors.jet diff --git a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java index 7a78f779de7..dbdf49047ca 100644 --- a/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java +++ b/idea/src/org/jetbrains/jet/lang/JetSemanticServices.java @@ -63,6 +63,11 @@ public class JetSemanticServices { @NotNull public WritableScope createWritableScope(@NotNull JetScope scope, @NotNull DeclarationDescriptor owner) { - return new WritableScopeImpl(scope, owner, errorHandler); + return new WritableScopeImpl(scope, owner, errorHandler, null); + } + + @NotNull + public WritableScope createWritableScope(@NotNull JetScope scope, @NotNull DeclarationDescriptor owner, @NotNull DeclarationDescriptorVisitor modificationListener) { + return new WritableScopeImpl(scope, owner, errorHandler, modificationListener); } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java index 195daad51b7..67cc96b3649 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.resolve; +import com.google.common.collect.Sets; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.JetSemanticServices; @@ -7,14 +8,16 @@ import org.jetbrains.jet.lang.types.*; import java.util.List; import java.util.Map; +import java.util.Set; /** * @author abreslav */ public class MutableClassDescriptor extends MutableDeclarationDescriptor implements ClassDescriptor { + private final WritableFunctionGroup constructors = new WritableFunctionGroup(""); private final WritableScope classHeaderScope; private final WritableScope writableMemberScope; - private final WritableFunctionGroup constructors = new WritableFunctionGroup(""); + private final Set properties = Sets.newHashSet(); private ConstructorDescriptor primaryConstructor; private TypeConstructor typeConstructor; @@ -23,7 +26,13 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme public MutableClassDescriptor(@NotNull JetSemanticServices semanticServices, @NotNull DeclarationDescriptor containingDeclaration, @NotNull JetScope outerScope) { super(containingDeclaration); this.classHeaderScope = semanticServices.createWritableScope(outerScope, this); - this.writableMemberScope = semanticServices.createWritableScope(classHeaderScope, this); + this.writableMemberScope = semanticServices.createWritableScope(classHeaderScope, this, new DeclarationDescriptorVisitor() { + @Override + public Void visitPropertyDescriptor(PropertyDescriptor descriptor, WritableScope data) { + properties.add(descriptor); + return null; + } + }); this.unsubstitutedMemberScope = this.writableMemberScope; } @@ -48,6 +57,10 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme this.typeConstructor = typeConstructor; } + public Set getProperties() { + return properties; + } + @NotNull @Override public JetScope getMemberScope(List typeArguments) { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 73b2f29bb05..2e4fb5cf33f 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -4,6 +4,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.JetSemanticServices; @@ -312,6 +313,23 @@ public class TopDownAnalyzer { resolveSecondaryConstructorBodies(); resolveFunctionBodies(); + + for (Map.Entry entry : classes.entrySet()) { + MutableClassDescriptor classDescriptor = entry.getValue(); + JetClass jetClass = entry.getKey(); + if (!jetClass.hasPrimaryConstructor()) { + for (PropertyDescriptor propertyDescriptor : classDescriptor.getProperties()) { + if (trace.hasBackingField(propertyDescriptor)) { + PsiElement nameIdentifier = jetClass.getNameIdentifier(); + if (nameIdentifier != null) { + semanticServices.getErrorHandler().genericError(nameIdentifier.getNode(), + "This class must have a primary constructor, because property " + propertyDescriptor.getName() + " has a backing field"); + } + break; + } + } + } + } } private void resolveDelegationSpecifierLists() { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java index 1f15ab77962..7511f8ca375 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/WritableScopeImpl.java @@ -34,10 +34,14 @@ public class WritableScopeImpl extends WritableScopeWithImports { @Nullable private JetType thisType; - public WritableScopeImpl(@NotNull JetScope scope, @NotNull DeclarationDescriptor owner, @NotNull ErrorHandler errorHandler) { + @Nullable + private final DeclarationDescriptorVisitor modificationListener; + + public WritableScopeImpl(@NotNull JetScope scope, @NotNull DeclarationDescriptor owner, @NotNull ErrorHandler errorHandler, @Nullable DeclarationDescriptorVisitor modificationListener) { super(scope); this.ownerDeclarationDescriptor = owner; this.errorHandler = errorHandler; + this.modificationListener = modificationListener; } @NotNull @@ -46,6 +50,12 @@ public class WritableScopeImpl extends WritableScopeWithImports { return ownerDeclarationDescriptor; } + private void notifyListeners(DeclarationDescriptor descriptor) { + if (modificationListener != null) { + descriptor.accept(modificationListener, this); + } + } + @NotNull private Map> getLabelsToDescriptors() { if (labelsToDescriptors == null) { @@ -80,6 +90,7 @@ public class WritableScopeImpl extends WritableScopeWithImports { labelsToDescriptors.put(name, declarationDescriptors); } declarationDescriptors.add(descriptor); + notifyListeners(descriptor); } @NotNull @@ -99,6 +110,7 @@ public class WritableScopeImpl extends WritableScopeWithImports { } // TODO : Should this always happen? propertyDescriptors.put(variableDescriptor.getName(), variableDescriptor); + notifyListeners(variableDescriptor); } @Override @@ -143,6 +155,7 @@ public class WritableScopeImpl extends WritableScopeWithImports { functionGroups.put(name, functionGroup); } functionGroup.addFunction(functionDescriptor); + notifyListeners(functionDescriptor); } @Override @@ -174,6 +187,7 @@ public class WritableScopeImpl extends WritableScopeWithImports { errorHandler.redeclaration(originalDescriptor, typeParameterDescriptor); } classifierDescriptors.put(name, typeParameterDescriptor); + notifyListeners(typeParameterDescriptor); } @NotNull @@ -197,6 +211,7 @@ public class WritableScopeImpl extends WritableScopeWithImports { errorHandler.redeclaration(originalDescriptor, classifierDescriptor); } classifierDescriptors.put(name, classifierDescriptor); + notifyListeners(classifierDescriptor); } @Override @@ -239,6 +254,7 @@ public class WritableScopeImpl extends WritableScopeWithImports { if (oldValue != null) { errorHandler.redeclaration(oldValue, namespaceDescriptor); } + notifyListeners(namespaceDescriptor); } @Override @@ -285,6 +301,7 @@ public class WritableScopeImpl extends WritableScopeWithImports { @Override public void addPropertyDescriptorByFieldName(@NotNull String fieldName, @NotNull PropertyDescriptor propertyDescriptor) { getPropertyDescriptorsByFieldNames().put(fieldName, propertyDescriptor); + notifyListeners(propertyDescriptor); } @Override diff --git a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java index 86f4c9a919d..3c609177b55 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetStandardClasses.java @@ -168,7 +168,7 @@ public class JetStandardClasses { /*package*/ static final JetScope STANDARD_CLASSES; static { - WritableScope writableScope = new WritableScopeImpl(JetScope.EMPTY, STANDARD_CLASSES_NAMESPACE, ErrorHandler.DO_NOTHING); + WritableScope writableScope = new WritableScopeImpl(JetScope.EMPTY, STANDARD_CLASSES_NAMESPACE, ErrorHandler.DO_NOTHING, null); STANDARD_CLASSES = writableScope; writableScope.addClassifierAlias("Unit", getTuple(0)); diff --git a/idea/testData/checker/Constructors.jet b/idea/testData/checker/Constructors.jet index d743cc0b5bd..a74bb2c2b99 100644 --- a/idea/testData/checker/Constructors.jet +++ b/idea/testData/checker/Constructors.jet @@ -43,7 +43,7 @@ class WithCPI(x : Int) { val xy : Int = x } -class NoCPI { +class NoCPI { val a = 1 var ab = 1 get() = 1 diff --git a/idea/testData/checker/ForRangeConventions.jet b/idea/testData/checker/ForRangeConventions.jet index a1b4022a7b4..7b031319857 100644 --- a/idea/testData/checker/ForRangeConventions.jet +++ b/idea/testData/checker/ForRangeConventions.jet @@ -35,7 +35,7 @@ class NotRange5() { class AmbiguousHasNextIterator { fun hasNext() : Boolean - val hasNext : Boolean + val hasNext : Boolean get() = false fun next() : Int } @@ -44,7 +44,7 @@ class NotRange6() { } class ImproperIterator4 { - val hasNext : Int + val hasNext : Int get() = 1 fun next() : Int } diff --git a/idea/testData/checker/NamespaceQualified.jet b/idea/testData/checker/NamespaceQualified.jet index 407c67e2cd0..2b8832aef89 100644 --- a/idea/testData/checker/NamespaceQualified.jet +++ b/idea/testData/checker/NamespaceQualified.jet @@ -31,7 +31,7 @@ class Iteratee { abstract fun done() : O } -class Sum : Iteratee { +class Sum() : Iteratee { override fun process(item : Int) : Iteratee { return foobar.done(item); } diff --git a/idea/testData/checker/PrimaryConstructors.jet b/idea/testData/checker/PrimaryConstructors.jet new file mode 100644 index 00000000000..1ce335c01e7 --- /dev/null +++ b/idea/testData/checker/PrimaryConstructors.jet @@ -0,0 +1,14 @@ +class X { + val x : Int +} + +class Y() { + val x : Int +} + +class Y1 { + val x : Int get() = 1 +} + +class Z : Y() { +}