| Index: pkg/compiler/lib/src/typechecker.dart
|
| diff --git a/pkg/compiler/lib/src/typechecker.dart b/pkg/compiler/lib/src/typechecker.dart
|
| index 3507c243dae5ff5e21239fa9c76c2e13a2ee02fa..a6b8b872fcfd9b8ee6a269d4997f8a1b8d1facaa 100644
|
| --- a/pkg/compiler/lib/src/typechecker.dart
|
| +++ b/pkg/compiler/lib/src/typechecker.dart
|
| @@ -418,10 +418,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;
|
| }
|
| @@ -540,6 +544,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>();
|
| @@ -550,7 +556,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);
|
| @@ -582,7 +590,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| checkCondition(Expression condition) {
|
| - checkAssignable(condition, analyze(condition), boolType);
|
| + checkAssignable(condition, analyzeNonVoid(condition), boolType);
|
| }
|
|
|
| void pushCascadeType(ResolutionDartType type) {
|
| @@ -596,8 +604,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) {
|
| @@ -610,7 +618,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| ResolutionDartType visitCascadeReceiver(CascadeReceiver node) {
|
| - ResolutionDartType type = analyze(node.expression);
|
| + ResolutionDartType type = analyzeNonVoid(node.expression);
|
| pushCascadeType(type);
|
| return type;
|
| }
|
| @@ -940,10 +948,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);
|
| }
|
| @@ -955,16 +963,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);
|
| }
|
| @@ -979,7 +987,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;
|
| }
|
| @@ -1035,8 +1043,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(
|
| @@ -1261,7 +1269,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.
|
| @@ -1269,12 +1277,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, '&&')) {
|
| @@ -1295,7 +1303,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;
|
| @@ -1324,11 +1332,7 @@ 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(
|
| + ElementAccess access = lookupMember(
|
| node, receiverType, operatorName, MemberKind.OPERATOR, null);
|
| LinkBuilder<ResolutionDartType> argumentTypesBuilder =
|
| new LinkBuilder<ResolutionDartType>();
|
| @@ -1387,6 +1391,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].
|
| @@ -1417,9 +1425,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 =
|
| @@ -1480,11 +1488,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();
|
| @@ -1515,7 +1523,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
|
| @@ -1576,13 +1584,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);
|
| }
|
| }
|
| @@ -1605,8 +1613,8 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
| }
|
|
|
| ResolutionDartType visitStringJuxtaposition(StringJuxtaposition node) {
|
| - analyze(node.first);
|
| - analyze(node.second);
|
| + analyzeNonVoid(node.first);
|
| + analyzeNonVoid(node.second);
|
| return stringType;
|
| }
|
|
|
| @@ -1663,7 +1671,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);
|
| }
|
| @@ -1706,6 +1714,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);
|
| @@ -1717,6 +1728,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:
|
| @@ -1729,7 +1743,7 @@ class TypeCheckerVisitor extends Visitor<ResolutionDartType> {
|
|
|
| ResolutionDartType visitThrow(Throw node) {
|
| // TODO(johnniwinther): Handle reachability.
|
| - analyze(node.expression);
|
| + analyzeNonVoid(node.expression);
|
| return const ResolutionDynamicType();
|
| }
|
|
|
| @@ -1743,7 +1757,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 =
|
| @@ -1842,7 +1856,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);
|
| }
|
|
|
| @@ -1881,7 +1895,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)) {
|
| @@ -1897,7 +1911,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,
|
| @@ -1917,13 +1934,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,
|
| @@ -1950,9 +1969,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;
|
| @@ -1964,7 +1983,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) {
|
| @@ -1972,7 +1991,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;
|
| @@ -1984,7 +2003,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);
|
| }
|
|
|
|
|