Chromium Code Reviews| Index: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ConstantVerifier.java |
| diff --git a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ConstantVerifier.java b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ConstantVerifier.java |
| index de9a2e27e45a9b0a7443a8518979b94cbcc96ca6..4eb3584609bb1f305b343a598612cee03e7840ba 100644 |
| --- a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ConstantVerifier.java |
| +++ b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ConstantVerifier.java |
| @@ -34,11 +34,16 @@ import com.google.dart.engine.ast.RedirectingConstructorInvocation; |
| import com.google.dart.engine.ast.SimpleIdentifier; |
| import com.google.dart.engine.ast.SuperConstructorInvocation; |
| import com.google.dart.engine.ast.SwitchCase; |
| +import com.google.dart.engine.ast.SwitchMember; |
| +import com.google.dart.engine.ast.SwitchStatement; |
| import com.google.dart.engine.ast.VariableDeclaration; |
| import com.google.dart.engine.ast.visitor.RecursiveAstVisitor; |
| import com.google.dart.engine.constant.DartObject; |
| +import com.google.dart.engine.element.ClassElement; |
| import com.google.dart.engine.element.ConstructorElement; |
| import com.google.dart.engine.element.Element; |
| +import com.google.dart.engine.element.LibraryElement; |
| +import com.google.dart.engine.element.MethodElement; |
| import com.google.dart.engine.element.ParameterElement; |
| import com.google.dart.engine.error.CompileTimeErrorCode; |
| import com.google.dart.engine.error.ErrorCode; |
| @@ -105,12 +110,19 @@ public class ConstantVerifier extends RecursiveAstVisitor<Void> { |
| private InterfaceType stringType; |
| /** |
| + * The current library that is being analyzed. |
| + */ |
| + private LibraryElement currentLibrary; |
| + |
| + /** |
| * Initialize a newly created constant verifier. |
| * |
| * @param errorReporter the error reporter by which errors will be reported |
| */ |
| - public ConstantVerifier(ErrorReporter errorReporter, TypeProvider typeProvider) { |
| + public ConstantVerifier(ErrorReporter errorReporter, LibraryElement currentLibrary, |
| + TypeProvider typeProvider) { |
| this.errorReporter = errorReporter; |
| + this.currentLibrary = currentLibrary; |
| this.typeProvider = typeProvider; |
| this.boolType = typeProvider.getBoolType(); |
| this.intType = typeProvider.getIntType(); |
| @@ -215,6 +227,13 @@ public class ConstantVerifier extends RecursiveAstVisitor<Void> { |
| } else { |
| keys.add(value); |
| } |
| + Type type = value.getType(); |
| + if (implementsEqualsWhenNotAllowed(type)) { |
| + errorReporter.reportErrorForNode( |
| + CompileTimeErrorCode.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, |
| + key, |
| + type.getDisplayName()); |
| + } |
| } |
| } else { |
| EvaluationResultImpl result = key.accept(new ConstantVisitor(typeProvider)); |
| @@ -246,18 +265,43 @@ public class ConstantVerifier extends RecursiveAstVisitor<Void> { |
| } |
| @Override |
| - public Void visitSwitchCase(SwitchCase node) { |
| - super.visitSwitchCase(node); |
| - Expression expression = node.getExpression(); |
| - EvaluationResultImpl result = validate( |
| - expression, |
| - CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION); |
| - if (result instanceof ValidResult) { |
| - reportErrorIfFromDeferredLibrary( |
| - expression, |
| - CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY); |
| + public Void visitSwitchStatement(SwitchStatement node) { |
| + // TODO(jwren) Revisit this algorithm, should there up to n-1 errors? |
|
Paul Berry
2014/05/03 13:33:06
Jaime: is this still an open issue?
Brian Wilkerson
2014/05/03 15:03:34
Yes, it's still an issue. I think what we really w
Paul Berry
2014/05/03 17:14:56
Ah, ok. I've changed the comment to "TODO(paulber
jwren
2014/05/03 23:03:59
Sounds good, although as far as priority this is a
|
| + NodeList<SwitchMember> switchMembers = node.getMembers(); |
| + boolean foundError = false; |
| + Type firstType = null; |
| + for (SwitchMember switchMember : switchMembers) { |
| + if (switchMember instanceof SwitchCase) { |
| + SwitchCase switchCase = (SwitchCase) switchMember; |
| + Expression expression = switchCase.getExpression(); |
| + EvaluationResultImpl caseResult = validate( |
| + expression, |
| + CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION); |
| + if (caseResult instanceof ValidResult) { |
| + reportErrorIfFromDeferredLibrary( |
| + expression, |
| + CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY); |
| + DartObject value = ((ValidResult) caseResult).getValue(); |
| + if (firstType == null) { |
| + firstType = value.getType(); |
| + } else { |
| + Type nType = value.getType(); |
| + if (!firstType.equals(nType)) { |
| + errorReporter.reportErrorForNode( |
| + CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES, |
| + expression, |
| + expression.toSource(), |
| + firstType.getDisplayName()); |
| + foundError = true; |
| + } |
| + } |
| + } |
| + } |
| } |
| - return null; |
| + if (!foundError) { |
| + checkForCaseExpressionTypeImplementsEquals(node, firstType); |
| + } |
| + return super.visitSwitchStatement(node); |
| } |
| @Override |
| @@ -290,6 +334,60 @@ public class ConstantVerifier extends RecursiveAstVisitor<Void> { |
| } |
| /** |
| + * This verifies that the passed switch statement does not have a case expression with the |
| + * operator '==' overridden. |
| + * |
| + * @param node the switch statement to evaluate |
| + * @param type the common type of all 'case' expressions |
| + * @return {@code true} if and only if an error code is generated on the passed node |
| + * @see CompileTimeErrorCode#CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS |
| + */ |
| + private boolean checkForCaseExpressionTypeImplementsEquals(SwitchStatement node, Type type) { |
| + if (!implementsEqualsWhenNotAllowed(type)) { |
| + return false; |
| + } |
| + // report error |
| + errorReporter.reportErrorForToken( |
| + CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS, |
| + node.getKeyword(), |
| + type.getDisplayName()); |
| + return true; |
| + } |
| + |
| + /** |
| + * @return {@code true} if given {@link Type} implements operator <i>==</i>, and it is not |
| + * <i>int</i> or <i>String</i>. |
| + */ |
| + private boolean implementsEqualsWhenNotAllowed(Type type) { |
| + // ignore int or String |
| + if (type == null || type.equals(intType) || type.equals(typeProvider.getStringType())) { |
|
jwren
2014/05/03 23:03:59
What about other nums such as the num type, double
Brian Wilkerson
2014/05/04 04:39:12
The specification explicitly states:
It is a comp
|
| + return false; |
| + } else if (type.equals(typeProvider.getDoubleType())) { |
| + return true; |
| + } |
| + // prepare ClassElement |
| + Element element = type.getElement(); |
| + if (!(element instanceof ClassElement)) { |
| + return false; |
| + } |
| + ClassElement classElement = (ClassElement) element; |
| + // lookup for == |
| + MethodElement method = classElement.lookUpMethod("==", currentLibrary); |
| + while (method != null && method.isAbstract()) { |
| + ClassElement definingClass = method.getEnclosingElement(); |
| + if (definingClass == null) { |
| + return false; |
| + } |
| + method = definingClass.lookUpInheritedMethod("==", currentLibrary); |
| + } |
| + if (method == null || method.getEnclosingElement().getType().isObject()) { |
| + return false; |
| + } |
| + // there is == that we don't like |
| + return true; |
| + } |
| + |
| + /** |
| * Given some computed {@link Expression}, this method generates the passed {@link ErrorCode} on |
| * the node if its' value consists of information from a deferred library. |
| * |