OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 class TypeCheckerTask extends CompilerTask { | |
6 TypeCheckerTask(Compiler compiler) : super(compiler); | |
7 String get name() => "Type checker"; | |
8 | |
9 static final bool LOG_FAILURES = false; | |
10 | |
11 void check(Node tree, TreeElements elements) { | |
12 measure(() { | |
13 Visitor visitor = | |
14 new TypeCheckerVisitor(compiler, elements, compiler.types); | |
15 try { | |
16 tree.accept(visitor); | |
17 } catch (CancelTypeCheckException e) { | |
18 if (LOG_FAILURES) { | |
19 // Do not warn about unimplemented features; log message instead. | |
20 compiler.log("'${e.node}': ${e.reason}"); | |
21 } | |
22 } | |
23 }); | |
24 } | |
25 } | |
26 | |
27 interface Type { | |
28 SourceString get name(); | |
29 Element get element(); | |
30 } | |
31 | |
32 class TypeVariableType implements Type { | |
33 final SourceString name; | |
34 Element element; | |
35 TypeVariableType(this.name, [this.element]); | |
36 } | |
37 | |
38 /** | |
39 * A statement type tracks whether a statement returns or may return. | |
40 */ | |
41 class StatementType implements Type { | |
42 final String stringName; | |
43 Element get element() => null; | |
44 | |
45 SourceString get name() => new SourceString(stringName); | |
46 | |
47 const StatementType(this.stringName); | |
48 | |
49 static final RETURNING = const StatementType('<returning>'); | |
50 static final NOT_RETURNING = const StatementType('<not returning>'); | |
51 static final MAYBE_RETURNING = const StatementType('<maybe returning>'); | |
52 | |
53 /** Combine the information about two control-flow edges that are joined. */ | |
54 StatementType join(StatementType other) { | |
55 return (this === other) ? this : MAYBE_RETURNING; | |
56 } | |
57 | |
58 String toString() => stringName; | |
59 } | |
60 | |
61 class SimpleType implements Type { | |
62 final SourceString name; | |
63 final Element element; | |
64 | |
65 const SimpleType(SourceString this.name, Element this.element); | |
66 | |
67 String toString() => name.slowToString(); | |
68 } | |
69 | |
70 class FunctionType implements Type { | |
71 final Element element; | |
72 final Type returnType; | |
73 final Link<Type> parameterTypes; | |
74 | |
75 const FunctionType(Type this.returnType, Link<Type> this.parameterTypes, | |
76 Element this.element); | |
77 | |
78 toString() { | |
79 StringBuffer sb = new StringBuffer(); | |
80 bool first = true; | |
81 sb.add('('); | |
82 parameterTypes.printOn(sb, ', '); | |
83 sb.add(') -> ${returnType}'); | |
84 return sb.toString(); | |
85 } | |
86 | |
87 SourceString get name() => const SourceString('Function'); | |
88 } | |
89 | |
90 class Types { | |
91 static final VOID = const SourceString('void'); | |
92 static final INT = const SourceString('int'); | |
93 static final DOUBLE = const SourceString('double'); | |
94 static final DYNAMIC = const SourceString('Dynamic'); | |
95 static final STRING = const SourceString('String'); | |
96 static final BOOL = const SourceString('bool'); | |
97 static final OBJECT = const SourceString('Object'); | |
98 static final LIST = const SourceString('List'); | |
99 | |
100 final SimpleType voidType; | |
101 final SimpleType dynamicType; | |
102 | |
103 Types() : this.with(new LibraryElement(new Script(null, null))); | |
104 Types.with(LibraryElement library) | |
105 : voidType = new SimpleType(VOID, new ClassElement(VOID, library)), | |
106 dynamicType = new SimpleType(DYNAMIC, new ClassElement(DYNAMIC, library)); | |
107 | |
108 Type lookup(SourceString s) { | |
109 if (VOID == s) { | |
110 return voidType; | |
111 } else if (DYNAMIC == s || s.stringValue === 'var') { | |
112 return dynamicType; | |
113 } | |
114 return null; | |
115 } | |
116 | |
117 /** Returns true if t is a subtype of s */ | |
118 bool isSubtype(Type t, Type s) { | |
119 if (t === s || t === dynamicType || s === dynamicType || | |
120 s.name == OBJECT) return true; | |
121 if (t is SimpleType) { | |
122 if (s is !SimpleType) return false; | |
123 ClassElement tc = t.element; | |
124 for (Link<Type> supertypes = tc.allSupertypes; | |
125 supertypes != null && !supertypes.isEmpty(); | |
126 supertypes = supertypes.tail) { | |
127 Type supertype = supertypes.head; | |
128 if (supertype.element === s.element) return true; | |
129 } | |
130 return false; | |
131 } else if (t is FunctionType) { | |
132 if (s is !FunctionType) return false; | |
133 FunctionType tf = t; | |
134 FunctionType sf = s; | |
135 Link<Type> tps = tf.parameterTypes; | |
136 Link<Type> sps = sf.parameterTypes; | |
137 while (!tps.isEmpty() && !sps.isEmpty()) { | |
138 if (!isAssignable(tps.head, sps.head)) return false; | |
139 tps = tps.tail; | |
140 sps = sps.tail; | |
141 } | |
142 if (!tps.isEmpty() || !sps.isEmpty()) return false; | |
143 if (!isAssignable(sf.returnType, tf.returnType)) return false; | |
144 return true; | |
145 } else { | |
146 throw 'internal error: unknown type kind'; | |
147 } | |
148 } | |
149 | |
150 bool isAssignable(Type r, Type s) { | |
151 return isSubtype(r, s) || isSubtype(s, r); | |
152 } | |
153 } | |
154 | |
155 class CancelTypeCheckException { | |
156 final Node node; | |
157 final String reason; | |
158 | |
159 CancelTypeCheckException(this.node, this.reason); | |
160 } | |
161 | |
162 Type lookupType(SourceString name, Compiler compiler, types) { | |
163 Type t = types.lookup(name); | |
164 if (t !== null) return t; | |
165 Element element = compiler.coreLibrary.find(name); | |
166 if (element !== null && element.kind === ElementKind.CLASS) { | |
167 return element.computeType(compiler); | |
168 } | |
169 return null; | |
170 } | |
171 | |
172 class TypeCheckerVisitor implements Visitor<Type> { | |
173 final Compiler compiler; | |
174 final TreeElements elements; | |
175 Node lastSeenNode; | |
176 final Types types; | |
177 | |
178 Type expectedReturnType; | |
179 ClassElement currentClass; | |
180 | |
181 Type intType; | |
182 Type doubleType; | |
183 Type boolType; | |
184 Type stringType; | |
185 Type objectType; | |
186 Type listType; | |
187 | |
188 TypeCheckerVisitor(Compiler this.compiler, TreeElements this.elements, | |
189 Types this.types) { | |
190 intType = lookupType(Types.INT, compiler, types); | |
191 doubleType = lookupType(Types.DOUBLE, compiler, types); | |
192 boolType = lookupType(Types.BOOL, compiler, types); | |
193 stringType = lookupType(Types.STRING, compiler, types); | |
194 objectType = lookupType(Types.OBJECT, compiler, types); | |
195 listType = lookupType(Types.LIST, compiler, types); | |
196 } | |
197 | |
198 Type fail(node, [reason]) { | |
199 String message = 'cannot type-check'; | |
200 if (reason !== null) { | |
201 message = '$message: $reason'; | |
202 } | |
203 throw new CancelTypeCheckException(node, message); | |
204 } | |
205 | |
206 reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) { | |
207 compiler.reportWarning(node, new TypeWarning(kind, arguments)); | |
208 } | |
209 | |
210 Type analyzeNonVoid(Node node) { | |
211 Type type = analyze(node); | |
212 if (type == types.voidType) { | |
213 reportTypeWarning(node, MessageKind.VOID_EXPRESSION); | |
214 } | |
215 return type; | |
216 } | |
217 | |
218 Type analyzeWithDefault(Node node, Type defaultValue) { | |
219 return node !== null ? analyze(node) : defaultValue; | |
220 } | |
221 | |
222 Type analyze(Node node) { | |
223 if (node == null) { | |
224 final String error = 'internal error: unexpected node: null'; | |
225 if (lastSeenNode != null) { | |
226 fail(null, error); | |
227 } else { | |
228 compiler.cancel(error); | |
229 } | |
230 } else { | |
231 lastSeenNode = node; | |
232 } | |
233 Type result = node.accept(this); | |
234 // TODO(karlklose): record type? | |
235 if (result === null) { | |
236 fail(node, 'internal error: type is null'); | |
237 } | |
238 return result; | |
239 } | |
240 | |
241 /** | |
242 * Check if a value of type t can be assigned to a variable, | |
243 * parameter or return value of type s. | |
244 */ | |
245 checkAssignable(Node node, Type s, Type t) { | |
246 if (!types.isAssignable(s, t)) { | |
247 reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE, [s, t]); | |
248 } | |
249 } | |
250 | |
251 checkCondition(Expression condition) { | |
252 checkAssignable(condition, boolType, analyze(condition)); | |
253 } | |
254 | |
255 Type visitBlock(Block node) { | |
256 return analyze(node.statements); | |
257 } | |
258 | |
259 Type visitClassNode(ClassNode node) { | |
260 fail(node); | |
261 } | |
262 | |
263 Type visitDoWhile(DoWhile node) { | |
264 StatementType bodyType = analyze(node.body); | |
265 checkCondition(node.condition); | |
266 return bodyType.join(StatementType.NOT_RETURNING); | |
267 } | |
268 | |
269 Type visitExpressionStatement(ExpressionStatement node) { | |
270 analyze(node.expression); | |
271 return StatementType.NOT_RETURNING; | |
272 } | |
273 | |
274 /** Dart Programming Language Specification: 11.5.1 For Loop */ | |
275 Type visitFor(For node) { | |
276 analyzeWithDefault(node.initializer, StatementType.NOT_RETURNING); | |
277 checkCondition(node.condition); | |
278 analyzeWithDefault(node.update, StatementType.NOT_RETURNING); | |
279 StatementType bodyType = analyze(node.body); | |
280 return bodyType.join(StatementType.NOT_RETURNING); | |
281 } | |
282 | |
283 Type visitFunctionDeclaration(FunctionDeclaration node) { | |
284 analyze(node.function); | |
285 return StatementType.NOT_RETURNING; | |
286 } | |
287 | |
288 Type visitFunctionExpression(FunctionExpression node) { | |
289 Type type; | |
290 Type returnType; | |
291 Type previousType; | |
292 final FunctionElement element = elements[node]; | |
293 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR || | |
294 element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | |
295 type = types.dynamicType; | |
296 returnType = types.voidType; | |
297 } else { | |
298 FunctionType functionType = computeType(element); | |
299 returnType = functionType.returnType; | |
300 type = functionType; | |
301 } | |
302 Type previous = expectedReturnType; | |
303 expectedReturnType = returnType; | |
304 if (element.isMember()) currentClass = element.enclosingElement; | |
305 StatementType bodyType = analyze(node.body); | |
306 if (returnType != types.voidType && returnType != types.dynamicType | |
307 && bodyType != StatementType.RETURNING) { | |
308 MessageKind kind; | |
309 if (bodyType == StatementType.MAYBE_RETURNING) { | |
310 kind = MessageKind.MAYBE_MISSING_RETURN; | |
311 } else { | |
312 kind = MessageKind.MISSING_RETURN; | |
313 } | |
314 reportTypeWarning(node.name, kind); | |
315 } | |
316 expectedReturnType = previous; | |
317 return type; | |
318 } | |
319 | |
320 Type visitIdentifier(Identifier node) { | |
321 if (node.isThis()) { | |
322 return currentClass.computeType(compiler); | |
323 } else { | |
324 fail(node, 'internal error: unexpected identifier'); | |
325 } | |
326 } | |
327 | |
328 Type visitIf(If node) { | |
329 checkCondition(node.condition); | |
330 StatementType thenType = analyze(node.thenPart); | |
331 StatementType elseType = node.hasElsePart ? analyze(node.elsePart) | |
332 : StatementType.NOT_RETURNING; | |
333 return thenType.join(elseType); | |
334 } | |
335 | |
336 Type visitLoop(Loop node) { | |
337 fail(node, 'internal error'); | |
338 } | |
339 | |
340 Type lookupMethodType(Node node, ClassElement classElement, | |
341 SourceString name) { | |
342 Element member = classElement.lookupLocalMember(name); | |
343 if (member === null) { | |
344 classElement.ensureResolved(compiler); | |
345 for (Link<Type> supertypes = classElement.allSupertypes; | |
346 !supertypes.isEmpty(); | |
347 supertypes = supertypes.tail) { | |
348 ClassElement lookupTarget = supertypes.head.element; | |
349 member = lookupTarget.lookupLocalMember(name); | |
350 if (member !== null) return computeType(member); | |
351 } | |
352 } | |
353 if (member !== null && member.kind == ElementKind.FUNCTION) { | |
354 return computeType(member); | |
355 } | |
356 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, | |
357 [classElement.name, name]); | |
358 return types.dynamicType; | |
359 } | |
360 | |
361 Link<Type> analyzeArguments(Link<Node> arguments) { | |
362 LinkBuilder<Type> builder = new LinkBuilder<Type>(); | |
363 while(!arguments.isEmpty()) { | |
364 builder.addLast(analyze(arguments.head)); | |
365 arguments = arguments.tail; | |
366 } | |
367 return builder.toLink(); | |
368 } | |
369 | |
370 Type visitSend(Send node) { | |
371 if (Elements.isClosureSend(node, elements)) { | |
372 // TODO(karlklose): Finish implementation. | |
373 return types.dynamicType; | |
374 } | |
375 | |
376 Identifier selector = node.selector.asIdentifier(); | |
377 String name = selector.source.stringValue; | |
378 | |
379 if (node.isOperator && name === 'is') { | |
380 analyze(node.receiver); | |
381 return boolType; | |
382 } else if (node.isOperator) { | |
383 final Node firstArgument = node.receiver; | |
384 final Type firstArgumentType = analyze(node.receiver); | |
385 final arguments = node.arguments; | |
386 final Node secondArgument = arguments.isEmpty() ? null : arguments.head; | |
387 final Type secondArgumentType = analyzeWithDefault(secondArgument, null); | |
388 | |
389 if (name === '+' || name === '=' || name === '-' | |
390 || name === '*' || name === '/' || name === '%' | |
391 || name === '~/' || name === '|' || name ==='&' | |
392 || name === '^' || name === '~'|| name === '<<' | |
393 || name === '>>' || name === '[]') { | |
394 return types.dynamicType; | |
395 } else if (name === '<' || name === '>' || name === '<=' | |
396 || name === '>=' || name === '==' || name === '!=' | |
397 || name === '===' || name === '!==') { | |
398 return boolType; | |
399 } else if (name === '||' || name === '&&' || name === '!') { | |
400 checkAssignable(firstArgument, boolType, firstArgumentType); | |
401 if (!arguments.isEmpty()) { | |
402 // TODO(karlklose): check number of arguments in validator. | |
403 checkAssignable(secondArgument, boolType, secondArgumentType); | |
404 } | |
405 return boolType; | |
406 } | |
407 fail(selector, 'unexpected operator ${name}'); | |
408 | |
409 } else if (node.isPropertyAccess) { | |
410 if (node.receiver !== null) fail(node, 'cannot handle fields'); | |
411 Element element = elements[node]; | |
412 if (element === null) fail(node.selector, 'unresolved property'); | |
413 return computeType(element); | |
414 | |
415 } else if (node.isFunctionObjectInvocation) { | |
416 fail(node.receiver, 'function object invocation unimplemented'); | |
417 | |
418 } else { | |
419 Link<Type> argumentTypes = analyzeArguments(node.arguments); | |
420 FunctionType funType; | |
421 if (node.receiver !== null) { | |
422 Type receiverType = analyze(node.receiver); | |
423 if (receiverType === types.dynamicType) return types.dynamicType; | |
424 if (receiverType === null) { | |
425 fail(node.receiver, 'receivertype is null'); | |
426 } | |
427 if (receiverType.element.kind !== ElementKind.CLASS) { | |
428 fail(node.receiver, 'receivertype is not a class'); | |
429 } | |
430 ClassElement classElement = receiverType.element; | |
431 // TODO(karlklose): substitute type arguments. | |
432 Type memberType = | |
433 lookupMethodType(selector, classElement, selector.source); | |
434 if (memberType === types.dynamicType) return types.dynamicType; | |
435 if (memberType is !FunctionType) { | |
436 fail(node, 'can only handle function types'); | |
437 } | |
438 funType = memberType; | |
439 } else { | |
440 Element element = elements[node]; | |
441 if (element === null) { | |
442 fail(node, 'unresolved ${node.selector}'); | |
443 } else if (element.kind === ElementKind.FUNCTION) { | |
444 funType = computeType(element); | |
445 } else if (element.kind === ElementKind.FOREIGN) { | |
446 return types.dynamicType; | |
447 } else { | |
448 fail(node, 'unexpected element kind ${element.kind}'); | |
449 } | |
450 } | |
451 Link<Type> parameterTypes = funType.parameterTypes; | |
452 Link<Node> argumentNodes = node.arguments; | |
453 while (!argumentTypes.isEmpty() && !parameterTypes.isEmpty()) { | |
454 checkAssignable(argumentNodes.head, parameterTypes.head, | |
455 argumentTypes.head); | |
456 argumentTypes = argumentTypes.tail; | |
457 parameterTypes = parameterTypes.tail; | |
458 argumentNodes = argumentNodes.tail; | |
459 } | |
460 if (!argumentTypes.isEmpty()) { | |
461 reportTypeWarning(argumentNodes.head, MessageKind.ADDITIONAL_ARGUMENT); | |
462 } else if (!parameterTypes.isEmpty()) { | |
463 reportTypeWarning(node, MessageKind.MISSING_ARGUMENT, | |
464 [parameterTypes.head]); | |
465 } | |
466 return funType.returnType; | |
467 } | |
468 } | |
469 | |
470 visitSendSet(SendSet node) { | |
471 Identifier selector = node.selector; | |
472 final name = node.assignmentOperator.source.stringValue; | |
473 if (name === '++' || name === '--') { | |
474 final Element element = elements[node.selector]; | |
475 final Type receiverType = computeType(element); | |
476 // TODO(karlklose): this should be the return type instead of int. | |
477 return node.isPrefix ? intType : receiverType; | |
478 } else { | |
479 Type targetType = computeType(elements[node]); | |
480 Node value = node.arguments.head; | |
481 checkAssignable(value, targetType, analyze(value)); | |
482 return targetType; | |
483 } | |
484 } | |
485 | |
486 Type visitLiteralInt(LiteralInt node) { | |
487 return intType; | |
488 } | |
489 | |
490 Type visitLiteralDouble(LiteralDouble node) { | |
491 return doubleType; | |
492 } | |
493 | |
494 Type visitLiteralBool(LiteralBool node) { | |
495 return boolType; | |
496 } | |
497 | |
498 Type visitLiteralString(LiteralString node) { | |
499 return stringType; | |
500 } | |
501 | |
502 Type visitStringJuxtaposition(StringJuxtaposition node) { | |
503 analyze(node.first); | |
504 analyze(node.second); | |
505 return stringType; | |
506 } | |
507 | |
508 Type visitLiteralNull(LiteralNull node) { | |
509 return types.dynamicType; | |
510 } | |
511 | |
512 Type visitNewExpression(NewExpression node) { | |
513 return analyze(node.send.selector); | |
514 } | |
515 | |
516 Type visitLiteralList(LiteralList node) { | |
517 return listType; | |
518 } | |
519 | |
520 Type visitNodeList(NodeList node) { | |
521 Type type = StatementType.NOT_RETURNING; | |
522 bool reportedDeadCode = false; | |
523 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { | |
524 Type nextType = analyze(link.head); | |
525 if (type == StatementType.RETURNING) { | |
526 if (!reportedDeadCode) { | |
527 reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE); | |
528 reportedDeadCode = true; | |
529 } | |
530 } else if (type == StatementType.MAYBE_RETURNING){ | |
531 if (nextType == StatementType.RETURNING) { | |
532 type = nextType; | |
533 } | |
534 } else { | |
535 type = nextType; | |
536 } | |
537 } | |
538 return type; | |
539 } | |
540 | |
541 Type visitOperator(Operator node) { | |
542 fail(node, 'internal error'); | |
543 } | |
544 | |
545 /** Dart Programming Language Specification: 11.10 Return */ | |
546 Type visitReturn(Return node) { | |
547 final expression = node.expression; | |
548 final isVoidFunction = (expectedReturnType === types.voidType); | |
549 | |
550 // Executing a return statement return e; [...] It is a static type warning | |
551 // if the type of e may not be assigned to the declared return type of the | |
552 // immediately enclosing function. | |
553 if (expression !== null) { | |
554 final expressionType = analyze(expression); | |
555 if (isVoidFunction | |
556 && !types.isAssignable(expressionType, types.voidType)) { | |
557 reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID, | |
558 [expressionType]); | |
559 } else { | |
560 checkAssignable(expression, expectedReturnType, expressionType); | |
561 } | |
562 | |
563 // Let f be the function immediately enclosing a return statement of the | |
564 // form 'return;' It is a static warning if both of the following conditions | |
565 // hold: | |
566 // - f is not a generative constructor. | |
567 // - The return type of f may not be assigned to void. | |
568 } else if (!types.isAssignable(expectedReturnType, types.voidType)) { | |
569 reportTypeWarning(node, MessageKind.RETURN_NOTHING, [expectedReturnType]); | |
570 } | |
571 return StatementType.RETURNING; | |
572 } | |
573 | |
574 Type visitThrow(Throw node) { | |
575 if (node.expression !== null) analyze(node.expression); | |
576 return StatementType.RETURNING; | |
577 } | |
578 | |
579 Type computeType(Element element) { | |
580 if (element === null) return types.dynamicType; | |
581 Type result = element.computeType(compiler); | |
582 return (result !== null) ? result : types.dynamicType; | |
583 } | |
584 | |
585 Type visitTypeAnnotation(TypeAnnotation node) { | |
586 if (node.typeName === null) return types.dynamicType; | |
587 Identifier identifier = node.typeName.asIdentifier(); | |
588 if (identifier === null) { | |
589 fail(node.typeName, 'library prefix not implemented'); | |
590 } | |
591 // TODO(ahe): Why wasn't this resolved by the resolver? | |
592 Type type = lookupType(identifier.source, compiler, types); | |
593 if (type === null) { | |
594 // The type name cannot be resolved, but the resolver | |
595 // already gave a warning, so we continue checking. | |
596 return types.dynamicType; | |
597 } | |
598 return type; | |
599 } | |
600 | |
601 visitTypeVariable(TypeVariable node) { | |
602 return types.dynamicType; | |
603 } | |
604 | |
605 Type visitVariableDefinitions(VariableDefinitions node) { | |
606 Type type = analyzeWithDefault(node.type, types.dynamicType); | |
607 if (type == types.voidType) { | |
608 reportTypeWarning(node.type, MessageKind.VOID_VARIABLE); | |
609 type = types.dynamicType; | |
610 } | |
611 for (Link<Node> link = node.definitions.nodes; !link.isEmpty(); | |
612 link = link.tail) { | |
613 Node initialization = link.head; | |
614 compiler.ensure(initialization is Identifier | |
615 || initialization is Send); | |
616 if (initialization is Send) { | |
617 Type initializer = analyzeNonVoid(link.head); | |
618 checkAssignable(node, type, initializer); | |
619 } | |
620 } | |
621 return StatementType.NOT_RETURNING; | |
622 } | |
623 | |
624 Type visitWhile(While node) { | |
625 checkCondition(node.condition); | |
626 StatementType bodyType = analyze(node.body); | |
627 return bodyType.join(StatementType.NOT_RETURNING); | |
628 } | |
629 | |
630 Type visitParenthesizedExpression(ParenthesizedExpression node) { | |
631 return analyze(node.expression); | |
632 } | |
633 | |
634 Type visitConditional(Conditional node) { | |
635 checkCondition(node.condition); | |
636 Type thenType = analyzeNonVoid(node.thenExpression); | |
637 Type elseType = analyzeNonVoid(node.elseExpression); | |
638 if (types.isSubtype(thenType, elseType)) { | |
639 return thenType; | |
640 } else if (types.isSubtype(elseType, thenType)) { | |
641 return elseType; | |
642 } else { | |
643 return objectType; | |
644 } | |
645 } | |
646 | |
647 Type visitModifiers(Modifiers node) {} | |
648 | |
649 visitStringInterpolation(StringInterpolation node) { | |
650 node.visitChildren(this); | |
651 return stringType; | |
652 } | |
653 | |
654 visitStringInterpolationPart(StringInterpolationPart node) { | |
655 node.visitChildren(this); | |
656 return stringType; | |
657 } | |
658 | |
659 visitEmptyStatement(EmptyStatement node) { | |
660 return StatementType.NOT_RETURNING; | |
661 } | |
662 | |
663 visitBreakStatement(BreakStatement node) { | |
664 return StatementType.NOT_RETURNING; | |
665 } | |
666 | |
667 visitContinueStatement(ContinueStatement node) { | |
668 return StatementType.NOT_RETURNING; | |
669 } | |
670 | |
671 visitForInStatement(ForInStatement node) { | |
672 analyze(node.expression); | |
673 StatementType bodyType = analyze(node.body); | |
674 return bodyType.join(StatementType.NOT_RETURNING); | |
675 } | |
676 | |
677 visitLabeledStatement(LabeledStatement node) { | |
678 return node.statement.accept(this); | |
679 } | |
680 | |
681 visitLiteralMap(LiteralMap node) { | |
682 fail(node); | |
683 } | |
684 | |
685 visitLiteralMapEntry(LiteralMapEntry node) { | |
686 fail(node); | |
687 } | |
688 | |
689 visitNamedArgument(NamedArgument node) { | |
690 fail(node, 'named argument not implemented'); | |
691 } | |
692 | |
693 visitSwitchStatement(SwitchStatement node) { | |
694 fail(node); | |
695 } | |
696 | |
697 visitSwitchCase(SwitchCase node) { | |
698 fail(node); | |
699 } | |
700 | |
701 visitTryStatement(TryStatement node) { | |
702 fail(node, 'unimplemented'); | |
703 } | |
704 | |
705 visitScriptTag(ScriptTag node) { | |
706 fail(node); | |
707 } | |
708 | |
709 visitCatchBlock(CatchBlock node) { | |
710 fail(node); | |
711 } | |
712 | |
713 visitTypedef(Typedef node) { | |
714 fail(node); | |
715 } | |
716 } | |
OLD | NEW |