diff --git a/grammar/src/class_members.grm b/grammar/src/class_members.grm index 7f8bdabc1e0..538d1767f30 100644 --- a/grammar/src/class_members.grm +++ b/grammar/src/class_members.grm @@ -88,10 +88,12 @@ property ; getter + : modifiers "get" : modifiers "get" "(" ")" (":" type)? functionBody ; setter + : modifiers "set" : modifiers "set" "(" modifiers (SimpleName | parameter) ")" functionBody ; diff --git a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java index 5b769895340..fad8ce46984 100644 --- a/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java +++ b/idea/src/org/jetbrains/jet/lang/parsing/JetParsing.java @@ -761,7 +761,7 @@ public class JetParsing extends AbstractJetParsing { } /* - * getter + * getterOrSetter * : modifiers ("get" | "set") * : * ( "get" "(" ")" @@ -791,6 +791,7 @@ public class JetParsing extends AbstractJetParsing { myBuilder.disableNewlines(); expect(LPAR, "Expecting '('", TokenSet.create(RPAR, IDENTIFIER, COLON, LBRACE, EQ)); if (setter) { + PsiBuilder.Marker parameterList = mark(); PsiBuilder.Marker setterParameter = mark(); int lastId = findLastBefore(TokenSet.create(IDENTIFIER), TokenSet.create(RPAR, COMMA, COLON), false); createTruncatedBuilder(lastId).parseModifierList(); @@ -802,6 +803,7 @@ public class JetParsing extends AbstractJetParsing { parseTypeRef(); } setterParameter.done(VALUE_PARAMETER); + parameterList.done(VALUE_PARAMETER_LIST); } if (!at(RPAR)) errorUntil("Expecting ')'", TokenSet.create(RPAR, COLON, LBRACE, EQ, EOL_OR_SEMICOLON)); expect(RPAR, "Expecting ')'", TokenSet.create(RPAR, COLON, LBRACE, EQ)); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetConstructor.java b/idea/src/org/jetbrains/jet/lang/psi/JetConstructor.java index df6251706cb..59944c3989a 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetConstructor.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetConstructor.java @@ -4,6 +4,7 @@ import com.intellij.lang.ASTNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; +import org.jetbrains.jet.lexer.JetTokens; import java.util.Collections; import java.util.List; @@ -47,4 +48,15 @@ public class JetConstructor extends JetDeclaration implements JetDeclarationWith public JetExpression getBodyExpression() { return findChildByClass(JetExpression.class); } + + @Override + public boolean hasBlockBody() { + return findChildByType(JetTokens.EQ) == null; + } + + @NotNull + @Override + public JetElement asElement() { + return this; + } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java b/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java index df4f9c63776..b6561152e36 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetDeclarationWithBody.java @@ -1,5 +1,7 @@ package org.jetbrains.jet.lang.psi; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -12,5 +14,10 @@ public interface JetDeclarationWithBody { @Nullable String getName(); + + boolean hasBlockBody(); + + @NotNull + JetElement asElement(); } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java b/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java index 5dc52c9c05c..ad7dce04d5e 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetFunction.java @@ -41,7 +41,6 @@ public class JetFunction extends JetTypeParameterListOwner implements JetDeclara return findChildByClass(JetExpression.class); } - @Nullable public JetTypeReference getReceiverTypeRef() { PsiElement child = getFirstChild(); @@ -75,7 +74,14 @@ public class JetFunction extends JetTypeParameterListOwner implements JetDeclara return null; } + @Override public boolean hasBlockBody() { return findChildByType(JetTokens.EQ) == null; } + + @NotNull + @Override + public JetElement asElement() { + return this; + } } diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetProperty.java b/idea/src/org/jetbrains/jet/lang/psi/JetProperty.java index b4843927b94..1832238692f 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetProperty.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetProperty.java @@ -86,6 +86,7 @@ public class JetProperty extends JetNamedDeclaration { return null; } + @Nullable public JetExpression getInitializer() { PsiElement eq = findChildByType(JetTokens.EQ); return PsiTreeUtil.getNextSiblingOfType(eq, JetExpression.class); diff --git a/idea/src/org/jetbrains/jet/lang/psi/JetPropertyAccessor.java b/idea/src/org/jetbrains/jet/lang/psi/JetPropertyAccessor.java index 14c8a029e4c..f614cc326a6 100644 --- a/idea/src/org/jetbrains/jet/lang/psi/JetPropertyAccessor.java +++ b/idea/src/org/jetbrains/jet/lang/psi/JetPropertyAccessor.java @@ -6,10 +6,12 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.lexer.JetTokens; +import java.util.List; + /** * @author max */ -public class JetPropertyAccessor extends JetDeclaration { +public class JetPropertyAccessor extends JetDeclaration implements JetDeclarationWithBody { public JetPropertyAccessor(@NotNull ASTNode node) { super(node); } @@ -29,11 +31,32 @@ public class JetPropertyAccessor extends JetDeclaration { @Nullable public JetParameter getParameter() { - return (JetParameter) findChildByType(JetNodeTypes.VALUE_PARAMETER); + JetParameterList parameterList = (JetParameterList) findChildByType(JetNodeTypes.VALUE_PARAMETER_LIST); + if (parameterList == null) return null; + List parameters = parameterList.getParameters(); + if (parameters.isEmpty()) return null; + return parameters.get(0); } @Nullable - public JetExpression getBody() { + @Override + public JetExpression getBodyExpression() { return findChildByClass(JetExpression.class); } + + @Override + public boolean hasBlockBody() { + return findChildByType(JetTokens.EQ) == null; + } + + @NotNull + @Override + public JetElement asElement() { + return this; + } + + @Nullable + public JetTypeReference getReturnTypeReference() { + return findChildByClass(JetTypeReference.class); + } } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index 733a5bd2b66..25be2a5c05b 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -185,7 +185,8 @@ public class ClassDescriptorResolver { return functionDescriptor; } - private List resolveValueParameters(FunctionDescriptorImpl functionDescriptor, WritableScope parameterScope, List valueParameters) { + @NotNull + private List resolveValueParameters(MutableFunctionDescriptor functionDescriptor, WritableScope parameterScope, List valueParameters) { List result = new ArrayList(); for (int i = 0, valueParametersSize = valueParameters.size(); i < valueParametersSize; i++) { JetParameter valueParameter = valueParameters.get(i); @@ -203,25 +204,32 @@ public class ClassDescriptorResolver { } else { type = typeResolver.resolveType(parameterScope, typeReference); } - ValueParameterDescriptor valueParameterDescriptor = new ValueParameterDescriptorImpl( - functionDescriptor, - i, - AttributeResolver.INSTANCE.resolveAttributes(valueParameter.getModifierList()), - JetPsiUtil.safeName(valueParameter.getName()), - valueParameter.isMutable() ? type : null, - type, - valueParameter.getDefaultValue() != null, - false // TODO : varargs - ); - // TODO : Default values??? - result.add(valueParameterDescriptor); - trace.recordDeclarationResolution(valueParameter, valueParameterDescriptor); + ValueParameterDescriptor valueParameterDescriptor = resolveValueParameterDescriptor(functionDescriptor, valueParameter, i, type); parameterScope.addVariableDescriptor(valueParameterDescriptor); + result.add(valueParameterDescriptor); } return result; } + @NotNull + private MutableValueParameterDescriptor resolveValueParameterDescriptor(DeclarationDescriptor declarationDescriptor, JetParameter valueParameter, int index, JetType type) { + MutableValueParameterDescriptor valueParameterDescriptor = new ValueParameterDescriptorImpl( + declarationDescriptor, + index, + AttributeResolver.INSTANCE.resolveAttributes(valueParameter.getModifierList()), + JetPsiUtil.safeName(valueParameter.getName()), + valueParameter.isMutable() ? type : null, + type, + valueParameter.getDefaultValue() != null, + false // TODO : varargs + ); + // TODO : Default values??? + + trace.recordDeclarationResolution(valueParameter, valueParameterDescriptor); + return valueParameterDescriptor; + } + public List resolveTypeParameters(DeclarationDescriptor containingDescriptor, WritableScope extensibleScope, List typeParameters) { // TODO : Where-clause List result = new ArrayList(); @@ -269,11 +277,11 @@ public class ClassDescriptorResolver { @NotNull public VariableDescriptor resolveLocalVariableDescriptor(@NotNull DeclarationDescriptor containingDeclaration, @NotNull JetScope scope, @NotNull JetParameter parameter) { - JetType type = getParameterType(scope, parameter); + JetType type = resolveParameterType(scope, parameter); return resolveLocalVariableDescriptor(containingDeclaration, parameter, type); } - private JetType getParameterType(JetScope scope, JetParameter parameter) { + private JetType resolveParameterType(JetScope scope, JetParameter parameter) { JetTypeReference typeReference = parameter.getTypeReference(); JetType type; if (typeReference != null) { @@ -310,19 +318,93 @@ public class ClassDescriptorResolver { return propertyDescriptor; } - public VariableDescriptor resolvePropertyDescriptor(@NotNull DeclarationDescriptor containingDeclaration, @NotNull JetScope scope, JetProperty property) { + public PropertyDescriptor resolvePropertyDescriptor(@NotNull DeclarationDescriptor containingDeclaration, @NotNull JetScope scope, JetProperty property) { JetType type = getType(scope, property); - VariableDescriptorImpl propertyDescriptor = new PropertyDescriptor( + PropertyDescriptor propertyDescriptor = new PropertyDescriptor( containingDeclaration, AttributeResolver.INSTANCE.resolveAttributes(property.getModifierList()), JetPsiUtil.safeName(property.getName()), property.isVar() ? type : null, type); + + propertyDescriptor.initialize( + resolvePropertyGetterDescriptor(scope, property, propertyDescriptor), + resolvePropertySetterDescriptors(scope, property, propertyDescriptor)); + trace.recordDeclarationResolution(property, propertyDescriptor); return propertyDescriptor; } + @Nullable + private PropertySetterDescriptor resolvePropertySetterDescriptors(@NotNull JetScope scope, @NotNull JetProperty property, @NotNull PropertyDescriptor propertyDescriptor) { + JetPropertyAccessor setter = property.getSetter(); + if (setter != null && !property.isVar()) { + semanticServices.getErrorHandler().genericError(setter.asElement().getNode(), "A 'val'-property cannot have a setter"); + return null; + } + PropertySetterDescriptor setterDescriptor = null; + if (setter != null) { + List attributes = AttributeResolver.INSTANCE.resolveAttributes(setter.getModifierList()); + JetParameter parameter = setter.getParameter(); + + setterDescriptor = new PropertySetterDescriptor(propertyDescriptor, attributes); + if (parameter != null) { + if (parameter.isRef()) { + semanticServices.getErrorHandler().genericError(parameter.getRefNode(), "Setter parameters can not be 'ref'"); + } + + // This check is redundant: the parser does not allow a default value, but we'll keep it just in case + JetExpression defaultValue = parameter.getDefaultValue(); + if (defaultValue != null) { + semanticServices.getErrorHandler().genericError(defaultValue.getNode(), "Setter parameters can not have default values"); + } + + JetType type; + JetTypeReference typeReference = parameter.getTypeReference(); + if (typeReference == null) { + type = propertyDescriptor.getInType(); // TODO : this maybe unknown at this point + } + else { + type = typeResolver.resolveType(scope, typeReference); + JetType inType = propertyDescriptor.getInType(); + if (inType != null) { + if (!semanticServices.getTypeChecker().isSubtypeOf(type, inType)) { + semanticServices.getErrorHandler().genericError(typeReference.getNode(), "Setter parameter type must be a subtype of the type of the property, i.e. " + inType); + } + } + else { + // TODO : the same check may be needed later??? + } + } + + MutableValueParameterDescriptor valueParameterDescriptor = resolveValueParameterDescriptor(setterDescriptor, parameter, 0, type); + setterDescriptor.initialize(valueParameterDescriptor); + } + trace.recordDeclarationResolution(setter, setterDescriptor); + } + return setterDescriptor; + } + + @Nullable + private PropertyGetterDescriptor resolvePropertyGetterDescriptor(@NotNull JetScope scope, @NotNull JetProperty property, @NotNull PropertyDescriptor propertyDescriptor) { + PropertyGetterDescriptor getterDescriptor = null; + JetPropertyAccessor getter = property.getGetter(); + if (getter != null) { + List attributes = AttributeResolver.INSTANCE.resolveAttributes(getter.getModifierList()); + + JetType returnType = null; + JetTypeReference returnTypeReference = getter.getReturnTypeReference(); + if (returnTypeReference != null) { + returnType = typeResolver.resolveType(scope, returnTypeReference); + } + + getterDescriptor = new PropertyGetterDescriptor(propertyDescriptor, attributes, returnType); + trace.recordDeclarationResolution(getter, getterDescriptor); + } + return getterDescriptor; + } + @NotNull private JetType getType(@NotNull JetScope scope, @NotNull JetProperty property) { // TODO : receiver? @@ -336,7 +418,7 @@ public class ClassDescriptorResolver { type = ErrorUtils.createErrorType("No type, no body"); } else { // TODO : ??? Fix-point here: what if we have something like "val a = foo {a.bar()}" - type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.THROW_EXCEPTION).getType(scope, initializer, false); + type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.THROW_EXCEPTION).safeGetType(scope, initializer, false); } } else { type = typeResolver.resolveType(scope, propertyTypeRef); @@ -395,7 +477,7 @@ public class ClassDescriptorResolver { @NotNull ClassDescriptor classDescriptor, @NotNull JetScope scope, @NotNull JetParameter parameter) { - JetType type = getParameterType(scope, parameter); + JetType type = resolveParameterType(scope, parameter); String name = parameter.getName(); VariableDescriptorImpl propertyDescriptor = new PropertyDescriptor( classDescriptor, diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index cd43e3949c7..3b55d47cb2f 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -18,6 +18,7 @@ public class TopDownAnalyzer { private final Map classes = new LinkedHashMap(); private final Map namespaceScopes = new LinkedHashMap(); private final Map functions = new LinkedHashMap(); + private final Map properties = new LinkedHashMap(); private final Map declaringScopes = new HashMap(); private final JetSemanticServices semanticServices; @@ -240,8 +241,9 @@ public class TopDownAnalyzer { private void processProperty(WritableScope declaringScope, JetProperty property) { declaringScopes.put(property, declaringScope); - VariableDescriptor descriptor = classDescriptorResolver.resolvePropertyDescriptor(declaringScope.getContainingDeclaration(), declaringScope, property); + PropertyDescriptor descriptor = classDescriptorResolver.resolvePropertyDescriptor(declaringScope.getContainingDeclaration(), declaringScope, property); declaringScope.addVariableDescriptor(descriptor); + properties.put(property, descriptor); } private void processClassObject(JetClassObject classObject) { @@ -258,52 +260,13 @@ public class TopDownAnalyzer { WritableScope declaringScope = declaringScopes.get(declaration); assert declaringScope != null; - assert declaration instanceof JetFunction || declaration instanceof JetConstructor; JetDeclarationWithBody declarationWithBody = (JetDeclarationWithBody) declaration; JetExpression bodyExpression = declarationWithBody.getBodyExpression(); if (declaration instanceof JetFunction) { - JetFunction function = (JetFunction) declaration; - FunctionDescriptorImpl functionDescriptorImpl = (FunctionDescriptorImpl) descriptor; - if (bodyExpression != null) { - JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression); - JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); - - assert readyToProcessExpressions : "Must be ready collecting types"; - - if (function.getReturnTypeRef() != null) { - typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptorImpl); - } - else { - JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, functionDescriptorImpl); - if (returnType == null) { - returnType = ErrorUtils.createErrorType("Unable to infer body type"); - } - functionDescriptorImpl.setUnsubstitutedReturnType(returnType); - } - - List unreachableElements = new ArrayList(); - flowInformationProvider.collectUnreachableExpressions(function, unreachableElements); - - // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well - Set rootElements = JetPsiUtil.findRootExpressions(unreachableElements); - - // TODO : (return 1) || (return 2) -- only || and right of it is unreachable - // TODO : try {return 1} finally {return 2}. Currently 'return 1' is reported as unreachable, - // though it'd better be reported more specifically - - for (JetElement element : rootElements) { - semanticServices.getErrorHandler().genericError(element.getNode(), "Unreachable code"); - } - } - else { - if (function.getReturnTypeRef() == null) { - semanticServices.getErrorHandler().genericError(function.getNode(), "This function must either declare a return type or have a body element"); - ((FunctionDescriptorImpl) descriptor).setUnsubstitutedReturnType(ErrorUtils.createErrorType("No type, no body")); - } - } + resolveFunctionBody((JetFunction) declaration, (FunctionDescriptorImpl) descriptor, declaringScope); } else if (declaration instanceof JetConstructor) { if (bodyExpression != null) { @@ -316,9 +279,77 @@ public class TopDownAnalyzer { } assert descriptor.getUnsubstitutedReturnType() != null; } + + for (Map.Entry entry : properties.entrySet()) { + JetProperty declaration = entry.getKey(); + PropertyDescriptor descriptor = entry.getValue(); + WritableScope declaringScope = declaringScopes.get(declaration); + + JetExpression initializer = declaration.getInitializer(); + if (initializer != null) { + JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, initializer); + JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); + JetType type = typeInferrer.getType(declaringScope, initializer, false); + // TODO : check type + } + + JetPropertyAccessor getter = declaration.getGetter(); + PropertyGetterDescriptor getterDescriptor = descriptor.getGetter(); + if (getter != null && getterDescriptor != null) { + resolveFunctionBody(getter, getterDescriptor, declaringScope); + } + + JetPropertyAccessor setter = declaration.getSetter(); + PropertySetterDescriptor setterDescriptor = descriptor.getSetter(); + if (setter != null && setterDescriptor != null) { + resolveFunctionBody(setter, setterDescriptor, declaringScope); + } + + } } - private JetFlowInformationProvider computeFlowData(@NotNull JetDeclaration declaration, @NotNull JetExpression bodyExpression) { + private void resolveFunctionBody(@NotNull JetDeclarationWithBody function, @NotNull MutableFunctionDescriptor functionDescriptor, @NotNull WritableScope declaringScope) { + JetExpression bodyExpression = function.getBodyExpression(); + if (bodyExpression != null) { + JetFlowInformationProvider flowInformationProvider = computeFlowData(function.asElement(), bodyExpression); + JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); + + assert readyToProcessExpressions : "Must be ready collecting types"; + + if (functionDescriptor.isReturnTypeSet()) { + typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptor); + } + else { + JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, functionDescriptor); + if (returnType == null) { + returnType = ErrorUtils.createErrorType("Unable to infer body type"); + } + functionDescriptor.setUnsubstitutedReturnType(returnType); + } + + List unreachableElements = new ArrayList(); + flowInformationProvider.collectUnreachableExpressions(function.asElement(), unreachableElements); + + // This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well + Set rootElements = JetPsiUtil.findRootExpressions(unreachableElements); + + // TODO : (return 1) || (return 2) -- only || and right of it is unreachable + // TODO : try {return 1} finally {return 2}. Currently 'return 1' is reported as unreachable, + // though it'd better be reported more specifically + + for (JetElement element : rootElements) { + semanticServices.getErrorHandler().genericError(element.getNode(), "Unreachable code"); + } + } + else { + if (!functionDescriptor.isReturnTypeSet()) { + semanticServices.getErrorHandler().genericError(function.asElement().getNode(), "This function must either declare a return type or have a body element"); + functionDescriptor.setUnsubstitutedReturnType(ErrorUtils.createErrorType("No type, no body")); + } + } + } + + private JetFlowInformationProvider computeFlowData(@NotNull JetElement declaration, @NotNull JetExpression bodyExpression) { final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration); final Map pseudocodeMap = new HashMap(); final Map representativeInstructions = new HashMap(); diff --git a/idea/src/org/jetbrains/jet/lang/types/DeclarationDescriptorVisitor.java b/idea/src/org/jetbrains/jet/lang/types/DeclarationDescriptorVisitor.java index 7041d78eb72..91c417de0c8 100644 --- a/idea/src/org/jetbrains/jet/lang/types/DeclarationDescriptorVisitor.java +++ b/idea/src/org/jetbrains/jet/lang/types/DeclarationDescriptorVisitor.java @@ -43,4 +43,16 @@ public class DeclarationDescriptorVisitor { public R visitValueParameterDescriptor(ValueParameterDescriptor descriptor, D data) { return visitVariableDescriptor(descriptor, data); } + + public R visitPropertyGetterDescriptor(PropertyGetterDescriptor descriptor, D data) { + return visitPropertyAccessorDescriptor(descriptor, data); + } + + private R visitPropertyAccessorDescriptor(PropertyAccessorDescriptor descriptor, D data) { + return visitFunctionDescriptor(descriptor, data); + } + + public R visitPropertySetterDescriptor(PropertySetterDescriptor descriptor, D data) { + return visitPropertyAccessorDescriptor(descriptor, data); + } } diff --git a/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorImpl.java index c55aa470811..4fb2f28a956 100644 --- a/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/FunctionDescriptorImpl.java @@ -8,7 +8,7 @@ import java.util.List; /** * @author abreslav */ -public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements FunctionDescriptor { +public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements MutableFunctionDescriptor { private List typeParameters; @@ -43,10 +43,16 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements return this; } + @Override public void setUnsubstitutedReturnType(@NotNull JetType unsubstitutedReturnType) { this.unsubstitutedReturnType = unsubstitutedReturnType; } + @Override + public boolean isReturnTypeSet() { + return unsubstitutedReturnType != null; + } + @Override @NotNull public List getTypeParameters() { diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 970b57b4db3..4b0706488ab 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -269,7 +269,7 @@ public class JetTypeInferrer { } @NotNull - public JetType getFunctionReturnType(@NotNull JetScope outerScope, JetFunction function, FunctionDescriptor functionDescriptor) { + public JetType getFunctionReturnType(@NotNull JetScope outerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) { Map typeMap = collectReturnedExpressions(outerScope, function, functionDescriptor); Collection types = typeMap.values(); return types.isEmpty() ? JetStandardClasses.getNothingType() : semanticServices.getTypeChecker().commonSupertype(types); @@ -280,7 +280,7 @@ public class JetTypeInferrer { return typeCache.get(expression); } - public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetFunction function, @NotNull FunctionDescriptor functionDescriptor) { + public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor) { Map typeMap = collectReturnedExpressions(outerScope, function, functionDescriptor); if (typeMap.isEmpty()) { return; // The function returns Nothing @@ -292,8 +292,8 @@ public class JetTypeInferrer { JetTypeChecker typeChecker = semanticServices.getTypeChecker(); if (!typeChecker.isSubtypeOf(actualType, expectedReturnType)) { if (typeChecker.isConvertibleBySpecialConversion(actualType, expectedReturnType)) { - if (expectedReturnType.getConstructor().equals(JetStandardClasses.getUnitType().getConstructor()) && - element.getParent() instanceof JetReturnExpression) { + if (expectedReturnType.getConstructor().equals(JetStandardClasses.getUnitType().getConstructor()) + && element.getParent() instanceof JetReturnExpression) { semanticServices.getErrorHandler().genericError(element.getNode(), "This function must return a value of type Unit"); } } @@ -315,14 +315,14 @@ public class JetTypeInferrer { } } - private Map collectReturnedExpressions(JetScope outerScope, JetFunction function, FunctionDescriptor functionDescriptor) { + private Map collectReturnedExpressions(JetScope outerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) { JetExpression bodyExpression = function.getBodyExpression(); assert bodyExpression != null; JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, semanticServices); getType(functionInnerScope, bodyExpression, function.hasBlockBody()); Collection returnedExpressions = new ArrayList(); Collection elementsReturningUnit = new ArrayList(); - flowInformationProvider.collectReturnedInformation(function, returnedExpressions, elementsReturningUnit); + flowInformationProvider.collectReturnedInformation(function.asElement(), returnedExpressions, elementsReturningUnit); Map typeMap = new HashMap(); for (JetExpression returnedExpression : returnedExpressions) { JetType cachedType = getCachedType(returnedExpression); diff --git a/idea/src/org/jetbrains/jet/lang/types/MutableFunctionDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/MutableFunctionDescriptor.java new file mode 100644 index 00000000000..ce757cd85f1 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/MutableFunctionDescriptor.java @@ -0,0 +1,12 @@ +package org.jetbrains.jet.lang.types; + +import org.jetbrains.annotations.NotNull; + +/** + * @author abreslav + */ +public interface MutableFunctionDescriptor extends FunctionDescriptor { + void setUnsubstitutedReturnType(@NotNull JetType type); + + boolean isReturnTypeSet(); +} diff --git a/idea/src/org/jetbrains/jet/lang/types/MutableValueParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/MutableValueParameterDescriptor.java new file mode 100644 index 00000000000..26440ca8170 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/MutableValueParameterDescriptor.java @@ -0,0 +1,10 @@ +package org.jetbrains.jet.lang.types; + +import org.jetbrains.annotations.NotNull; + +/** + * @author abreslav + */ +public interface MutableValueParameterDescriptor extends ValueParameterDescriptor { + void setType(@NotNull JetType type); +} diff --git a/idea/src/org/jetbrains/jet/lang/types/PropertyAccessorDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/PropertyAccessorDescriptor.java index ca07110f5d5..d06eb547037 100644 --- a/idea/src/org/jetbrains/jet/lang/types/PropertyAccessorDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/PropertyAccessorDescriptor.java @@ -8,40 +8,10 @@ import java.util.List; /** * @author abreslav */ -public class PropertyAccessorDescriptor extends DeclarationDescriptorImpl implements FunctionDescriptor { - public enum AccessorType { - GETTER("get"), - SETTER("set"); +public abstract class PropertyAccessorDescriptor extends DeclarationDescriptorImpl implements FunctionDescriptor { - private final String name; - - AccessorType(String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - - public static PropertyAccessorDescriptor createGetterDescriptor( - @NotNull PropertyDescriptor correspondingProperty, - @NotNull List attributes, - @NotNull JetType returnType - ) { - return new PropertyAccessorDescriptor(correspondingProperty, attributes, AccessorType.GETTER); - } - - public static PropertyAccessorDescriptor createSetterDescriptor( - @NotNull PropertyDescriptor correspondingProperty, - @NotNull List attributes, - @NotNull ValueParameterDescriptor parameter - ) { - return new PropertyAccessorDescriptor(correspondingProperty, attributes, AccessorType.SETTER); - } - - protected PropertyAccessorDescriptor(@NotNull PropertyDescriptor correspondingProperty, @NotNull List attributes, @NotNull AccessorType accessorType) { - super(correspondingProperty.getContainingDeclaration(), attributes, accessorType.getName()); + protected PropertyAccessorDescriptor(@NotNull PropertyDescriptor correspondingProperty, @NotNull List attributes, @NotNull String name) { + super(correspondingProperty.getContainingDeclaration(), attributes, name); } @NotNull @@ -61,21 +31,4 @@ public class PropertyAccessorDescriptor extends DeclarationDescriptorImpl implem public List getTypeParameters() { return Collections.emptyList(); } - - @NotNull - @Override - public List getUnsubstitutedValueParameters() { - throw new UnsupportedOperationException(); // TODO - } - - @NotNull - @Override - public JetType getUnsubstitutedReturnType() { - throw new UnsupportedOperationException(); // TODO - } - - @Override - public R accept(DeclarationDescriptorVisitor visitor, D data) { - throw new UnsupportedOperationException(); // TODO - } } diff --git a/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java index c373c8faa98..36053939463 100644 --- a/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/types/PropertyDescriptor.java @@ -10,8 +10,8 @@ import java.util.List; */ public class PropertyDescriptor extends VariableDescriptorImpl { - private PropertyAccessorDescriptor getter; - private PropertyAccessorDescriptor setter; + private PropertyGetterDescriptor getter; + private PropertySetterDescriptor setter; private boolean hasBackingField; public PropertyDescriptor( @@ -23,11 +23,18 @@ public class PropertyDescriptor extends VariableDescriptorImpl { super(containingDeclaration, attributes, name, inType, outType); } - public PropertyAccessorDescriptor getGetter() { + public void initialize(@Nullable PropertyGetterDescriptor getter, @Nullable PropertySetterDescriptor setter) { + this.getter = getter; + this.setter = setter; + } + + @Nullable + public PropertyGetterDescriptor getGetter() { return getter; } - public PropertyAccessorDescriptor getSetter() { + @Nullable + public PropertySetterDescriptor getSetter() { return setter; } diff --git a/idea/src/org/jetbrains/jet/lang/types/PropertyGetterDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/PropertyGetterDescriptor.java new file mode 100644 index 00000000000..4306294df2a --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/PropertyGetterDescriptor.java @@ -0,0 +1,47 @@ +package org.jetbrains.jet.lang.types; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +/** + * @author abreslav + */ +public class PropertyGetterDescriptor extends PropertyAccessorDescriptor implements MutableFunctionDescriptor { + private JetType returnType; + + public PropertyGetterDescriptor(@NotNull PropertyDescriptor correspondingProperty, @NotNull List attributes, @Nullable JetType returnType) { + super(correspondingProperty, attributes, "get-" + correspondingProperty.getName()); + this.returnType = returnType == null ? correspondingProperty.getOutType() : returnType; + } + + @NotNull + @Override + public List getUnsubstitutedValueParameters() { + return Collections.emptyList(); + } + + @NotNull + @Override + public JetType getUnsubstitutedReturnType() { + return returnType; + } + + @Override + public void setUnsubstitutedReturnType(@NotNull JetType type) { + assert this.returnType == null : this.returnType; + this.returnType = type; + } + + @Override + public boolean isReturnTypeSet() { + return returnType != null; + } + + @Override + public R accept(DeclarationDescriptorVisitor visitor, D data) { + return visitor.visitPropertyGetterDescriptor(this, data); + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/PropertySetterDescriptor.java b/idea/src/org/jetbrains/jet/lang/types/PropertySetterDescriptor.java new file mode 100644 index 00000000000..d4094268d8e --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/types/PropertySetterDescriptor.java @@ -0,0 +1,54 @@ +package org.jetbrains.jet.lang.types; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +/** + * @author abreslav + */ +public class PropertySetterDescriptor extends PropertyAccessorDescriptor implements MutableFunctionDescriptor { + + private MutableValueParameterDescriptor parameter; + + public PropertySetterDescriptor(@NotNull PropertyDescriptor correspondingProperty, @NotNull List attributes) { + super(correspondingProperty, attributes, "set-" + correspondingProperty.getName()); + } + + public void initialize(@NotNull MutableValueParameterDescriptor parameter) { + assert this.parameter == null; + this.parameter = parameter; + } + + public void setParameterType(@NotNull JetType type) { + parameter.setType(type); + } + + @NotNull + @Override + public List getUnsubstitutedValueParameters() { + return Collections.singletonList(parameter); + } + + @NotNull + @Override + public JetType getUnsubstitutedReturnType() { + return JetStandardClasses.getUnitType(); + } + + @Override + public R accept(DeclarationDescriptorVisitor visitor, D data) { + return visitor.visitPropertySetterDescriptor(this, data); + } + + @Override + public void setUnsubstitutedReturnType(@NotNull JetType type) { + throw new UnsupportedOperationException("Can't set return type for a setter"); + } + + @Override + public boolean isReturnTypeSet() { + return true; + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/ValueParameterDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/types/ValueParameterDescriptorImpl.java index e14bf3e438a..bd6bd623ff0 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ValueParameterDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/ValueParameterDescriptorImpl.java @@ -8,9 +8,10 @@ import java.util.List; /** * @author abreslav */ -public class ValueParameterDescriptorImpl extends VariableDescriptorImpl implements ValueParameterDescriptor { +public class ValueParameterDescriptorImpl extends VariableDescriptorImpl implements MutableValueParameterDescriptor { private final boolean hasDefaultValue; private final boolean isVararg; + private final boolean isVar; private final int index; public ValueParameterDescriptorImpl( @@ -26,6 +27,32 @@ public class ValueParameterDescriptorImpl extends VariableDescriptorImpl impleme this.index = index; this.hasDefaultValue = hasDefaultValue; this.isVararg = isVararg; + this.isVar = inType != null; + } + + public ValueParameterDescriptorImpl( + @NotNull DeclarationDescriptor containingDeclaration, + int index, + @NotNull List attributes, + @NotNull String name, + boolean isVar, + boolean hasDefaultValue, + boolean isVararg) { + super(containingDeclaration, attributes, name, null, null); + this.index = index; + this.hasDefaultValue = hasDefaultValue; + this.isVararg = isVararg; + this.isVar = isVar; + } + + @Override + public void setType(@NotNull JetType type) { + assert getOutType() == null; + setOutType(type); + if (isVar) { + assert getInType() == null; + setInType(type); + } } @Override diff --git a/idea/src/org/jetbrains/jet/lang/types/VariableDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/types/VariableDescriptorImpl.java index ba44c08dd3f..8af5b5900bd 100644 --- a/idea/src/org/jetbrains/jet/lang/types/VariableDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/VariableDescriptorImpl.java @@ -9,8 +9,8 @@ import java.util.List; * @author abreslav */ public abstract class VariableDescriptorImpl extends DeclarationDescriptorImpl implements VariableDescriptor { - private final JetType inType; - private final JetType outType; + private JetType inType; + private JetType outType; public VariableDescriptorImpl( @NotNull DeclarationDescriptor containingDeclaration, @@ -25,7 +25,6 @@ public abstract class VariableDescriptorImpl extends DeclarationDescriptorImpl i this.outType = outType; } - @NotNull @Override public JetType getOutType() { return outType; @@ -35,4 +34,12 @@ public abstract class VariableDescriptorImpl extends DeclarationDescriptorImpl i public JetType getInType() { return inType; } + + protected void setInType(JetType inType) { + this.inType = inType; + } + + protected void setOutType(JetType outType) { + this.outType = outType; + } } diff --git a/idea/testData/checker/Properties.jet b/idea/testData/checker/Properties.jet index a4ad01200e6..b96992e50ef 100644 --- a/idea/testData/checker/Properties.jet +++ b/idea/testData/checker/Properties.jet @@ -1 +1,7 @@ -val x : Int \ No newline at end of file + var x : Int = 1 + x + get() : Int = 1 + set(ref value : Long) {$x = value} + + val xx : Int = 1 + x + get() : Int = 1 + set(ref value : Long) {$x = value} diff --git a/idea/testData/psi/Properties.txt b/idea/testData/psi/Properties.txt index 29544f3195c..a6cc1090297 100644 --- a/idea/testData/psi/Properties.txt +++ b/idea/testData/psi/Properties.txt @@ -149,8 +149,9 @@ JetFile: Properties.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('sad') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('sad') PsiElement(RPAR)(')') PsiWhiteSpace(' ') PsiElement(EQ)('=') @@ -204,8 +205,9 @@ JetFile: Properties.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('it') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('it') PsiElement(RPAR)(')') PsiWhiteSpace(' ') BLOCK @@ -265,8 +267,9 @@ JetFile: Properties.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('it') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('it') PsiElement(RPAR)(')') PsiWhiteSpace(' ') BLOCK @@ -341,15 +344,16 @@ JetFile: Properties.jet PsiWhiteSpace(' ') PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('a') - PsiWhiteSpace(' ') - PsiElement(COLON)(':') - PsiWhiteSpace(' ') - TYPE_REFERENCE - USER_TYPE - REFERENCE_EXPRESSION - PsiElement(IDENTIFIER)('b') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('b') PsiElement(RPAR)(')') PsiWhiteSpace(' ') BLOCK @@ -403,15 +407,16 @@ JetFile: Properties.jet PsiWhiteSpace(' ') PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('a') - PsiWhiteSpace(' ') - PsiElement(COLON)(':') - PsiWhiteSpace(' ') - TYPE_REFERENCE - USER_TYPE - REFERENCE_EXPRESSION - PsiElement(IDENTIFIER)('b') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('b') PsiElement(RPAR)(')') PsiWhiteSpace(' ') BLOCK @@ -548,8 +553,9 @@ JetFile: Properties.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('sad') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('sad') PsiElement(RPAR)(')') PsiWhiteSpace(' ') PsiElement(EQ)('=') @@ -654,8 +660,9 @@ JetFile: Properties.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('sad') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('sad') PsiElement(RPAR)(')') PsiWhiteSpace(' ') PsiElement(EQ)('=') diff --git a/idea/testData/psi/Properties_ERR.txt b/idea/testData/psi/Properties_ERR.txt index 86eea34e3bf..12fd9ae5008 100644 --- a/idea/testData/psi/Properties_ERR.txt +++ b/idea/testData/psi/Properties_ERR.txt @@ -235,9 +235,10 @@ JetFile: Properties_ERR.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiErrorElement:Expecting parameter name - + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiErrorElement:Expecting parameter name + PsiElement(RPAR)(')') PsiWhiteSpace(' ') BLOCK diff --git a/idea/testData/psi/SoftKeywords.txt b/idea/testData/psi/SoftKeywords.txt index 3089e4e29d3..ef481f3943a 100644 --- a/idea/testData/psi/SoftKeywords.txt +++ b/idea/testData/psi/SoftKeywords.txt @@ -292,15 +292,16 @@ JetFile: SoftKeywords.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('S') - PsiWhiteSpace(' ') - PsiElement(COLON)(':') - PsiWhiteSpace(' ') - TYPE_REFERENCE - USER_TYPE - REFERENCE_EXPRESSION - PsiElement(IDENTIFIER)('s') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('S') + PsiWhiteSpace(' ') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('s') PsiElement(RPAR)(')') PsiWhiteSpace(' ') BLOCK diff --git a/idea/testData/psi/examples/priorityqueues/BinaryHeap.txt b/idea/testData/psi/examples/priorityqueues/BinaryHeap.txt index 63f9cc2f588..b239b7ca9b6 100644 --- a/idea/testData/psi/examples/priorityqueues/BinaryHeap.txt +++ b/idea/testData/psi/examples/priorityqueues/BinaryHeap.txt @@ -1081,8 +1081,9 @@ JetFile: BinaryHeap.jet PROPERTY_ACCESSOR PsiElement(set)('set') PsiElement(LPAR)('(') - VALUE_PARAMETER - PsiElement(IDENTIFIER)('it') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('it') PsiElement(RPAR)(')') PsiWhiteSpace(' ') BLOCK