| Index: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ErrorVerifier.java
|
| diff --git a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ErrorVerifier.java b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ErrorVerifier.java
|
| index 7089bb75a63fe3093113f2639357ddb4d01a2681..bcb6343d25bc0c73e74ac75becb1796188cdd2a8 100644
|
| --- a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ErrorVerifier.java
|
| +++ b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/verifier/ErrorVerifier.java
|
| @@ -847,6 +847,7 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER);
|
| }
|
| checkForTypeAnnotationDeferredClass(returnType);
|
| + checkForIllegalReturnType(returnType);
|
| return super.visitFunctionDeclaration(node);
|
| } finally {
|
| enclosingFunction = outerFunction;
|
| @@ -1028,6 +1029,7 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| checkForConcreteClassWithAbstractMember(node);
|
| checkForAllInvalidOverrideErrorCodesForMethod(node);
|
| checkForTypeAnnotationDeferredClass(returnTypeName);
|
| + checkForIllegalReturnType(returnTypeName);
|
| return super.visitMethodDeclaration(node);
|
| } finally {
|
| enclosingFunction = previousFunction;
|
| @@ -1254,7 +1256,9 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
|
|
| @Override
|
| public Void visitYieldStatement(YieldStatement node) {
|
| - if (!inGenerator) {
|
| + if (inGenerator) {
|
| + checkForYieldOfInvalidType(node.getExpression(), node.getStar() != null);
|
| + } else {
|
| CompileTimeErrorCode errorCode;
|
| if (node.getStar() != null) {
|
| errorCode = CompileTimeErrorCode.YIELD_EACH_IN_NON_GENERATOR;
|
| @@ -1932,7 +1936,7 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| }
|
| // RETURN_WITHOUT_VALUE
|
| if (returnExpression == null) {
|
| - if (VoidTypeImpl.getInstance().isAssignableTo(expectedReturnType)) {
|
| + if (inGenerator || computeReturnTypeForMethod(null).isAssignableTo(expectedReturnType)) {
|
| return false;
|
| }
|
| hasReturnWithoutValue = true;
|
| @@ -3554,6 +3558,35 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| }
|
|
|
| /**
|
| + * If the current function is async, async*, or sync*, verify that its declared return type is
|
| + * assignable to Future, Stream, or Iterable, respectively. If not, report the error using [node].
|
| + */
|
| + private void checkForIllegalReturnType(TypeName node) {
|
| + if (node == null) {
|
| + // No declared return type, so the return type must be dynamic, which is assignable to
|
| + // everything.
|
| + return;
|
| + }
|
| + if (enclosingFunction.isAsynchronous()) {
|
| + if (enclosingFunction.isGenerator()) {
|
| + // TODO(paulberry): We should report an error if enclosingFunction.getReturnType() isn't
|
| + // assignable to Stream<dynamic>. But we can't because the Stream type isn't available in
|
| + // the type provider. So to avoid bogus warnings, don't do any check.
|
| + } else {
|
| + // TODO(paulberry): We should report an error if enclosingFunction.getReturnType() isn't
|
| + // assignable to Future<dynamic>. But we can't because the Future type isn't available in
|
| + // the type provider. So to avoid bogus warnings, don't do any check.
|
| + }
|
| + } else if (enclosingFunction.isGenerator()) {
|
| + if (!enclosingFunction.getReturnType().isAssignableTo(typeProvider.getIterableDynamicType())) {
|
| + errorReporter.reportErrorForNode(
|
| + StaticTypeWarningCode.ILLEGAL_SYNC_GENERATOR_RETURN_TYPE,
|
| + node);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| * This verifies that the passed implements clause does not implement classes that are deferred.
|
| *
|
| * @param node the implements clause to test
|
| @@ -5166,7 +5199,13 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| if (enclosingFunction == null) {
|
| return false;
|
| }
|
| - Type staticReturnType = getStaticType(returnExpression);
|
| + if (inGenerator) {
|
| + // "return expression;" is disallowed in generators, but this is checked elsewhere. Bare
|
| + // "return" is always allowed in generators regardless of the return type. So no need to do
|
| + // any further checking.
|
| + return false;
|
| + }
|
| + Type staticReturnType = computeReturnTypeForMethod(returnExpression);
|
| if (expectedReturnType.isVoid()) {
|
| if (staticReturnType.isVoid() || staticReturnType.isDynamic() || staticReturnType.isBottom()) {
|
| return false;
|
| @@ -5179,22 +5218,6 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| enclosingFunction.getDisplayName());
|
| return true;
|
| }
|
| - if (enclosingFunction.isAsynchronous() && !enclosingFunction.isGenerator()) {
|
| - // TODO(brianwilkerson) Figure out how to get the type "Future" so that we can build the type
|
| - // we need to test against.
|
| -// InterfaceType impliedType = "Future<" + flatten(staticReturnType) + ">"
|
| -// if (impliedType.isAssignableTo(expectedReturnType)) {
|
| -// return false;
|
| -// }
|
| -// errorReporter.reportTypeErrorForNode(
|
| -// StaticTypeWarningCode.RETURN_OF_INVALID_TYPE,
|
| -// returnExpression,
|
| -// impliedType,
|
| -// expectedReturnType.getDisplayName(),
|
| -// enclosingFunction.getDisplayName());
|
| -// return true;
|
| - return false;
|
| - }
|
| if (staticReturnType.isAssignableTo(expectedReturnType)) {
|
| return false;
|
| }
|
| @@ -5686,6 +5709,59 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| }
|
|
|
| /**
|
| + * Check for a type mis-match between the yielded type and the declared return type of a generator
|
| + * function. This method should only be called in generator functions.
|
| + */
|
| + private boolean checkForYieldOfInvalidType(Expression yieldExpression, boolean isYieldEach) {
|
| + if (enclosingFunction == null) {
|
| + return false;
|
| + }
|
| + Type declaredReturnType = enclosingFunction.getReturnType();
|
| + Type staticYieldedType = getStaticType(yieldExpression);
|
| + Type impliedReturnType;
|
| + if (isYieldEach) {
|
| + impliedReturnType = staticYieldedType;
|
| + } else if (enclosingFunction.isAsynchronous()) {
|
| + // TODO(paulberry): We should set impliedReturnType to Stream<staticYieldedType>. But we
|
| + // can't because the Stream type isn't available in the type provider. So to avoid bogus
|
| + // warnings, use dynamic.
|
| + impliedReturnType = typeProvider.getDynamicType();
|
| + } else {
|
| + impliedReturnType = typeProvider.getIterableType().substitute(new Type[] {staticYieldedType});
|
| + }
|
| + if (!impliedReturnType.isAssignableTo(declaredReturnType)) {
|
| + errorReporter.reportTypeErrorForNode(
|
| + StaticTypeWarningCode.YIELD_OF_INVALID_TYPE,
|
| + yieldExpression,
|
| + impliedReturnType,
|
| + declaredReturnType);
|
| + return true;
|
| + }
|
| + if (isYieldEach) {
|
| + // Since the declared return type might have been "dynamic", we need to also check that the
|
| + // implied return type is assignable to generic Stream/Iterable.
|
| + Type requiredReturnType;
|
| + if (enclosingFunction.isAsynchronous()) {
|
| + // TODO(paulberry): We should set requiredReturnType to Stream<dynamic>. But we can't
|
| + // because the Stream type isn't available in the type provider. So to avoid bogus
|
| + // warnings, use dynamic.
|
| + requiredReturnType = typeProvider.getDynamicType();
|
| + } else {
|
| + requiredReturnType = typeProvider.getIterableDynamicType();
|
| + }
|
| + if (!impliedReturnType.isAssignableTo(requiredReturnType)) {
|
| + errorReporter.reportTypeErrorForNode(
|
| + StaticTypeWarningCode.YIELD_OF_INVALID_TYPE,
|
| + yieldExpression,
|
| + impliedReturnType,
|
| + requiredReturnType);
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| * This verifies that if the given class declaration implements the class Function that it has a
|
| * concrete implementation of the call method.
|
| *
|
| @@ -5749,6 +5825,29 @@ public class ErrorVerifier extends RecursiveAstVisitor<Void> {
|
| return hasProblem;
|
| }
|
|
|
| + private Type computeReturnTypeForMethod(Expression returnExpression) {
|
| + // This method should never be called for generators, since generators are never allowed to
|
| + // contain return statements with expressions.
|
| + if (returnExpression == null) {
|
| + if (enclosingFunction.isAsynchronous()) {
|
| + // TODO(paulberry): We should return Future<Null>. But we can't because the Future type
|
| + // isn't available in the type provider. So to avoid bogus warnings, return dynamic.
|
| + return typeProvider.getDynamicType();
|
| + } else {
|
| + return VoidTypeImpl.getInstance();
|
| + }
|
| + }
|
| + Type staticReturnType = getStaticType(returnExpression);
|
| + if (staticReturnType != null && enclosingFunction.isAsynchronous()) {
|
| + // TODO(paulberry): We should return Future<flatten(staticReturnType)>. But we can't
|
| + // implement the flatten function, and even if we could, we wouldn't be able to return the
|
| + // proper type, because the Future type isn't available in the type provider. So to avoid
|
| + // bogus warnings, return dynamic.
|
| + return typeProvider.getDynamicType();
|
| + }
|
| + return staticReturnType;
|
| + }
|
| +
|
| /**
|
| * Return the error code that should be used when the given class references itself directly.
|
| *
|
|
|