| Index: pkg/compiler/lib/src/typechecker.dart
|
| diff --git a/pkg/compiler/lib/src/typechecker.dart b/pkg/compiler/lib/src/typechecker.dart
|
| index cbb5c246a66d1ee357fc11a5a80400aed921869d..0e6df8f3a70192ec39c3ac4def7e75df9fdde44f 100644
|
| --- a/pkg/compiler/lib/src/typechecker.dart
|
| +++ b/pkg/compiler/lib/src/typechecker.dart
|
| @@ -417,10 +417,14 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| // TODO(karlklose): remove these functions.
|
| ResolutionDartType unhandledExpression() => const ResolutionDynamicType();
|
|
|
| + /// Checks that the analyzed node is not `void`.
|
| + ///
|
| + /// If it is, a warning is emitted, and the returned type is `dynamic`.
|
| ResolutionDartType analyzeNonVoid(Node node) {
|
| ResolutionDartType type = analyze(node);
|
| if (type.isVoid) {
|
| reportTypeWarning(node, MessageKind.VOID_EXPRESSION);
|
| + return const ResolutionDynamicType();
|
| }
|
| return type;
|
| }
|
| @@ -539,6 +543,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| /// Analyze [node] in the context of the known types shown in [context].
|
| + ///
|
| + /// If the node [mustHaveType] then it must also not be void.
|
| ResolutionDartType analyzeInPromotedContext(Node context, Node node,
|
| {bool mustHaveType: true}) {
|
| Link<TypePromotion> knownForNode = const Link<TypePromotion>();
|
| @@ -549,7 +555,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| registerKnownTypePromotion(typePromotion);
|
| }
|
|
|
| - final ResolutionDartType type = analyze(node, mustHaveType: mustHaveType);
|
| + final ResolutionDartType type = mustHaveType
|
| + ? analyzeNonVoid(node)
|
| + : analyze(node, mustHaveType: false);
|
|
|
| while (!knownForNode.isEmpty) {
|
| unregisterKnownTypePromotion(knownForNode.head);
|
| @@ -581,7 +589,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| checkCondition(Expression condition) {
|
| - checkAssignable(condition, analyze(condition), boolType);
|
| + checkAssignable(condition, analyzeNonVoid(condition), boolType);
|
| }
|
|
|
| void pushCascadeType(ResolutionDartType type) {
|
| @@ -595,8 +603,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| visitAssert(Assert node) {
|
| - analyze(node.condition);
|
| - if (node.hasMessage) analyze(node.message);
|
| + analyzeNonVoid(node.condition);
|
| + if (node.hasMessage) analyzeNonVoid(node.message);
|
| }
|
|
|
| visitBlock(Block node) {
|
| @@ -609,7 +617,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| ResolutionDartType visitCascadeReceiver(CascadeReceiver node) {
|
| - ResolutionDartType type = analyze(node.expression);
|
| + ResolutionDartType type = analyzeNonVoid(node.expression);
|
| pushCascadeType(type);
|
| return type;
|
| }
|
| @@ -939,10 +947,10 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| MessageKind.NAMED_ARGUMENT_NOT_FOUND,
|
| {'argumentName': argumentName}));
|
|
|
| - ResolutionDartType argumentType = analyze(argument);
|
| + ResolutionDartType argumentType = analyzeNonVoid(argument);
|
| if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| } else {
|
| - ResolutionDartType argumentType = analyze(argument);
|
| + ResolutionDartType argumentType = analyzeNonVoid(argument);
|
| if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| checkAssignable(argument, argumentType, namedParameterType);
|
| }
|
| @@ -954,16 +962,16 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| reportWarning(reporter.createMessage(
|
| argument, MessageKind.ADDITIONAL_ARGUMENT));
|
|
|
| - ResolutionDartType argumentType = analyze(argument);
|
| + ResolutionDartType argumentType = analyzeNonVoid(argument);
|
| if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| } else {
|
| - ResolutionDartType argumentType = analyze(argument);
|
| + ResolutionDartType argumentType = analyzeNonVoid(argument);
|
| if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| checkAssignable(
|
| argument, argumentType, optionalParameterTypes.current);
|
| }
|
| } else {
|
| - ResolutionDartType argumentType = analyze(argument);
|
| + ResolutionDartType argumentType = analyzeNonVoid(argument);
|
| if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| checkAssignable(argument, argumentType, parameterTypes.current);
|
| }
|
| @@ -978,7 +986,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
| } else {
|
| while (!arguments.isEmpty) {
|
| - ResolutionDartType argumentType = analyze(arguments.head);
|
| + ResolutionDartType argumentType = analyzeNonVoid(arguments.head);
|
| if (argumentTypes != null) argumentTypes.addLast(argumentType);
|
| arguments = arguments.tail;
|
| }
|
| @@ -1034,8 +1042,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
| }
|
| // e.foo() for some expression e.
|
| - ResolutionDartType receiverType = analyze(node.receiver);
|
| - if (receiverType.treatAsDynamic || receiverType.isVoid) {
|
| + ResolutionDartType receiverType = analyzeNonVoid(node.receiver);
|
| + if (receiverType.treatAsDynamic) {
|
| return const DynamicAccess();
|
| }
|
| return lookupMember(
|
| @@ -1260,7 +1268,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| return elements.getType(node.arguments.head);
|
| } else if (node.isOperator) {
|
| final Node receiver = node.receiver;
|
| - final ResolutionDartType receiverType = analyze(receiver);
|
| + final ResolutionDartType receiverType = analyzeNonVoid(receiver);
|
| if (identical(name, '==') ||
|
| identical(name, '!=')
|
| // TODO(johnniwinther): Remove these.
|
| @@ -1268,12 +1276,12 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| identical(name, '===') ||
|
| identical(name, '!==')) {
|
| // Analyze argument.
|
| - analyze(node.arguments.head);
|
| + analyzeNonVoid(node.arguments.head);
|
| return boolType;
|
| } else if (identical(name, '||')) {
|
| checkAssignable(receiver, receiverType, boolType);
|
| final Node argument = node.arguments.head;
|
| - final ResolutionDartType argumentType = analyze(argument);
|
| + final ResolutionDartType argumentType = analyzeNonVoid(argument);
|
| checkAssignable(argument, argumentType, boolType);
|
| return boolType;
|
| } else if (identical(name, '&&')) {
|
| @@ -1294,7 +1302,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| return boolType;
|
| } else if (identical(name, '??')) {
|
| final Node argument = node.arguments.head;
|
| - final ResolutionDartType argumentType = analyze(argument);
|
| + final ResolutionDartType argumentType = analyzeNonVoid(argument);
|
| return types.computeLeastUpperBound(receiverType, argumentType);
|
| }
|
| String operatorName = selector.source;
|
| @@ -1323,12 +1331,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| identical(name, '[]'),
|
| message: 'Unexpected operator $name'));
|
|
|
| - // TODO(karlklose): handle `void` in expression context by calling
|
| - // [analyzeNonVoid] instead of [analyze].
|
| - ElementAccess access = receiverType.isVoid
|
| - ? const DynamicAccess()
|
| - : lookupMember(
|
| - node, receiverType, operatorName, MemberKind.OPERATOR, null);
|
| + ElementAccess access = lookupMember(
|
| + node, receiverType, operatorName, MemberKind.OPERATOR, null);
|
| LinkBuilder<ResolutionDartType> argumentTypesBuilder =
|
| new LinkBuilder<ResolutionDartType>();
|
| ResolutionDartType resultType =
|
| @@ -1386,6 +1390,10 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| Identifier selector = node.selector;
|
| ResolutionDartType getter = computeAccessType(
|
| node, selector.source, getterElement, MemberKind.GETTER);
|
| + if (getter.isVoid) {
|
| + reportTypeWarning(node, MessageKind.VOID_EXPRESSION);
|
| + getter = const ResolutionDynamicType();
|
| + }
|
| ResolutionDartType setter = computeAccessType(
|
| node, selector.source, setterElement, MemberKind.SETTER);
|
| // [operator] is the type of operator+ or operator- on [target].
|
| @@ -1416,9 +1424,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| ResolutionDartType checkIndexAssignmentOperator(SendSet node,
|
| String operatorName, Node valueNode, ResolutionDartType value) {
|
| assert(invariant(node, node.isIndex));
|
| - final ResolutionDartType base = analyze(node.receiver);
|
| + final ResolutionDartType base = analyzeNonVoid(node.receiver);
|
| final Node keyNode = node.arguments.head;
|
| - final ResolutionDartType key = analyze(keyNode);
|
| + final ResolutionDartType key = analyzeNonVoid(keyNode);
|
|
|
| // [indexGet] is the type of operator[] on [base].
|
| ResolutionDartType indexGet =
|
| @@ -1479,11 +1487,11 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| // e1 = value
|
| if (node.isIndex) {
|
| // base[key] = value
|
| - final ResolutionDartType base = analyze(node.receiver);
|
| + final ResolutionDartType base = analyzeNonVoid(node.receiver);
|
| final Node keyNode = node.arguments.head;
|
| - final ResolutionDartType key = analyze(keyNode);
|
| + final ResolutionDartType key = analyzeNonVoid(keyNode);
|
| final Node valueNode = node.arguments.tail.head;
|
| - final ResolutionDartType value = analyze(valueNode);
|
| + final ResolutionDartType value = analyzeNonVoid(valueNode);
|
| ResolutionDartType indexSet =
|
| lookupMemberType(node, base, '[]=', MemberKind.OPERATOR);
|
| ResolutionDartType indexSetValue = const ResolutionDynamicType();
|
| @@ -1514,7 +1522,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| node, selector.source, element, MemberKind.SETTER);
|
| }
|
| final Node valueNode = node.arguments.head;
|
| - final ResolutionDartType value = analyze(valueNode);
|
| + final ResolutionDartType value = analyzeNonVoid(valueNode);
|
| checkAssignable(node.assignmentOperator, value, target);
|
| return identical(name, '=')
|
| ? value
|
| @@ -1575,13 +1583,13 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| if (node.isIndex) {
|
| // base[key] o= value for some operator o.
|
| final Node valueNode = node.arguments.tail.head;
|
| - final ResolutionDartType value = analyze(valueNode);
|
| + final ResolutionDartType value = analyzeNonVoid(valueNode);
|
| return checkIndexAssignmentOperator(
|
| node, operatorName, valueNode, value);
|
| } else {
|
| // target o= value for some operator o.
|
| final Node valueNode = node.arguments.head;
|
| - final ResolutionDartType value = analyze(valueNode);
|
| + final ResolutionDartType value = analyzeNonVoid(valueNode);
|
| return checkAssignmentOperator(node, operatorName, valueNode, value);
|
| }
|
| }
|
| @@ -1604,8 +1612,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| ResolutionDartType visitStringJuxtaposition(StringJuxtaposition node) {
|
| - analyze(node.first);
|
| - analyze(node.second);
|
| + analyzeNonVoid(node.first);
|
| + analyzeNonVoid(node.second);
|
| return stringType;
|
| }
|
|
|
| @@ -1662,7 +1670,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| !link.isEmpty;
|
| link = link.tail) {
|
| Node element = link.head;
|
| - ResolutionDartType elementType = analyze(element);
|
| + ResolutionDartType elementType = analyzeNonVoid(element);
|
| checkAssignable(element, elementType, listElementType,
|
| isConst: node.isConst);
|
| }
|
| @@ -1705,6 +1713,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| commonElements.futureType(types.flatten(expressionType));
|
| expressionType = futureOfFlattenedType;
|
| }
|
| + // TODO(floitsch): we want to break this exception.
|
| + // No return x; allowed in a void function. Even if the return type is
|
| + // void.
|
| if (expectedReturnType.isVoid &&
|
| !types.isAssignable(expressionType, const ResolutionVoidType())) {
|
| reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID);
|
| @@ -1716,6 +1727,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| // `return;` is allowed.
|
| } else if (!types.isAssignable(
|
| expectedReturnType, const ResolutionVoidType())) {
|
| + // TODO(floitsch): this is probably where we want to have the checks
|
| + // that `void` must not have a `return` unless it returns void.
|
| +
|
| // Let f be the function immediately enclosing a return statement of the
|
| // form 'return;' It is a static warning if both of the following
|
| // conditions hold:
|
| @@ -1728,7 +1742,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
|
|
| ResolutionDartType visitThrow(Throw node) {
|
| // TODO(johnniwinther): Handle reachability.
|
| - analyze(node.expression);
|
| + analyzeNonVoid(node.expression);
|
| return const ResolutionDynamicType();
|
| }
|
|
|
| @@ -1742,7 +1756,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| visitYield(Yield node) {
|
| - ResolutionDartType resultType = analyze(node.expression);
|
| + ResolutionDartType resultType = analyzeNonVoid(node.expression);
|
| if (!node.hasStar) {
|
| if (currentAsyncMarker.isAsync) {
|
| ResolutionInterfaceType streamOfResultType =
|
| @@ -1841,7 +1855,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| ResolutionDartType thenType =
|
| analyzeInPromotedContext(condition, thenExpression);
|
|
|
| - ResolutionDartType elseType = analyze(node.elseExpression);
|
| + ResolutionDartType elseType = analyzeNonVoid(node.elseExpression);
|
| return types.computeLeastUpperBound(thenType, elseType);
|
| }
|
|
|
| @@ -1880,7 +1894,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
|
|
| visitAsyncForIn(AsyncForIn node) {
|
| ResolutionDartType elementType = computeForInElementType(node);
|
| - ResolutionDartType expressionType = analyze(node.expression);
|
| + ResolutionDartType expressionType = analyzeNonVoid(node.expression);
|
| if (resolution.target.supportsAsyncAwait) {
|
| ResolutionInterfaceType streamOfDynamic = commonElements.streamType();
|
| if (!types.isAssignable(expressionType, streamOfDynamic)) {
|
| @@ -1896,7 +1910,10 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| if (streamType != null) {
|
| ResolutionDartType streamElementType =
|
| streamType.typeArguments.first;
|
| - if (!types.isAssignable(streamElementType, elementType)) {
|
| + if (streamElementType.isVoid) {
|
| + reportTypeWarning(
|
| + node.expression, MessageKind.AWAIT_FORIN_VOID_GENERIC);
|
| + } else if (!types.isAssignable(streamElementType, elementType)) {
|
| reportMessage(
|
| node.expression,
|
| MessageKind.FORIN_NOT_ASSIGNABLE,
|
| @@ -1916,13 +1933,15 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
|
|
| visitSyncForIn(SyncForIn node) {
|
| ResolutionDartType elementType = computeForInElementType(node);
|
| - ResolutionDartType expressionType = analyze(node.expression);
|
| + ResolutionDartType expressionType = analyzeNonVoid(node.expression);
|
| ResolutionDartType iteratorType = lookupMemberType(node.expression,
|
| expressionType, Identifiers.iterator, MemberKind.GETTER);
|
| ResolutionDartType currentType = lookupMemberType(
|
| node.expression, iteratorType, Identifiers.current, MemberKind.GETTER,
|
| isHint: true);
|
| - if (!types.isAssignable(currentType, elementType)) {
|
| + if (currentType.isVoid) {
|
| + reportTypeWarning(node.expression, MessageKind.FORIN_VOID_GENERIC);
|
| + } else if (!types.isAssignable(currentType, elementType)) {
|
| reportMessage(
|
| node.expression,
|
| MessageKind.FORIN_NOT_ASSIGNABLE,
|
| @@ -1949,9 +1968,9 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| !link.isEmpty;
|
| link = link.tail) {
|
| LiteralMapEntry entry = link.head;
|
| - ResolutionDartType keyType = analyze(entry.key);
|
| + ResolutionDartType keyType = analyzeNonVoid(entry.key);
|
| checkAssignable(entry.key, keyType, mapKeyType, isConst: isConst);
|
| - ResolutionDartType valueType = analyze(entry.value);
|
| + ResolutionDartType valueType = analyzeNonVoid(entry.value);
|
| checkAssignable(entry.value, valueType, mapValueType, isConst: isConst);
|
| }
|
| return mapType;
|
| @@ -1963,7 +1982,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| // found in the enclosing scope nor through lookup on 'this' or
|
| // [: x.foo(b: 42); :] where 'foo' cannot be not found through lookup on
|
| // the static type of 'x'.
|
| - return analyze(node.expression);
|
| + return analyzeNonVoid(node.expression);
|
| }
|
|
|
| visitSwitchStatement(SwitchStatement node) {
|
| @@ -1971,7 +1990,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| // switch cases.
|
| // TODO(johnniwinther): Provide hint of duplicate case constants.
|
|
|
| - ResolutionDartType expressionType = analyze(node.expression);
|
| + ResolutionDartType expressionType = analyzeNonVoid(node.expression);
|
|
|
| // Check that all the case expressions are assignable to the expression.
|
| bool hasDefaultCase = false;
|
| @@ -1983,7 +2002,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| CaseMatch caseMatch = labelOrCase.asCaseMatch();
|
| if (caseMatch == null) continue;
|
|
|
| - ResolutionDartType caseType = analyze(caseMatch.expression);
|
| + ResolutionDartType caseType = analyzeNonVoid(caseMatch.expression);
|
| checkAssignable(caseMatch, expressionType, caseType);
|
| }
|
|
|
|
|