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 part of dart2js; | |
6 | |
7 class TypeCheckerTask extends CompilerTask { | |
8 TypeCheckerTask(Compiler compiler) : super(compiler); | |
9 String get name => "Type checker"; | |
10 | |
11 void check(TreeElements elements) { | |
12 AstElement element = elements.analyzedElement; | |
13 compiler.withCurrentElement(element, () { | |
14 measure(() { | |
15 Node tree = element.node; | |
16 TypeCheckerVisitor visitor = | |
17 new TypeCheckerVisitor(compiler, elements, compiler.types); | |
18 if (element.isField) { | |
19 visitor.analyzingInitializer = true; | |
20 } | |
21 tree.accept(visitor); | |
22 }); | |
23 }); | |
24 } | |
25 } | |
26 | |
27 /** | |
28 * Class used to report different warnings for differrent kinds of members. | |
29 */ | |
30 class MemberKind { | |
31 static const MemberKind METHOD = const MemberKind("method"); | |
32 static const MemberKind OPERATOR = const MemberKind("operator"); | |
33 static const MemberKind GETTER = const MemberKind("getter"); | |
34 static const MemberKind SETTER = const MemberKind("setter"); | |
35 | |
36 final String name; | |
37 | |
38 const MemberKind(this.name); | |
39 | |
40 String toString() => name; | |
41 } | |
42 | |
43 /** | |
44 * [ElementAccess] represents the access of [element], either as a property | |
45 * access or invocation. | |
46 */ | |
47 abstract class ElementAccess { | |
48 Element get element; | |
49 | |
50 DartType computeType(Compiler compiler); | |
51 | |
52 /// Returns [: true :] if the element can be access as an invocation. | |
53 bool isCallable(Compiler compiler) { | |
54 if (element != null && element.isAbstractField) { | |
55 AbstractFieldElement abstractFieldElement = element; | |
56 if (abstractFieldElement.getter == null) { | |
57 // Setters cannot be invoked as function invocations. | |
58 return false; | |
59 } | |
60 } | |
61 return compiler.types.isAssignable( | |
62 computeType(compiler), compiler.functionClass.computeType(compiler)); | |
63 } | |
64 } | |
65 | |
66 /// An access of a instance member. | |
67 class MemberAccess extends ElementAccess { | |
68 final MemberSignature member; | |
69 | |
70 MemberAccess(MemberSignature this.member); | |
71 | |
72 Element get element => member.declarations.first.element; | |
73 | |
74 DartType computeType(Compiler compiler) => member.type; | |
75 | |
76 String toString() => 'MemberAccess($member)'; | |
77 } | |
78 | |
79 /// An access of an unresolved element. | |
80 class DynamicAccess implements ElementAccess { | |
81 const DynamicAccess(); | |
82 | |
83 Element get element => null; | |
84 | |
85 DartType computeType(Compiler compiler) => const DynamicType(); | |
86 | |
87 bool isCallable(Compiler compiler) => true; | |
88 | |
89 String toString() => 'DynamicAccess'; | |
90 } | |
91 | |
92 /// An access of the `assert` method. | |
93 class AssertAccess implements ElementAccess { | |
94 const AssertAccess(); | |
95 | |
96 Element get element => null; | |
97 | |
98 DartType computeType(Compiler compiler) { | |
99 return new FunctionType.synthesized( | |
100 const VoidType(), | |
101 <DartType>[const DynamicType()]); | |
102 } | |
103 | |
104 bool isCallable(Compiler compiler) => true; | |
105 | |
106 String toString() => 'AssertAccess'; | |
107 } | |
108 | |
109 /** | |
110 * An access of a resolved top-level or static property or function, or an | |
111 * access of a resolved element through [:this:]. | |
112 */ | |
113 class ResolvedAccess extends ElementAccess { | |
114 final Element element; | |
115 | |
116 ResolvedAccess(Element this.element) { | |
117 assert(element != null); | |
118 } | |
119 | |
120 DartType computeType(Compiler compiler) { | |
121 if (element.isGetter) { | |
122 FunctionType functionType = element.computeType(compiler); | |
123 return functionType.returnType; | |
124 } else if (element.isSetter) { | |
125 FunctionType functionType = element.computeType(compiler); | |
126 if (functionType.parameterTypes.length != 1) { | |
127 // TODO(johnniwinther,karlklose): this happens for malformed static | |
128 // setters. Treat them the same as instance members. | |
129 return const DynamicType(); | |
130 } | |
131 return functionType.parameterTypes.first; | |
132 } else { | |
133 return element.computeType(compiler); | |
134 } | |
135 } | |
136 | |
137 String toString() => 'ResolvedAccess($element)'; | |
138 } | |
139 | |
140 /// An access to a promoted variable. | |
141 class PromotedAccess extends ElementAccess { | |
142 final VariableElement element; | |
143 final DartType type; | |
144 | |
145 PromotedAccess(VariableElement this.element, DartType this.type) { | |
146 assert(element != null); | |
147 assert(type != null); | |
148 } | |
149 | |
150 DartType computeType(Compiler compiler) => type; | |
151 | |
152 String toString() => 'PromotedAccess($element,$type)'; | |
153 } | |
154 | |
155 /** | |
156 * An access of a resolved top-level or static property or function, or an | |
157 * access of a resolved element through [:this:]. | |
158 */ | |
159 class TypeAccess extends ElementAccess { | |
160 final DartType type; | |
161 TypeAccess(DartType this.type) { | |
162 assert(type != null); | |
163 } | |
164 | |
165 Element get element => type.element; | |
166 | |
167 DartType computeType(Compiler compiler) => type; | |
168 | |
169 String toString() => 'TypeAccess($type)'; | |
170 } | |
171 | |
172 /** | |
173 * An access of a type literal. | |
174 */ | |
175 class TypeLiteralAccess extends ElementAccess { | |
176 final DartType type; | |
177 | |
178 TypeLiteralAccess(this.type) { | |
179 assert(type != null); | |
180 } | |
181 | |
182 Element get element => type.element; | |
183 | |
184 DartType computeType(Compiler compiler) => compiler.typeClass.rawType; | |
185 | |
186 String toString() => 'TypeLiteralAccess($type)'; | |
187 } | |
188 | |
189 | |
190 /// An access to the 'call' method of a function type. | |
191 class FunctionCallAccess implements ElementAccess { | |
192 final Element element; | |
193 final DartType type; | |
194 | |
195 const FunctionCallAccess(this.element, this.type); | |
196 | |
197 DartType computeType(Compiler compiler) => type; | |
198 | |
199 bool isCallable(Compiler compiler) => true; | |
200 | |
201 String toString() => 'FunctionAccess($element, $type)'; | |
202 } | |
203 | |
204 | |
205 /// An is-expression that potentially promotes a variable. | |
206 class TypePromotion { | |
207 final Send node; | |
208 final VariableElement variable; | |
209 final DartType type; | |
210 final List<TypePromotionMessage> messages = <TypePromotionMessage>[]; | |
211 | |
212 TypePromotion(this.node, this.variable, this.type); | |
213 | |
214 bool get isValid => messages.isEmpty; | |
215 | |
216 TypePromotion copy() { | |
217 return new TypePromotion(node, variable, type)..messages.addAll(messages); | |
218 } | |
219 | |
220 void addHint(Spannable spannable, MessageKind kind, [Map arguments]) { | |
221 messages.add(new TypePromotionMessage(api.Diagnostic.HINT, | |
222 spannable, kind, arguments)); | |
223 } | |
224 | |
225 void addInfo(Spannable spannable, MessageKind kind, [Map arguments]) { | |
226 messages.add(new TypePromotionMessage(api.Diagnostic.INFO, | |
227 spannable, kind, arguments)); | |
228 } | |
229 | |
230 String toString() { | |
231 return 'Promote ${variable} to ${type}${isValid ? '' : ' (invalid)'}'; | |
232 } | |
233 } | |
234 | |
235 /// A hint or info message attached to a type promotion. | |
236 class TypePromotionMessage { | |
237 api.Diagnostic diagnostic; | |
238 Spannable spannable; | |
239 MessageKind messageKind; | |
240 Map messageArguments; | |
241 | |
242 TypePromotionMessage(this.diagnostic, this.spannable, this.messageKind, | |
243 [this.messageArguments]); | |
244 } | |
245 | |
246 class TypeCheckerVisitor extends Visitor<DartType> { | |
247 final Compiler compiler; | |
248 final TreeElements elements; | |
249 final Types types; | |
250 | |
251 Node lastSeenNode; | |
252 DartType expectedReturnType; | |
253 AsyncMarker currentAsyncMarker = AsyncMarker.SYNC; | |
254 | |
255 final ClassElement currentClass; | |
256 | |
257 InterfaceType thisType; | |
258 InterfaceType superType; | |
259 | |
260 Link<DartType> cascadeTypes = const Link<DartType>(); | |
261 | |
262 bool analyzingInitializer = false; | |
263 | |
264 DartType intType; | |
265 DartType doubleType; | |
266 DartType boolType; | |
267 DartType stringType; | |
268 DartType objectType; | |
269 DartType listType; | |
270 | |
271 Map<Node, List<TypePromotion>> shownTypePromotionsMap = | |
272 new Map<Node, List<TypePromotion>>(); | |
273 | |
274 Map<VariableElement, Link<TypePromotion>> typePromotionsMap = | |
275 new Map<VariableElement, Link<TypePromotion>>(); | |
276 | |
277 Set<TypePromotion> reportedTypePromotions = new Set<TypePromotion>(); | |
278 | |
279 void showTypePromotion(Node node, TypePromotion typePromotion) { | |
280 List<TypePromotion> shownTypePromotions = | |
281 shownTypePromotionsMap.putIfAbsent(node, () => <TypePromotion>[]); | |
282 shownTypePromotions.add(typePromotion); | |
283 } | |
284 | |
285 void registerKnownTypePromotion(TypePromotion typePromotion) { | |
286 VariableElement variable = typePromotion.variable; | |
287 Link<TypePromotion> knownTypes = | |
288 typePromotionsMap.putIfAbsent(variable, | |
289 () => const Link<TypePromotion>()); | |
290 typePromotionsMap[variable] = knownTypes.prepend(typePromotion); | |
291 } | |
292 | |
293 void unregisterKnownTypePromotion(TypePromotion typePromotion) { | |
294 VariableElement variable = typePromotion.variable; | |
295 Link<TypePromotion> knownTypes = typePromotionsMap[variable].tail; | |
296 if (knownTypes.isEmpty) { | |
297 typePromotionsMap.remove(variable); | |
298 } else { | |
299 typePromotionsMap[variable] = knownTypes; | |
300 } | |
301 } | |
302 | |
303 List<TypePromotion> getShownTypePromotionsFor(Node node) { | |
304 List<TypePromotion> shownTypePromotions = shownTypePromotionsMap[node]; | |
305 return shownTypePromotions != null ? shownTypePromotions : const []; | |
306 } | |
307 | |
308 TypePromotion getKnownTypePromotion(VariableElement element) { | |
309 Link<TypePromotion> promotions = typePromotionsMap[element]; | |
310 if (promotions != null) { | |
311 while (!promotions.isEmpty) { | |
312 TypePromotion typePromotion = promotions.head; | |
313 if (typePromotion.isValid) { | |
314 return typePromotion; | |
315 } | |
316 promotions = promotions.tail; | |
317 } | |
318 } | |
319 return null; | |
320 } | |
321 | |
322 DartType getKnownType(VariableElement element) { | |
323 TypePromotion typePromotion = getKnownTypePromotion(element); | |
324 if (typePromotion != null) return typePromotion.type; | |
325 return element.type; | |
326 } | |
327 | |
328 TypeCheckerVisitor(this.compiler, TreeElements elements, this.types) | |
329 : this.elements = elements, | |
330 currentClass = elements.analyzedElement != null | |
331 ? elements.analyzedElement.enclosingClass : null { | |
332 intType = compiler.intClass.computeType(compiler); | |
333 doubleType = compiler.doubleClass.computeType(compiler); | |
334 boolType = compiler.boolClass.computeType(compiler); | |
335 stringType = compiler.stringClass.computeType(compiler); | |
336 objectType = compiler.objectClass.computeType(compiler); | |
337 listType = compiler.listClass.computeType(compiler); | |
338 | |
339 if (currentClass != null) { | |
340 thisType = currentClass.thisType; | |
341 superType = currentClass.supertype; | |
342 } | |
343 } | |
344 | |
345 LibraryElement get currentLibrary => elements.analyzedElement.library; | |
346 | |
347 reportTypeWarning(Spannable spannable, MessageKind kind, | |
348 [Map arguments = const {}]) { | |
349 compiler.reportWarning(spannable, kind, arguments); | |
350 } | |
351 | |
352 reportTypeInfo(Spannable spannable, MessageKind kind, | |
353 [Map arguments = const {}]) { | |
354 compiler.reportInfo(spannable, kind, arguments); | |
355 } | |
356 | |
357 reportTypePromotionHint(TypePromotion typePromotion) { | |
358 if (!reportedTypePromotions.contains(typePromotion)) { | |
359 reportedTypePromotions.add(typePromotion); | |
360 for (TypePromotionMessage message in typePromotion.messages) { | |
361 switch (message.diagnostic) { | |
362 case api.Diagnostic.HINT: | |
363 compiler.reportHint(message.spannable, | |
364 message.messageKind, | |
365 message.messageArguments); | |
366 break; | |
367 case api.Diagnostic.INFO: | |
368 compiler.reportInfo(message.spannable, | |
369 message.messageKind, | |
370 message.messageArguments); | |
371 break; | |
372 } | |
373 } | |
374 } | |
375 } | |
376 | |
377 // TODO(karlklose): remove these functions. | |
378 DartType unhandledExpression() => const DynamicType(); | |
379 | |
380 DartType analyzeNonVoid(Node node) { | |
381 DartType type = analyze(node); | |
382 if (type.isVoid) { | |
383 reportTypeWarning(node, MessageKind.VOID_EXPRESSION); | |
384 } | |
385 return type; | |
386 } | |
387 | |
388 DartType analyzeWithDefault(Node node, DartType defaultValue) { | |
389 return node != null ? analyze(node) : defaultValue; | |
390 } | |
391 | |
392 /// If [inInitializer] is true, assignment should be interpreted as write to | |
393 /// a field and not to a setter. | |
394 DartType analyze(Node node, {bool inInitializer: false}) { | |
395 if (node == null) { | |
396 final String error = 'Unexpected node: null'; | |
397 if (lastSeenNode != null) { | |
398 compiler.internalError(lastSeenNode, error); | |
399 } else { | |
400 compiler.internalError(elements.analyzedElement, error); | |
401 } | |
402 } else { | |
403 lastSeenNode = node; | |
404 } | |
405 bool previouslyInitializer = analyzingInitializer; | |
406 analyzingInitializer = inInitializer; | |
407 DartType result = node.accept(this); | |
408 analyzingInitializer = previouslyInitializer; | |
409 if (result == null) { | |
410 compiler.internalError(node, 'Type is null.'); | |
411 } | |
412 return result; | |
413 } | |
414 | |
415 void checkTypePromotion(Node node, TypePromotion typePromotion, | |
416 {bool checkAccesses: false}) { | |
417 VariableElement variable = typePromotion.variable; | |
418 String variableName = variable.name; | |
419 List<Node> potentialMutationsIn = | |
420 elements.getPotentialMutationsIn(node, variable); | |
421 if (!potentialMutationsIn.isEmpty) { | |
422 typePromotion.addHint(typePromotion.node, | |
423 MessageKind.POTENTIAL_MUTATION, | |
424 {'variableName': variableName, 'shownType': typePromotion.type}); | |
425 for (Node mutation in potentialMutationsIn) { | |
426 typePromotion.addInfo(mutation, | |
427 MessageKind.POTENTIAL_MUTATION_HERE, | |
428 {'variableName': variableName}); | |
429 } | |
430 } | |
431 List<Node> potentialMutationsInClosures = | |
432 elements.getPotentialMutationsInClosure(variable); | |
433 if (!potentialMutationsInClosures.isEmpty) { | |
434 typePromotion.addHint(typePromotion.node, | |
435 MessageKind.POTENTIAL_MUTATION_IN_CLOSURE, | |
436 {'variableName': variableName, 'shownType': typePromotion.type}); | |
437 for (Node mutation in potentialMutationsInClosures) { | |
438 typePromotion.addInfo(mutation, | |
439 MessageKind.POTENTIAL_MUTATION_IN_CLOSURE_HERE, | |
440 {'variableName': variableName}); | |
441 } | |
442 } | |
443 if (checkAccesses) { | |
444 List<Node> accesses = elements.getAccessesByClosureIn(node, variable); | |
445 List<Node> mutations = elements.getPotentialMutations(variable); | |
446 if (!accesses.isEmpty && !mutations.isEmpty) { | |
447 typePromotion.addHint(typePromotion.node, | |
448 MessageKind.ACCESSED_IN_CLOSURE, | |
449 {'variableName': variableName, 'shownType': typePromotion.type}); | |
450 for (Node access in accesses) { | |
451 typePromotion.addInfo(access, | |
452 MessageKind.ACCESSED_IN_CLOSURE_HERE, | |
453 {'variableName': variableName}); | |
454 } | |
455 for (Node mutation in mutations) { | |
456 typePromotion.addInfo(mutation, | |
457 MessageKind.POTENTIAL_MUTATION_HERE, | |
458 {'variableName': variableName}); | |
459 } | |
460 } | |
461 } | |
462 } | |
463 | |
464 /// Show type promotions from [left] and [right] in [node] given that the | |
465 /// promoted variables are not potentially mutated in [right]. | |
466 void reshowTypePromotions(Node node, Node left, Node right) { | |
467 for (TypePromotion typePromotion in getShownTypePromotionsFor(left)) { | |
468 typePromotion = typePromotion.copy(); | |
469 checkTypePromotion(right, typePromotion); | |
470 showTypePromotion(node, typePromotion); | |
471 } | |
472 | |
473 for (TypePromotion typePromotion in getShownTypePromotionsFor(right)) { | |
474 typePromotion = typePromotion.copy(); | |
475 checkTypePromotion(right, typePromotion); | |
476 showTypePromotion(node, typePromotion); | |
477 } | |
478 } | |
479 | |
480 /// Analyze [node] in the context of the known types shown in [context]. | |
481 DartType analyzeInPromotedContext(Node context, Node node) { | |
482 Link<TypePromotion> knownForNode = const Link<TypePromotion>(); | |
483 for (TypePromotion typePromotion in getShownTypePromotionsFor(context)) { | |
484 typePromotion = typePromotion.copy(); | |
485 checkTypePromotion(node, typePromotion, checkAccesses: true); | |
486 knownForNode = knownForNode.prepend(typePromotion); | |
487 registerKnownTypePromotion(typePromotion); | |
488 } | |
489 | |
490 final DartType type = analyze(node); | |
491 | |
492 while (!knownForNode.isEmpty) { | |
493 unregisterKnownTypePromotion(knownForNode.head); | |
494 knownForNode = knownForNode.tail; | |
495 } | |
496 | |
497 return type; | |
498 } | |
499 | |
500 /** | |
501 * Check if a value of type [from] can be assigned to a variable, parameter or | |
502 * return value of type [to]. If `isConst == true`, an error is emitted in | |
503 * checked mode, otherwise a warning is issued. | |
504 */ | |
505 bool checkAssignable(Spannable spannable, DartType from, DartType to, | |
506 {bool isConst: false}) { | |
507 if (!types.isAssignable(from, to)) { | |
508 if (compiler.enableTypeAssertions && isConst) { | |
509 compiler.reportError(spannable, MessageKind.NOT_ASSIGNABLE, | |
510 {'fromType': from, 'toType': to}); | |
511 } else { | |
512 reportTypeWarning(spannable, MessageKind.NOT_ASSIGNABLE, | |
513 {'fromType': from, 'toType': to}); | |
514 } | |
515 return false; | |
516 } | |
517 return true; | |
518 } | |
519 | |
520 checkCondition(Expression condition) { | |
521 checkAssignable(condition, analyze(condition), boolType); | |
522 } | |
523 | |
524 void pushCascadeType(DartType type) { | |
525 cascadeTypes = cascadeTypes.prepend(type); | |
526 } | |
527 | |
528 DartType popCascadeType() { | |
529 DartType type = cascadeTypes.head; | |
530 cascadeTypes = cascadeTypes.tail; | |
531 return type; | |
532 } | |
533 | |
534 DartType visitBlock(Block node) { | |
535 return analyze(node.statements); | |
536 } | |
537 | |
538 DartType visitCascade(Cascade node) { | |
539 analyze(node.expression); | |
540 return popCascadeType(); | |
541 } | |
542 | |
543 DartType visitCascadeReceiver(CascadeReceiver node) { | |
544 DartType type = analyze(node.expression); | |
545 pushCascadeType(type); | |
546 return type; | |
547 } | |
548 | |
549 DartType visitDoWhile(DoWhile node) { | |
550 analyze(node.body); | |
551 checkCondition(node.condition); | |
552 return const StatementType(); | |
553 } | |
554 | |
555 DartType visitExpressionStatement(ExpressionStatement node) { | |
556 Expression expression = node.expression; | |
557 analyze(expression); | |
558 return const StatementType(); | |
559 } | |
560 | |
561 /** Dart Programming Language Specification: 11.5.1 For Loop */ | |
562 DartType visitFor(For node) { | |
563 if (node.initializer != null) { | |
564 analyze(node.initializer); | |
565 } | |
566 if (node.condition != null) { | |
567 checkCondition(node.condition); | |
568 } | |
569 if (node.update != null) { | |
570 analyze(node.update); | |
571 } | |
572 return analyze(node.body); | |
573 } | |
574 | |
575 DartType visitFunctionDeclaration(FunctionDeclaration node) { | |
576 analyze(node.function); | |
577 return const StatementType(); | |
578 } | |
579 | |
580 DartType visitFunctionExpression(FunctionExpression node) { | |
581 DartType type; | |
582 DartType returnType; | |
583 DartType previousType; | |
584 final FunctionElement element = elements.getFunctionDefinition(node); | |
585 assert(invariant(node, element != null, | |
586 message: 'FunctionExpression with no element')); | |
587 if (Elements.isUnresolved(element)) return const DynamicType(); | |
588 if (identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR) || | |
589 identical(element.kind, ElementKind.GENERATIVE_CONSTRUCTOR_BODY)) { | |
590 type = const DynamicType(); | |
591 returnType = const VoidType(); | |
592 | |
593 element.functionSignature.forEachParameter((ParameterElement parameter) { | |
594 if (parameter.isInitializingFormal) { | |
595 InitializingFormalElement fieldParameter = parameter; | |
596 checkAssignable(parameter, parameter.type, | |
597 fieldParameter.fieldElement.computeType(compiler)); | |
598 } | |
599 }); | |
600 if (node.initializers != null) { | |
601 analyze(node.initializers, inInitializer: true); | |
602 } | |
603 } else { | |
604 FunctionType functionType = element.computeType(compiler); | |
605 returnType = functionType.returnType; | |
606 type = functionType; | |
607 } | |
608 DartType previousReturnType = expectedReturnType; | |
609 expectedReturnType = returnType; | |
610 AsyncMarker previousAsyncMarker = currentAsyncMarker; | |
611 currentAsyncMarker = element.asyncMarker; | |
612 analyze(node.body); | |
613 expectedReturnType = previousReturnType; | |
614 currentAsyncMarker = previousAsyncMarker; | |
615 return type; | |
616 } | |
617 | |
618 DartType visitIdentifier(Identifier node) { | |
619 if (node.isThis()) { | |
620 return thisType; | |
621 } else if (node.isSuper()) { | |
622 return superType; | |
623 } else { | |
624 Element element = elements[node]; | |
625 assert(invariant(node, element != null, | |
626 message: 'Missing element for identifier')); | |
627 assert(invariant(node, element.isVariable || | |
628 element.isParameter || | |
629 element.isField, | |
630 message: 'Unexpected context element ${element}')); | |
631 return element.computeType(compiler); | |
632 } | |
633 } | |
634 | |
635 DartType visitIf(If node) { | |
636 Expression condition = node.condition.expression; | |
637 Statement thenPart = node.thenPart; | |
638 | |
639 checkCondition(node.condition); | |
640 analyzeInPromotedContext(condition, thenPart); | |
641 if (node.elsePart != null) { | |
642 analyze(node.elsePart); | |
643 } | |
644 return const StatementType(); | |
645 } | |
646 | |
647 void checkPrivateAccess(Node node, Element element, String name) { | |
648 if (name != null && | |
649 isPrivateName(name) && | |
650 element.library != currentLibrary) { | |
651 reportTypeWarning( | |
652 node, | |
653 MessageKind.PRIVATE_ACCESS, | |
654 {'name': name, | |
655 'libraryName': element.library.getLibraryOrScriptName()}); | |
656 } | |
657 | |
658 } | |
659 | |
660 ElementAccess lookupMember(Node node, DartType receiverType, String name, | |
661 MemberKind memberKind, Element receiverElement, | |
662 {bool lookupClassMember: false}) { | |
663 if (receiverType.treatAsDynamic) { | |
664 return const DynamicAccess(); | |
665 } | |
666 | |
667 Name memberName = new Name(name, currentLibrary, | |
668 isSetter: memberKind == MemberKind.SETTER); | |
669 | |
670 // Compute the unaliased type of the first non type variable bound of | |
671 // [type]. | |
672 DartType computeUnaliasedBound(DartType type) { | |
673 DartType originalType = type; | |
674 while (identical(type.kind, TypeKind.TYPE_VARIABLE)) { | |
675 TypeVariableType variable = type; | |
676 type = variable.element.bound; | |
677 if (type == originalType) { | |
678 type = compiler.objectClass.rawType; | |
679 } | |
680 } | |
681 if (type.isMalformed) { | |
682 return const DynamicType(); | |
683 } | |
684 return type.unalias(compiler); | |
685 } | |
686 | |
687 // Compute the interface type of [type]. For type variable it is the | |
688 // interface type of the bound, for function types and typedefs it is the | |
689 // `Function` type. | |
690 InterfaceType computeInterfaceType(DartType type) { | |
691 if (type.isFunctionType) { | |
692 type = compiler.functionClass.rawType; | |
693 } | |
694 assert(invariant(node, type.isInterfaceType, | |
695 message: "unexpected type kind ${type.kind}.")); | |
696 return type; | |
697 } | |
698 | |
699 // Lookup the class or interface member [name] in [interface]. | |
700 MemberSignature lookupMemberSignature(Name name, InterfaceType interface) { | |
701 MembersCreator.computeClassMembersByName( | |
702 compiler, interface.element, name.text); | |
703 return lookupClassMember || analyzingInitializer | |
704 ? interface.lookupClassMember(name) | |
705 : interface.lookupInterfaceMember(name); | |
706 } | |
707 | |
708 // Compute the access of [name] on [type]. This function takes the special | |
709 // 'call' method into account. | |
710 ElementAccess getAccess(Name name, | |
711 DartType unaliasedBound, InterfaceType interface) { | |
712 MemberSignature member = lookupMemberSignature(memberName, interface); | |
713 if (member != null) { | |
714 return new MemberAccess(member); | |
715 } | |
716 if (name == const PublicName('call')) { | |
717 if (unaliasedBound.isFunctionType) { | |
718 // This is an access the implicit 'call' method of a function type. | |
719 return new FunctionCallAccess(receiverElement, unaliasedBound); | |
720 } | |
721 if (types.isSubtype(interface, compiler.functionClass.rawType)) { | |
722 // This is an access of the special 'call' method implicitly defined | |
723 // on 'Function'. This method can be called with any arguments, which | |
724 // we ensure by giving it the type 'dynamic'. | |
725 return new FunctionCallAccess(null, const DynamicType()); | |
726 } | |
727 } | |
728 return null; | |
729 } | |
730 | |
731 DartType unaliasedBound = computeUnaliasedBound(receiverType); | |
732 if (unaliasedBound.treatAsDynamic) { | |
733 return new DynamicAccess(); | |
734 } | |
735 InterfaceType interface = computeInterfaceType(unaliasedBound); | |
736 ElementAccess access = getAccess(memberName, unaliasedBound, interface); | |
737 if (access != null) { | |
738 return access; | |
739 } | |
740 if (receiverElement != null && | |
741 (receiverElement.isVariable || receiverElement.isParameter)) { | |
742 Link<TypePromotion> typePromotions = typePromotionsMap[receiverElement]; | |
743 if (typePromotions != null) { | |
744 while (!typePromotions.isEmpty) { | |
745 TypePromotion typePromotion = typePromotions.head; | |
746 if (!typePromotion.isValid) { | |
747 DartType unaliasedBound = computeUnaliasedBound(typePromotion.type); | |
748 if (!unaliasedBound.treatAsDynamic) { | |
749 InterfaceType interface = computeInterfaceType(unaliasedBound); | |
750 if (getAccess(memberName, unaliasedBound, interface) != null) { | |
751 reportTypePromotionHint(typePromotion); | |
752 } | |
753 } | |
754 } | |
755 typePromotions = typePromotions.tail; | |
756 } | |
757 } | |
758 } | |
759 // We didn't find a member with the correct name. If this lookup is for a | |
760 // super or redirecting initializer, the resolver has already emitted an | |
761 // error message. If the target is a proxy, no warning needs to be emitted. | |
762 // Otherwise, try to emit the most precise warning. | |
763 if (!interface.element.isProxy && !analyzingInitializer) { | |
764 bool foundPrivateMember = false; | |
765 if (memberName.isPrivate) { | |
766 void findPrivateMember(MemberSignature member) { | |
767 if (memberName.isSimilarTo(member.name)) { | |
768 PrivateName privateName = member.name; | |
769 reportTypeWarning( | |
770 node, | |
771 MessageKind.PRIVATE_ACCESS, | |
772 {'name': name, | |
773 'libraryName': privateName.library.getLibraryOrScriptName()}); | |
774 foundPrivateMember = true; | |
775 } | |
776 } | |
777 // TODO(johnniwinther): Avoid computation of all class members. | |
778 MembersCreator.computeAllClassMembers(compiler, interface.element); | |
779 if (lookupClassMember) { | |
780 interface.element.forEachClassMember(findPrivateMember); | |
781 } else { | |
782 interface.element.forEachInterfaceMember(findPrivateMember); | |
783 } | |
784 | |
785 } | |
786 if (!foundPrivateMember) { | |
787 switch (memberKind) { | |
788 case MemberKind.METHOD: | |
789 reportTypeWarning(node, MessageKind.METHOD_NOT_FOUND, | |
790 {'className': receiverType.name, 'memberName': name}); | |
791 break; | |
792 case MemberKind.OPERATOR: | |
793 reportTypeWarning(node, MessageKind.OPERATOR_NOT_FOUND, | |
794 {'className': receiverType.name, 'memberName': name}); | |
795 break; | |
796 case MemberKind.GETTER: | |
797 if (lookupMemberSignature(memberName.setter, interface) != null) { | |
798 // A setter is present so warn explicitly about the missing | |
799 // getter. | |
800 reportTypeWarning(node, MessageKind.GETTER_NOT_FOUND, | |
801 {'className': receiverType.name, 'memberName': name}); | |
802 } else { | |
803 reportTypeWarning(node, MessageKind.MEMBER_NOT_FOUND, | |
804 {'className': receiverType.name, 'memberName': name}); | |
805 } | |
806 break; | |
807 case MemberKind.SETTER: | |
808 reportTypeWarning(node, MessageKind.SETTER_NOT_FOUND, | |
809 {'className': receiverType.name, 'memberName': name}); | |
810 break; | |
811 } | |
812 } | |
813 } | |
814 return const DynamicAccess(); | |
815 } | |
816 | |
817 DartType lookupMemberType(Node node, DartType type, String name, | |
818 MemberKind memberKind) { | |
819 return lookupMember(node, type, name, memberKind, null) | |
820 .computeType(compiler); | |
821 } | |
822 | |
823 void analyzeArguments(Send send, Element element, DartType type, | |
824 [LinkBuilder<DartType> argumentTypes]) { | |
825 Link<Node> arguments = send.arguments; | |
826 DartType unaliasedType = type.unalias(compiler); | |
827 if (identical(unaliasedType.kind, TypeKind.FUNCTION)) { | |
828 bool error = false; | |
829 FunctionType funType = unaliasedType; | |
830 Iterator<DartType> parameterTypes = funType.parameterTypes.iterator; | |
831 Iterator<DartType> optionalParameterTypes = | |
832 funType.optionalParameterTypes.iterator; | |
833 while (!arguments.isEmpty) { | |
834 Node argument = arguments.head; | |
835 NamedArgument namedArgument = argument.asNamedArgument(); | |
836 if (namedArgument != null) { | |
837 argument = namedArgument.expression; | |
838 String argumentName = namedArgument.name.source; | |
839 DartType namedParameterType = | |
840 funType.getNamedParameterType(argumentName); | |
841 if (namedParameterType == null) { | |
842 error = true; | |
843 // TODO(johnniwinther): Provide better information on the called | |
844 // function. | |
845 reportTypeWarning(argument, MessageKind.NAMED_ARGUMENT_NOT_FOUND, | |
846 {'argumentName': argumentName}); | |
847 | |
848 DartType argumentType = analyze(argument); | |
849 if (argumentTypes != null) argumentTypes.addLast(argumentType); | |
850 } else { | |
851 DartType argumentType = analyze(argument); | |
852 if (argumentTypes != null) argumentTypes.addLast(argumentType); | |
853 if (!checkAssignable(argument, argumentType, namedParameterType)) { | |
854 error = true; | |
855 } | |
856 } | |
857 } else { | |
858 if (!parameterTypes.moveNext()) { | |
859 if (!optionalParameterTypes.moveNext()) { | |
860 error = true; | |
861 // TODO(johnniwinther): Provide better information on the | |
862 // called function. | |
863 reportTypeWarning(argument, MessageKind.ADDITIONAL_ARGUMENT); | |
864 | |
865 DartType argumentType = analyze(argument); | |
866 if (argumentTypes != null) argumentTypes.addLast(argumentType); | |
867 } else { | |
868 DartType argumentType = analyze(argument); | |
869 if (argumentTypes != null) argumentTypes.addLast(argumentType); | |
870 if (!checkAssignable(argument, | |
871 argumentType, | |
872 optionalParameterTypes.current)) { | |
873 error = true; | |
874 } | |
875 } | |
876 } else { | |
877 DartType argumentType = analyze(argument); | |
878 if (argumentTypes != null) argumentTypes.addLast(argumentType); | |
879 if (!checkAssignable(argument, argumentType, | |
880 parameterTypes.current)) { | |
881 error = true; | |
882 } | |
883 } | |
884 } | |
885 arguments = arguments.tail; | |
886 } | |
887 if (parameterTypes.moveNext()) { | |
888 error = true; | |
889 // TODO(johnniwinther): Provide better information on the called | |
890 // function. | |
891 reportTypeWarning(send, MessageKind.MISSING_ARGUMENT, | |
892 {'argumentType': parameterTypes.current}); | |
893 } | |
894 if (error) { | |
895 // TODO(johnniwinther): Improve access to declaring element and handle | |
896 // synthesized member signatures. Currently function typed instance | |
897 // members provide no access to there own name. | |
898 if (element == null) { | |
899 element = type.element; | |
900 } else if (type.element.isTypedef) { | |
901 if (element != null) { | |
902 reportTypeInfo(element, | |
903 MessageKind.THIS_IS_THE_DECLARATION, | |
904 {'name': element.name}); | |
905 } | |
906 element = type.element; | |
907 } | |
908 reportTypeInfo(element, MessageKind.THIS_IS_THE_METHOD); | |
909 } | |
910 } else { | |
911 while(!arguments.isEmpty) { | |
912 DartType argumentType = analyze(arguments.head); | |
913 if (argumentTypes != null) argumentTypes.addLast(argumentType); | |
914 arguments = arguments.tail; | |
915 } | |
916 } | |
917 } | |
918 | |
919 // Analyze the invocation [node] of [elementAccess]. | |
920 // | |
921 // If provided [argumentTypes] is filled with the argument types during | |
922 // analysis. | |
923 DartType analyzeInvocation(Send node, ElementAccess elementAccess, | |
924 [LinkBuilder<DartType> argumentTypes]) { | |
925 DartType type = elementAccess.computeType(compiler); | |
926 if (elementAccess.isCallable(compiler)) { | |
927 analyzeArguments(node, elementAccess.element, type, argumentTypes); | |
928 } else { | |
929 reportTypeWarning(node, MessageKind.NOT_CALLABLE, | |
930 {'elementName': elementAccess.element.name}); | |
931 analyzeArguments(node, elementAccess.element, const DynamicType(), | |
932 argumentTypes); | |
933 } | |
934 type = type.unalias(compiler); | |
935 if (identical(type.kind, TypeKind.FUNCTION)) { | |
936 FunctionType funType = type; | |
937 return funType.returnType; | |
938 } else { | |
939 return const DynamicType(); | |
940 } | |
941 } | |
942 | |
943 /** | |
944 * Computes the [ElementAccess] for [name] on the [node] possibly using the | |
945 * [element] provided for [node] by the resolver. | |
946 */ | |
947 ElementAccess computeAccess(Send node, String name, Element element, | |
948 MemberKind memberKind, | |
949 {bool lookupClassMember: false}) { | |
950 if (element != null && element.isErroneous) { | |
951 // An error has already been reported for this node. | |
952 return const DynamicAccess(); | |
953 } | |
954 if (node.receiver != null) { | |
955 Element receiverElement = elements[node.receiver]; | |
956 if (receiverElement != null) { | |
957 if (receiverElement.isPrefix) { | |
958 assert(invariant(node, element != null, | |
959 message: 'Prefixed node has no element.')); | |
960 return computeResolvedAccess(node, name, element, memberKind); | |
961 } | |
962 } | |
963 // e.foo() for some expression e. | |
964 DartType receiverType = analyze(node.receiver); | |
965 if (receiverType.treatAsDynamic || receiverType.isVoid) { | |
966 return const DynamicAccess(); | |
967 } | |
968 TypeKind receiverKind = receiverType.kind; | |
969 return lookupMember(node, receiverType, name, memberKind, | |
970 elements[node.receiver], | |
971 lookupClassMember: lookupClassMember || | |
972 element != null && element.isStatic); | |
973 } else { | |
974 return computeResolvedAccess(node, name, element, memberKind); | |
975 } | |
976 } | |
977 | |
978 /** | |
979 * Computes the [ElementAccess] for [name] on the [node] using the [element] | |
980 * provided for [node] by the resolver. | |
981 */ | |
982 ElementAccess computeResolvedAccess(Send node, String name, | |
983 Element element, MemberKind memberKind) { | |
984 if (element == null) { | |
985 // foo() where foo is unresolved. | |
986 return lookupMember(node, thisType, name, memberKind, null); | |
987 } else if (element.isErroneous) { | |
988 // foo() where foo is erroneous. | |
989 return const DynamicAccess(); | |
990 } else if (element.impliesType) { | |
991 // The literal `Foo` where Foo is a class, a typedef, or a type variable. | |
992 if (elements.isTypeLiteral(node)) { | |
993 return new TypeLiteralAccess(elements.getTypeLiteralType(node)); | |
994 } | |
995 return createResolvedAccess(node, name, element); | |
996 } else if (element.isClassMember) { | |
997 // foo() where foo is a member. | |
998 return lookupMember(node, thisType, name, memberKind, null, | |
999 lookupClassMember: element.isStatic); | |
1000 } else if (element.isFunction) { | |
1001 // foo() where foo is a method in the same class. | |
1002 return createResolvedAccess(node, name, element); | |
1003 } else if (element.isVariable || | |
1004 element.isParameter || | |
1005 element.isField) { | |
1006 // foo() where foo is a field in the same class. | |
1007 return createResolvedAccess(node, name, element); | |
1008 } else if (element.isGetter || element.isSetter) { | |
1009 return createResolvedAccess(node, name, element); | |
1010 } else { | |
1011 compiler.internalError(element, | |
1012 'Unexpected element kind ${element.kind}.'); | |
1013 return null; | |
1014 } | |
1015 } | |
1016 | |
1017 ElementAccess createResolvedAccess(Send node, String name, | |
1018 Element element) { | |
1019 checkPrivateAccess(node, element, name); | |
1020 return createPromotedAccess(element); | |
1021 } | |
1022 | |
1023 ElementAccess createPromotedAccess(Element element) { | |
1024 if (element.isVariable || element.isParameter) { | |
1025 TypePromotion typePromotion = getKnownTypePromotion(element); | |
1026 if (typePromotion != null) { | |
1027 return new PromotedAccess(element, typePromotion.type); | |
1028 } | |
1029 } | |
1030 return new ResolvedAccess(element); | |
1031 } | |
1032 | |
1033 /** | |
1034 * Computes the type of the access of [name] on the [node] possibly using the | |
1035 * [element] provided for [node] by the resolver. | |
1036 */ | |
1037 DartType computeAccessType(Send node, String name, Element element, | |
1038 MemberKind memberKind, | |
1039 {bool lookupClassMember: false}) { | |
1040 DartType type = | |
1041 computeAccess(node, name, element, memberKind, | |
1042 lookupClassMember: lookupClassMember).computeType(compiler); | |
1043 if (type == null) { | |
1044 compiler.internalError(node, 'Type is null on access of $name on $node.'); | |
1045 } | |
1046 return type; | |
1047 } | |
1048 | |
1049 /// Compute a version of [shownType] that is more specific that [knownType]. | |
1050 /// This is used to provided better hints when trying to promote a supertype | |
1051 /// to a raw subtype. For instance trying to promote `Iterable<int>` to `List` | |
1052 /// we suggest the use of `List<int>`, which would make promotion valid. | |
1053 DartType computeMoreSpecificType(DartType shownType, | |
1054 DartType knownType) { | |
1055 if (knownType.isInterfaceType && | |
1056 shownType.isInterfaceType && | |
1057 types.isSubtype(shownType.asRaw(), knownType)) { | |
1058 // For the comments in the block, assume the hierarchy: | |
1059 // class A<T, V> {} | |
1060 // class B<S, U> extends A<S, int> {} | |
1061 // and a promotion from a [knownType] of `A<double, int>` to a | |
1062 // [shownType] of `B`. | |
1063 InterfaceType knownInterfaceType = knownType; | |
1064 ClassElement shownClass = shownType.element; | |
1065 | |
1066 // Compute `B<double, dynamic>` as the subtype of `A<double, int>` using | |
1067 // the relation between `A<S, int>` and `A<double, int>`. | |
1068 MoreSpecificSubtypeVisitor visitor = | |
1069 new MoreSpecificSubtypeVisitor(compiler); | |
1070 InterfaceType shownTypeGeneric = visitor.computeMoreSpecific( | |
1071 shownClass, knownInterfaceType); | |
1072 | |
1073 if (shownTypeGeneric != null && | |
1074 types.isMoreSpecific(shownTypeGeneric, knownType)) { | |
1075 // This should be the case but we double-check. | |
1076 // TODO(johnniwinther): Ensure that we don't suggest malbounded types. | |
1077 return shownTypeGeneric; | |
1078 } | |
1079 } | |
1080 return null; | |
1081 | |
1082 } | |
1083 | |
1084 DartType visitSend(Send node) { | |
1085 if (elements.isAssert(node)) { | |
1086 return analyzeInvocation(node, const AssertAccess()); | |
1087 } | |
1088 | |
1089 Element element = elements[node]; | |
1090 | |
1091 if (element != null && element.isConstructor) { | |
1092 DartType receiverType; | |
1093 if (node.receiver != null) { | |
1094 receiverType = analyze(node.receiver); | |
1095 } else if (node.selector.isSuper()) { | |
1096 // TODO(johnniwinther): Lookup super-member in class members. | |
1097 receiverType = superType; | |
1098 } else { | |
1099 assert(node.selector.isThis()); | |
1100 receiverType = thisType; | |
1101 } | |
1102 DartType constructorType = computeConstructorType(element, receiverType); | |
1103 analyzeArguments(node, element, constructorType); | |
1104 return const DynamicType(); | |
1105 } | |
1106 | |
1107 if (Elements.isClosureSend(node, element)) { | |
1108 if (element != null) { | |
1109 // foo() where foo is a local or a parameter. | |
1110 return analyzeInvocation(node, createPromotedAccess(element)); | |
1111 } else { | |
1112 // exp() where exp is some complex expression like (o) or foo(). | |
1113 DartType type = analyze(node.selector); | |
1114 return analyzeInvocation(node, new TypeAccess(type)); | |
1115 } | |
1116 } | |
1117 | |
1118 Identifier selector = node.selector.asIdentifier(); | |
1119 String name = selector.source; | |
1120 | |
1121 if (node.isOperator && identical(name, 'is')) { | |
1122 analyze(node.receiver); | |
1123 if (!node.isIsNotCheck) { | |
1124 Element variable = elements[node.receiver]; | |
1125 if (variable == null) { | |
1126 // Look for the variable element within parenthesized expressions. | |
1127 ParenthesizedExpression parentheses = | |
1128 node.receiver.asParenthesizedExpression(); | |
1129 while (parentheses != null) { | |
1130 variable = elements[parentheses.expression]; | |
1131 if (variable != null) break; | |
1132 parentheses = parentheses.expression.asParenthesizedExpression(); | |
1133 } | |
1134 } | |
1135 | |
1136 if (variable != null && | |
1137 (variable.isVariable || variable.isParameter)) { | |
1138 DartType knownType = getKnownType(variable); | |
1139 if (!knownType.isDynamic) { | |
1140 DartType shownType = elements.getType(node.arguments.head); | |
1141 TypePromotion typePromotion = | |
1142 new TypePromotion(node, variable, shownType); | |
1143 if (!types.isMoreSpecific(shownType, knownType)) { | |
1144 String variableName = variable.name; | |
1145 if (!types.isSubtype(shownType, knownType)) { | |
1146 typePromotion.addHint(node, | |
1147 MessageKind.NOT_MORE_SPECIFIC_SUBTYPE, | |
1148 {'variableName': variableName, | |
1149 'shownType': shownType, | |
1150 'knownType': knownType}); | |
1151 } else { | |
1152 DartType shownTypeSuggestion = | |
1153 computeMoreSpecificType(shownType, knownType); | |
1154 if (shownTypeSuggestion != null) { | |
1155 typePromotion.addHint(node, | |
1156 MessageKind.NOT_MORE_SPECIFIC_SUGGESTION, | |
1157 {'variableName': variableName, | |
1158 'shownType': shownType, | |
1159 'shownTypeSuggestion': shownTypeSuggestion, | |
1160 'knownType': knownType}); | |
1161 } else { | |
1162 typePromotion.addHint(node, | |
1163 MessageKind.NOT_MORE_SPECIFIC, | |
1164 {'variableName': variableName, | |
1165 'shownType': shownType, | |
1166 'knownType': knownType}); | |
1167 } | |
1168 } | |
1169 } | |
1170 showTypePromotion(node, typePromotion); | |
1171 } | |
1172 } | |
1173 } | |
1174 return boolType; | |
1175 } if (node.isOperator && identical(name, 'as')) { | |
1176 analyze(node.receiver); | |
1177 return elements.getType(node.arguments.head); | |
1178 } else if (node.isOperator) { | |
1179 final Node receiver = node.receiver; | |
1180 final DartType receiverType = analyze(receiver); | |
1181 if (identical(name, '==') || identical(name, '!=') | |
1182 // TODO(johnniwinther): Remove these. | |
1183 || identical(name, '===') || identical(name, '!==')) { | |
1184 // Analyze argument. | |
1185 analyze(node.arguments.head); | |
1186 return boolType; | |
1187 } else if (identical(name, '||')) { | |
1188 checkAssignable(receiver, receiverType, boolType); | |
1189 final Node argument = node.arguments.head; | |
1190 final DartType argumentType = analyze(argument); | |
1191 checkAssignable(argument, argumentType, boolType); | |
1192 return boolType; | |
1193 } else if (identical(name, '&&')) { | |
1194 checkAssignable(receiver, receiverType, boolType); | |
1195 final Node argument = node.arguments.head; | |
1196 | |
1197 final DartType argumentType = | |
1198 analyzeInPromotedContext(receiver, argument); | |
1199 | |
1200 reshowTypePromotions(node, receiver, argument); | |
1201 | |
1202 checkAssignable(argument, argumentType, boolType); | |
1203 return boolType; | |
1204 } else if (identical(name, '!')) { | |
1205 checkAssignable(receiver, receiverType, boolType); | |
1206 return boolType; | |
1207 } else if (identical(name, '?')) { | |
1208 return boolType; | |
1209 } | |
1210 String operatorName = selector.source; | |
1211 if (identical(name, '-') && node.arguments.isEmpty) { | |
1212 operatorName = 'unary-'; | |
1213 } | |
1214 assert(invariant(node, | |
1215 identical(name, '+') || identical(name, '=') || | |
1216 identical(name, '-') || identical(name, '*') || | |
1217 identical(name, '/') || identical(name, '%') || | |
1218 identical(name, '~/') || identical(name, '|') || | |
1219 identical(name, '&') || identical(name, '^') || | |
1220 identical(name, '~')|| identical(name, '<<') || | |
1221 identical(name, '>>') || | |
1222 identical(name, '<') || identical(name, '>') || | |
1223 identical(name, '<=') || identical(name, '>=') || | |
1224 identical(name, '[]'), | |
1225 message: 'Unexpected operator $name')); | |
1226 | |
1227 // TODO(karlklose): handle `void` in expression context by calling | |
1228 // [analyzeNonVoid] instead of [analyze]. | |
1229 ElementAccess access = receiverType.isVoid ? const DynamicAccess() | |
1230 : lookupMember(node, receiverType, operatorName, | |
1231 MemberKind.OPERATOR, null); | |
1232 LinkBuilder<DartType> argumentTypesBuilder = new LinkBuilder<DartType>(); | |
1233 DartType resultType = | |
1234 analyzeInvocation(node, access, argumentTypesBuilder); | |
1235 if (identical(receiverType.element, compiler.intClass)) { | |
1236 if (identical(name, '+') || | |
1237 identical(operatorName, '-') || | |
1238 identical(name, '*') || | |
1239 identical(name, '%')) { | |
1240 DartType argumentType = argumentTypesBuilder.toLink().head; | |
1241 if (identical(argumentType.element, compiler.intClass)) { | |
1242 return intType; | |
1243 } else if (identical(argumentType.element, compiler.doubleClass)) { | |
1244 return doubleType; | |
1245 } | |
1246 } | |
1247 } | |
1248 return resultType; | |
1249 } else if (node.isPropertyAccess) { | |
1250 ElementAccess access = | |
1251 computeAccess(node, selector.source, element, MemberKind.GETTER); | |
1252 return access.computeType(compiler); | |
1253 } else if (node.isFunctionObjectInvocation) { | |
1254 return unhandledExpression(); | |
1255 } else { | |
1256 ElementAccess access = | |
1257 computeAccess(node, selector.source, element, MemberKind.METHOD); | |
1258 return analyzeInvocation(node, access); | |
1259 } | |
1260 } | |
1261 | |
1262 /// Returns the first type in the list or [:dynamic:] if the list is empty. | |
1263 DartType firstType(List<DartType> list) { | |
1264 return list.isEmpty ? const DynamicType() : list.first; | |
1265 } | |
1266 | |
1267 /** | |
1268 * Returns the second type in the list or [:dynamic:] if the list is too | |
1269 * short. | |
1270 */ | |
1271 DartType secondType(List<DartType> list) { | |
1272 return list.length < 2 ? const DynamicType() : list[1]; | |
1273 } | |
1274 | |
1275 /** | |
1276 * Checks [: target o= value :] for some operator o, and returns the type | |
1277 * of the result. This method also handles increment/decrement expressions | |
1278 * like [: target++ :]. | |
1279 */ | |
1280 DartType checkAssignmentOperator(SendSet node, | |
1281 String operatorName, | |
1282 Node valueNode, | |
1283 DartType value) { | |
1284 assert(invariant(node, !node.isIndex)); | |
1285 Element setterElement = elements[node]; | |
1286 Element getterElement = elements[node.selector]; | |
1287 Identifier selector = node.selector; | |
1288 DartType getter = computeAccessType( | |
1289 node, selector.source, getterElement, MemberKind.GETTER); | |
1290 DartType setter = computeAccessType( | |
1291 node, selector.source, setterElement, MemberKind.SETTER); | |
1292 // [operator] is the type of operator+ or operator- on [target]. | |
1293 DartType operator = | |
1294 lookupMemberType(node, getter, operatorName, MemberKind.OPERATOR); | |
1295 if (operator is FunctionType) { | |
1296 FunctionType operatorType = operator; | |
1297 // [result] is the type of target o value. | |
1298 DartType result = operatorType.returnType; | |
1299 DartType operatorArgument = firstType(operatorType.parameterTypes); | |
1300 // Check target o value. | |
1301 bool validValue = checkAssignable(valueNode, value, operatorArgument); | |
1302 if (validValue || !(node.isPrefix || node.isPostfix)) { | |
1303 // Check target = result. | |
1304 checkAssignable(node.assignmentOperator, result, setter); | |
1305 } | |
1306 return node.isPostfix ? getter : result; | |
1307 } | |
1308 return const DynamicType(); | |
1309 } | |
1310 | |
1311 /** | |
1312 * Checks [: base[key] o= value :] for some operator o, and returns the type | |
1313 * of the result. This method also handles increment/decrement expressions | |
1314 * like [: base[key]++ :]. | |
1315 */ | |
1316 DartType checkIndexAssignmentOperator(SendSet node, | |
1317 String operatorName, | |
1318 Node valueNode, | |
1319 DartType value) { | |
1320 assert(invariant(node, node.isIndex)); | |
1321 final DartType base = analyze(node.receiver); | |
1322 final Node keyNode = node.arguments.head; | |
1323 final DartType key = analyze(keyNode); | |
1324 | |
1325 // [indexGet] is the type of operator[] on [base]. | |
1326 DartType indexGet = lookupMemberType( | |
1327 node, base, '[]', MemberKind.OPERATOR); | |
1328 if (indexGet is FunctionType) { | |
1329 FunctionType indexGetType = indexGet; | |
1330 DartType indexGetKey = firstType(indexGetType.parameterTypes); | |
1331 // Check base[key]. | |
1332 bool validKey = checkAssignable(keyNode, key, indexGetKey); | |
1333 | |
1334 // [element] is the type of base[key]. | |
1335 DartType element = indexGetType.returnType; | |
1336 // [operator] is the type of operator o on [element]. | |
1337 DartType operator = lookupMemberType( | |
1338 node, element, operatorName, MemberKind.OPERATOR); | |
1339 if (operator is FunctionType) { | |
1340 FunctionType operatorType = operator; | |
1341 | |
1342 // Check base[key] o value. | |
1343 DartType operatorArgument = firstType(operatorType.parameterTypes); | |
1344 bool validValue = checkAssignable(valueNode, value, operatorArgument); | |
1345 | |
1346 // [result] is the type of base[key] o value. | |
1347 DartType result = operatorType.returnType; | |
1348 | |
1349 // [indexSet] is the type of operator[]= on [base]. | |
1350 DartType indexSet = lookupMemberType( | |
1351 node, base, '[]=', MemberKind.OPERATOR); | |
1352 if (indexSet is FunctionType) { | |
1353 FunctionType indexSetType = indexSet; | |
1354 DartType indexSetKey = firstType(indexSetType.parameterTypes); | |
1355 DartType indexSetValue = secondType(indexSetType.parameterTypes); | |
1356 | |
1357 if (validKey || indexGetKey != indexSetKey) { | |
1358 // Only check base[key] on []= if base[key] was valid for [] or | |
1359 // if the key types differ. | |
1360 checkAssignable(keyNode, key, indexSetKey); | |
1361 } | |
1362 // Check base[key] = result | |
1363 if (validValue || !(node.isPrefix || node.isPostfix)) { | |
1364 checkAssignable(node.assignmentOperator, result, indexSetValue); | |
1365 } | |
1366 } | |
1367 return node.isPostfix ? element : result; | |
1368 } | |
1369 } | |
1370 return const DynamicType(); | |
1371 } | |
1372 | |
1373 visitSendSet(SendSet node) { | |
1374 Element element = elements[node]; | |
1375 Identifier selector = node.selector; | |
1376 final name = node.assignmentOperator.source; | |
1377 if (identical(name, '=')) { | |
1378 // e1 = value | |
1379 if (node.isIndex) { | |
1380 // base[key] = value | |
1381 final DartType base = analyze(node.receiver); | |
1382 final Node keyNode = node.arguments.head; | |
1383 final DartType key = analyze(keyNode); | |
1384 final Node valueNode = node.arguments.tail.head; | |
1385 final DartType value = analyze(valueNode); | |
1386 DartType indexSet = lookupMemberType( | |
1387 node, base, '[]=', MemberKind.OPERATOR); | |
1388 if (indexSet is FunctionType) { | |
1389 FunctionType indexSetType = indexSet; | |
1390 DartType indexSetKey = firstType(indexSetType.parameterTypes); | |
1391 checkAssignable(keyNode, key, indexSetKey); | |
1392 DartType indexSetValue = secondType(indexSetType.parameterTypes); | |
1393 checkAssignable(node.assignmentOperator, value, indexSetValue); | |
1394 } | |
1395 return value; | |
1396 } else { | |
1397 // target = value | |
1398 DartType target; | |
1399 if (analyzingInitializer) { | |
1400 // Field declaration `Foo target = value;` or initializer | |
1401 // `this.target = value`. Lookup the getter `target` in the class | |
1402 // members. | |
1403 target = computeAccessType(node, selector.source, element, | |
1404 MemberKind.GETTER, lookupClassMember: true); | |
1405 } else { | |
1406 // Normal assignment `target = value`. | |
1407 target = computeAccessType( | |
1408 node, selector.source, element, MemberKind.SETTER); | |
1409 } | |
1410 final Node valueNode = node.arguments.head; | |
1411 final DartType value = analyze(valueNode); | |
1412 checkAssignable(node.assignmentOperator, value, target); | |
1413 return value; | |
1414 } | |
1415 } else if (identical(name, '++') || identical(name, '--')) { | |
1416 // e++ or e-- | |
1417 String operatorName = identical(name, '++') ? '+' : '-'; | |
1418 if (node.isIndex) { | |
1419 // base[key]++, base[key]--, ++base[key], or --base[key] | |
1420 return checkIndexAssignmentOperator( | |
1421 node, operatorName, node.assignmentOperator, intType); | |
1422 } else { | |
1423 // target++, target--, ++target, or --target | |
1424 return checkAssignmentOperator( | |
1425 node, operatorName, node.assignmentOperator, intType); | |
1426 } | |
1427 } else { | |
1428 // e1 o= e2 for some operator o. | |
1429 String operatorName; | |
1430 switch (name) { | |
1431 case '+=': operatorName = '+'; break; | |
1432 case '-=': operatorName = '-'; break; | |
1433 case '*=': operatorName = '*'; break; | |
1434 case '/=': operatorName = '/'; break; | |
1435 case '%=': operatorName = '%'; break; | |
1436 case '~/=': operatorName = '~/'; break; | |
1437 case '&=': operatorName = '&'; break; | |
1438 case '|=': operatorName = '|'; break; | |
1439 case '^=': operatorName = '^'; break; | |
1440 case '<<=': operatorName = '<<'; break; | |
1441 case '>>=': operatorName = '>>'; break; | |
1442 default: | |
1443 compiler.internalError(node, 'Unexpected assignment operator $name.'); | |
1444 } | |
1445 if (node.isIndex) { | |
1446 // base[key] o= value for some operator o. | |
1447 final Node valueNode = node.arguments.tail.head; | |
1448 final DartType value = analyze(valueNode); | |
1449 return checkIndexAssignmentOperator( | |
1450 node, operatorName, valueNode, value); | |
1451 } else { | |
1452 // target o= value for some operator o. | |
1453 final Node valueNode = node.arguments.head; | |
1454 final DartType value = analyze(valueNode); | |
1455 return checkAssignmentOperator(node, operatorName, valueNode, value); | |
1456 } | |
1457 } | |
1458 } | |
1459 | |
1460 DartType visitLiteralInt(LiteralInt node) { | |
1461 return intType; | |
1462 } | |
1463 | |
1464 DartType visitLiteralDouble(LiteralDouble node) { | |
1465 return doubleType; | |
1466 } | |
1467 | |
1468 DartType visitLiteralBool(LiteralBool node) { | |
1469 return boolType; | |
1470 } | |
1471 | |
1472 DartType visitLiteralString(LiteralString node) { | |
1473 return stringType; | |
1474 } | |
1475 | |
1476 DartType visitStringJuxtaposition(StringJuxtaposition node) { | |
1477 analyze(node.first); | |
1478 analyze(node.second); | |
1479 return stringType; | |
1480 } | |
1481 | |
1482 DartType visitLiteralNull(LiteralNull node) { | |
1483 return const DynamicType(); | |
1484 } | |
1485 | |
1486 DartType visitLiteralSymbol(LiteralSymbol node) { | |
1487 return compiler.symbolClass.rawType; | |
1488 } | |
1489 | |
1490 DartType computeConstructorType(Element constructor, DartType type) { | |
1491 if (Elements.isUnresolved(constructor)) return const DynamicType(); | |
1492 DartType constructorType = constructor.computeType(compiler); | |
1493 if (identical(type.kind, TypeKind.INTERFACE)) { | |
1494 if (constructor.isSynthesized) { | |
1495 // TODO(johnniwinther): Remove this when synthesized constructors handle | |
1496 // type variables correctly. | |
1497 InterfaceType interfaceType = type; | |
1498 ClassElement receiverElement = interfaceType.element; | |
1499 while (receiverElement.isMixinApplication) { | |
1500 receiverElement = receiverElement.supertype.element; | |
1501 } | |
1502 constructorType = constructorType.substByContext( | |
1503 interfaceType.asInstanceOf(receiverElement)); | |
1504 } else { | |
1505 constructorType = constructorType.substByContext(type); | |
1506 } | |
1507 } | |
1508 return constructorType; | |
1509 } | |
1510 | |
1511 DartType visitNewExpression(NewExpression node) { | |
1512 Element element = elements[node.send]; | |
1513 if (Elements.isUnresolved(element)) return const DynamicType(); | |
1514 | |
1515 checkPrivateAccess(node, element, element.name); | |
1516 | |
1517 DartType newType = elements.getType(node); | |
1518 DartType constructorType = computeConstructorType(element, newType); | |
1519 analyzeArguments(node.send, element, constructorType); | |
1520 return newType; | |
1521 } | |
1522 | |
1523 DartType visitLiteralList(LiteralList node) { | |
1524 InterfaceType listType = elements.getType(node); | |
1525 DartType listElementType = firstType(listType.typeArguments); | |
1526 for (Link<Node> link = node.elements.nodes; | |
1527 !link.isEmpty; | |
1528 link = link.tail) { | |
1529 Node element = link.head; | |
1530 DartType elementType = analyze(element); | |
1531 checkAssignable(element, elementType, listElementType, | |
1532 isConst: node.isConst); | |
1533 } | |
1534 return listType; | |
1535 } | |
1536 | |
1537 DartType visitNodeList(NodeList node) { | |
1538 for (Link<Node> link = node.nodes; !link.isEmpty; link = link.tail) { | |
1539 analyze(link.head, inInitializer: analyzingInitializer); | |
1540 } | |
1541 return const StatementType(); | |
1542 } | |
1543 | |
1544 DartType visitRedirectingFactoryBody(RedirectingFactoryBody node) { | |
1545 // TODO(lrn): Typecheck the body. It must refer to the constructor | |
1546 // of a subtype. | |
1547 return const StatementType(); | |
1548 } | |
1549 | |
1550 DartType visitRethrow(Rethrow node) { | |
1551 return const StatementType(); | |
1552 } | |
1553 | |
1554 /** Dart Programming Language Specification: 11.10 Return */ | |
1555 DartType visitReturn(Return node) { | |
1556 if (identical(node.beginToken.stringValue, 'native')) { | |
1557 return const StatementType(); | |
1558 } | |
1559 | |
1560 final expression = node.expression; | |
1561 final isVoidFunction = expectedReturnType.isVoid; | |
1562 | |
1563 // Executing a return statement return e; [...] It is a static type warning | |
1564 // if the type of e may not be assigned to the declared return type of the | |
1565 // immediately enclosing function. | |
1566 if (expression != null) { | |
1567 final expressionType = analyze(expression); | |
1568 Element element = elements.analyzedElement; | |
1569 if (element != null && element.isGenerativeConstructor) { | |
1570 // The resolver already emitted an error for this expression. | |
1571 } else if (isVoidFunction | |
1572 && !types.isAssignable(expressionType, const VoidType())) { | |
1573 reportTypeWarning(expression, MessageKind.RETURN_VALUE_IN_VOID); | |
1574 } else { | |
1575 checkAssignable(expression, expressionType, expectedReturnType); | |
1576 } | |
1577 | |
1578 // Let f be the function immediately enclosing a return statement of the | |
1579 // form 'return;' It is a static warning if both of the following conditions | |
1580 // hold: | |
1581 // - f is not a generative constructor. | |
1582 // - The return type of f may not be assigned to void. | |
1583 } else if (!types.isAssignable(expectedReturnType, const VoidType())) { | |
1584 reportTypeWarning(node, MessageKind.RETURN_NOTHING, | |
1585 {'returnType': expectedReturnType}); | |
1586 } | |
1587 return const StatementType(); | |
1588 } | |
1589 | |
1590 DartType visitThrow(Throw node) { | |
1591 // TODO(johnniwinther): Handle reachability. | |
1592 analyze(node.expression); | |
1593 return const DynamicType(); | |
1594 } | |
1595 | |
1596 DartType visitAwait(Await node) { | |
1597 DartType expressionType = analyze(node.expression); | |
1598 DartType resultType = expressionType; | |
1599 if (expressionType is InterfaceType) { | |
1600 InterfaceType futureType = | |
1601 expressionType.asInstanceOf(compiler.futureClass); | |
1602 if (futureType != null) { | |
1603 resultType = futureType.typeArguments.first; | |
1604 } | |
1605 } | |
1606 return resultType; | |
1607 } | |
1608 | |
1609 DartType visitYield(Yield node) { | |
1610 DartType resultType = analyze(node.expression); | |
1611 if (!node.hasStar) { | |
1612 if (currentAsyncMarker.isAsync) { | |
1613 resultType = | |
1614 compiler.streamClass.thisType.createInstantiation( | |
1615 <DartType>[resultType]); | |
1616 } else { | |
1617 resultType = | |
1618 compiler.iterableClass.thisType.createInstantiation( | |
1619 <DartType>[resultType]); | |
1620 } | |
1621 } | |
1622 checkAssignable(node, resultType, expectedReturnType); | |
1623 return const StatementType(); | |
1624 } | |
1625 | |
1626 DartType visitTypeAnnotation(TypeAnnotation node) { | |
1627 return elements.getType(node); | |
1628 } | |
1629 | |
1630 DartType visitVariableDefinitions(VariableDefinitions node) { | |
1631 DartType type = analyzeWithDefault(node.type, const DynamicType()); | |
1632 if (type.isVoid) { | |
1633 reportTypeWarning(node.type, MessageKind.VOID_VARIABLE); | |
1634 type = const DynamicType(); | |
1635 } | |
1636 for (Link<Node> link = node.definitions.nodes; !link.isEmpty; | |
1637 link = link.tail) { | |
1638 Node definition = link.head; | |
1639 invariant(definition, definition is Identifier || definition is SendSet, | |
1640 message: 'expected identifier or initialization'); | |
1641 if (definition is SendSet) { | |
1642 SendSet initialization = definition; | |
1643 DartType initializer = analyzeNonVoid(initialization.arguments.head); | |
1644 checkAssignable(initialization.assignmentOperator, initializer, type); | |
1645 } | |
1646 } | |
1647 return const StatementType(); | |
1648 } | |
1649 | |
1650 DartType visitWhile(While node) { | |
1651 checkCondition(node.condition); | |
1652 analyze(node.body); | |
1653 Expression cond = node.condition.asParenthesizedExpression().expression; | |
1654 return const StatementType(); | |
1655 } | |
1656 | |
1657 DartType visitParenthesizedExpression(ParenthesizedExpression node) { | |
1658 Expression expression = node.expression; | |
1659 DartType type = analyze(expression); | |
1660 for (TypePromotion typePromotion in getShownTypePromotionsFor(expression)) { | |
1661 showTypePromotion(node, typePromotion); | |
1662 } | |
1663 return type; | |
1664 } | |
1665 | |
1666 DartType visitConditional(Conditional node) { | |
1667 Expression condition = node.condition; | |
1668 Expression thenExpression = node.thenExpression; | |
1669 | |
1670 checkCondition(condition); | |
1671 | |
1672 DartType thenType = analyzeInPromotedContext(condition, thenExpression); | |
1673 | |
1674 DartType elseType = analyze(node.elseExpression); | |
1675 return compiler.types.computeLeastUpperBound(thenType, elseType); | |
1676 } | |
1677 | |
1678 visitStringInterpolation(StringInterpolation node) { | |
1679 node.visitChildren(this); | |
1680 return stringType; | |
1681 } | |
1682 | |
1683 visitStringInterpolationPart(StringInterpolationPart node) { | |
1684 node.visitChildren(this); | |
1685 return stringType; | |
1686 } | |
1687 | |
1688 visitEmptyStatement(EmptyStatement node) { | |
1689 return const StatementType(); | |
1690 } | |
1691 | |
1692 visitBreakStatement(BreakStatement node) { | |
1693 return const StatementType(); | |
1694 } | |
1695 | |
1696 visitContinueStatement(ContinueStatement node) { | |
1697 return const StatementType(); | |
1698 } | |
1699 | |
1700 visitForIn(ForIn node) { | |
1701 analyze(node.expression); | |
1702 analyze(node.body); | |
1703 return const StatementType(); | |
1704 } | |
1705 | |
1706 visitLabeledStatement(LabeledStatement node) { | |
1707 return analyze(node.statement); | |
1708 } | |
1709 | |
1710 visitLiteralMap(LiteralMap node) { | |
1711 InterfaceType mapType = elements.getType(node); | |
1712 DartType mapKeyType = firstType(mapType.typeArguments); | |
1713 DartType mapValueType = secondType(mapType.typeArguments); | |
1714 bool isConst = node.isConst; | |
1715 for (Link<Node> link = node.entries.nodes; | |
1716 !link.isEmpty; | |
1717 link = link.tail) { | |
1718 LiteralMapEntry entry = link.head; | |
1719 DartType keyType = analyze(entry.key); | |
1720 checkAssignable(entry.key, keyType, mapKeyType, isConst: isConst); | |
1721 DartType valueType = analyze(entry.value); | |
1722 checkAssignable(entry.value, valueType, mapValueType, isConst: isConst); | |
1723 } | |
1724 return mapType; | |
1725 } | |
1726 | |
1727 visitNamedArgument(NamedArgument node) { | |
1728 // Named arguments are visited as part of analyzing invocations of | |
1729 // unresolved methods. For instance [: foo(a: 42); :] where 'foo' is neither | |
1730 // found in the enclosing scope nor through lookup on 'this' or | |
1731 // [: x.foo(b: 42); :] where 'foo' cannot be not found through lookup on | |
1732 // the static type of 'x'. | |
1733 return analyze(node.expression); | |
1734 } | |
1735 | |
1736 visitSwitchStatement(SwitchStatement node) { | |
1737 // TODO(johnniwinther): Handle reachability based on reachability of | |
1738 // switch cases. | |
1739 | |
1740 DartType expressionType = analyze(node.expression); | |
1741 | |
1742 // Check that all the case expressions are assignable to the expression. | |
1743 for (SwitchCase switchCase in node.cases) { | |
1744 for (Node labelOrCase in switchCase.labelsAndCases) { | |
1745 CaseMatch caseMatch = labelOrCase.asCaseMatch(); | |
1746 if (caseMatch == null) continue; | |
1747 | |
1748 DartType caseType = analyze(caseMatch.expression); | |
1749 checkAssignable(caseMatch, expressionType, caseType); | |
1750 } | |
1751 | |
1752 analyze(switchCase); | |
1753 } | |
1754 | |
1755 return const StatementType(); | |
1756 } | |
1757 | |
1758 visitSwitchCase(SwitchCase node) { | |
1759 return analyze(node.statements); | |
1760 } | |
1761 | |
1762 visitTryStatement(TryStatement node) { | |
1763 // TODO(johnniwinther): Use reachability information of try-block, | |
1764 // catch-blocks and finally-block to compute the whether the try statement | |
1765 // is returning. | |
1766 analyze(node.tryBlock); | |
1767 for (CatchBlock catchBlock in node.catchBlocks) { | |
1768 analyze(catchBlock); | |
1769 } | |
1770 analyzeWithDefault(node.finallyBlock, null); | |
1771 return const StatementType(); | |
1772 } | |
1773 | |
1774 visitCatchBlock(CatchBlock node) { | |
1775 return analyze(node.block); | |
1776 } | |
1777 | |
1778 visitTypedef(Typedef node) { | |
1779 // Do not typecheck [Typedef] nodes. | |
1780 } | |
1781 | |
1782 visitNode(Node node) { | |
1783 compiler.internalError(node, | |
1784 'Unexpected node ${node.getObjectDescription()} in the type checker.'); | |
1785 } | |
1786 } | |
OLD | NEW |