Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: lib/src/checker/resolver.dart

Issue 1406983003: Remove the checker and corresponding dead code (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/checker/checker.dart ('k') | lib/src/checker/rules.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « lib/src/checker/checker.dart ('k') | lib/src/checker/rules.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698