| 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?
|
| -
|
| -}
|
|
|