| Index: frog/leg/typechecker.dart
|
| ===================================================================
|
| --- frog/leg/typechecker.dart (revision 5925)
|
| +++ frog/leg/typechecker.dart (working copy)
|
| @@ -1,716 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -class TypeCheckerTask extends CompilerTask {
|
| - TypeCheckerTask(Compiler compiler) : super(compiler);
|
| - String get name() => "Type checker";
|
| -
|
| - static final bool LOG_FAILURES = false;
|
| -
|
| - void check(Node tree, TreeElements elements) {
|
| - measure(() {
|
| - Visitor visitor =
|
| - new TypeCheckerVisitor(compiler, elements, compiler.types);
|
| - try {
|
| - tree.accept(visitor);
|
| - } catch (CancelTypeCheckException e) {
|
| - if (LOG_FAILURES) {
|
| - // Do not warn about unimplemented features; log message instead.
|
| - compiler.log("'${e.node}': ${e.reason}");
|
| - }
|
| - }
|
| - });
|
| - }
|
| -}
|
| -
|
| -interface Type {
|
| - SourceString get name();
|
| - Element get element();
|
| -}
|
| -
|
| -class TypeVariableType implements Type {
|
| - final SourceString name;
|
| - Element element;
|
| - TypeVariableType(this.name, [this.element]);
|
| -}
|
| -
|
| -/**
|
| - * A statement type tracks whether a statement returns or may return.
|
| - */
|
| -class StatementType implements Type {
|
| - final String stringName;
|
| - Element get element() => null;
|
| -
|
| - SourceString get name() => new SourceString(stringName);
|
| -
|
| - const StatementType(this.stringName);
|
| -
|
| - static final RETURNING = const StatementType('<returning>');
|
| - static final NOT_RETURNING = const StatementType('<not returning>');
|
| - static final MAYBE_RETURNING = const StatementType('<maybe returning>');
|
| -
|
| - /** Combine the information about two control-flow edges that are joined. */
|
| - StatementType join(StatementType other) {
|
| - return (this === other) ? this : MAYBE_RETURNING;
|
| - }
|
| -
|
| - String toString() => stringName;
|
| -}
|
| -
|
| -class SimpleType implements Type {
|
| - final SourceString name;
|
| - final Element element;
|
| -
|
| - const SimpleType(SourceString this.name, Element this.element);
|
| -
|
| - String toString() => name.slowToString();
|
| -}
|
| -
|
| -class FunctionType implements Type {
|
| - final Element element;
|
| - final Type returnType;
|
| - final Link<Type> parameterTypes;
|
| -
|
| - const FunctionType(Type this.returnType, Link<Type> this.parameterTypes,
|
| - Element this.element);
|
| -
|
| - toString() {
|
| - StringBuffer sb = new StringBuffer();
|
| - bool first = true;
|
| - sb.add('(');
|
| - parameterTypes.printOn(sb, ', ');
|
| - sb.add(') -> ${returnType}');
|
| - return sb.toString();
|
| - }
|
| -
|
| - SourceString get name() => const SourceString('Function');
|
| -}
|
| -
|
| -class Types {
|
| - static final VOID = const SourceString('void');
|
| - static final INT = const SourceString('int');
|
| - static final DOUBLE = const SourceString('double');
|
| - static final DYNAMIC = const SourceString('Dynamic');
|
| - static final STRING = const SourceString('String');
|
| - static final BOOL = const SourceString('bool');
|
| - static final OBJECT = const SourceString('Object');
|
| - static final LIST = const SourceString('List');
|
| -
|
| - final SimpleType voidType;
|
| - final SimpleType dynamicType;
|
| -
|
| - Types() : this.with(new LibraryElement(new Script(null, null)));
|
| - Types.with(LibraryElement library)
|
| - : voidType = new SimpleType(VOID, new ClassElement(VOID, library)),
|
| - dynamicType = new SimpleType(DYNAMIC, new ClassElement(DYNAMIC, library));
|
| -
|
| - Type lookup(SourceString s) {
|
| - if (VOID == s) {
|
| - return voidType;
|
| - } else if (DYNAMIC == s || s.stringValue === 'var') {
|
| - return dynamicType;
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /** Returns true if t is a subtype of s */
|
| - bool isSubtype(Type t, Type s) {
|
| - if (t === s || t === dynamicType || s === dynamicType ||
|
| - s.name == OBJECT) return true;
|
| - if (t is SimpleType) {
|
| - if (s is !SimpleType) return false;
|
| - ClassElement tc = t.element;
|
| - for (Link<Type> supertypes = tc.allSupertypes;
|
| - supertypes != null && !supertypes.isEmpty();
|
| - supertypes = supertypes.tail) {
|
| - Type supertype = supertypes.head;
|
| - if (supertype.element === s.element) return true;
|
| - }
|
| - return false;
|
| - } else if (t is FunctionType) {
|
| - if (s is !FunctionType) return false;
|
| - FunctionType tf = t;
|
| - FunctionType sf = s;
|
| - Link<Type> tps = tf.parameterTypes;
|
| - Link<Type> sps = sf.parameterTypes;
|
| - while (!tps.isEmpty() && !sps.isEmpty()) {
|
| - if (!isAssignable(tps.head, sps.head)) return false;
|
| - tps = tps.tail;
|
| - sps = sps.tail;
|
| - }
|
| - if (!tps.isEmpty() || !sps.isEmpty()) return false;
|
| - if (!isAssignable(sf.returnType, tf.returnType)) return false;
|
| - return true;
|
| - } else {
|
| - throw 'internal error: unknown type kind';
|
| - }
|
| - }
|
| -
|
| - bool isAssignable(Type r, Type s) {
|
| - return isSubtype(r, s) || isSubtype(s, r);
|
| - }
|
| -}
|
| -
|
| -class CancelTypeCheckException {
|
| - final Node node;
|
| - final String reason;
|
| -
|
| - CancelTypeCheckException(this.node, this.reason);
|
| -}
|
| -
|
| -Type lookupType(SourceString name, Compiler compiler, types) {
|
| - Type t = types.lookup(name);
|
| - if (t !== null) return t;
|
| - Element element = compiler.coreLibrary.find(name);
|
| - if (element !== null && element.kind === ElementKind.CLASS) {
|
| - return element.computeType(compiler);
|
| - }
|
| - return null;
|
| -}
|
| -
|
| -class TypeCheckerVisitor implements Visitor<Type> {
|
| - final Compiler compiler;
|
| - final TreeElements elements;
|
| - Node lastSeenNode;
|
| - final Types types;
|
| -
|
| - Type expectedReturnType;
|
| - ClassElement currentClass;
|
| -
|
| - Type intType;
|
| - Type doubleType;
|
| - Type boolType;
|
| - Type stringType;
|
| - Type objectType;
|
| - Type listType;
|
| -
|
| - TypeCheckerVisitor(Compiler this.compiler, TreeElements this.elements,
|
| - Types this.types) {
|
| - intType = lookupType(Types.INT, compiler, types);
|
| - doubleType = lookupType(Types.DOUBLE, compiler, types);
|
| - boolType = lookupType(Types.BOOL, compiler, types);
|
| - stringType = lookupType(Types.STRING, compiler, types);
|
| - objectType = lookupType(Types.OBJECT, compiler, types);
|
| - listType = lookupType(Types.LIST, compiler, types);
|
| - }
|
| -
|
| - Type fail(node, [reason]) {
|
| - String message = 'cannot type-check';
|
| - if (reason !== null) {
|
| - message = '$message: $reason';
|
| - }
|
| - throw new CancelTypeCheckException(node, message);
|
| - }
|
| -
|
| - reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) {
|
| - compiler.reportWarning(node, new TypeWarning(kind, arguments));
|
| - }
|
| -
|
| - Type analyzeNonVoid(Node node) {
|
| - Type type = analyze(node);
|
| - if (type == types.voidType) {
|
| - reportTypeWarning(node, MessageKind.VOID_EXPRESSION);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - Type analyzeWithDefault(Node node, Type defaultValue) {
|
| - return node !== null ? analyze(node) : defaultValue;
|
| - }
|
| -
|
| - Type analyze(Node node) {
|
| - if (node == null) {
|
| - final String error = 'internal error: unexpected node: null';
|
| - if (lastSeenNode != null) {
|
| - fail(null, error);
|
| - } else {
|
| - compiler.cancel(error);
|
| - }
|
| - } else {
|
| - lastSeenNode = node;
|
| - }
|
| - Type result = node.accept(this);
|
| - // TODO(karlklose): record type?
|
| - if (result === null) {
|
| - fail(node, 'internal error: type is null');
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Check if a value of type t can be assigned to a variable,
|
| - * parameter or return value of type s.
|
| - */
|
| - checkAssignable(Node node, Type s, Type t) {
|
| - if (!types.isAssignable(s, t)) {
|
| - reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE, [s, t]);
|
| - }
|
| - }
|
| -
|
| - checkCondition(Expression condition) {
|
| - checkAssignable(condition, boolType, analyze(condition));
|
| - }
|
| -
|
| - Type visitBlock(Block node) {
|
| - return analyze(node.statements);
|
| - }
|
| -
|
| - Type visitClassNode(ClassNode node) {
|
| - fail(node);
|
| - }
|
| -
|
| - Type visitDoWhile(DoWhile node) {
|
| - StatementType bodyType = analyze(node.body);
|
| - checkCondition(node.condition);
|
| - return bodyType.join(StatementType.NOT_RETURNING);
|
| - }
|
| -
|
| - Type visitExpressionStatement(ExpressionStatement node) {
|
| - analyze(node.expression);
|
| - return StatementType.NOT_RETURNING;
|
| - }
|
| -
|
| - /** Dart Programming Language Specification: 11.5.1 For Loop */
|
| - Type visitFor(For node) {
|
| - analyzeWithDefault(node.initializer, StatementType.NOT_RETURNING);
|
| - checkCondition(node.condition);
|
| - analyzeWithDefault(node.update, StatementType.NOT_RETURNING);
|
| - StatementType bodyType = analyze(node.body);
|
| - return bodyType.join(StatementType.NOT_RETURNING);
|
| - }
|
| -
|
| - Type visitFunctionDeclaration(FunctionDeclaration node) {
|
| - analyze(node.function);
|
| - return StatementType.NOT_RETURNING;
|
| - }
|
| -
|
| - Type visitFunctionExpression(FunctionExpression node) {
|
| - Type type;
|
| - Type returnType;
|
| - Type previousType;
|
| - final FunctionElement element = elements[node];
|
| - if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR ||
|
| - element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
|
| - type = types.dynamicType;
|
| - returnType = types.voidType;
|
| - } else {
|
| - FunctionType functionType = computeType(element);
|
| - returnType = functionType.returnType;
|
| - type = functionType;
|
| - }
|
| - Type previous = expectedReturnType;
|
| - expectedReturnType = returnType;
|
| - if (element.isMember()) currentClass = element.enclosingElement;
|
| - StatementType bodyType = analyze(node.body);
|
| - if (returnType != types.voidType && returnType != types.dynamicType
|
| - && bodyType != StatementType.RETURNING) {
|
| - MessageKind kind;
|
| - if (bodyType == StatementType.MAYBE_RETURNING) {
|
| - kind = MessageKind.MAYBE_MISSING_RETURN;
|
| - } else {
|
| - kind = MessageKind.MISSING_RETURN;
|
| - }
|
| - reportTypeWarning(node.name, kind);
|
| - }
|
| - expectedReturnType = previous;
|
| - return type;
|
| - }
|
| -
|
| - Type visitIdentifier(Identifier node) {
|
| - if (node.isThis()) {
|
| - return currentClass.computeType(compiler);
|
| - } else {
|
| - fail(node, 'internal error: unexpected identifier');
|
| - }
|
| - }
|
| -
|
| - Type visitIf(If node) {
|
| - checkCondition(node.condition);
|
| - StatementType thenType = analyze(node.thenPart);
|
| - StatementType elseType = node.hasElsePart ? analyze(node.elsePart)
|
| - : StatementType.NOT_RETURNING;
|
| - return thenType.join(elseType);
|
| - }
|
| -
|
| - Type visitLoop(Loop node) {
|
| - fail(node, 'internal error');
|
| - }
|
| -
|
| - Type lookupMethodType(Node node, ClassElement classElement,
|
| - SourceString name) {
|
| - Element member = classElement.lookupLocalMember(name);
|
| - if (member === null) {
|
| - classElement.ensureResolved(compiler);
|
| - for (Link<Type> supertypes = classElement.allSupertypes;
|
| - !supertypes.isEmpty();
|
| - supertypes = supertypes.tail) {
|
| - ClassElement lookupTarget = supertypes.head.element;
|
| - member = lookupTarget.lookupLocalMember(name);
|
| - if (member !== null) return computeType(member);
|
| - }
|
| - }
|
| - if (member !== null && member.kind == ElementKind.FUNCTION) {
|
| - return computeType(member);
|
| - }
|
| - reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND,
|
| - [classElement.name, name]);
|
| - return types.dynamicType;
|
| - }
|
| -
|
| - Link<Type> analyzeArguments(Link<Node> arguments) {
|
| - LinkBuilder<Type> builder = new LinkBuilder<Type>();
|
| - while(!arguments.isEmpty()) {
|
| - builder.addLast(analyze(arguments.head));
|
| - arguments = arguments.tail;
|
| - }
|
| - return builder.toLink();
|
| - }
|
| -
|
| - Type visitSend(Send node) {
|
| - if (Elements.isClosureSend(node, elements)) {
|
| - // TODO(karlklose): Finish implementation.
|
| - return types.dynamicType;
|
| - }
|
| -
|
| - Identifier selector = node.selector.asIdentifier();
|
| - String name = selector.source.stringValue;
|
| -
|
| - if (node.isOperator && name === 'is') {
|
| - analyze(node.receiver);
|
| - return boolType;
|
| - } else if (node.isOperator) {
|
| - final Node firstArgument = node.receiver;
|
| - final Type firstArgumentType = analyze(node.receiver);
|
| - final arguments = node.arguments;
|
| - final Node secondArgument = arguments.isEmpty() ? null : arguments.head;
|
| - final Type secondArgumentType = analyzeWithDefault(secondArgument, null);
|
| -
|
| - if (name === '+' || name === '=' || name === '-'
|
| - || name === '*' || name === '/' || name === '%'
|
| - || name === '~/' || name === '|' || name ==='&'
|
| - || name === '^' || name === '~'|| name === '<<'
|
| - || name === '>>' || name === '[]') {
|
| - return types.dynamicType;
|
| - } else if (name === '<' || name === '>' || name === '<='
|
| - || name === '>=' || name === '==' || name === '!='
|
| - || name === '===' || name === '!==') {
|
| - return boolType;
|
| - } else if (name === '||' || name === '&&' || name === '!') {
|
| - checkAssignable(firstArgument, boolType, firstArgumentType);
|
| - if (!arguments.isEmpty()) {
|
| - // TODO(karlklose): check number of arguments in validator.
|
| - checkAssignable(secondArgument, boolType, secondArgumentType);
|
| - }
|
| - return boolType;
|
| - }
|
| - fail(selector, 'unexpected operator ${name}');
|
| -
|
| - } else if (node.isPropertyAccess) {
|
| - if (node.receiver !== null) fail(node, 'cannot handle fields');
|
| - Element element = elements[node];
|
| - if (element === null) fail(node.selector, 'unresolved property');
|
| - return computeType(element);
|
| -
|
| - } else if (node.isFunctionObjectInvocation) {
|
| - fail(node.receiver, 'function object invocation unimplemented');
|
| -
|
| - } else {
|
| - Link<Type> argumentTypes = analyzeArguments(node.arguments);
|
| - FunctionType funType;
|
| - if (node.receiver !== null) {
|
| - Type receiverType = analyze(node.receiver);
|
| - if (receiverType === types.dynamicType) return types.dynamicType;
|
| - if (receiverType === null) {
|
| - fail(node.receiver, 'receivertype is null');
|
| - }
|
| - if (receiverType.element.kind !== ElementKind.CLASS) {
|
| - fail(node.receiver, 'receivertype is not a class');
|
| - }
|
| - ClassElement classElement = receiverType.element;
|
| - // TODO(karlklose): substitute type arguments.
|
| - Type memberType =
|
| - lookupMethodType(selector, classElement, selector.source);
|
| - if (memberType === types.dynamicType) return types.dynamicType;
|
| - if (memberType is !FunctionType) {
|
| - fail(node, 'can only handle function types');
|
| - }
|
| - funType = memberType;
|
| - } else {
|
| - Element element = elements[node];
|
| - if (element === null) {
|
| - fail(node, 'unresolved ${node.selector}');
|
| - } else if (element.kind === ElementKind.FUNCTION) {
|
| - funType = computeType(element);
|
| - } else if (element.kind === ElementKind.FOREIGN) {
|
| - return types.dynamicType;
|
| - } else {
|
| - fail(node, 'unexpected element kind ${element.kind}');
|
| - }
|
| - }
|
| - Link<Type> parameterTypes = funType.parameterTypes;
|
| - Link<Node> argumentNodes = node.arguments;
|
| - while (!argumentTypes.isEmpty() && !parameterTypes.isEmpty()) {
|
| - checkAssignable(argumentNodes.head, parameterTypes.head,
|
| - argumentTypes.head);
|
| - argumentTypes = argumentTypes.tail;
|
| - parameterTypes = parameterTypes.tail;
|
| - argumentNodes = argumentNodes.tail;
|
| - }
|
| - if (!argumentTypes.isEmpty()) {
|
| - reportTypeWarning(argumentNodes.head, MessageKind.ADDITIONAL_ARGUMENT);
|
| - } else if (!parameterTypes.isEmpty()) {
|
| - reportTypeWarning(node, MessageKind.MISSING_ARGUMENT,
|
| - [parameterTypes.head]);
|
| - }
|
| - return funType.returnType;
|
| - }
|
| - }
|
| -
|
| - visitSendSet(SendSet node) {
|
| - Identifier selector = node.selector;
|
| - final name = node.assignmentOperator.source.stringValue;
|
| - if (name === '++' || name === '--') {
|
| - final Element element = elements[node.selector];
|
| - final Type receiverType = computeType(element);
|
| - // TODO(karlklose): this should be the return type instead of int.
|
| - return node.isPrefix ? intType : receiverType;
|
| - } else {
|
| - Type targetType = computeType(elements[node]);
|
| - Node value = node.arguments.head;
|
| - checkAssignable(value, targetType, analyze(value));
|
| - return targetType;
|
| - }
|
| - }
|
| -
|
| - Type visitLiteralInt(LiteralInt node) {
|
| - return intType;
|
| - }
|
| -
|
| - Type visitLiteralDouble(LiteralDouble node) {
|
| - return doubleType;
|
| - }
|
| -
|
| - Type visitLiteralBool(LiteralBool node) {
|
| - return boolType;
|
| - }
|
| -
|
| - Type visitLiteralString(LiteralString node) {
|
| - return stringType;
|
| - }
|
| -
|
| - Type visitStringJuxtaposition(StringJuxtaposition node) {
|
| - analyze(node.first);
|
| - analyze(node.second);
|
| - return stringType;
|
| - }
|
| -
|
| - Type visitLiteralNull(LiteralNull node) {
|
| - return types.dynamicType;
|
| - }
|
| -
|
| - Type visitNewExpression(NewExpression node) {
|
| - return analyze(node.send.selector);
|
| - }
|
| -
|
| - Type visitLiteralList(LiteralList node) {
|
| - return listType;
|
| - }
|
| -
|
| - Type visitNodeList(NodeList node) {
|
| - Type type = StatementType.NOT_RETURNING;
|
| - bool reportedDeadCode = false;
|
| - for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) {
|
| - Type nextType = analyze(link.head);
|
| - if (type == StatementType.RETURNING) {
|
| - if (!reportedDeadCode) {
|
| - reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE);
|
| - reportedDeadCode = true;
|
| - }
|
| - } else if (type == StatementType.MAYBE_RETURNING){
|
| - if (nextType == StatementType.RETURNING) {
|
| - type = nextType;
|
| - }
|
| - } else {
|
| - type = nextType;
|
| - }
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - Type visitOperator(Operator node) {
|
| - fail(node, 'internal error');
|
| - }
|
| -
|
| - /** Dart Programming Language Specification: 11.10 Return */
|
| - Type visitReturn(Return node) {
|
| - final expression = node.expression;
|
| - final isVoidFunction = (expectedReturnType === types.voidType);
|
| -
|
| - // Executing a return statement return e; [...] It is a static type warning
|
| - // if the type of e may not be assigned to the declared return type of the
|
| - // immediately enclosing function.
|
| - if (expression !== null) {
|
| - final expressionType = analyze(expression);
|
| - if (isVoidFunction
|
| - && !types.isAssignable(expressionType, types.voidType)) {
|
| - reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID,
|
| - [expressionType]);
|
| - } else {
|
| - checkAssignable(expression, expectedReturnType, expressionType);
|
| - }
|
| -
|
| - // 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:
|
| - // - f is not a generative constructor.
|
| - // - The return type of f may not be assigned to void.
|
| - } else if (!types.isAssignable(expectedReturnType, types.voidType)) {
|
| - reportTypeWarning(node, MessageKind.RETURN_NOTHING, [expectedReturnType]);
|
| - }
|
| - return StatementType.RETURNING;
|
| - }
|
| -
|
| - Type visitThrow(Throw node) {
|
| - if (node.expression !== null) analyze(node.expression);
|
| - return StatementType.RETURNING;
|
| - }
|
| -
|
| - Type computeType(Element element) {
|
| - if (element === null) return types.dynamicType;
|
| - Type result = element.computeType(compiler);
|
| - return (result !== null) ? result : types.dynamicType;
|
| - }
|
| -
|
| - Type visitTypeAnnotation(TypeAnnotation node) {
|
| - if (node.typeName === null) return types.dynamicType;
|
| - Identifier identifier = node.typeName.asIdentifier();
|
| - if (identifier === null) {
|
| - fail(node.typeName, 'library prefix not implemented');
|
| - }
|
| - // TODO(ahe): Why wasn't this resolved by the resolver?
|
| - Type type = lookupType(identifier.source, compiler, types);
|
| - if (type === null) {
|
| - // The type name cannot be resolved, but the resolver
|
| - // already gave a warning, so we continue checking.
|
| - return types.dynamicType;
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - visitTypeVariable(TypeVariable node) {
|
| - return types.dynamicType;
|
| - }
|
| -
|
| - Type visitVariableDefinitions(VariableDefinitions node) {
|
| - Type type = analyzeWithDefault(node.type, types.dynamicType);
|
| - if (type == types.voidType) {
|
| - reportTypeWarning(node.type, MessageKind.VOID_VARIABLE);
|
| - type = types.dynamicType;
|
| - }
|
| - for (Link<Node> link = node.definitions.nodes; !link.isEmpty();
|
| - link = link.tail) {
|
| - Node initialization = link.head;
|
| - compiler.ensure(initialization is Identifier
|
| - || initialization is Send);
|
| - if (initialization is Send) {
|
| - Type initializer = analyzeNonVoid(link.head);
|
| - checkAssignable(node, type, initializer);
|
| - }
|
| - }
|
| - return StatementType.NOT_RETURNING;
|
| - }
|
| -
|
| - Type visitWhile(While node) {
|
| - checkCondition(node.condition);
|
| - StatementType bodyType = analyze(node.body);
|
| - return bodyType.join(StatementType.NOT_RETURNING);
|
| - }
|
| -
|
| - Type visitParenthesizedExpression(ParenthesizedExpression node) {
|
| - return analyze(node.expression);
|
| - }
|
| -
|
| - Type visitConditional(Conditional node) {
|
| - checkCondition(node.condition);
|
| - Type thenType = analyzeNonVoid(node.thenExpression);
|
| - Type elseType = analyzeNonVoid(node.elseExpression);
|
| - if (types.isSubtype(thenType, elseType)) {
|
| - return thenType;
|
| - } else if (types.isSubtype(elseType, thenType)) {
|
| - return elseType;
|
| - } else {
|
| - return objectType;
|
| - }
|
| - }
|
| -
|
| - Type visitModifiers(Modifiers node) {}
|
| -
|
| - visitStringInterpolation(StringInterpolation node) {
|
| - node.visitChildren(this);
|
| - return stringType;
|
| - }
|
| -
|
| - visitStringInterpolationPart(StringInterpolationPart node) {
|
| - node.visitChildren(this);
|
| - return stringType;
|
| - }
|
| -
|
| - visitEmptyStatement(EmptyStatement node) {
|
| - return StatementType.NOT_RETURNING;
|
| - }
|
| -
|
| - visitBreakStatement(BreakStatement node) {
|
| - return StatementType.NOT_RETURNING;
|
| - }
|
| -
|
| - visitContinueStatement(ContinueStatement node) {
|
| - return StatementType.NOT_RETURNING;
|
| - }
|
| -
|
| - visitForInStatement(ForInStatement node) {
|
| - analyze(node.expression);
|
| - StatementType bodyType = analyze(node.body);
|
| - return bodyType.join(StatementType.NOT_RETURNING);
|
| - }
|
| -
|
| - visitLabeledStatement(LabeledStatement node) {
|
| - return node.statement.accept(this);
|
| - }
|
| -
|
| - visitLiteralMap(LiteralMap node) {
|
| - fail(node);
|
| - }
|
| -
|
| - visitLiteralMapEntry(LiteralMapEntry node) {
|
| - fail(node);
|
| - }
|
| -
|
| - visitNamedArgument(NamedArgument node) {
|
| - fail(node, 'named argument not implemented');
|
| - }
|
| -
|
| - visitSwitchStatement(SwitchStatement node) {
|
| - fail(node);
|
| - }
|
| -
|
| - visitSwitchCase(SwitchCase node) {
|
| - fail(node);
|
| - }
|
| -
|
| - visitTryStatement(TryStatement node) {
|
| - fail(node, 'unimplemented');
|
| - }
|
| -
|
| - visitScriptTag(ScriptTag node) {
|
| - fail(node);
|
| - }
|
| -
|
| - visitCatchBlock(CatchBlock node) {
|
| - fail(node);
|
| - }
|
| -
|
| - visitTypedef(Typedef node) {
|
| - fail(node);
|
| - }
|
| -}
|
|
|