OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, 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 /// Encapsulates how to invoke the analyzer resolver and overrides how it | |
6 /// computes types on expressions to use our restricted set of types. | |
7 library dev_compiler.src.checker.resolver; | |
8 | |
9 import 'package:analyzer/analyzer.dart'; | |
10 import 'package:analyzer/src/generated/ast.dart'; | |
11 import 'package:analyzer/src/generated/element.dart'; | |
12 import 'package:analyzer/src/generated/resolver.dart'; | |
13 import 'package:analyzer/src/generated/source.dart' show Source; | |
14 import 'package:analyzer/src/generated/source_io.dart'; | |
15 import 'package:analyzer/src/generated/static_type_analyzer.dart'; | |
16 import 'package:analyzer/src/generated/utilities_collection.dart' | |
17 show DirectedGraph; | |
18 import 'package:logging/logging.dart' as logger; | |
19 | |
20 import '../../strong_mode.dart' show StrongModeOptions; | |
21 import '../utils.dart'; | |
22 import 'rules.dart'; | |
23 | |
24 final _log = new logger.Logger('dev_compiler.src.resolver'); | |
25 | |
26 /// A [LibraryResolver] that performs inference on top-levels and fields based | |
27 /// on the value of the initializer, and on fields and methods based on | |
28 /// overridden members in super classes. | |
29 class LibraryResolverWithInference extends LibraryResolver { | |
30 final StrongModeOptions _options; | |
31 | |
32 LibraryResolverWithInference(context, this._options) : super(context); | |
33 | |
34 @override | |
35 void resolveReferencesAndTypes() { | |
36 _resolveVariableReferences(); | |
37 | |
38 // Run resolution in two stages, skipping method bodies first, so we can run | |
39 // type-inference before we fully analyze methods. | |
40 var visitors = _createVisitors(); | |
41 _resolveEverything(visitors); | |
42 _runInference(visitors); | |
43 | |
44 visitors.values.forEach((v) => v.skipMethodBodies = false); | |
45 _resolveEverything(visitors); | |
46 } | |
47 | |
48 // Note: this was split from _resolveReferencesAndTypesInLibrary so we do it | |
49 // only once. | |
50 void _resolveVariableReferences() { | |
51 for (Library library in resolvedLibraries) { | |
52 for (Source source in library.compilationUnitSources) { | |
53 library.getAST(source).accept(new VariableResolverVisitor( | |
54 library.libraryElement, source, typeProvider, library.errorListener, | |
55 nameScope: library.libraryScope)); | |
56 } | |
57 } | |
58 } | |
59 | |
60 // Note: this was split from _resolveReferencesAndTypesInLibrary so we can do | |
61 // resolution in pieces. | |
62 Map<Source, RestrictedResolverVisitor> _createVisitors() { | |
63 var visitors = <Source, RestrictedResolverVisitor>{}; | |
64 for (Library library in resolvedLibraries) { | |
65 for (Source source in library.compilationUnitSources) { | |
66 var visitor = new RestrictedResolverVisitor( | |
67 library, source, typeProvider, _options); | |
68 visitors[source] = visitor; | |
69 } | |
70 } | |
71 return visitors; | |
72 } | |
73 | |
74 /// Runs the resolver on the entire library cycle. | |
75 void _resolveEverything(Map<Source, RestrictedResolverVisitor> visitors) { | |
76 for (Library library in resolvedLibraries) { | |
77 for (Source source in library.compilationUnitSources) { | |
78 library.getAST(source).accept(visitors[source]); | |
79 } | |
80 } | |
81 } | |
82 | |
83 _runInference(Map<Source, RestrictedResolverVisitor> visitors) { | |
84 var globalsAndStatics = <VariableDeclaration>[]; | |
85 var classes = <ClassDeclaration>[]; | |
86 | |
87 // Extract top-level members that are const, statics, or classes. | |
88 for (Library library in resolvedLibraries) { | |
89 for (Source source in library.compilationUnitSources) { | |
90 CompilationUnit ast = library.getAST(source); | |
91 for (var declaration in ast.declarations) { | |
92 if (declaration is TopLevelVariableDeclaration) { | |
93 globalsAndStatics.addAll(declaration.variables.variables); | |
94 } else if (declaration is ClassDeclaration) { | |
95 classes.add(declaration); | |
96 for (var member in declaration.members) { | |
97 if (member is FieldDeclaration && | |
98 (member.fields.isConst || member.isStatic)) { | |
99 globalsAndStatics.addAll(member.fields.variables); | |
100 } | |
101 } | |
102 } | |
103 } | |
104 } | |
105 } | |
106 _inferGlobalsAndStatics(globalsAndStatics, visitors); | |
107 _inferInstanceFields(classes, visitors); | |
108 } | |
109 | |
110 _inferGlobalsAndStatics(List<VariableDeclaration> globalsAndStatics, | |
111 Map<Source, RestrictedResolverVisitor> visitors) { | |
112 var elementToDeclaration = {}; | |
113 for (var c in globalsAndStatics) { | |
114 elementToDeclaration[c.element] = c; | |
115 } | |
116 var constGraph = new DirectedGraph<VariableDeclaration>(); | |
117 globalsAndStatics.forEach(constGraph.addNode); | |
118 for (var c in globalsAndStatics) { | |
119 for (var e in _VarExtractor.extract(c.initializer)) { | |
120 // Note: declaration is null for variables that come from other strongly | |
121 // connected components. | |
122 var declaration = elementToDeclaration[e]; | |
123 if (declaration != null) constGraph.addEdge(c, declaration); | |
124 } | |
125 } | |
126 | |
127 for (var component in constGraph.computeTopologicalSort()) { | |
128 component.forEach((v) => _reanalyzeVar(visitors, v)); | |
129 _inferVariableFromInitializer(component); | |
130 } | |
131 } | |
132 | |
133 _inferInstanceFields(List<ClassDeclaration> classes, | |
134 Map<Source, RestrictedResolverVisitor> visitors) { | |
135 // First propagate what was inferred from globals to all instance fields. | |
136 | |
137 // TODO(sigmund): also do a fine-grain propagation between fields. We want | |
138 // infer-by-override to take precedence, so we would have to include | |
139 // classes in the dependency graph and ensure that fields depend on their | |
140 // class, and classes depend on superclasses. | |
141 classes | |
142 .expand((c) => c.members.where(_isInstanceField)) | |
143 .expand((f) => f.fields.variables) | |
144 .forEach((v) => _reanalyzeVar(visitors, v)); | |
145 | |
146 // Track types in this strongly connected component, ensure we visit | |
147 // supertypes before subtypes. | |
148 var typeToDeclaration = <InterfaceType, ClassDeclaration>{}; | |
149 classes.forEach((c) => typeToDeclaration[c.element.type] = c); | |
150 var seen = new Set<InterfaceType>(); | |
151 visit(ClassDeclaration cls) { | |
152 var element = cls.element; | |
153 var type = element.type; | |
154 if (seen.contains(type)) return; | |
155 seen.add(type); | |
156 for (var supertype in element.allSupertypes) { | |
157 var supertypeClass = typeToDeclaration[supertype]; | |
158 if (supertypeClass != null) visit(supertypeClass); | |
159 } | |
160 | |
161 // Infer field types from overrides first, otherwise from initializers. | |
162 var pending = new Set<VariableDeclaration>(); | |
163 cls.members | |
164 .where(_isInstanceField) | |
165 .forEach((f) => _inferFieldTypeFromOverride(f, pending)); | |
166 if (pending.isNotEmpty) _inferVariableFromInitializer(pending); | |
167 | |
168 // Infer return-types and param-types from overrides | |
169 cls.members | |
170 .where((m) => m is MethodDeclaration && !m.isStatic) | |
171 .forEach(_inferMethodTypesFromOverride); | |
172 } | |
173 classes.forEach(visit); | |
174 } | |
175 | |
176 void _reanalyzeVar(Map<Source, RestrictedResolverVisitor> visitors, | |
177 VariableDeclaration variable) { | |
178 if (variable.initializer == null) return; | |
179 var visitor = visitors[(variable.root as CompilationUnit).element.source]; | |
180 visitor.reanalyzeInitializer(variable); | |
181 } | |
182 | |
183 static bool _isInstanceField(f) => | |
184 f is FieldDeclaration && !f.isStatic && !f.fields.isConst; | |
185 | |
186 /// Attempts to infer the type on [field] from overridden fields or getters if | |
187 /// a type was not specified. If no type could be inferred, but it contains an | |
188 /// initializer, we add it to [pending] so we can try to infer it using the | |
189 /// initializer type instead. | |
190 void _inferFieldTypeFromOverride( | |
191 FieldDeclaration field, Set<VariableDeclaration> pending) { | |
192 var variables = field.fields; | |
193 for (var variable in variables.variables) { | |
194 var varElement = variable.element as FieldElement; | |
195 if (!varElement.type.isDynamic || variables.type != null) continue; | |
196 var getter = varElement.getter; | |
197 // Note: type will be null only when there are no overrides. When some | |
198 // override's type was not specified and couldn't be inferred, the type | |
199 // here will be dynamic. | |
200 var enclosingElement = varElement.enclosingElement; | |
201 var type = searchTypeFor(enclosingElement.type, getter); | |
202 | |
203 // Infer from the RHS when there are no overrides. | |
204 if (type == null) { | |
205 if (variable.initializer != null) pending.add(variable); | |
206 continue; | |
207 } | |
208 | |
209 // When field is final and overridden getter is dynamic, we can infer from | |
210 // the RHS without breaking subtyping rules (return type is covariant). | |
211 if (type.returnType.isDynamic) { | |
212 if (variables.isFinal && variable.initializer != null) { | |
213 pending.add(variable); | |
214 } | |
215 continue; | |
216 } | |
217 | |
218 // Use type from the override. | |
219 var newType = type.returnType; | |
220 varElement.type = newType; | |
221 varElement.getter.returnType = newType; | |
222 if (!varElement.isFinal) varElement.setter.parameters[0].type = newType; | |
223 } | |
224 } | |
225 | |
226 void _inferMethodTypesFromOverride(MethodDeclaration method) { | |
227 var methodElement = method.element; | |
228 if (methodElement is! MethodElement && | |
229 methodElement is! PropertyAccessorElement) return; | |
230 | |
231 var enclosingElement = methodElement.enclosingElement as ClassElement; | |
232 FunctionType type = null; | |
233 | |
234 // Infer the return type if omitted | |
235 if (methodElement.returnType.isDynamic && method.returnType == null) { | |
236 type = searchTypeFor(enclosingElement.type, methodElement); | |
237 if (type == null) return; | |
238 if (!type.returnType.isDynamic) { | |
239 methodElement.returnType = type.returnType; | |
240 } | |
241 } | |
242 | |
243 // Infer parameter types if omitted | |
244 if (method.parameters == null) return; | |
245 var parameters = method.parameters.parameters; | |
246 var length = parameters.length; | |
247 for (int i = 0; i < length; ++i) { | |
248 var parameter = parameters[i]; | |
249 if (parameter is DefaultFormalParameter) parameter = parameter.parameter; | |
250 if (parameter is SimpleFormalParameter && parameter.type == null) { | |
251 type = type ?? searchTypeFor(enclosingElement.type, methodElement); | |
252 if (type == null) return; | |
253 if (type.parameters.length > i && !type.parameters[i].type.isDynamic) { | |
254 parameter.element.type = type.parameters[i].type; | |
255 } | |
256 } | |
257 } | |
258 } | |
259 | |
260 void _inferVariableFromInitializer(Iterable<VariableDeclaration> variables) { | |
261 for (var variable in variables) { | |
262 var declaration = variable.parent as VariableDeclarationList; | |
263 // Only infer on variables that don't have any declared type. | |
264 if (declaration.type != null) continue; | |
265 var initializer = variable.initializer; | |
266 if (initializer == null) continue; | |
267 var type = initializer.staticType; | |
268 if (type == null || type.isDynamic || type.isBottom) continue; | |
269 var element = variable.element as PropertyInducingElement; | |
270 // Note: it's ok to update the type here, since initializer.staticType | |
271 // is already computed for all declarations in the library cycle. The | |
272 // new types will only be propagated on a second run of the | |
273 // ResolverVisitor. | |
274 element.type = type; | |
275 element.getter.returnType = type; | |
276 if (!element.isFinal && !element.isConst) { | |
277 element.setter.parameters[0].type = type; | |
278 } | |
279 } | |
280 } | |
281 } | |
282 | |
283 /// Extracts the [VariableElement]s used in an initializer expression. | |
284 class _VarExtractor extends RecursiveAstVisitor { | |
285 final elements = <VariableElement>[]; | |
286 visitSimpleIdentifier(SimpleIdentifier node) { | |
287 var e = node.staticElement; | |
288 if (e is PropertyAccessorElement) elements.add(e.variable); | |
289 } | |
290 | |
291 static List<VariableElement> extract(Expression initializer) { | |
292 if (initializer == null) return const []; | |
293 var extractor = new _VarExtractor(); | |
294 initializer.accept(extractor); | |
295 return extractor.elements; | |
296 } | |
297 } | |
298 | |
299 /// Overrides the default [ResolverVisitor] to support type inference in | |
300 /// [LibraryResolverWithInference] above. | |
301 /// | |
302 /// Before inference, this visitor is used to resolve top-levels, classes, and | |
303 /// fields, but nothing within method bodies. After inference, this visitor is | |
304 /// used again to step into method bodies and complete resolution as a second | |
305 /// phase. | |
306 class RestrictedResolverVisitor extends ResolverVisitor { | |
307 final TypeProvider _typeProvider; | |
308 | |
309 /// Whether to skip resolution within method bodies. | |
310 bool skipMethodBodies = true; | |
311 | |
312 /// State of the resolver at the point a field or variable was declared. | |
313 final _stateAtDeclaration = <AstNode, _ResolverState>{}; | |
314 | |
315 /// Internal tracking of whether a node was skipped while visiting, for | |
316 /// example, if it contained a function expression with a function body. | |
317 bool _nodeWasSkipped = false; | |
318 | |
319 /// Internal state, whether we are revisiting an initializer, so we minimize | |
320 /// the work being done elsewhere. | |
321 bool _revisiting = false; | |
322 | |
323 /// Initializers that have been visited, reanalyzed, and for which no node was | |
324 /// internally skipped. These initializers are fully resolved and don't need | |
325 /// to be re-resolved on a sunsequent pass. | |
326 final _visitedInitializers = new Set<VariableDeclaration>(); | |
327 | |
328 RestrictedResolverVisitor(Library library, Source source, | |
329 TypeProvider typeProvider, StrongModeOptions options) | |
330 : _typeProvider = typeProvider, | |
331 super( | |
332 library.libraryElement, source, typeProvider, library.errorListener, | |
333 nameScope: library.libraryScope, | |
334 inheritanceManager: library.inheritanceManager, | |
335 typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor); | |
336 | |
337 reanalyzeInitializer(VariableDeclaration variable) { | |
338 try { | |
339 _revisiting = true; | |
340 _nodeWasSkipped = false; | |
341 var node = variable.parent.parent; | |
342 var oldState; | |
343 var state = _stateAtDeclaration[node]; | |
344 if (state != null) { | |
345 oldState = new _ResolverState(this); | |
346 state.restore(this); | |
347 if (node is FieldDeclaration) { | |
348 var cls = node.parent as ClassDeclaration; | |
349 enclosingClass = cls.element; | |
350 } | |
351 } | |
352 visitNode(variable.initializer); | |
353 if (!_nodeWasSkipped) _visitedInitializers.add(variable); | |
354 if (oldState != null) oldState.restore(this); | |
355 } finally { | |
356 _revisiting = false; | |
357 } | |
358 } | |
359 | |
360 @override | |
361 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
362 _stateAtDeclaration[node] = new _ResolverState(this); | |
363 return super.visitTopLevelVariableDeclaration(node); | |
364 } | |
365 | |
366 @override | |
367 Object visitFieldDeclaration(FieldDeclaration node) { | |
368 _stateAtDeclaration[node] = new _ResolverState(this); | |
369 return super.visitFieldDeclaration(node); | |
370 } | |
371 | |
372 Object visitVariableDeclaration(VariableDeclaration node) { | |
373 var state = new _ResolverState(this); | |
374 try { | |
375 if (_revisiting) { | |
376 _stateAtDeclaration[node].restore(this); | |
377 } else { | |
378 _stateAtDeclaration[node] = state; | |
379 } | |
380 return super.visitVariableDeclaration(node); | |
381 } finally { | |
382 state.restore(this); | |
383 } | |
384 } | |
385 | |
386 @override | |
387 Object visitNode(AstNode node) { | |
388 if (skipMethodBodies && node is FunctionBody) { | |
389 _nodeWasSkipped = true; | |
390 return null; | |
391 } | |
392 if (_visitedInitializers.contains(node)) return null; | |
393 assert(node is! Statement || !skipMethodBodies); | |
394 return super.visitNode(node); | |
395 } | |
396 | |
397 @override | |
398 Object visitMethodDeclaration(MethodDeclaration node) { | |
399 if (skipMethodBodies) { | |
400 node.accept(elementResolver); | |
401 node.accept(typeAnalyzer); | |
402 return null; | |
403 } else { | |
404 return super.visitMethodDeclaration(node); | |
405 } | |
406 } | |
407 | |
408 @override | |
409 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
410 if (skipMethodBodies) { | |
411 node.accept(elementResolver); | |
412 node.accept(typeAnalyzer); | |
413 return null; | |
414 } else { | |
415 return super.visitFunctionDeclaration(node); | |
416 } | |
417 } | |
418 | |
419 @override | |
420 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
421 if (skipMethodBodies) { | |
422 node.accept(elementResolver); | |
423 node.accept(typeAnalyzer); | |
424 return null; | |
425 } else { | |
426 return super.visitConstructorDeclaration(node); | |
427 } | |
428 } | |
429 | |
430 @override | |
431 visitFieldFormalParameter(FieldFormalParameter node) { | |
432 // Ensure the field formal parameter's type is updated after inference. | |
433 // Normally this happens during TypeResolver, but that's before we've done | |
434 // inference on the field type. | |
435 var element = node.element; | |
436 if (element is FieldFormalParameterElement) { | |
437 if (element.type.isDynamic) { | |
438 // In malformed code, there may be no actual field. | |
439 if (element.field != null) { | |
440 element.type = element.field.type; | |
441 } | |
442 } | |
443 } | |
444 super.visitFieldFormalParameter(node); | |
445 } | |
446 } | |
447 | |
448 /// Internal state of the resolver, stored so we can reanalyze portions of the | |
449 /// AST quickly, without recomputing everything from the top. | |
450 class _ResolverState { | |
451 final TypePromotionManager_TypePromoteScope promotionScope; | |
452 final TypeOverrideManager_TypeOverrideScope overrideScope; | |
453 final Scope nameScope; | |
454 | |
455 _ResolverState(ResolverVisitor visitor) | |
456 : promotionScope = visitor.promoteManager.currentScope, | |
457 overrideScope = visitor.overrideManager.currentScope, | |
458 nameScope = visitor.nameScope; | |
459 | |
460 void restore(ResolverVisitor visitor) { | |
461 visitor.promoteManager.currentScope = promotionScope; | |
462 visitor.overrideManager.currentScope = overrideScope; | |
463 visitor.nameScope = nameScope; | |
464 } | |
465 } | |
466 | |
467 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter | |
468 /// in the restricted type system and to infer types for untyped local | |
469 /// variables. | |
470 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { | |
471 final TypeProvider _typeProvider; | |
472 Map<String, DartType> _objectMembers; | |
473 | |
474 RestrictedStaticTypeAnalyzer(ResolverVisitor r) | |
475 : _typeProvider = r.typeProvider, | |
476 super(r) { | |
477 _objectMembers = getObjectMemberMap(_typeProvider); | |
478 } | |
479 | |
480 static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r); | |
481 | |
482 @override // to infer type from initializers | |
483 visitVariableDeclaration(VariableDeclaration node) { | |
484 _inferType(node); | |
485 return super.visitVariableDeclaration(node); | |
486 } | |
487 | |
488 /// Infer the type of a variable based on the initializer's type. | |
489 void _inferType(VariableDeclaration node) { | |
490 var initializer = node.initializer; | |
491 if (initializer == null) return; | |
492 | |
493 var declaredType = (node.parent as VariableDeclarationList).type; | |
494 if (declaredType != null) return; | |
495 var element = node.element; | |
496 if (element is! LocalVariableElement) return; | |
497 if (element.type != _typeProvider.dynamicType) return; | |
498 | |
499 var type = initializer.staticType; | |
500 if (type == null || type == _typeProvider.bottomType) return; | |
501 element.type = type; | |
502 if (element is PropertyInducingElement) { | |
503 element.getter.returnType = type; | |
504 if (!element.isFinal && !element.isConst) { | |
505 element.setter.parameters[0].type = type; | |
506 } | |
507 } | |
508 } | |
509 | |
510 // TODO(vsm): Use leafp's matchType here? | |
511 DartType _findIteratedType(InterfaceType type) { | |
512 if (type.element == _typeProvider.iterableType.element) { | |
513 var typeArguments = type.typeArguments; | |
514 assert(typeArguments.length == 1); | |
515 return typeArguments[0]; | |
516 } | |
517 | |
518 if (type == _typeProvider.objectType) return null; | |
519 | |
520 var result = _findIteratedType(type.superclass); | |
521 if (result != null) return result; | |
522 | |
523 for (final parent in type.interfaces) { | |
524 result = _findIteratedType(parent); | |
525 if (result != null) return result; | |
526 } | |
527 | |
528 for (final parent in type.mixins) { | |
529 result = _findIteratedType(parent); | |
530 if (result != null) return result; | |
531 } | |
532 | |
533 return null; | |
534 } | |
535 | |
536 @override | |
537 visitDeclaredIdentifier(DeclaredIdentifier node) { | |
538 super.visitDeclaredIdentifier(node); | |
539 if (node.type != null) return; | |
540 | |
541 var parent = node.parent as ForEachStatement; | |
542 var expr = parent.iterable; | |
543 var element = node.element as LocalVariableElementImpl; | |
544 var exprType = expr.staticType; | |
545 if (exprType is InterfaceType) { | |
546 var iteratedType = _findIteratedType(exprType); | |
547 if (iteratedType != null) { | |
548 element.type = iteratedType; | |
549 } | |
550 } | |
551 } | |
552 | |
553 bool _isSealed(DartType t) { | |
554 return _typeProvider.nonSubtypableTypes.contains(t); | |
555 } | |
556 | |
557 List<List> _genericList = null; | |
558 | |
559 DartType _matchGeneric(MethodInvocation node, Element element) { | |
560 var e = node.methodName.staticElement; | |
561 | |
562 if (_genericList == null) { | |
563 var minmax = (DartType tx, DartType ty) => (tx == ty && | |
564 (tx == _typeProvider.intType || tx == _typeProvider.doubleType)) | |
565 ? tx | |
566 : null; | |
567 | |
568 var map = (DartType tx) => (tx is FunctionType) | |
569 ? _typeProvider.iterableType.substitute4([tx.returnType]) | |
570 : null; | |
571 | |
572 // TODO(vsm): LUB? | |
573 var fold = (DartType tx, DartType ty) => | |
574 (ty is FunctionType && tx == ty.returnType) ? tx : null; | |
575 | |
576 // TODO(vsm): Flatten? | |
577 var then = (DartType tx) => (tx is FunctionType) | |
578 ? _typeProvider.futureType.substitute4([tx.returnType]) | |
579 : null; | |
580 | |
581 var wait = (DartType tx) { | |
582 // Iterable<Future<T>> -> Future<List<T>> | |
583 var futureType = _findIteratedType(tx); | |
584 if (futureType.element.type != _typeProvider.futureType) return null; | |
585 var typeArguments = futureType.typeArguments; | |
586 if (typeArguments.length != 1) return null; | |
587 var baseType = typeArguments[0]; | |
588 if (baseType.isDynamic) return null; | |
589 return _typeProvider.futureType.substitute4([ | |
590 _typeProvider.listType.substitute4([baseType]) | |
591 ]); | |
592 }; | |
593 | |
594 _genericList = [ | |
595 // Top-level methods | |
596 ['dart:math', 'max', 2, minmax], | |
597 ['dart:math', 'min', 2, minmax], | |
598 // Static methods | |
599 [_typeProvider.futureType, 'wait', 1, wait], | |
600 // Instance methods | |
601 [_typeProvider.iterableDynamicType, 'map', 1, map], | |
602 [_typeProvider.iterableDynamicType, 'fold', 2, fold], | |
603 [_typeProvider.futureDynamicType, 'then', 1, then], | |
604 ]; | |
605 } | |
606 | |
607 var targetType = node.target?.staticType; | |
608 var arguments = node.argumentList.arguments; | |
609 | |
610 for (var generic in _genericList) { | |
611 if (e?.name == generic[1]) { | |
612 if ((generic[0] is String && | |
613 element?.library.source.uri.toString() == generic[0]) || | |
614 (generic[0] is DartType && | |
615 targetType != null && | |
616 targetType.isSubtypeOf(generic[0]))) { | |
617 if (arguments.length == generic[2]) { | |
618 return Function.apply( | |
619 generic[3], arguments.map((arg) => arg.staticType).toList()); | |
620 } | |
621 } | |
622 } | |
623 } | |
624 | |
625 return null; | |
626 } | |
627 | |
628 @override // to propagate types to identifiers | |
629 visitMethodInvocation(MethodInvocation node) { | |
630 // TODO(jmesserly): we rely on having a staticType propagated to the | |
631 // methodName identifier. This shouldn't be necessary for method calls, so | |
632 // analyzer doesn't do it by default. Conceptually what we're doing here | |
633 // is asking for a tear off. We need this until we can fix #132, and rely | |
634 // on `node.staticElement == null` instead of `rules.isDynamicCall(node)`. | |
635 visitSimpleIdentifier(node.methodName); | |
636 | |
637 super.visitMethodInvocation(node); | |
638 | |
639 // Search for Object methods. | |
640 var name = node.methodName.name; | |
641 if (node.staticType.isDynamic && | |
642 _objectMembers.containsKey(name) && | |
643 isDynamicTarget(node.target)) { | |
644 var type = _objectMembers[name]; | |
645 if (type is FunctionType && | |
646 type.parameters.isEmpty && | |
647 node.argumentList.arguments.isEmpty) { | |
648 node.methodName.staticType = type; | |
649 // Only infer the type of the overall expression if we have an exact | |
650 // type - e.g., a sealed type. Otherwise, it may be too strict. | |
651 if (_isSealed(type.returnType)) { | |
652 node.staticType = type.returnType; | |
653 } | |
654 } | |
655 } | |
656 | |
657 var e = node.methodName.staticElement; | |
658 if (isInlineJS(e)) { | |
659 // Fix types for JS builtin calls. | |
660 // | |
661 // This code was taken from analyzer. It's not super sophisticated: | |
662 // only looks for the type name in dart:core, so we just copy it here. | |
663 // | |
664 // TODO(jmesserly): we'll likely need something that can handle a wider | |
665 // variety of types, especially when we get to JS interop. | |
666 var args = node.argumentList.arguments; | |
667 var first = args.isNotEmpty ? args.first : null; | |
668 if (first is SimpleStringLiteral) { | |
669 var typeStr = first.stringValue; | |
670 if (typeStr == '-dynamic') { | |
671 node.staticType = _typeProvider.bottomType; | |
672 } else { | |
673 var coreLib = _typeProvider.objectType.element.library; | |
674 var classElem = coreLib.getType(typeStr); | |
675 if (classElem != null) { | |
676 var type = fillDynamicTypeArgs(classElem.type, _typeProvider); | |
677 node.staticType = type; | |
678 } | |
679 } | |
680 } | |
681 } | |
682 | |
683 // Pretend dart:math's min and max are generic: | |
684 // | |
685 // T min<T extends num>(T x, T y); | |
686 // | |
687 // and infer T. In practice, this just means if the type of x and y are | |
688 // both double or both int, we treat that as the return type. | |
689 // | |
690 // The Dart spec has similar treatment for binary operations on numbers. | |
691 // | |
692 // TODO(jmesserly): remove this when we have a fix for | |
693 // https://github.com/dart-lang/dev_compiler/issues/28 | |
694 var inferred = _matchGeneric(node, e); | |
695 // TODO(vsm): If the inferred type is not a subtype, should we use a GLB ins
tead? | |
696 if (inferred != null && inferred.isSubtypeOf(node.staticType)) { | |
697 node.staticType = inferred; | |
698 } | |
699 } | |
700 | |
701 void _inferObjectAccess( | |
702 Expression node, Expression target, SimpleIdentifier id) { | |
703 // Search for Object accesses. | |
704 var name = id.name; | |
705 if (node.staticType.isDynamic && | |
706 _objectMembers.containsKey(name) && | |
707 isDynamicTarget(target)) { | |
708 var type = _objectMembers[name]; | |
709 id.staticType = type; | |
710 // Only infer the type of the overall expression if we have an exact | |
711 // type - e.g., a sealed type. Otherwise, it may be too strict. | |
712 if (_isSealed(type)) { | |
713 node.staticType = type; | |
714 } | |
715 } | |
716 } | |
717 | |
718 @override | |
719 visitPropertyAccess(PropertyAccess node) { | |
720 super.visitPropertyAccess(node); | |
721 | |
722 _inferObjectAccess(node, node.target, node.propertyName); | |
723 } | |
724 | |
725 @override | |
726 visitPrefixedIdentifier(PrefixedIdentifier node) { | |
727 super.visitPrefixedIdentifier(node); | |
728 | |
729 _inferObjectAccess(node, node.prefix, node.identifier); | |
730 } | |
731 | |
732 @override | |
733 visitConditionalExpression(ConditionalExpression node) { | |
734 // TODO(vsm): The static type of a conditional should be the LUB of the | |
735 // then and else expressions. The analyzer appears to compute dynamic when | |
736 // one or the other is the null literal. Remove this fix once the | |
737 // corresponding analyzer bug is fixed: | |
738 // https://code.google.com/p/dart/issues/detail?id=22854 | |
739 super.visitConditionalExpression(node); | |
740 if (node.staticType.isDynamic) { | |
741 var thenExpr = node.thenExpression; | |
742 var elseExpr = node.elseExpression; | |
743 if (thenExpr.staticType.isBottom) { | |
744 node.staticType = elseExpr.staticType; | |
745 } else if (elseExpr.staticType.isBottom) { | |
746 node.staticType = thenExpr.staticType; | |
747 } | |
748 } | |
749 } | |
750 | |
751 // Review note: no longer need to override visitFunctionExpression, this is | |
752 // handled by the analyzer internally. | |
753 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? | |
754 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression | |
755 // type in a (...) => expr or just the written type? | |
756 | |
757 } | |
OLD | NEW |