From 64a8da32963b36e32f84a7aee98131791327c141 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 26 Apr 2011 18:13:16 +0400 Subject: [PATCH] JET-14 Support anonymous initializers --- grammar/src/class.grm | 3 +- .../jet/lang/parsing/JetParsing.java | 26 +++++---- .../org/jetbrains/jet/lang/psi/JetClass.java | 12 ++++ .../jetbrains/jet/lang/psi/JetClassBody.java | 6 ++ .../lang/resolve/ClassDescriptorResolver.java | 5 +- .../LazySubstitutingClassDescriptor.java | 5 ++ .../lang/resolve/MutableClassDescriptor.java | 5 ++ .../jet/lang/resolve/TopDownAnalyzer.java | 56 +++++++++++++++---- .../jet/lang/resolve/TypeResolver.java | 8 ++- .../jet/lang/types/ClassDescriptor.java | 2 + .../jet/lang/types/ClassDescriptorImpl.java | 5 ++ .../checker/AnonymousInitializers.jet | 34 +++++++++++ .../codegen/inheritingFromArrayList.jet | 2 +- 13 files changed, 140 insertions(+), 29 deletions(-) create mode 100644 idea/testData/checker/AnonymousInitializers.jet diff --git a/grammar/src/class.grm b/grammar/src/class.grm index d808ea17add..796d0ffee29 100644 --- a/grammar/src/class.grm +++ b/grammar/src/class.grm @@ -13,8 +13,7 @@ internal class Example>(protected val x : Foo, y : So class : modifiers "class" SimpleName typeParameters? - ("wraps" | modifiers)? - valueParameters? + ("wraps" valueParameters? | modifiers valueParameters?)? (":" attributes delegationSpecifier{","})? (classBody? | enumClassBody) ; diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java index 935b3c07dfa..068e9e0bf3f 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java @@ -253,8 +253,8 @@ public class JetParsing extends AbstractJetParsing { /* * (modifier | attribute)* */ - public void parseModifierList(JetNodeType nodeType) { - parseModifierList(nodeType, null); + public boolean parseModifierList(JetNodeType nodeType) { + return parseModifierList(nodeType, null); } /** @@ -262,7 +262,7 @@ public class JetParsing extends AbstractJetParsing { * * Feeds modifiers (not attributes) into the passed consumer, if it is not null */ - public void parseModifierList(JetNodeType nodeType, Consumer tokenConsumer) { + public boolean parseModifierList(JetNodeType nodeType, Consumer tokenConsumer) { PsiBuilder.Marker list = mark(); boolean empty = true; while (!eof()) { @@ -282,6 +282,7 @@ public class JetParsing extends AbstractJetParsing { } else { list.done(nodeType); } + return !empty; } /* @@ -376,8 +377,9 @@ public class JetParsing extends AbstractJetParsing { * class * : modifiers "class" SimpleName * typeParameters? - * ("wraps" | modifiers)? - * ("(" primaryConstructorParameter{","} ")")? + * ( + * ("wraps" "(" primaryConstructorParameter{","} ")") | + * (modifiers "(" primaryConstructorParameter{","} ")"))? * (":" attributes delegationSpecifier{","})? * (classBody? | enumClassBody) * ; @@ -391,13 +393,17 @@ public class JetParsing extends AbstractJetParsing { if (at(WRAPS_KEYWORD)) { advance(); // WRAPS_KEYWORD + parseValueParameterList(false, TokenSet.create(COLON, LBRACE)); } else { - parseModifierList(PRIMARY_CONSTRUCTOR_MODIFIER_LIST); - } - - if (at(LPAR)) { - parseValueParameterList(false, TokenSet.EMPTY); + if (parseModifierList(PRIMARY_CONSTRUCTOR_MODIFIER_LIST)) { + parseValueParameterList(false, TokenSet.create(COLON, LBRACE)); + } + else { + if (at(LPAR)) { + parseValueParameterList(false, TokenSet.create(COLON, LBRACE)); + } + } } if (at(COLON)) { diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetClass.java b/idea/src/org/jetbrains/jet/lang/psi/JetClass.java index 2dc9127aec5..33e3c243c0f 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetClass.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetClass.java @@ -66,4 +66,16 @@ public class JetClass extends JetTypeParameterListOwner implements JetClassOrObj public JetModifierList getPrimaryConstructorModifierList() { return (JetModifierList) findChildByType(JetNodeTypes.PRIMARY_CONSTRUCTOR_MODIFIER_LIST); } + + @NotNull + public List getAnonymousInitializers() { + JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY); + if (body == null) return Collections.emptyList(); + + return body.getAnonymousInitializers(); + } + + public boolean hasPrimaryConstructor() { + return getPrimaryConstructorParameterList() != null; + } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java b/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java index 3764491912b..693dd3da6ef 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetClassBody.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.lang.psi; import com.intellij.lang.ASTNode; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.JetNodeTypes; import java.util.List; @@ -26,4 +27,9 @@ public class JetClassBody extends JetElement { public void accept(@NotNull JetVisitor visitor) { visitor.visitClassBody(this); } + + @NotNull + public List getAnonymousInitializers() { + return findChildrenByType(JetNodeTypes.ANONYMOUS_INITIALIZER); + } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index f6de56ae9e1..0f667215811 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -473,15 +473,14 @@ public class ClassDescriptorResolver { @Nullable public ConstructorDescriptor resolvePrimaryConstructorDescriptor(@NotNull JetScope scope, @NotNull ClassDescriptor classDescriptor, @NotNull JetClass classElement) { - JetParameterList primaryConstructorParameterList = classElement.getPrimaryConstructorParameterList(); - if (primaryConstructorParameterList == null) return null; + if (!classElement.hasPrimaryConstructor()) return null; return createConstructorDescriptor( scope, classDescriptor, true, classElement.getPrimaryConstructorModifierList(), classElement, - primaryConstructorParameterList.getParameters()); + classElement.getPrimaryConstructorParameters()); } @NotNull diff --git a/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java index cf2a954e4c7..3db02359589 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/LazySubstitutingClassDescriptor.java @@ -54,6 +54,11 @@ public class LazySubstitutingClassDescriptor implements ClassDescriptor { return original.getUnsubstitutedPrimaryConstructor(); } + @Override + public boolean hasConstructors() { + return original.hasConstructors(); + } + @Override public List getAttributes() { throw new UnsupportedOperationException(); // TODO diff --git a/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java index 9fa30fd8146..659cd038c23 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/MutableClassDescriptor.java @@ -112,4 +112,9 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme public ConstructorDescriptor getUnsubstitutedPrimaryConstructor() { return primaryConstructor; } + + @Override + public boolean hasConstructors() { + return !constructors.isEmpty(); + } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index d7df0678d21..69c6c98aee2 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -220,9 +220,14 @@ public class TopDownAnalyzer { } } + @Override + public void visitAnonymousInitializer(JetClassInitializer initializer) { + // Nothing + } + @Override public void visitDeclaration(JetDeclaration dcl) { - semanticServices.getErrorHandler().genericError(dcl.getNode(), "Unsupported declaration: " + dcl); // TODO + semanticServices.getErrorHandler().genericError(dcl.getNode(), "[TopDownAnalyzer] Unsupported declaration: " + dcl); // TODO } }); } @@ -230,7 +235,7 @@ public class TopDownAnalyzer { } private void processPrimaryConstructor(MutableClassDescriptor classDescriptor, JetClass klass) { - if (klass.getPrimaryConstructorParameterList() == null) return; // No constructors + if (!klass.hasPrimaryConstructor()) return; // TODO : not all the parameters are real properties WritableScope memberScope = classDescriptor.getWritableUnsubstitutedMemberScope(); // TODO : this is REALLY questionable @@ -282,6 +287,9 @@ public class TopDownAnalyzer { private void resolveBehaviorDeclarationBodies() { resolveDelegationSpecifierLists(); + + resolveAnonymousInitializers(); + resolveSecondaryConstructorBodies(); //TODO : anonymous initializers @@ -316,7 +324,7 @@ public class TopDownAnalyzer { JetTypeReference typeReference = call.getTypeReference(); if (typeReference != null) { typeInferrer.checkConstructorCall(descriptor.getWritableUnsubstitutedMemberScope(), typeReference, call); - if (jetClass.getPrimaryConstructorParameterList() == null) { + if (!jetClass.hasPrimaryConstructor()) { JetArgumentList valueArgumentList = call.getValueArgumentList(); assert valueArgumentList != null; semanticServices.getErrorHandler().genericError(valueArgumentList.getNode(), @@ -332,7 +340,7 @@ public class TopDownAnalyzer { DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor(); if (declarationDescriptor instanceof ClassDescriptor) { ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; - if (classDescriptor.getUnsubstitutedPrimaryConstructor() != null) { + if (classDescriptor.hasConstructors()) { semanticServices.getErrorHandler().genericError(specifier.getNode(), "This type has a constructor, and thus must be initialized here"); } } @@ -357,6 +365,29 @@ public class TopDownAnalyzer { } } + private void resolveAnonymousInitializers() { + for (Map.Entry entry : classes.entrySet()) { + JetClass jetClass = entry.getKey(); + MutableClassDescriptor classDescriptor = entry.getValue(); + + List anonymousInitializers = jetClass.getAnonymousInitializers(); + if (jetClass.hasPrimaryConstructor()) { + ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); + assert primaryConstructor != null; + final JetScope scopeForConstructor = getInnerScopeForConstructor(primaryConstructor, classDescriptor.getWritableUnsubstitutedMemberScope()); + JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow + for (JetClassInitializer anonymousInitializer : anonymousInitializers) { + typeInferrer.getType(scopeForConstructor, anonymousInitializer.getBody(), true); + } + } + else { + for (JetClassInitializer anonymousInitializer : anonymousInitializers) { + semanticServices.getErrorHandler().genericError(anonymousInitializer.getNode(), "Anonymous initializers are only allowed in the presence of a primary constructor"); + } + } + } + } + private void resolveSecondaryConstructorBodies() { for (Map.Entry entry : constructors.entrySet()) { JetDeclaration declaration = entry.getKey(); @@ -372,17 +403,13 @@ public class TopDownAnalyzer { } private void resolveSecondaryConstructorBody(JetConstructor declaration, final ConstructorDescriptor descriptor, final WritableScope declaringScope) { - WritableScope constructorScope = semanticServices.createWritableScope(declaringScope, declaringScope.getContainingDeclaration()); - for (PropertyDescriptor propertyDescriptor : declaringScopesToProperties.get(descriptor.getContainingDeclaration())) { - constructorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor); - } - final JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(constructorScope, descriptor, semanticServices); + final JetScope functionInnerScope = getInnerScopeForConstructor(descriptor, declaringScope); final JetTypeInferrer typeInferrerForInitializers = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE); JetClass containingClass = PsiTreeUtil.getParentOfType(declaration, JetClass.class); assert containingClass != null : "This must be guaranteed by the parser"; - if (containingClass.getPrimaryConstructorParameterList() == null) { + if (!containingClass.hasPrimaryConstructor()) { semanticServices.getErrorHandler().genericError(declaration.getNameNode(), "A secondary constructor may appear only in a class that has a primary constructor"); } else { @@ -444,6 +471,15 @@ public class TopDownAnalyzer { } } + @NotNull + private JetScope getInnerScopeForConstructor(@NotNull ConstructorDescriptor descriptor, @NotNull JetScope declaringScope) { + WritableScope constructorScope = semanticServices.createWritableScope(declaringScope, declaringScope.getContainingDeclaration()); + for (PropertyDescriptor propertyDescriptor : declaringScopesToProperties.get(descriptor.getContainingDeclaration())) { + constructorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor); + } + return FunctionDescriptorUtil.getFunctionInnerScope(constructorScope, descriptor, semanticServices); + } + private void resolvePropertyDeclarationBodies() { for (Map.Entry entry : properties.entrySet()) { JetProperty declaration = entry.getKey(); diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java index 0b3cbd64e85..dd626dd2c2f 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TypeResolver.java @@ -122,9 +122,11 @@ public class TypeResolver { parameterTypes.add(resolveType(scope, parameter.getTypeReference())); } - JetType returnType = resolveType(scope, type.getReturnTypeRef()); - - result[0] = JetStandardClasses.getFunctionType(attributes, receiverType, parameterTypes, returnType); + JetTypeReference returnTypeRef = type.getReturnTypeRef(); + if (returnTypeRef != null) { + JetType returnType = resolveType(scope, returnTypeRef); + result[0] = JetStandardClasses.getFunctionType(attributes, receiverType, parameterTypes, returnType); + } } @Override diff --git a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java index 0ad8ac14a87..b28df08c970 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptor.java @@ -20,6 +20,8 @@ public interface ClassDescriptor extends ClassifierDescriptor { @Nullable ConstructorDescriptor getUnsubstitutedPrimaryConstructor(); + boolean hasConstructors(); + @Override @NotNull DeclarationDescriptor getContainingDeclaration(); diff --git a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptorImpl.java index 2e47f3c7c3f..b67a6d48346 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ClassDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/ClassDescriptorImpl.java @@ -89,4 +89,9 @@ public class ClassDescriptorImpl extends DeclarationDescriptorImpl implements Cl public ConstructorDescriptor getUnsubstitutedPrimaryConstructor() { return primaryConstructor; } + + @Override + public boolean hasConstructors() { + return !constructors.isEmpty(); + } } \ No newline at end of file diff --git a/idea/testData/checker/AnonymousInitializers.jet b/idea/testData/checker/AnonymousInitializers.jet new file mode 100644 index 00000000000..d579cb6f7c8 --- /dev/null +++ b/idea/testData/checker/AnonymousInitializers.jet @@ -0,0 +1,34 @@ +class NoC { + { + + } + + val a : Int get() = 1 + + { + + } +} + +class WithC() { + val x : Int + { + $x = 1 + $y = 2 + val b = x + + } + + val a : Int get() = 1 + + { + val z = b + val zz = x + val zzz = $a + } + + this(a : Int) : this() { + val b = x + } + +} \ No newline at end of file diff --git a/idea/testData/codegen/inheritingFromArrayList.jet b/idea/testData/codegen/inheritingFromArrayList.jet index 9a28bf5a303..d0ee9df62d0 100644 --- a/idea/testData/codegen/inheritingFromArrayList.jet +++ b/idea/testData/codegen/inheritingFromArrayList.jet @@ -1 +1 @@ -class Foo : java.util.ArrayList<*> \ No newline at end of file +class Foo() : java.util.ArrayList() \ No newline at end of file