Index: compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
diff --git a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
index fa91e72cfef0861a610dea8de842c66435738882..583ad3f0e08ac9f98e69f8b6bb9d226429a30f66 100644 |
--- a/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
+++ b/compiler/java/com/google/dart/compiler/type/TypeAnalyzer.java |
@@ -17,6 +17,7 @@ import com.google.dart.compiler.DartCompilationError; |
import com.google.dart.compiler.DartCompilationPhase; |
import com.google.dart.compiler.DartCompilerContext; |
import com.google.dart.compiler.ErrorCode; |
+import com.google.dart.compiler.ast.ASTVisitor; |
import com.google.dart.compiler.ast.DartArrayAccess; |
import com.google.dart.compiler.ast.DartArrayLiteral; |
import com.google.dart.compiler.ast.DartAssertion; |
@@ -62,7 +63,6 @@ import com.google.dart.compiler.ast.DartNativeBlock; |
import com.google.dart.compiler.ast.DartNativeDirective; |
import com.google.dart.compiler.ast.DartNewExpression; |
import com.google.dart.compiler.ast.DartNode; |
-import com.google.dart.compiler.ast.ASTVisitor; |
import com.google.dart.compiler.ast.DartNullLiteral; |
import com.google.dart.compiler.ast.DartParameter; |
import com.google.dart.compiler.ast.DartParameterizedTypeNode; |
@@ -803,7 +803,8 @@ public class TypeAnalyzer implements DartCompilationPhase { |
node.setReferencedElement(element); |
return checkInvocation(node, node, name, element.getType()); |
} |
- Type receiver = nonVoidTypeOf(node.getTarget()); |
+ DartNode target = node.getTarget(); |
+ Type receiver = nonVoidTypeOf(target); |
List<DartExpression> arguments = node.getArgs(); |
Member member = lookupMember(receiver, name, node); |
if (member != null) { |
@@ -1157,6 +1158,20 @@ public class TypeAnalyzer implements DartCompilationPhase { |
if (returnType != null && returnType.getType() != voidType) { |
typeError(returnType, TypeErrorCode.SETTER_RETURN_TYPE, methodElement.getName()); |
} |
+ if (methodElement.getParameters().size() > 0) { |
+ Element parameterElement = methodElement.getParameters().get(0); |
+ Type setterType = parameterElement.getType(); |
+ MethodElement getterElement = Elements.lookupFieldElementGetter(currentClass.getElement(), |
+ methodElement.getName()); |
+ if (getterElement != null) { |
+ Type getterType = getterElement.getReturnType(); |
+ if (!types.isAssignable(setterType, getterType)) { |
+ typeError(parameterElement.getNode(), TypeErrorCode.SETTER_TYPE_MUST_BE_ASSIGNABLE, |
+ setterType.getElement().getName(), |
+ getterType.getElement().getName()); |
+ } |
+ } |
+ } |
} |
return typeAsVoid(node); |
} |
@@ -1366,83 +1381,74 @@ public class TypeAnalyzer implements DartCompilationPhase { |
case FIELD: |
FieldElement fieldElement = (FieldElement) element; |
+ MethodElement getter = fieldElement.getGetter(); |
+ MethodElement setter = fieldElement.getSetter(); |
+ boolean inSetterContext = inSetterContext(node); |
+ boolean inGetterContext = inGetterContext(node); |
+ ClassElement enclosingClass = null; |
+ if (fieldElement.getEnclosingElement() instanceof ClassElement) { |
+ enclosingClass = (ClassElement) fieldElement.getEnclosingElement(); |
+ } |
// Check for cases when property has no setter or getter. |
- if (fieldElement.getModifiers().isAbstractField() |
- && fieldElement.getEnclosingElement() instanceof ClassElement) { |
- ClassElement enclosingClass = (ClassElement) fieldElement.getEnclosingElement(); |
+ if (fieldElement.getModifiers().isAbstractField() && enclosingClass != null) { |
// Check for using field without getter in other operation that assignment. |
- if (fieldElement.getGetter() == null && !hasFieldElementGetter(enclosingClass, name)) { |
- if (!(node.getParent() instanceof DartBinaryExpression) |
- || ((DartBinaryExpression) node.getParent()).getOperator() != Token.ASSIGN |
- || ((DartBinaryExpression) node.getParent()).getArg1() != node) { |
- return typeError(node.getName(), TypeErrorCode.FIELD_HAS_NO_GETTER, node.getName()); |
- } |
+ if (inGetterContext && getter == null |
+ && Elements.lookupFieldElementGetter(enclosingClass, name) == null) { |
+ return typeError(node.getName(), TypeErrorCode.FIELD_HAS_NO_GETTER, node.getName()); |
} |
// Check for using field without setter in some assignment variant. |
- if (fieldElement.getSetter() == null && !hasFieldElementSetter(enclosingClass, name)) { |
- if (node.getParent() instanceof DartBinaryExpression) { |
- DartBinaryExpression expr = (DartBinaryExpression) node.getParent(); |
- if (ASSIGN_OPERATORS.contains(expr.getOperator()) && expr.getArg1() == node) { |
- return typeError( |
- node.getName(), |
- TypeErrorCode.FIELD_HAS_NO_SETTER, |
- node.getName()); |
- } |
+ if (inSetterContext && setter == null |
+ && Elements.lookupFieldElementSetter(enclosingClass, name) == null) { |
+ return typeError(node.getName(), |
+ TypeErrorCode.FIELD_HAS_NO_SETTER, |
+ node.getName()); |
+ } |
+ } |
+ |
+ Type result = member.getType(); |
+ if (fieldElement.getModifiers().isAbstractField()) { |
+ if (inSetterContext) { |
+ result = member.getSetterType(); |
+ if (result == null) { |
+ return typeError(node.getName(), TypeErrorCode.FIELD_HAS_NO_SETTER, node.getName()); |
+ } |
+ } |
+ if (inGetterContext) { |
+ result = member.getGetterType(); |
+ if (result == null) { |
+ return typeError(node.getName(), TypeErrorCode.FIELD_HAS_NO_GETTER, node.getName()); |
} |
} |
} |
- // Return field type. |
- return member.getType(); |
+ return result; |
default: |
throw internalError(node.getName(), "unexpected kind %s", element.getKind()); |
} |
} |
- /** |
- * @return <code>true</code> if "holder", or one of its interfaces, or its superclass has |
- * {@link FieldElement} with getter. |
- */ |
- private static boolean hasFieldElementGetter(ClassElement holder, String name) { |
- Element element = holder.lookupLocalElement(name); |
- if (element instanceof FieldElement) { |
- FieldElement fieldElement = (FieldElement) element; |
- if (fieldElement.getGetter() != null) { |
+ private boolean inSetterContext(DartNode node) { |
+ if (node.getParent() instanceof DartBinaryExpression) { |
+ DartBinaryExpression expr = (DartBinaryExpression) node.getParent(); |
+ if (ASSIGN_OPERATORS.contains(expr.getOperator()) && expr.getArg1() == node) { |
return true; |
} |
} |
- for (InterfaceType interfaceType : holder.getInterfaces()) { |
- if (hasFieldElementGetter(interfaceType.getElement(), name)) { |
- return true; |
- } |
- } |
- if (holder.getSupertype() != null) { |
- return hasFieldElementGetter(holder.getSupertype().getElement(), name); |
- } |
return false; |
} |
/** |
- * @return <code>true</code> if "holder", or one of its interfaces, or its superclass has |
- * {@link FieldElement} with setter. |
+ * An assignment of the form node = <expr> is a write-only expression. Other types |
+ * of assignments also read the value and require a getter access. |
*/ |
- private static boolean hasFieldElementSetter(ClassElement holder, String name) { |
- Element element = holder.lookupLocalElement(name); |
- if (element instanceof FieldElement) { |
- FieldElement fieldElement = (FieldElement) element; |
- if (fieldElement.getSetter() != null) { |
- return true; |
- } |
- } |
- for (InterfaceType interfaceType : holder.getInterfaces()) { |
- if (hasFieldElementSetter(interfaceType.getElement(), name)) { |
- return true; |
+ private boolean inGetterContext(DartNode node) { |
+ if (node.getParent() instanceof DartBinaryExpression) { |
+ DartBinaryExpression expr = (DartBinaryExpression) node.getParent(); |
+ if (Token.ASSIGN.equals(expr.getOperator()) && expr.getArg1() == node) { |
+ return false; |
} |
} |
- if (holder.getSupertype() != null) { |
- return hasFieldElementSetter(holder.getSupertype().getElement(), name); |
- } |
- return false; |
+ return true; |
} |
@Override |