Index: lib/src/checker/resolver.dart |
diff --git a/lib/src/checker/resolver.dart b/lib/src/checker/resolver.dart |
deleted file mode 100644 |
index 1e141cf0157a4f78009184448e041f64db6baf0a..0000000000000000000000000000000000000000 |
--- a/lib/src/checker/resolver.dart |
+++ /dev/null |
@@ -1,757 +0,0 @@ |
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-/// Encapsulates how to invoke the analyzer resolver and overrides how it |
-/// computes types on expressions to use our restricted set of types. |
-library dev_compiler.src.checker.resolver; |
- |
-import 'package:analyzer/analyzer.dart'; |
-import 'package:analyzer/src/generated/ast.dart'; |
-import 'package:analyzer/src/generated/element.dart'; |
-import 'package:analyzer/src/generated/resolver.dart'; |
-import 'package:analyzer/src/generated/source.dart' show Source; |
-import 'package:analyzer/src/generated/source_io.dart'; |
-import 'package:analyzer/src/generated/static_type_analyzer.dart'; |
-import 'package:analyzer/src/generated/utilities_collection.dart' |
- show DirectedGraph; |
-import 'package:logging/logging.dart' as logger; |
- |
-import '../../strong_mode.dart' show StrongModeOptions; |
-import '../utils.dart'; |
-import 'rules.dart'; |
- |
-final _log = new logger.Logger('dev_compiler.src.resolver'); |
- |
-/// A [LibraryResolver] that performs inference on top-levels and fields based |
-/// on the value of the initializer, and on fields and methods based on |
-/// overridden members in super classes. |
-class LibraryResolverWithInference extends LibraryResolver { |
- final StrongModeOptions _options; |
- |
- LibraryResolverWithInference(context, this._options) : super(context); |
- |
- @override |
- void resolveReferencesAndTypes() { |
- _resolveVariableReferences(); |
- |
- // Run resolution in two stages, skipping method bodies first, so we can run |
- // type-inference before we fully analyze methods. |
- var visitors = _createVisitors(); |
- _resolveEverything(visitors); |
- _runInference(visitors); |
- |
- visitors.values.forEach((v) => v.skipMethodBodies = false); |
- _resolveEverything(visitors); |
- } |
- |
- // Note: this was split from _resolveReferencesAndTypesInLibrary so we do it |
- // only once. |
- void _resolveVariableReferences() { |
- for (Library library in resolvedLibraries) { |
- for (Source source in library.compilationUnitSources) { |
- library.getAST(source).accept(new VariableResolverVisitor( |
- library.libraryElement, source, typeProvider, library.errorListener, |
- nameScope: library.libraryScope)); |
- } |
- } |
- } |
- |
- // Note: this was split from _resolveReferencesAndTypesInLibrary so we can do |
- // resolution in pieces. |
- Map<Source, RestrictedResolverVisitor> _createVisitors() { |
- var visitors = <Source, RestrictedResolverVisitor>{}; |
- for (Library library in resolvedLibraries) { |
- for (Source source in library.compilationUnitSources) { |
- var visitor = new RestrictedResolverVisitor( |
- library, source, typeProvider, _options); |
- visitors[source] = visitor; |
- } |
- } |
- return visitors; |
- } |
- |
- /// Runs the resolver on the entire library cycle. |
- void _resolveEverything(Map<Source, RestrictedResolverVisitor> visitors) { |
- for (Library library in resolvedLibraries) { |
- for (Source source in library.compilationUnitSources) { |
- library.getAST(source).accept(visitors[source]); |
- } |
- } |
- } |
- |
- _runInference(Map<Source, RestrictedResolverVisitor> visitors) { |
- var globalsAndStatics = <VariableDeclaration>[]; |
- var classes = <ClassDeclaration>[]; |
- |
- // Extract top-level members that are const, statics, or classes. |
- for (Library library in resolvedLibraries) { |
- for (Source source in library.compilationUnitSources) { |
- CompilationUnit ast = library.getAST(source); |
- for (var declaration in ast.declarations) { |
- if (declaration is TopLevelVariableDeclaration) { |
- globalsAndStatics.addAll(declaration.variables.variables); |
- } else if (declaration is ClassDeclaration) { |
- classes.add(declaration); |
- for (var member in declaration.members) { |
- if (member is FieldDeclaration && |
- (member.fields.isConst || member.isStatic)) { |
- globalsAndStatics.addAll(member.fields.variables); |
- } |
- } |
- } |
- } |
- } |
- } |
- _inferGlobalsAndStatics(globalsAndStatics, visitors); |
- _inferInstanceFields(classes, visitors); |
- } |
- |
- _inferGlobalsAndStatics(List<VariableDeclaration> globalsAndStatics, |
- Map<Source, RestrictedResolverVisitor> visitors) { |
- var elementToDeclaration = {}; |
- for (var c in globalsAndStatics) { |
- elementToDeclaration[c.element] = c; |
- } |
- var constGraph = new DirectedGraph<VariableDeclaration>(); |
- globalsAndStatics.forEach(constGraph.addNode); |
- for (var c in globalsAndStatics) { |
- for (var e in _VarExtractor.extract(c.initializer)) { |
- // Note: declaration is null for variables that come from other strongly |
- // connected components. |
- var declaration = elementToDeclaration[e]; |
- if (declaration != null) constGraph.addEdge(c, declaration); |
- } |
- } |
- |
- for (var component in constGraph.computeTopologicalSort()) { |
- component.forEach((v) => _reanalyzeVar(visitors, v)); |
- _inferVariableFromInitializer(component); |
- } |
- } |
- |
- _inferInstanceFields(List<ClassDeclaration> classes, |
- Map<Source, RestrictedResolverVisitor> visitors) { |
- // First propagate what was inferred from globals to all instance fields. |
- |
- // TODO(sigmund): also do a fine-grain propagation between fields. We want |
- // infer-by-override to take precedence, so we would have to include |
- // classes in the dependency graph and ensure that fields depend on their |
- // class, and classes depend on superclasses. |
- classes |
- .expand((c) => c.members.where(_isInstanceField)) |
- .expand((f) => f.fields.variables) |
- .forEach((v) => _reanalyzeVar(visitors, v)); |
- |
- // Track types in this strongly connected component, ensure we visit |
- // supertypes before subtypes. |
- var typeToDeclaration = <InterfaceType, ClassDeclaration>{}; |
- classes.forEach((c) => typeToDeclaration[c.element.type] = c); |
- var seen = new Set<InterfaceType>(); |
- visit(ClassDeclaration cls) { |
- var element = cls.element; |
- var type = element.type; |
- if (seen.contains(type)) return; |
- seen.add(type); |
- for (var supertype in element.allSupertypes) { |
- var supertypeClass = typeToDeclaration[supertype]; |
- if (supertypeClass != null) visit(supertypeClass); |
- } |
- |
- // Infer field types from overrides first, otherwise from initializers. |
- var pending = new Set<VariableDeclaration>(); |
- cls.members |
- .where(_isInstanceField) |
- .forEach((f) => _inferFieldTypeFromOverride(f, pending)); |
- if (pending.isNotEmpty) _inferVariableFromInitializer(pending); |
- |
- // Infer return-types and param-types from overrides |
- cls.members |
- .where((m) => m is MethodDeclaration && !m.isStatic) |
- .forEach(_inferMethodTypesFromOverride); |
- } |
- classes.forEach(visit); |
- } |
- |
- void _reanalyzeVar(Map<Source, RestrictedResolverVisitor> visitors, |
- VariableDeclaration variable) { |
- if (variable.initializer == null) return; |
- var visitor = visitors[(variable.root as CompilationUnit).element.source]; |
- visitor.reanalyzeInitializer(variable); |
- } |
- |
- static bool _isInstanceField(f) => |
- f is FieldDeclaration && !f.isStatic && !f.fields.isConst; |
- |
- /// Attempts to infer the type on [field] from overridden fields or getters if |
- /// a type was not specified. If no type could be inferred, but it contains an |
- /// initializer, we add it to [pending] so we can try to infer it using the |
- /// initializer type instead. |
- void _inferFieldTypeFromOverride( |
- FieldDeclaration field, Set<VariableDeclaration> pending) { |
- var variables = field.fields; |
- for (var variable in variables.variables) { |
- var varElement = variable.element as FieldElement; |
- if (!varElement.type.isDynamic || variables.type != null) continue; |
- var getter = varElement.getter; |
- // Note: type will be null only when there are no overrides. When some |
- // override's type was not specified and couldn't be inferred, the type |
- // here will be dynamic. |
- var enclosingElement = varElement.enclosingElement; |
- var type = searchTypeFor(enclosingElement.type, getter); |
- |
- // Infer from the RHS when there are no overrides. |
- if (type == null) { |
- if (variable.initializer != null) pending.add(variable); |
- continue; |
- } |
- |
- // When field is final and overridden getter is dynamic, we can infer from |
- // the RHS without breaking subtyping rules (return type is covariant). |
- if (type.returnType.isDynamic) { |
- if (variables.isFinal && variable.initializer != null) { |
- pending.add(variable); |
- } |
- continue; |
- } |
- |
- // Use type from the override. |
- var newType = type.returnType; |
- varElement.type = newType; |
- varElement.getter.returnType = newType; |
- if (!varElement.isFinal) varElement.setter.parameters[0].type = newType; |
- } |
- } |
- |
- void _inferMethodTypesFromOverride(MethodDeclaration method) { |
- var methodElement = method.element; |
- if (methodElement is! MethodElement && |
- methodElement is! PropertyAccessorElement) return; |
- |
- var enclosingElement = methodElement.enclosingElement as ClassElement; |
- FunctionType type = null; |
- |
- // Infer the return type if omitted |
- if (methodElement.returnType.isDynamic && method.returnType == null) { |
- type = searchTypeFor(enclosingElement.type, methodElement); |
- if (type == null) return; |
- if (!type.returnType.isDynamic) { |
- methodElement.returnType = type.returnType; |
- } |
- } |
- |
- // Infer parameter types if omitted |
- if (method.parameters == null) return; |
- var parameters = method.parameters.parameters; |
- var length = parameters.length; |
- for (int i = 0; i < length; ++i) { |
- var parameter = parameters[i]; |
- if (parameter is DefaultFormalParameter) parameter = parameter.parameter; |
- if (parameter is SimpleFormalParameter && parameter.type == null) { |
- type = type ?? searchTypeFor(enclosingElement.type, methodElement); |
- if (type == null) return; |
- if (type.parameters.length > i && !type.parameters[i].type.isDynamic) { |
- parameter.element.type = type.parameters[i].type; |
- } |
- } |
- } |
- } |
- |
- void _inferVariableFromInitializer(Iterable<VariableDeclaration> variables) { |
- for (var variable in variables) { |
- var declaration = variable.parent as VariableDeclarationList; |
- // Only infer on variables that don't have any declared type. |
- if (declaration.type != null) continue; |
- var initializer = variable.initializer; |
- if (initializer == null) continue; |
- var type = initializer.staticType; |
- if (type == null || type.isDynamic || type.isBottom) continue; |
- var element = variable.element as PropertyInducingElement; |
- // Note: it's ok to update the type here, since initializer.staticType |
- // is already computed for all declarations in the library cycle. The |
- // new types will only be propagated on a second run of the |
- // ResolverVisitor. |
- element.type = type; |
- element.getter.returnType = type; |
- if (!element.isFinal && !element.isConst) { |
- element.setter.parameters[0].type = type; |
- } |
- } |
- } |
-} |
- |
-/// Extracts the [VariableElement]s used in an initializer expression. |
-class _VarExtractor extends RecursiveAstVisitor { |
- final elements = <VariableElement>[]; |
- visitSimpleIdentifier(SimpleIdentifier node) { |
- var e = node.staticElement; |
- if (e is PropertyAccessorElement) elements.add(e.variable); |
- } |
- |
- static List<VariableElement> extract(Expression initializer) { |
- if (initializer == null) return const []; |
- var extractor = new _VarExtractor(); |
- initializer.accept(extractor); |
- return extractor.elements; |
- } |
-} |
- |
-/// Overrides the default [ResolverVisitor] to support type inference in |
-/// [LibraryResolverWithInference] above. |
-/// |
-/// Before inference, this visitor is used to resolve top-levels, classes, and |
-/// fields, but nothing within method bodies. After inference, this visitor is |
-/// used again to step into method bodies and complete resolution as a second |
-/// phase. |
-class RestrictedResolverVisitor extends ResolverVisitor { |
- final TypeProvider _typeProvider; |
- |
- /// Whether to skip resolution within method bodies. |
- bool skipMethodBodies = true; |
- |
- /// State of the resolver at the point a field or variable was declared. |
- final _stateAtDeclaration = <AstNode, _ResolverState>{}; |
- |
- /// Internal tracking of whether a node was skipped while visiting, for |
- /// example, if it contained a function expression with a function body. |
- bool _nodeWasSkipped = false; |
- |
- /// Internal state, whether we are revisiting an initializer, so we minimize |
- /// the work being done elsewhere. |
- bool _revisiting = false; |
- |
- /// Initializers that have been visited, reanalyzed, and for which no node was |
- /// internally skipped. These initializers are fully resolved and don't need |
- /// to be re-resolved on a sunsequent pass. |
- final _visitedInitializers = new Set<VariableDeclaration>(); |
- |
- RestrictedResolverVisitor(Library library, Source source, |
- TypeProvider typeProvider, StrongModeOptions options) |
- : _typeProvider = typeProvider, |
- super( |
- library.libraryElement, source, typeProvider, library.errorListener, |
- nameScope: library.libraryScope, |
- inheritanceManager: library.inheritanceManager, |
- typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor); |
- |
- reanalyzeInitializer(VariableDeclaration variable) { |
- try { |
- _revisiting = true; |
- _nodeWasSkipped = false; |
- var node = variable.parent.parent; |
- var oldState; |
- var state = _stateAtDeclaration[node]; |
- if (state != null) { |
- oldState = new _ResolverState(this); |
- state.restore(this); |
- if (node is FieldDeclaration) { |
- var cls = node.parent as ClassDeclaration; |
- enclosingClass = cls.element; |
- } |
- } |
- visitNode(variable.initializer); |
- if (!_nodeWasSkipped) _visitedInitializers.add(variable); |
- if (oldState != null) oldState.restore(this); |
- } finally { |
- _revisiting = false; |
- } |
- } |
- |
- @override |
- Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
- _stateAtDeclaration[node] = new _ResolverState(this); |
- return super.visitTopLevelVariableDeclaration(node); |
- } |
- |
- @override |
- Object visitFieldDeclaration(FieldDeclaration node) { |
- _stateAtDeclaration[node] = new _ResolverState(this); |
- return super.visitFieldDeclaration(node); |
- } |
- |
- Object visitVariableDeclaration(VariableDeclaration node) { |
- var state = new _ResolverState(this); |
- try { |
- if (_revisiting) { |
- _stateAtDeclaration[node].restore(this); |
- } else { |
- _stateAtDeclaration[node] = state; |
- } |
- return super.visitVariableDeclaration(node); |
- } finally { |
- state.restore(this); |
- } |
- } |
- |
- @override |
- Object visitNode(AstNode node) { |
- if (skipMethodBodies && node is FunctionBody) { |
- _nodeWasSkipped = true; |
- return null; |
- } |
- if (_visitedInitializers.contains(node)) return null; |
- assert(node is! Statement || !skipMethodBodies); |
- return super.visitNode(node); |
- } |
- |
- @override |
- Object visitMethodDeclaration(MethodDeclaration node) { |
- if (skipMethodBodies) { |
- node.accept(elementResolver); |
- node.accept(typeAnalyzer); |
- return null; |
- } else { |
- return super.visitMethodDeclaration(node); |
- } |
- } |
- |
- @override |
- Object visitFunctionDeclaration(FunctionDeclaration node) { |
- if (skipMethodBodies) { |
- node.accept(elementResolver); |
- node.accept(typeAnalyzer); |
- return null; |
- } else { |
- return super.visitFunctionDeclaration(node); |
- } |
- } |
- |
- @override |
- Object visitConstructorDeclaration(ConstructorDeclaration node) { |
- if (skipMethodBodies) { |
- node.accept(elementResolver); |
- node.accept(typeAnalyzer); |
- return null; |
- } else { |
- return super.visitConstructorDeclaration(node); |
- } |
- } |
- |
- @override |
- visitFieldFormalParameter(FieldFormalParameter node) { |
- // Ensure the field formal parameter's type is updated after inference. |
- // Normally this happens during TypeResolver, but that's before we've done |
- // inference on the field type. |
- var element = node.element; |
- if (element is FieldFormalParameterElement) { |
- if (element.type.isDynamic) { |
- // In malformed code, there may be no actual field. |
- if (element.field != null) { |
- element.type = element.field.type; |
- } |
- } |
- } |
- super.visitFieldFormalParameter(node); |
- } |
-} |
- |
-/// Internal state of the resolver, stored so we can reanalyze portions of the |
-/// AST quickly, without recomputing everything from the top. |
-class _ResolverState { |
- final TypePromotionManager_TypePromoteScope promotionScope; |
- final TypeOverrideManager_TypeOverrideScope overrideScope; |
- final Scope nameScope; |
- |
- _ResolverState(ResolverVisitor visitor) |
- : promotionScope = visitor.promoteManager.currentScope, |
- overrideScope = visitor.overrideManager.currentScope, |
- nameScope = visitor.nameScope; |
- |
- void restore(ResolverVisitor visitor) { |
- visitor.promoteManager.currentScope = promotionScope; |
- visitor.overrideManager.currentScope = overrideScope; |
- visitor.nameScope = nameScope; |
- } |
-} |
- |
-/// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter |
-/// in the restricted type system and to infer types for untyped local |
-/// variables. |
-class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
- final TypeProvider _typeProvider; |
- Map<String, DartType> _objectMembers; |
- |
- RestrictedStaticTypeAnalyzer(ResolverVisitor r) |
- : _typeProvider = r.typeProvider, |
- super(r) { |
- _objectMembers = getObjectMemberMap(_typeProvider); |
- } |
- |
- static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r); |
- |
- @override // to infer type from initializers |
- visitVariableDeclaration(VariableDeclaration node) { |
- _inferType(node); |
- return super.visitVariableDeclaration(node); |
- } |
- |
- /// Infer the type of a variable based on the initializer's type. |
- void _inferType(VariableDeclaration node) { |
- var initializer = node.initializer; |
- if (initializer == null) return; |
- |
- var declaredType = (node.parent as VariableDeclarationList).type; |
- if (declaredType != null) return; |
- var element = node.element; |
- if (element is! LocalVariableElement) return; |
- if (element.type != _typeProvider.dynamicType) return; |
- |
- var type = initializer.staticType; |
- if (type == null || type == _typeProvider.bottomType) return; |
- element.type = type; |
- if (element is PropertyInducingElement) { |
- element.getter.returnType = type; |
- if (!element.isFinal && !element.isConst) { |
- element.setter.parameters[0].type = type; |
- } |
- } |
- } |
- |
- // TODO(vsm): Use leafp's matchType here? |
- DartType _findIteratedType(InterfaceType type) { |
- if (type.element == _typeProvider.iterableType.element) { |
- var typeArguments = type.typeArguments; |
- assert(typeArguments.length == 1); |
- return typeArguments[0]; |
- } |
- |
- if (type == _typeProvider.objectType) return null; |
- |
- var result = _findIteratedType(type.superclass); |
- if (result != null) return result; |
- |
- for (final parent in type.interfaces) { |
- result = _findIteratedType(parent); |
- if (result != null) return result; |
- } |
- |
- for (final parent in type.mixins) { |
- result = _findIteratedType(parent); |
- if (result != null) return result; |
- } |
- |
- return null; |
- } |
- |
- @override |
- visitDeclaredIdentifier(DeclaredIdentifier node) { |
- super.visitDeclaredIdentifier(node); |
- if (node.type != null) return; |
- |
- var parent = node.parent as ForEachStatement; |
- var expr = parent.iterable; |
- var element = node.element as LocalVariableElementImpl; |
- var exprType = expr.staticType; |
- if (exprType is InterfaceType) { |
- var iteratedType = _findIteratedType(exprType); |
- if (iteratedType != null) { |
- element.type = iteratedType; |
- } |
- } |
- } |
- |
- bool _isSealed(DartType t) { |
- return _typeProvider.nonSubtypableTypes.contains(t); |
- } |
- |
- List<List> _genericList = null; |
- |
- DartType _matchGeneric(MethodInvocation node, Element element) { |
- var e = node.methodName.staticElement; |
- |
- if (_genericList == null) { |
- var minmax = (DartType tx, DartType ty) => (tx == ty && |
- (tx == _typeProvider.intType || tx == _typeProvider.doubleType)) |
- ? tx |
- : null; |
- |
- var map = (DartType tx) => (tx is FunctionType) |
- ? _typeProvider.iterableType.substitute4([tx.returnType]) |
- : null; |
- |
- // TODO(vsm): LUB? |
- var fold = (DartType tx, DartType ty) => |
- (ty is FunctionType && tx == ty.returnType) ? tx : null; |
- |
- // TODO(vsm): Flatten? |
- var then = (DartType tx) => (tx is FunctionType) |
- ? _typeProvider.futureType.substitute4([tx.returnType]) |
- : null; |
- |
- var wait = (DartType tx) { |
- // Iterable<Future<T>> -> Future<List<T>> |
- var futureType = _findIteratedType(tx); |
- if (futureType.element.type != _typeProvider.futureType) return null; |
- var typeArguments = futureType.typeArguments; |
- if (typeArguments.length != 1) return null; |
- var baseType = typeArguments[0]; |
- if (baseType.isDynamic) return null; |
- return _typeProvider.futureType.substitute4([ |
- _typeProvider.listType.substitute4([baseType]) |
- ]); |
- }; |
- |
- _genericList = [ |
- // Top-level methods |
- ['dart:math', 'max', 2, minmax], |
- ['dart:math', 'min', 2, minmax], |
- // Static methods |
- [_typeProvider.futureType, 'wait', 1, wait], |
- // Instance methods |
- [_typeProvider.iterableDynamicType, 'map', 1, map], |
- [_typeProvider.iterableDynamicType, 'fold', 2, fold], |
- [_typeProvider.futureDynamicType, 'then', 1, then], |
- ]; |
- } |
- |
- var targetType = node.target?.staticType; |
- var arguments = node.argumentList.arguments; |
- |
- for (var generic in _genericList) { |
- if (e?.name == generic[1]) { |
- if ((generic[0] is String && |
- element?.library.source.uri.toString() == generic[0]) || |
- (generic[0] is DartType && |
- targetType != null && |
- targetType.isSubtypeOf(generic[0]))) { |
- if (arguments.length == generic[2]) { |
- return Function.apply( |
- generic[3], arguments.map((arg) => arg.staticType).toList()); |
- } |
- } |
- } |
- } |
- |
- return null; |
- } |
- |
- @override // to propagate types to identifiers |
- visitMethodInvocation(MethodInvocation node) { |
- // TODO(jmesserly): we rely on having a staticType propagated to the |
- // methodName identifier. This shouldn't be necessary for method calls, so |
- // analyzer doesn't do it by default. Conceptually what we're doing here |
- // is asking for a tear off. We need this until we can fix #132, and rely |
- // on `node.staticElement == null` instead of `rules.isDynamicCall(node)`. |
- visitSimpleIdentifier(node.methodName); |
- |
- super.visitMethodInvocation(node); |
- |
- // Search for Object methods. |
- var name = node.methodName.name; |
- if (node.staticType.isDynamic && |
- _objectMembers.containsKey(name) && |
- isDynamicTarget(node.target)) { |
- var type = _objectMembers[name]; |
- if (type is FunctionType && |
- type.parameters.isEmpty && |
- node.argumentList.arguments.isEmpty) { |
- node.methodName.staticType = type; |
- // Only infer the type of the overall expression if we have an exact |
- // type - e.g., a sealed type. Otherwise, it may be too strict. |
- if (_isSealed(type.returnType)) { |
- node.staticType = type.returnType; |
- } |
- } |
- } |
- |
- var e = node.methodName.staticElement; |
- if (isInlineJS(e)) { |
- // Fix types for JS builtin calls. |
- // |
- // This code was taken from analyzer. It's not super sophisticated: |
- // only looks for the type name in dart:core, so we just copy it here. |
- // |
- // TODO(jmesserly): we'll likely need something that can handle a wider |
- // variety of types, especially when we get to JS interop. |
- var args = node.argumentList.arguments; |
- var first = args.isNotEmpty ? args.first : null; |
- if (first is SimpleStringLiteral) { |
- var typeStr = first.stringValue; |
- if (typeStr == '-dynamic') { |
- node.staticType = _typeProvider.bottomType; |
- } else { |
- var coreLib = _typeProvider.objectType.element.library; |
- var classElem = coreLib.getType(typeStr); |
- if (classElem != null) { |
- var type = fillDynamicTypeArgs(classElem.type, _typeProvider); |
- node.staticType = type; |
- } |
- } |
- } |
- } |
- |
- // Pretend dart:math's min and max are generic: |
- // |
- // T min<T extends num>(T x, T y); |
- // |
- // and infer T. In practice, this just means if the type of x and y are |
- // both double or both int, we treat that as the return type. |
- // |
- // The Dart spec has similar treatment for binary operations on numbers. |
- // |
- // TODO(jmesserly): remove this when we have a fix for |
- // https://github.com/dart-lang/dev_compiler/issues/28 |
- var inferred = _matchGeneric(node, e); |
- // TODO(vsm): If the inferred type is not a subtype, should we use a GLB instead? |
- if (inferred != null && inferred.isSubtypeOf(node.staticType)) { |
- node.staticType = inferred; |
- } |
- } |
- |
- void _inferObjectAccess( |
- Expression node, Expression target, SimpleIdentifier id) { |
- // Search for Object accesses. |
- var name = id.name; |
- if (node.staticType.isDynamic && |
- _objectMembers.containsKey(name) && |
- isDynamicTarget(target)) { |
- var type = _objectMembers[name]; |
- id.staticType = type; |
- // Only infer the type of the overall expression if we have an exact |
- // type - e.g., a sealed type. Otherwise, it may be too strict. |
- if (_isSealed(type)) { |
- node.staticType = type; |
- } |
- } |
- } |
- |
- @override |
- visitPropertyAccess(PropertyAccess node) { |
- super.visitPropertyAccess(node); |
- |
- _inferObjectAccess(node, node.target, node.propertyName); |
- } |
- |
- @override |
- visitPrefixedIdentifier(PrefixedIdentifier node) { |
- super.visitPrefixedIdentifier(node); |
- |
- _inferObjectAccess(node, node.prefix, node.identifier); |
- } |
- |
- @override |
- visitConditionalExpression(ConditionalExpression node) { |
- // TODO(vsm): The static type of a conditional should be the LUB of the |
- // then and else expressions. The analyzer appears to compute dynamic when |
- // one or the other is the null literal. Remove this fix once the |
- // corresponding analyzer bug is fixed: |
- // https://code.google.com/p/dart/issues/detail?id=22854 |
- super.visitConditionalExpression(node); |
- if (node.staticType.isDynamic) { |
- var thenExpr = node.thenExpression; |
- var elseExpr = node.elseExpression; |
- if (thenExpr.staticType.isBottom) { |
- node.staticType = elseExpr.staticType; |
- } else if (elseExpr.staticType.isBottom) { |
- node.staticType = thenExpr.staticType; |
- } |
- } |
- } |
- |
- // Review note: no longer need to override visitFunctionExpression, this is |
- // handled by the analyzer internally. |
- // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? |
- // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression |
- // type in a (...) => expr or just the written type? |
- |
-} |