OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 class TypeCheckerTask extends CompilerTask { | 5 class TypeCheckerTask extends CompilerTask { |
6 TypeCheckerTask(Compiler compiler) : super(compiler); | 6 TypeCheckerTask(Compiler compiler) : super(compiler); |
7 String get name => "Type checker"; | 7 String get name => "Type checker"; |
8 | 8 |
9 static const bool LOG_FAILURES = false; | 9 static const bool LOG_FAILURES = false; |
10 | 10 |
11 void check(Node tree, TreeElements elements) { | 11 void check(Node tree, TreeElements elements) { |
12 measure(() { | 12 measure(() { |
13 Visitor visitor = | 13 Visitor visitor = |
14 new TypeCheckerVisitor(compiler, elements, compiler.types); | 14 new TypeCheckerVisitor(compiler, elements, compiler.types); |
15 try { | 15 try { |
16 tree.accept(visitor); | 16 tree.accept(visitor); |
17 } on CancelTypeCheckException catch (e) { | 17 } on CancelTypeCheckException catch (e) { |
18 if (LOG_FAILURES) { | 18 if (LOG_FAILURES) { |
19 // Do not warn about unimplemented features; log message instead. | 19 // Do not warn about unimplemented features; log message instead. |
20 compiler.log("'${e.node}': ${e.reason}"); | 20 compiler.log("'${e.node}': ${e.reason}"); |
21 } | 21 } |
22 } | 22 } |
23 }); | 23 }); |
24 } | 24 } |
25 } | 25 } |
26 | 26 |
27 interface Type { | 27 interface DartType { |
28 SourceString get name(); | 28 SourceString get name(); |
29 Element get element(); | 29 Element get element(); |
30 | 30 |
31 /** | 31 /** |
32 * Returns the unaliased type of this type. | 32 * Returns the unaliased type of this type. |
33 * | 33 * |
34 * The unaliased type of a typedef'd type is the unaliased type to which its | 34 * The unaliased type of a typedef'd type is the unaliased type to which its |
35 * name is bound. The unaliased version of any other type is the type itself. | 35 * name is bound. The unaliased version of any other type is the type itself. |
36 * | 36 * |
37 * For example, the unaliased type of [: typedef A Func<A,B>(B b) :] is the | 37 * For example, the unaliased type of [: typedef A Func<A,B>(B b) :] is the |
38 * function type [: (B) -> A :] and the unaliased type of | 38 * function type [: (B) -> A :] and the unaliased type of |
39 * [: Func<int,String> :] is the function type [: (String) -> int :]. | 39 * [: Func<int,String> :] is the function type [: (String) -> int :]. |
40 */ | 40 */ |
41 Type unalias(Compiler compiler); | 41 DartType unalias(Compiler compiler); |
42 } | 42 } |
43 | 43 |
44 class TypeVariableType implements Type { | 44 class TypeVariableType implements DartType { |
45 final TypeVariableElement element; | 45 final TypeVariableElement element; |
46 | 46 |
47 TypeVariableType(this.element); | 47 TypeVariableType(this.element); |
48 | 48 |
49 SourceString get name => element.name; | 49 SourceString get name => element.name; |
50 | 50 |
51 Type unalias(Compiler compiler) => this; | 51 DartType unalias(Compiler compiler) => this; |
52 | 52 |
53 String toString() => name.slowToString(); | 53 String toString() => name.slowToString(); |
54 } | 54 } |
55 | 55 |
56 /** | 56 /** |
57 * A statement type tracks whether a statement returns or may return. | 57 * A statement type tracks whether a statement returns or may return. |
58 */ | 58 */ |
59 class StatementType implements Type { | 59 class StatementType implements DartType { |
60 final String stringName; | 60 final String stringName; |
61 Element get element => null; | 61 Element get element => null; |
62 | 62 |
63 SourceString get name => new SourceString(stringName); | 63 SourceString get name => new SourceString(stringName); |
64 | 64 |
65 const StatementType(this.stringName); | 65 const StatementType(this.stringName); |
66 | 66 |
67 static const RETURNING = const StatementType('<returning>'); | 67 static const RETURNING = const StatementType('<returning>'); |
68 static const NOT_RETURNING = const StatementType('<not returning>'); | 68 static const NOT_RETURNING = const StatementType('<not returning>'); |
69 static const MAYBE_RETURNING = const StatementType('<maybe returning>'); | 69 static const MAYBE_RETURNING = const StatementType('<maybe returning>'); |
70 | 70 |
71 /** Combine the information about two control-flow edges that are joined. */ | 71 /** Combine the information about two control-flow edges that are joined. */ |
72 StatementType join(StatementType other) { | 72 StatementType join(StatementType other) { |
73 return (this === other) ? this : MAYBE_RETURNING; | 73 return (this === other) ? this : MAYBE_RETURNING; |
74 } | 74 } |
75 | 75 |
76 Type unalias(Compiler compiler) => this; | 76 DartType unalias(Compiler compiler) => this; |
77 | 77 |
78 String toString() => stringName; | 78 String toString() => stringName; |
79 } | 79 } |
80 | 80 |
81 class VoidType implements Type { | 81 class VoidType implements DartType { |
82 const VoidType(this.element); | 82 const VoidType(this.element); |
83 SourceString get name => element.name; | 83 SourceString get name => element.name; |
84 final VoidElement element; | 84 final VoidElement element; |
85 | 85 |
86 Type unalias(Compiler compiler) => this; | 86 DartType unalias(Compiler compiler) => this; |
87 | 87 |
88 String toString() => name.slowToString(); | 88 String toString() => name.slowToString(); |
89 } | 89 } |
90 | 90 |
91 class InterfaceType implements Type { | 91 class InterfaceType implements DartType { |
92 final Element element; | 92 final Element element; |
93 final Link<Type> arguments; | 93 final Link<DartType> arguments; |
94 | 94 |
95 const InterfaceType(this.element, | 95 const InterfaceType(this.element, |
96 [this.arguments = const EmptyLink<Type>()]); | 96 [this.arguments = const EmptyLink<DartType>()]); |
97 | 97 |
98 SourceString get name => element.name; | 98 SourceString get name => element.name; |
99 | 99 |
100 Type unalias(Compiler compiler) => this; | 100 DartType unalias(Compiler compiler) => this; |
101 | 101 |
102 String toString() { | 102 String toString() { |
103 StringBuffer sb = new StringBuffer(); | 103 StringBuffer sb = new StringBuffer(); |
104 sb.add(name.slowToString()); | 104 sb.add(name.slowToString()); |
105 if (!arguments.isEmpty()) { | 105 if (!arguments.isEmpty()) { |
106 sb.add('<'); | 106 sb.add('<'); |
107 arguments.printOn(sb, ', '); | 107 arguments.printOn(sb, ', '); |
108 sb.add('>'); | 108 sb.add('>'); |
109 } | 109 } |
110 return sb.toString(); | 110 return sb.toString(); |
111 } | 111 } |
112 } | 112 } |
113 | 113 |
114 class FunctionType implements Type { | 114 class FunctionType implements DartType { |
115 final Element element; | 115 final Element element; |
116 Type returnType; | 116 DartType returnType; |
117 Link<Type> parameterTypes; | 117 Link<DartType> parameterTypes; |
118 | 118 |
119 FunctionType(Type this.returnType, Link<Type> this.parameterTypes, | 119 FunctionType(DartType this.returnType, Link<DartType> this.parameterTypes, |
120 Element this.element); | 120 Element this.element); |
121 | 121 |
122 Type unalias(Compiler compiler) => this; | 122 DartType unalias(Compiler compiler) => this; |
123 | 123 |
124 String toString() { | 124 String toString() { |
125 StringBuffer sb = new StringBuffer(); | 125 StringBuffer sb = new StringBuffer(); |
126 bool first = true; | 126 bool first = true; |
127 sb.add('('); | 127 sb.add('('); |
128 parameterTypes.printOn(sb, ', '); | 128 parameterTypes.printOn(sb, ', '); |
129 sb.add(') -> ${returnType}'); | 129 sb.add(') -> ${returnType}'); |
130 return sb.toString(); | 130 return sb.toString(); |
131 } | 131 } |
132 | 132 |
133 SourceString get name => const SourceString('Function'); | 133 SourceString get name => const SourceString('Function'); |
134 | 134 |
135 int computeArity() { | 135 int computeArity() { |
136 int arity = 0; | 136 int arity = 0; |
137 parameterTypes.forEach((_) { arity++; }); | 137 parameterTypes.forEach((_) { arity++; }); |
138 return arity; | 138 return arity; |
139 } | 139 } |
140 | 140 |
141 void initializeFrom(FunctionType other) { | 141 void initializeFrom(FunctionType other) { |
142 assert(returnType === null); | 142 assert(returnType === null); |
143 assert(parameterTypes === null); | 143 assert(parameterTypes === null); |
144 returnType = other.returnType; | 144 returnType = other.returnType; |
145 parameterTypes = other.parameterTypes; | 145 parameterTypes = other.parameterTypes; |
146 } | 146 } |
147 } | 147 } |
148 | 148 |
149 class TypedefType implements Type { | 149 class TypedefType implements DartType { |
150 final TypedefElement element; | 150 final TypedefElement element; |
151 final Link<Type> typeArguments; | 151 final Link<DartType> typeArguments; |
152 | 152 |
153 const TypedefType(this.element, | 153 const TypedefType(this.element, |
154 [this.typeArguments = const EmptyLink<Type>()]); | 154 [this.typeArguments = const EmptyLink<DartType>()]); |
155 | 155 |
156 SourceString get name => element.name; | 156 SourceString get name => element.name; |
157 | 157 |
158 Type unalias(Compiler compiler) { | 158 DartType unalias(Compiler compiler) { |
159 // TODO(ahe): This should be [ensureResolved]. | 159 // TODO(ahe): This should be [ensureResolved]. |
160 compiler.resolveTypedef(element); | 160 compiler.resolveTypedef(element); |
161 return element.alias.unalias(compiler); | 161 return element.alias.unalias(compiler); |
162 } | 162 } |
163 | 163 |
164 String toString() { | 164 String toString() { |
165 StringBuffer sb = new StringBuffer(); | 165 StringBuffer sb = new StringBuffer(); |
166 sb.add(name.slowToString()); | 166 sb.add(name.slowToString()); |
167 if (!typeArguments.isEmpty()) { | 167 if (!typeArguments.isEmpty()) { |
168 sb.add('<'); | 168 sb.add('<'); |
(...skipping 14 matching lines...) Expand all Loading... |
183 : this.with(compiler, dynamicElement, | 183 : this.with(compiler, dynamicElement, |
184 new LibraryElement(new Script(null, null))); | 184 new LibraryElement(new Script(null, null))); |
185 | 185 |
186 Types.with(Compiler this.compiler, | 186 Types.with(Compiler this.compiler, |
187 Element dynamicElement, | 187 Element dynamicElement, |
188 LibraryElement library) | 188 LibraryElement library) |
189 : voidType = new VoidType(new VoidElement(library)), | 189 : voidType = new VoidType(new VoidElement(library)), |
190 dynamicType = new InterfaceType(dynamicElement); | 190 dynamicType = new InterfaceType(dynamicElement); |
191 | 191 |
192 /** Returns true if t is a subtype of s */ | 192 /** Returns true if t is a subtype of s */ |
193 bool isSubtype(Type t, Type s) { | 193 bool isSubtype(DartType t, DartType s) { |
194 if (t === s || | 194 if (t === s || |
195 t === dynamicType || | 195 t === dynamicType || |
196 s === dynamicType || | 196 s === dynamicType || |
197 s.element === compiler.objectClass) { | 197 s.element === compiler.objectClass) { |
198 return true; | 198 return true; |
199 } | 199 } |
200 t = t.unalias(compiler); | 200 t = t.unalias(compiler); |
201 s = s.unalias(compiler); | 201 s = s.unalias(compiler); |
202 | 202 |
203 if (t is VoidType) { | 203 if (t is VoidType) { |
204 return false; | 204 return false; |
205 } else if (t is InterfaceType) { | 205 } else if (t is InterfaceType) { |
206 if (s is !InterfaceType) return false; | 206 if (s is !InterfaceType) return false; |
207 ClassElement tc = t.element; | 207 ClassElement tc = t.element; |
208 if (tc === s.element) return true; | 208 if (tc === s.element) return true; |
209 for (Link<Type> supertypes = tc.allSupertypes; | 209 for (Link<DartType> supertypes = tc.allSupertypes; |
210 supertypes != null && !supertypes.isEmpty(); | 210 supertypes != null && !supertypes.isEmpty(); |
211 supertypes = supertypes.tail) { | 211 supertypes = supertypes.tail) { |
212 Type supertype = supertypes.head; | 212 DartType supertype = supertypes.head; |
213 if (supertype.element === s.element) return true; | 213 if (supertype.element === s.element) return true; |
214 } | 214 } |
215 return false; | 215 return false; |
216 } else if (t is FunctionType) { | 216 } else if (t is FunctionType) { |
217 if (s.element === compiler.functionClass) return true; | 217 if (s.element === compiler.functionClass) return true; |
218 if (s is !FunctionType) return false; | 218 if (s is !FunctionType) return false; |
219 FunctionType tf = t; | 219 FunctionType tf = t; |
220 FunctionType sf = s; | 220 FunctionType sf = s; |
221 Link<Type> tps = tf.parameterTypes; | 221 Link<DartType> tps = tf.parameterTypes; |
222 Link<Type> sps = sf.parameterTypes; | 222 Link<DartType> sps = sf.parameterTypes; |
223 while (!tps.isEmpty() && !sps.isEmpty()) { | 223 while (!tps.isEmpty() && !sps.isEmpty()) { |
224 if (!isAssignable(tps.head, sps.head)) return false; | 224 if (!isAssignable(tps.head, sps.head)) return false; |
225 tps = tps.tail; | 225 tps = tps.tail; |
226 sps = sps.tail; | 226 sps = sps.tail; |
227 } | 227 } |
228 if (!tps.isEmpty() || !sps.isEmpty()) return false; | 228 if (!tps.isEmpty() || !sps.isEmpty()) return false; |
229 if (!isAssignable(sf.returnType, tf.returnType)) return false; | 229 if (!isAssignable(sf.returnType, tf.returnType)) return false; |
230 return true; | 230 return true; |
231 } else if (t is TypeVariableType) { | 231 } else if (t is TypeVariableType) { |
232 if (s is !TypeVariableType) return false; | 232 if (s is !TypeVariableType) return false; |
233 return (t.element === s.element); | 233 return (t.element === s.element); |
234 } else { | 234 } else { |
235 throw 'internal error: unknown type kind'; | 235 throw 'internal error: unknown type kind'; |
236 } | 236 } |
237 } | 237 } |
238 | 238 |
239 bool isAssignable(Type r, Type s) { | 239 bool isAssignable(DartType r, DartType s) { |
240 return isSubtype(r, s) || isSubtype(s, r); | 240 return isSubtype(r, s) || isSubtype(s, r); |
241 } | 241 } |
242 } | 242 } |
243 | 243 |
244 class CancelTypeCheckException { | 244 class CancelTypeCheckException { |
245 final Node node; | 245 final Node node; |
246 final String reason; | 246 final String reason; |
247 | 247 |
248 CancelTypeCheckException(this.node, this.reason); | 248 CancelTypeCheckException(this.node, this.reason); |
249 } | 249 } |
250 | 250 |
251 class TypeCheckerVisitor implements Visitor<Type> { | 251 class TypeCheckerVisitor implements Visitor<DartType> { |
252 final Compiler compiler; | 252 final Compiler compiler; |
253 final TreeElements elements; | 253 final TreeElements elements; |
254 final Types types; | 254 final Types types; |
255 | 255 |
256 Node lastSeenNode; | 256 Node lastSeenNode; |
257 Type expectedReturnType; | 257 DartType expectedReturnType; |
258 ClassElement currentClass; | 258 ClassElement currentClass; |
259 | 259 |
260 Link<Type> cascadeTypes = const EmptyLink<Type>(); | 260 Link<DartType> cascadeTypes = const EmptyLink<DartType>(); |
261 | 261 |
262 Type intType; | 262 DartType intType; |
263 Type doubleType; | 263 DartType doubleType; |
264 Type boolType; | 264 DartType boolType; |
265 Type stringType; | 265 DartType stringType; |
266 Type objectType; | 266 DartType objectType; |
267 Type listType; | 267 DartType listType; |
268 | 268 |
269 TypeCheckerVisitor(this.compiler, this.elements, this.types) { | 269 TypeCheckerVisitor(this.compiler, this.elements, this.types) { |
270 intType = compiler.intClass.computeType(compiler); | 270 intType = compiler.intClass.computeType(compiler); |
271 doubleType = compiler.doubleClass.computeType(compiler); | 271 doubleType = compiler.doubleClass.computeType(compiler); |
272 boolType = compiler.boolClass.computeType(compiler); | 272 boolType = compiler.boolClass.computeType(compiler); |
273 stringType = compiler.stringClass.computeType(compiler); | 273 stringType = compiler.stringClass.computeType(compiler); |
274 objectType = compiler.objectClass.computeType(compiler); | 274 objectType = compiler.objectClass.computeType(compiler); |
275 listType = compiler.listClass.computeType(compiler); | 275 listType = compiler.listClass.computeType(compiler); |
276 } | 276 } |
277 | 277 |
278 Type fail(node, [reason]) { | 278 DartType fail(node, [reason]) { |
279 String message = 'cannot type-check'; | 279 String message = 'cannot type-check'; |
280 if (reason !== null) { | 280 if (reason !== null) { |
281 message = '$message: $reason'; | 281 message = '$message: $reason'; |
282 } | 282 } |
283 throw new CancelTypeCheckException(node, message); | 283 throw new CancelTypeCheckException(node, message); |
284 } | 284 } |
285 | 285 |
286 reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) { | 286 reportTypeWarning(Node node, MessageKind kind, [List arguments = const []]) { |
287 compiler.reportWarning(node, new TypeWarning(kind, arguments)); | 287 compiler.reportWarning(node, new TypeWarning(kind, arguments)); |
288 } | 288 } |
289 | 289 |
290 // TODO(karlklose): remove these functions. | 290 // TODO(karlklose): remove these functions. |
291 Type unhandledStatement() => StatementType.NOT_RETURNING; | 291 DartType unhandledStatement() => StatementType.NOT_RETURNING; |
292 Type unhandledExpression() => types.dynamicType; | 292 DartType unhandledExpression() => types.dynamicType; |
293 | 293 |
294 Type analyzeNonVoid(Node node) { | 294 DartType analyzeNonVoid(Node node) { |
295 Type type = analyze(node); | 295 DartType type = analyze(node); |
296 if (type == types.voidType) { | 296 if (type == types.voidType) { |
297 reportTypeWarning(node, MessageKind.VOID_EXPRESSION); | 297 reportTypeWarning(node, MessageKind.VOID_EXPRESSION); |
298 } | 298 } |
299 return type; | 299 return type; |
300 } | 300 } |
301 | 301 |
302 Type analyzeWithDefault(Node node, Type defaultValue) { | 302 DartType analyzeWithDefault(Node node, DartType defaultValue) { |
303 return node !== null ? analyze(node) : defaultValue; | 303 return node !== null ? analyze(node) : defaultValue; |
304 } | 304 } |
305 | 305 |
306 Type analyze(Node node) { | 306 DartType analyze(Node node) { |
307 if (node == null) { | 307 if (node == null) { |
308 final String error = 'internal error: unexpected node: null'; | 308 final String error = 'internal error: unexpected node: null'; |
309 if (lastSeenNode != null) { | 309 if (lastSeenNode != null) { |
310 fail(null, error); | 310 fail(null, error); |
311 } else { | 311 } else { |
312 compiler.cancel(error); | 312 compiler.cancel(error); |
313 } | 313 } |
314 } else { | 314 } else { |
315 lastSeenNode = node; | 315 lastSeenNode = node; |
316 } | 316 } |
317 Type result = node.accept(this); | 317 DartType result = node.accept(this); |
318 // TODO(karlklose): record type? | 318 // TODO(karlklose): record type? |
319 if (result === null) { | 319 if (result === null) { |
320 fail(node, 'internal error: type is null'); | 320 fail(node, 'internal error: type is null'); |
321 } | 321 } |
322 return result; | 322 return result; |
323 } | 323 } |
324 | 324 |
325 /** | 325 /** |
326 * Check if a value of type t can be assigned to a variable, | 326 * Check if a value of type t can be assigned to a variable, |
327 * parameter or return value of type s. | 327 * parameter or return value of type s. |
328 */ | 328 */ |
329 checkAssignable(Node node, Type s, Type t) { | 329 checkAssignable(Node node, DartType s, DartType t) { |
330 if (!types.isAssignable(s, t)) { | 330 if (!types.isAssignable(s, t)) { |
331 reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE, [s, t]); | 331 reportTypeWarning(node, MessageKind.NOT_ASSIGNABLE, [s, t]); |
332 } | 332 } |
333 } | 333 } |
334 | 334 |
335 checkCondition(Expression condition) { | 335 checkCondition(Expression condition) { |
336 checkAssignable(condition, boolType, analyze(condition)); | 336 checkAssignable(condition, boolType, analyze(condition)); |
337 } | 337 } |
338 | 338 |
339 void pushCascadeType(Type type) { | 339 void pushCascadeType(DartType type) { |
340 cascadeTypes = cascadeTypes.prepend(type); | 340 cascadeTypes = cascadeTypes.prepend(type); |
341 } | 341 } |
342 | 342 |
343 Type popCascadeType() { | 343 DartType popCascadeType() { |
344 Type type = cascadeTypes.head; | 344 DartType type = cascadeTypes.head; |
345 cascadeTypes = cascadeTypes.tail; | 345 cascadeTypes = cascadeTypes.tail; |
346 return type; | 346 return type; |
347 } | 347 } |
348 | 348 |
349 Type visitBlock(Block node) { | 349 DartType visitBlock(Block node) { |
350 return analyze(node.statements); | 350 return analyze(node.statements); |
351 } | 351 } |
352 | 352 |
353 Type visitCascade(Cascade node) { | 353 DartType visitCascade(Cascade node) { |
354 analyze(node.expression); | 354 analyze(node.expression); |
355 return popCascadeType(); | 355 return popCascadeType(); |
356 } | 356 } |
357 | 357 |
358 Type visitCascadeReceiver(CascadeReceiver node) { | 358 DartType visitCascadeReceiver(CascadeReceiver node) { |
359 Type type = analyze(node.expression); | 359 DartType type = analyze(node.expression); |
360 pushCascadeType(type); | 360 pushCascadeType(type); |
361 return type; | 361 return type; |
362 } | 362 } |
363 | 363 |
364 Type visitClassNode(ClassNode node) { | 364 DartType visitClassNode(ClassNode node) { |
365 fail(node); | 365 fail(node); |
366 } | 366 } |
367 | 367 |
368 Type visitDoWhile(DoWhile node) { | 368 DartType visitDoWhile(DoWhile node) { |
369 StatementType bodyType = analyze(node.body); | 369 StatementType bodyType = analyze(node.body); |
370 checkCondition(node.condition); | 370 checkCondition(node.condition); |
371 return bodyType.join(StatementType.NOT_RETURNING); | 371 return bodyType.join(StatementType.NOT_RETURNING); |
372 } | 372 } |
373 | 373 |
374 Type visitExpressionStatement(ExpressionStatement node) { | 374 DartType visitExpressionStatement(ExpressionStatement node) { |
375 analyze(node.expression); | 375 analyze(node.expression); |
376 return StatementType.NOT_RETURNING; | 376 return StatementType.NOT_RETURNING; |
377 } | 377 } |
378 | 378 |
379 /** Dart Programming Language Specification: 11.5.1 For Loop */ | 379 /** Dart Programming Language Specification: 11.5.1 For Loop */ |
380 Type visitFor(For node) { | 380 DartType visitFor(For node) { |
381 analyzeWithDefault(node.initializer, StatementType.NOT_RETURNING); | 381 analyzeWithDefault(node.initializer, StatementType.NOT_RETURNING); |
382 checkCondition(node.condition); | 382 checkCondition(node.condition); |
383 analyzeWithDefault(node.update, StatementType.NOT_RETURNING); | 383 analyzeWithDefault(node.update, StatementType.NOT_RETURNING); |
384 StatementType bodyType = analyze(node.body); | 384 StatementType bodyType = analyze(node.body); |
385 return bodyType.join(StatementType.NOT_RETURNING); | 385 return bodyType.join(StatementType.NOT_RETURNING); |
386 } | 386 } |
387 | 387 |
388 Type visitFunctionDeclaration(FunctionDeclaration node) { | 388 DartType visitFunctionDeclaration(FunctionDeclaration node) { |
389 analyze(node.function); | 389 analyze(node.function); |
390 return StatementType.NOT_RETURNING; | 390 return StatementType.NOT_RETURNING; |
391 } | 391 } |
392 | 392 |
393 Type visitFunctionExpression(FunctionExpression node) { | 393 DartType visitFunctionExpression(FunctionExpression node) { |
394 Type type; | 394 DartType type; |
395 Type returnType; | 395 DartType returnType; |
396 Type previousType; | 396 DartType previousType; |
397 final FunctionElement element = elements[node]; | 397 final FunctionElement element = elements[node]; |
398 if (Element.isInvalid(element)) return types.dynamicType; | 398 if (Element.isInvalid(element)) return types.dynamicType; |
399 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR || | 399 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR || |
400 element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { | 400 element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { |
401 type = types.dynamicType; | 401 type = types.dynamicType; |
402 returnType = types.voidType; | 402 returnType = types.voidType; |
403 } else { | 403 } else { |
404 FunctionType functionType = computeType(element); | 404 FunctionType functionType = computeType(element); |
405 returnType = functionType.returnType; | 405 returnType = functionType.returnType; |
406 type = functionType; | 406 type = functionType; |
407 } | 407 } |
408 Type previous = expectedReturnType; | 408 DartType previous = expectedReturnType; |
409 expectedReturnType = returnType; | 409 expectedReturnType = returnType; |
410 if (element.isMember()) currentClass = element.getEnclosingClass(); | 410 if (element.isMember()) currentClass = element.getEnclosingClass(); |
411 StatementType bodyType = analyze(node.body); | 411 StatementType bodyType = analyze(node.body); |
412 if (returnType != types.voidType && returnType != types.dynamicType | 412 if (returnType != types.voidType && returnType != types.dynamicType |
413 && bodyType != StatementType.RETURNING) { | 413 && bodyType != StatementType.RETURNING) { |
414 MessageKind kind; | 414 MessageKind kind; |
415 if (bodyType == StatementType.MAYBE_RETURNING) { | 415 if (bodyType == StatementType.MAYBE_RETURNING) { |
416 kind = MessageKind.MAYBE_MISSING_RETURN; | 416 kind = MessageKind.MAYBE_MISSING_RETURN; |
417 } else { | 417 } else { |
418 kind = MessageKind.MISSING_RETURN; | 418 kind = MessageKind.MISSING_RETURN; |
419 } | 419 } |
420 reportTypeWarning(node.name, kind); | 420 reportTypeWarning(node.name, kind); |
421 } | 421 } |
422 expectedReturnType = previous; | 422 expectedReturnType = previous; |
423 return type; | 423 return type; |
424 } | 424 } |
425 | 425 |
426 Type visitIdentifier(Identifier node) { | 426 DartType visitIdentifier(Identifier node) { |
427 if (node.isThis()) { | 427 if (node.isThis()) { |
428 return currentClass.computeType(compiler); | 428 return currentClass.computeType(compiler); |
429 } else { | 429 } else { |
430 // This is an identifier of a formal parameter. | 430 // This is an identifier of a formal parameter. |
431 return types.dynamicType; | 431 return types.dynamicType; |
432 } | 432 } |
433 } | 433 } |
434 | 434 |
435 Type visitIf(If node) { | 435 DartType visitIf(If node) { |
436 checkCondition(node.condition); | 436 checkCondition(node.condition); |
437 StatementType thenType = analyze(node.thenPart); | 437 StatementType thenType = analyze(node.thenPart); |
438 StatementType elseType = node.hasElsePart ? analyze(node.elsePart) | 438 StatementType elseType = node.hasElsePart ? analyze(node.elsePart) |
439 : StatementType.NOT_RETURNING; | 439 : StatementType.NOT_RETURNING; |
440 return thenType.join(elseType); | 440 return thenType.join(elseType); |
441 } | 441 } |
442 | 442 |
443 Type visitLoop(Loop node) { | 443 DartType visitLoop(Loop node) { |
444 return unhandledStatement(); | 444 return unhandledStatement(); |
445 } | 445 } |
446 | 446 |
447 Type lookupMethodType(Node node, ClassElement classElement, | 447 DartType lookupMethodType(Node node, ClassElement classElement, |
448 SourceString name) { | 448 SourceString name) { |
449 Element member = classElement.lookupLocalMember(name); | 449 Element member = classElement.lookupLocalMember(name); |
450 if (member === null) { | 450 if (member === null) { |
451 classElement.ensureResolved(compiler); | 451 classElement.ensureResolved(compiler); |
452 for (Link<Type> supertypes = classElement.allSupertypes; | 452 for (Link<DartType> supertypes = classElement.allSupertypes; |
453 !supertypes.isEmpty() && member === null; | 453 !supertypes.isEmpty() && member === null; |
454 supertypes = supertypes.tail) { | 454 supertypes = supertypes.tail) { |
455 ClassElement lookupTarget = supertypes.head.element; | 455 ClassElement lookupTarget = supertypes.head.element; |
456 member = lookupTarget.lookupLocalMember(name); | 456 member = lookupTarget.lookupLocalMember(name); |
457 } | 457 } |
458 } | 458 } |
459 if (member !== null && member.kind == ElementKind.FUNCTION) { | 459 if (member !== null && member.kind == ElementKind.FUNCTION) { |
460 return computeType(member); | 460 return computeType(member); |
461 } | 461 } |
462 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, | 462 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, |
463 [classElement.name, name]); | 463 [classElement.name, name]); |
464 return types.dynamicType; | 464 return types.dynamicType; |
465 } | 465 } |
466 | 466 |
467 void analyzeArguments(Send send, Type type) { | 467 void analyzeArguments(Send send, DartType type) { |
468 Link<Node> arguments = send.arguments; | 468 Link<Node> arguments = send.arguments; |
469 if (type === null || type === types.dynamicType) { | 469 if (type === null || type === types.dynamicType) { |
470 while(!arguments.isEmpty()) { | 470 while(!arguments.isEmpty()) { |
471 analyze(arguments.head); | 471 analyze(arguments.head); |
472 arguments = arguments.tail; | 472 arguments = arguments.tail; |
473 } | 473 } |
474 } else { | 474 } else { |
475 FunctionType funType = type; | 475 FunctionType funType = type; |
476 Link<Type> parameterTypes = funType.parameterTypes; | 476 Link<DartType> parameterTypes = funType.parameterTypes; |
477 while (!arguments.isEmpty() && !parameterTypes.isEmpty()) { | 477 while (!arguments.isEmpty() && !parameterTypes.isEmpty()) { |
478 checkAssignable(arguments.head, parameterTypes.head, | 478 checkAssignable(arguments.head, parameterTypes.head, |
479 analyze(arguments.head)); | 479 analyze(arguments.head)); |
480 arguments = arguments.tail; | 480 arguments = arguments.tail; |
481 parameterTypes = parameterTypes.tail; | 481 parameterTypes = parameterTypes.tail; |
482 } | 482 } |
483 if (!arguments.isEmpty()) { | 483 if (!arguments.isEmpty()) { |
484 reportTypeWarning(arguments.head, MessageKind.ADDITIONAL_ARGUMENT); | 484 reportTypeWarning(arguments.head, MessageKind.ADDITIONAL_ARGUMENT); |
485 } else if (!parameterTypes.isEmpty()) { | 485 } else if (!parameterTypes.isEmpty()) { |
486 reportTypeWarning(send, MessageKind.MISSING_ARGUMENT, | 486 reportTypeWarning(send, MessageKind.MISSING_ARGUMENT, |
487 [parameterTypes.head]); | 487 [parameterTypes.head]); |
488 } | 488 } |
489 } | 489 } |
490 } | 490 } |
491 | 491 |
492 Type visitSend(Send node) { | 492 DartType visitSend(Send node) { |
493 Element element = elements[node]; | 493 Element element = elements[node]; |
494 | 494 |
495 if (Elements.isClosureSend(node, element)) { | 495 if (Elements.isClosureSend(node, element)) { |
496 // TODO(karlklose): Finish implementation. | 496 // TODO(karlklose): Finish implementation. |
497 return types.dynamicType; | 497 return types.dynamicType; |
498 } | 498 } |
499 | 499 |
500 Identifier selector = node.selector.asIdentifier(); | 500 Identifier selector = node.selector.asIdentifier(); |
501 String name = selector.source.stringValue; | 501 String name = selector.source.stringValue; |
502 | 502 |
503 if (node.isOperator && name === 'is') { | 503 if (node.isOperator && name === 'is') { |
504 analyze(node.receiver); | 504 analyze(node.receiver); |
505 return boolType; | 505 return boolType; |
506 } else if (node.isOperator) { | 506 } else if (node.isOperator) { |
507 final Node firstArgument = node.receiver; | 507 final Node firstArgument = node.receiver; |
508 final Type firstArgumentType = analyze(node.receiver); | 508 final DartType firstArgumentType = analyze(node.receiver); |
509 final arguments = node.arguments; | 509 final arguments = node.arguments; |
510 final Node secondArgument = arguments.isEmpty() ? null : arguments.head; | 510 final Node secondArgument = arguments.isEmpty() ? null : arguments.head; |
511 final Type secondArgumentType = analyzeWithDefault(secondArgument, null); | 511 final DartType secondArgumentType = |
| 512 analyzeWithDefault(secondArgument, null); |
512 | 513 |
513 if (name === '+' || name === '=' || name === '-' | 514 if (name === '+' || name === '=' || name === '-' |
514 || name === '*' || name === '/' || name === '%' | 515 || name === '*' || name === '/' || name === '%' |
515 || name === '~/' || name === '|' || name ==='&' | 516 || name === '~/' || name === '|' || name ==='&' |
516 || name === '^' || name === '~'|| name === '<<' | 517 || name === '^' || name === '~'|| name === '<<' |
517 || name === '>>' || name === '[]') { | 518 || name === '>>' || name === '[]') { |
518 return types.dynamicType; | 519 return types.dynamicType; |
519 } else if (name === '<' || name === '>' || name === '<=' | 520 } else if (name === '<' || name === '>' || name === '<=' |
520 || name === '>=' || name === '==' || name === '!=' | 521 || name === '>=' || name === '==' || name === '!=' |
521 || name === '===' || name === '!==') { | 522 || name === '===' || name === '!==') { |
(...skipping 15 matching lines...) Expand all Loading... |
537 } | 538 } |
538 if (element === null) return types.dynamicType; | 539 if (element === null) return types.dynamicType; |
539 return computeType(element); | 540 return computeType(element); |
540 | 541 |
541 } else if (node.isFunctionObjectInvocation) { | 542 } else if (node.isFunctionObjectInvocation) { |
542 fail(node.receiver, 'function object invocation unimplemented'); | 543 fail(node.receiver, 'function object invocation unimplemented'); |
543 | 544 |
544 } else { | 545 } else { |
545 FunctionType computeFunType() { | 546 FunctionType computeFunType() { |
546 if (node.receiver !== null) { | 547 if (node.receiver !== null) { |
547 Type receiverType = analyze(node.receiver); | 548 DartType receiverType = analyze(node.receiver); |
548 if (receiverType.element == compiler.dynamicClass) return null; | 549 if (receiverType.element == compiler.dynamicClass) return null; |
549 if (receiverType === null) { | 550 if (receiverType === null) { |
550 fail(node.receiver, 'receivertype is null'); | 551 fail(node.receiver, 'receivertype is null'); |
551 } | 552 } |
552 if (receiverType.element.kind === ElementKind.GETTER) { | 553 if (receiverType.element.kind === ElementKind.GETTER) { |
553 FunctionType getterType = receiverType; | 554 FunctionType getterType = receiverType; |
554 receiverType = getterType.returnType; | 555 receiverType = getterType.returnType; |
555 } | 556 } |
556 ElementKind receiverKind = receiverType.element.kind; | 557 ElementKind receiverKind = receiverType.element.kind; |
557 if (receiverKind === ElementKind.TYPEDEF) { | 558 if (receiverKind === ElementKind.TYPEDEF) { |
558 // TODO(karlklose): handle typedefs. | 559 // TODO(karlklose): handle typedefs. |
559 return null; | 560 return null; |
560 } | 561 } |
561 if (receiverKind === ElementKind.TYPE_VARIABLE) { | 562 if (receiverKind === ElementKind.TYPE_VARIABLE) { |
562 // TODO(karlklose): handle type variables. | 563 // TODO(karlklose): handle type variables. |
563 return null; | 564 return null; |
564 } | 565 } |
565 if (receiverKind !== ElementKind.CLASS) { | 566 if (receiverKind !== ElementKind.CLASS) { |
566 fail(node.receiver, 'unexpected receiver kind: ${receiverKind}'); | 567 fail(node.receiver, 'unexpected receiver kind: ${receiverKind}'); |
567 } | 568 } |
568 ClassElement classElement = receiverType.element; | 569 ClassElement classElement = receiverType.element; |
569 // TODO(karlklose): substitute type arguments. | 570 // TODO(karlklose): substitute type arguments. |
570 Type memberType = | 571 DartType memberType = |
571 lookupMethodType(selector, classElement, selector.source); | 572 lookupMethodType(selector, classElement, selector.source); |
572 if (memberType.element === compiler.dynamicClass) return null; | 573 if (memberType.element === compiler.dynamicClass) return null; |
573 return memberType; | 574 return memberType; |
574 } else { | 575 } else { |
575 if (element === null) { | 576 if (element === null) { |
576 fail(node, 'unresolved ${node.selector}'); | 577 fail(node, 'unresolved ${node.selector}'); |
577 } else if (element.kind === ElementKind.FUNCTION) { | 578 } else if (element.kind === ElementKind.FUNCTION) { |
578 return computeType(element); | 579 return computeType(element); |
579 } else if (element.kind === ElementKind.FOREIGN) { | 580 } else if (element.kind === ElementKind.FOREIGN) { |
580 return null; | 581 return null; |
(...skipping 10 matching lines...) Expand all Loading... |
591 analyzeArguments(node, funType); | 592 analyzeArguments(node, funType); |
592 return (funType !== null) ? funType.returnType : types.dynamicType; | 593 return (funType !== null) ? funType.returnType : types.dynamicType; |
593 } | 594 } |
594 } | 595 } |
595 | 596 |
596 visitSendSet(SendSet node) { | 597 visitSendSet(SendSet node) { |
597 Identifier selector = node.selector; | 598 Identifier selector = node.selector; |
598 final name = node.assignmentOperator.source.stringValue; | 599 final name = node.assignmentOperator.source.stringValue; |
599 if (name === '++' || name === '--') { | 600 if (name === '++' || name === '--') { |
600 final Element element = elements[node.selector]; | 601 final Element element = elements[node.selector]; |
601 final Type receiverType = computeType(element); | 602 final DartType receiverType = computeType(element); |
602 // TODO(karlklose): this should be the return type instead of int. | 603 // TODO(karlklose): this should be the return type instead of int. |
603 return node.isPrefix ? intType : receiverType; | 604 return node.isPrefix ? intType : receiverType; |
604 } else { | 605 } else { |
605 Type targetType = computeType(elements[node]); | 606 DartType targetType = computeType(elements[node]); |
606 Node value = node.arguments.head; | 607 Node value = node.arguments.head; |
607 checkAssignable(value, targetType, analyze(value)); | 608 checkAssignable(value, targetType, analyze(value)); |
608 return targetType; | 609 return targetType; |
609 } | 610 } |
610 } | 611 } |
611 | 612 |
612 Type visitLiteralInt(LiteralInt node) { | 613 DartType visitLiteralInt(LiteralInt node) { |
613 return intType; | 614 return intType; |
614 } | 615 } |
615 | 616 |
616 Type visitLiteralDouble(LiteralDouble node) { | 617 DartType visitLiteralDouble(LiteralDouble node) { |
617 return doubleType; | 618 return doubleType; |
618 } | 619 } |
619 | 620 |
620 Type visitLiteralBool(LiteralBool node) { | 621 DartType visitLiteralBool(LiteralBool node) { |
621 return boolType; | 622 return boolType; |
622 } | 623 } |
623 | 624 |
624 Type visitLiteralString(LiteralString node) { | 625 DartType visitLiteralString(LiteralString node) { |
625 return stringType; | 626 return stringType; |
626 } | 627 } |
627 | 628 |
628 Type visitStringJuxtaposition(StringJuxtaposition node) { | 629 DartType visitStringJuxtaposition(StringJuxtaposition node) { |
629 analyze(node.first); | 630 analyze(node.first); |
630 analyze(node.second); | 631 analyze(node.second); |
631 return stringType; | 632 return stringType; |
632 } | 633 } |
633 | 634 |
634 Type visitLiteralNull(LiteralNull node) { | 635 DartType visitLiteralNull(LiteralNull node) { |
635 return types.dynamicType; | 636 return types.dynamicType; |
636 } | 637 } |
637 | 638 |
638 Type visitNewExpression(NewExpression node) { | 639 DartType visitNewExpression(NewExpression node) { |
639 Element element = elements[node.send]; | 640 Element element = elements[node.send]; |
640 analyzeArguments(node.send, computeType(element)); | 641 analyzeArguments(node.send, computeType(element)); |
641 return analyze(node.send.selector); | 642 return analyze(node.send.selector); |
642 } | 643 } |
643 | 644 |
644 Type visitLiteralList(LiteralList node) { | 645 DartType visitLiteralList(LiteralList node) { |
645 return listType; | 646 return listType; |
646 } | 647 } |
647 | 648 |
648 Type visitNodeList(NodeList node) { | 649 DartType visitNodeList(NodeList node) { |
649 Type type = StatementType.NOT_RETURNING; | 650 DartType type = StatementType.NOT_RETURNING; |
650 bool reportedDeadCode = false; | 651 bool reportedDeadCode = false; |
651 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { | 652 for (Link<Node> link = node.nodes; !link.isEmpty(); link = link.tail) { |
652 Type nextType = analyze(link.head); | 653 DartType nextType = analyze(link.head); |
653 if (type == StatementType.RETURNING) { | 654 if (type == StatementType.RETURNING) { |
654 if (!reportedDeadCode) { | 655 if (!reportedDeadCode) { |
655 reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE); | 656 reportTypeWarning(link.head, MessageKind.UNREACHABLE_CODE); |
656 reportedDeadCode = true; | 657 reportedDeadCode = true; |
657 } | 658 } |
658 } else if (type == StatementType.MAYBE_RETURNING){ | 659 } else if (type == StatementType.MAYBE_RETURNING){ |
659 if (nextType == StatementType.RETURNING) { | 660 if (nextType == StatementType.RETURNING) { |
660 type = nextType; | 661 type = nextType; |
661 } | 662 } |
662 } else { | 663 } else { |
663 type = nextType; | 664 type = nextType; |
664 } | 665 } |
665 } | 666 } |
666 return type; | 667 return type; |
667 } | 668 } |
668 | 669 |
669 Type visitOperator(Operator node) { | 670 DartType visitOperator(Operator node) { |
670 fail(node, 'internal error'); | 671 fail(node, 'internal error'); |
671 } | 672 } |
672 | 673 |
673 /** Dart Programming Language Specification: 11.10 Return */ | 674 /** Dart Programming Language Specification: 11.10 Return */ |
674 Type visitReturn(Return node) { | 675 DartType visitReturn(Return node) { |
675 if (node.getBeginToken().stringValue === 'native') { | 676 if (node.getBeginToken().stringValue === 'native') { |
676 return StatementType.RETURNING; | 677 return StatementType.RETURNING; |
677 } | 678 } |
678 | 679 |
679 final expression = node.expression; | 680 final expression = node.expression; |
680 final isVoidFunction = (expectedReturnType === types.voidType); | 681 final isVoidFunction = (expectedReturnType === types.voidType); |
681 | 682 |
682 // Executing a return statement return e; [...] It is a static type warning | 683 // Executing a return statement return e; [...] It is a static type warning |
683 // if the type of e may not be assigned to the declared return type of the | 684 // if the type of e may not be assigned to the declared return type of the |
684 // immediately enclosing function. | 685 // immediately enclosing function. |
(...skipping 11 matching lines...) Expand all Loading... |
696 // form 'return;' It is a static warning if both of the following conditions | 697 // form 'return;' It is a static warning if both of the following conditions |
697 // hold: | 698 // hold: |
698 // - f is not a generative constructor. | 699 // - f is not a generative constructor. |
699 // - The return type of f may not be assigned to void. | 700 // - The return type of f may not be assigned to void. |
700 } else if (!types.isAssignable(expectedReturnType, types.voidType)) { | 701 } else if (!types.isAssignable(expectedReturnType, types.voidType)) { |
701 reportTypeWarning(node, MessageKind.RETURN_NOTHING, [expectedReturnType]); | 702 reportTypeWarning(node, MessageKind.RETURN_NOTHING, [expectedReturnType]); |
702 } | 703 } |
703 return StatementType.RETURNING; | 704 return StatementType.RETURNING; |
704 } | 705 } |
705 | 706 |
706 Type visitThrow(Throw node) { | 707 DartType visitThrow(Throw node) { |
707 if (node.expression !== null) analyze(node.expression); | 708 if (node.expression !== null) analyze(node.expression); |
708 return StatementType.RETURNING; | 709 return StatementType.RETURNING; |
709 } | 710 } |
710 | 711 |
711 Type computeType(Element element) { | 712 DartType computeType(Element element) { |
712 if (Element.isInvalid(element)) return types.dynamicType; | 713 if (Element.isInvalid(element)) return types.dynamicType; |
713 Type result = element.computeType(compiler); | 714 DartType result = element.computeType(compiler); |
714 return (result !== null) ? result : types.dynamicType; | 715 return (result !== null) ? result : types.dynamicType; |
715 } | 716 } |
716 | 717 |
717 Type visitTypeAnnotation(TypeAnnotation node) { | 718 DartType visitTypeAnnotation(TypeAnnotation node) { |
718 return elements.getType(node); | 719 return elements.getType(node); |
719 } | 720 } |
720 | 721 |
721 visitTypeVariable(TypeVariable node) { | 722 visitTypeVariable(TypeVariable node) { |
722 return types.dynamicType; | 723 return types.dynamicType; |
723 } | 724 } |
724 | 725 |
725 Type visitVariableDefinitions(VariableDefinitions node) { | 726 DartType visitVariableDefinitions(VariableDefinitions node) { |
726 Type type = analyzeWithDefault(node.type, types.dynamicType); | 727 DartType type = analyzeWithDefault(node.type, types.dynamicType); |
727 if (type == types.voidType) { | 728 if (type == types.voidType) { |
728 reportTypeWarning(node.type, MessageKind.VOID_VARIABLE); | 729 reportTypeWarning(node.type, MessageKind.VOID_VARIABLE); |
729 type = types.dynamicType; | 730 type = types.dynamicType; |
730 } | 731 } |
731 for (Link<Node> link = node.definitions.nodes; !link.isEmpty(); | 732 for (Link<Node> link = node.definitions.nodes; !link.isEmpty(); |
732 link = link.tail) { | 733 link = link.tail) { |
733 Node initialization = link.head; | 734 Node initialization = link.head; |
734 compiler.ensure(initialization is Identifier | 735 compiler.ensure(initialization is Identifier |
735 || initialization is Send); | 736 || initialization is Send); |
736 if (initialization is Send) { | 737 if (initialization is Send) { |
737 Type initializer = analyzeNonVoid(link.head); | 738 DartType initializer = analyzeNonVoid(link.head); |
738 checkAssignable(node, type, initializer); | 739 checkAssignable(node, type, initializer); |
739 } | 740 } |
740 } | 741 } |
741 return StatementType.NOT_RETURNING; | 742 return StatementType.NOT_RETURNING; |
742 } | 743 } |
743 | 744 |
744 Type visitWhile(While node) { | 745 DartType visitWhile(While node) { |
745 checkCondition(node.condition); | 746 checkCondition(node.condition); |
746 StatementType bodyType = analyze(node.body); | 747 StatementType bodyType = analyze(node.body); |
747 Expression cond = node.condition.asParenthesizedExpression().expression; | 748 Expression cond = node.condition.asParenthesizedExpression().expression; |
748 if (cond.asLiteralBool() !== null && cond.asLiteralBool().value == true) { | 749 if (cond.asLiteralBool() !== null && cond.asLiteralBool().value == true) { |
749 // If the condition is a constant boolean expression denoting true, | 750 // If the condition is a constant boolean expression denoting true, |
750 // control-flow always enters the loop body. | 751 // control-flow always enters the loop body. |
751 // TODO(karlklose): this should be StatementType.RETURNING unless there | 752 // TODO(karlklose): this should be StatementType.RETURNING unless there |
752 // is a break in the loop body that has the loop or a label outside the | 753 // is a break in the loop body that has the loop or a label outside the |
753 // loop as a target. | 754 // loop as a target. |
754 return bodyType; | 755 return bodyType; |
755 } else { | 756 } else { |
756 return bodyType.join(StatementType.NOT_RETURNING); | 757 return bodyType.join(StatementType.NOT_RETURNING); |
757 } | 758 } |
758 } | 759 } |
759 | 760 |
760 Type visitParenthesizedExpression(ParenthesizedExpression node) { | 761 DartType visitParenthesizedExpression(ParenthesizedExpression node) { |
761 return analyze(node.expression); | 762 return analyze(node.expression); |
762 } | 763 } |
763 | 764 |
764 Type visitConditional(Conditional node) { | 765 DartType visitConditional(Conditional node) { |
765 checkCondition(node.condition); | 766 checkCondition(node.condition); |
766 Type thenType = analyzeNonVoid(node.thenExpression); | 767 DartType thenType = analyzeNonVoid(node.thenExpression); |
767 Type elseType = analyzeNonVoid(node.elseExpression); | 768 DartType elseType = analyzeNonVoid(node.elseExpression); |
768 if (types.isSubtype(thenType, elseType)) { | 769 if (types.isSubtype(thenType, elseType)) { |
769 return thenType; | 770 return thenType; |
770 } else if (types.isSubtype(elseType, thenType)) { | 771 } else if (types.isSubtype(elseType, thenType)) { |
771 return elseType; | 772 return elseType; |
772 } else { | 773 } else { |
773 return objectType; | 774 return objectType; |
774 } | 775 } |
775 } | 776 } |
776 | 777 |
777 Type visitModifiers(Modifiers node) {} | 778 DartType visitModifiers(Modifiers node) {} |
778 | 779 |
779 visitStringInterpolation(StringInterpolation node) { | 780 visitStringInterpolation(StringInterpolation node) { |
780 node.visitChildren(this); | 781 node.visitChildren(this); |
781 return stringType; | 782 return stringType; |
782 } | 783 } |
783 | 784 |
784 visitStringInterpolationPart(StringInterpolationPart node) { | 785 visitStringInterpolationPart(StringInterpolationPart node) { |
785 node.visitChildren(this); | 786 node.visitChildren(this); |
786 return stringType; | 787 return stringType; |
787 } | 788 } |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
843 } | 844 } |
844 | 845 |
845 visitCatchBlock(CatchBlock node) { | 846 visitCatchBlock(CatchBlock node) { |
846 return unhandledStatement(); | 847 return unhandledStatement(); |
847 } | 848 } |
848 | 849 |
849 visitTypedef(Typedef node) { | 850 visitTypedef(Typedef node) { |
850 return unhandledStatement(); | 851 return unhandledStatement(); |
851 } | 852 } |
852 } | 853 } |
OLD | NEW |