Linter Demo Errors: 0Warnings: 2File: /home/fstrocco/Dart/dart/benchmark/analyzer/lib/src/generated/static_type_analyzer.dart // Copyright (c) 2014, 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. library engine.resolver.static_type_analyzer; import 'dart:collection'; import 'package:analyzer/src/generated/scanner.dart'; import 'ast.dart'; import 'element.dart'; import 'java_engine.dart'; import 'resolver.dart'; import 'scanner.dart' as sc; /** * Instances of the class `StaticTypeAnalyzer` perform two type-related tasks. First, they * compute the static type of every expression. Second, they look for any static type errors or * warnings that might need to be generated. The requirements for the type analyzer are: * * * Every element that refers to types should be fully populated. * * Every node representing an expression should be resolved to the Type of the expression. * */ class StaticTypeAnalyzer extends SimpleAstVisitor { /** * A table mapping HTML tag names to the names of the classes (in 'dart:html') that implement * those tags. */ static HashMap _HTML_ELEMENT_TO_CLASS_MAP = _createHtmlTagToClassMap(); /** * The resolver driving the resolution and type analysis. */ final ResolverVisitor _resolver; /** * The object providing access to the types defined by the language. */ TypeProvider _typeProvider; /** * The type representing the type 'dynamic'. */ DartType _dynamicType; /** * The type representing the class containing the nodes being analyzed, * or `null` if the nodes are not within a class. */ InterfaceType thisType; /** * The object keeping track of which elements have had their types overridden. */ TypeOverrideManager _overrideManager; /** * The object keeping track of which elements have had their types promoted. */ TypePromotionManager _promoteManager; /** * A table mapping [ExecutableElement]s to their propagated return types. */ HashMap _propagatedReturnTypes = new HashMap(); /** * Initialize a newly created type analyzer. * * @param resolver the resolver driving this participant */ StaticTypeAnalyzer(this._resolver) { _typeProvider = _resolver.typeProvider; _dynamicType = _typeProvider.dynamicType; _overrideManager = _resolver.overrideManager; _promoteManager = _resolver.promoteManager; } /** * The Dart Language Specification, 12.5: The static type of a string literal is * `String`. */ @override Object visitAdjacentStrings(AdjacentStrings node) { _recordStaticType(node, _typeProvider.stringType); return null; } /** * The Dart Language Specification, 12.32: ... the cast expression e as T ... * * It is a static warning if T does not denote a type available in the current lexical * scope. * * The static type of a cast expression e as T is T. */ @override Object visitAsExpression(AsExpression node) { _recordStaticType(node, _getType(node.type)); return null; } /** * The Dart Language Specification, 12.18: ... an assignment a of the form v * = e ... * * It is a static type warning if the static type of e may not be assigned to the static * type of v. * * The static type of the expression v = e is the static type of e. * * ... an assignment of the form C.v = e ... * * It is a static type warning if the static type of e may not be assigned to the static * type of C.v. * * The static type of the expression C.v = e is the static type of e. * * ... an assignment of the form e1.v = e2 ... * * Let T be the static type of e1. It is a static type warning if * T does not have an accessible instance setter named v=. It is a static type * warning if the static type of e2 may not be assigned to T. * * The static type of the expression e1.v = e2 is the static type of * e2. * * ... an assignment of the form e1[e2] = e3 ... * * The static type of the expression e1[e2] = e3 is the * static type of e3. * * A compound assignment of the form v op= e is equivalent to v = v op e. A compound * assignment of the form C.v op= e is equivalent to C.v = C.v op e. A compound * assignment of the form e1.v op= e2 is equivalent to ((x) => x.v * = x.v op e2)(e1) where x is a variable that is not used in * e2. A compound assignment of the form e1[e2] op= * e3 is equivalent to ((a, i) => a[i] = a[i] op e3)(e1, * e2) where a and i are a variables that are not used in * e3. */ @override Object visitAssignmentExpression(AssignmentExpression node) { sc.TokenType operator = node.operator.type; if (operator == sc.TokenType.EQ) { Expression rightHandSide = node.rightHandSide; DartType staticType = _getStaticType(rightHandSide); _recordStaticType(node, staticType); DartType overrideType = staticType; DartType propagatedType = rightHandSide.propagatedType; if (propagatedType != null) { _resolver.recordPropagatedTypeIfBetter(node, propagatedType); overrideType = propagatedType; } _resolver.overrideExpression(node.leftHandSide, overrideType, true); } else if (operator == sc.TokenType.QUESTION_QUESTION_EQ) { // The static type of a compound assignment using ??= is the least upper // bound of the static types of the LHS and RHS. _analyzeLeastUpperBound(node, node.leftHandSide, node.rightHandSide); return null; } else { ExecutableElement staticMethodElement = node.staticElement; DartType staticType = _computeStaticReturnType(staticMethodElement); _recordStaticType(node, staticType); MethodElement propagatedMethodElement = node.propagatedElement; if (!identical(propagatedMethodElement, staticMethodElement)) { DartType propagatedType = _computeStaticReturnType(propagatedMethodElement); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); } } return null; } /** * The Dart Language Specification, 16.29 (Await Expressions): * * The static type of [the expression "await e"] is flatten(T) where T is * the static type of e. */ @override Object visitAwaitExpression(AwaitExpression node) { DartType staticExpressionType = _getStaticType(node.expression); if (staticExpressionType == null) { // TODO(brianwilkerson) Determine whether this can still happen. staticExpressionType = _dynamicType; } DartType staticType = flattenFutures(_typeProvider, staticExpressionType); _recordStaticType(node, staticType); DartType propagatedExpressionType = node.expression.propagatedType; DartType propagatedType = flattenFutures(_typeProvider, propagatedExpressionType); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); return null; } /** * The Dart Language Specification, 12.20: The static type of a logical boolean * expression is `bool`. * * The Dart Language Specification, 12.21:A bitwise expression of the form * e1 op e2 is equivalent to the method invocation * e1.op(e2). A bitwise expression of the form super op * e2 is equivalent to the method invocation * super.op(e2). * * The Dart Language Specification, 12.22: The static type of an equality expression * is `bool`. * * The Dart Language Specification, 12.23: A relational expression of the form * e1 op e2 is equivalent to the method invocation * e1.op(e2). A relational expression of the form super op * e2 is equivalent to the method invocation * super.op(e2). * * The Dart Language Specification, 12.24: A shift expression of the form * e1 op e2 is equivalent to the method invocation * e1.op(e2). A shift expression of the form super op * e2 is equivalent to the method invocation * super.op(e2). * * The Dart Language Specification, 12.25: An additive expression of the form * e1 op e2 is equivalent to the method invocation * e1.op(e2). An additive expression of the form super op * e2 is equivalent to the method invocation * super.op(e2). * * The Dart Language Specification, 12.26: A multiplicative expression of the form * e1 op e2 is equivalent to the method invocation * e1.op(e2). A multiplicative expression of the form super op * e2 is equivalent to the method invocation * super.op(e2). */ @override Object visitBinaryExpression(BinaryExpression node) { if (node.operator.type == TokenType.QUESTION_QUESTION) { // Evaluation of an if-null expresion e of the form e1 ?? e2 is // equivalent to the evaluation of the expression // ((x) => x == null ? e2 : x)(e1). The static type of e is the least // upper bound of the static type of e1 and the static type of e2. _analyzeLeastUpperBound(node, node.leftOperand, node.rightOperand); return null; } ExecutableElement staticMethodElement = node.staticElement; DartType staticType = _computeStaticReturnType(staticMethodElement); staticType = _refineBinaryExpressionType(node, staticType); _recordStaticType(node, staticType); MethodElement propagatedMethodElement = node.propagatedElement; if (!identical(propagatedMethodElement, staticMethodElement)) { DartType propagatedType = _computeStaticReturnType(propagatedMethodElement); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); } return null; } /** * The Dart Language Specification, 12.4: The static type of a boolean literal is * bool. */ @override Object visitBooleanLiteral(BooleanLiteral node) { _recordStaticType(node, _typeProvider.boolType); return null; } /** * The Dart Language Specification, 12.15.2: A cascaded method invocation expression * of the form e..suffix is equivalent to the expression (t) {t.suffix; return * t;}(e). */ @override Object visitCascadeExpression(CascadeExpression node) { _recordStaticType(node, _getStaticType(node.target)); _resolver.recordPropagatedTypeIfBetter(node, node.target.propagatedType); return null; } /** * The Dart Language Specification, 12.19: ... a conditional expression c of * the form e1 ? e2 : e3 ... * * It is a static type warning if the type of e1 may not be assigned to `bool`. * * The static type of c is the least upper bound of the static type of e2 * and the static type of e3. */ @override Object visitConditionalExpression(ConditionalExpression node) { _analyzeLeastUpperBound(node, node.thenExpression, node.elseExpression); return null; } /** * The Dart Language Specification, 12.3: The static type of a literal double is * double. */ @override Object visitDoubleLiteral(DoubleLiteral node) { _recordStaticType(node, _typeProvider.doubleType); return null; } @override Object visitFunctionDeclaration(FunctionDeclaration node) { FunctionExpression function = node.functionExpression; ExecutableElementImpl functionElement = node.element as ExecutableElementImpl; functionElement.returnType = _computeStaticReturnTypeOfFunctionDeclaration(node); _recordPropagatedTypeOfFunction(functionElement, function.body); _recordStaticType(function, functionElement.type); return null; } /** * The Dart Language Specification, 12.9: The static type of a function literal of the * form (T1 a1, …, Tn an, [Tn+1 * xn+1 = d1, …, Tn+k xn+k = dk]) => e is * (T1, …, Tn, [Tn+1 xn+1, …, Tn+k * xn+k]) → T0, where T0 is the static type of * e. In any case where Ti, 1 <= i <= n, is not specified, it is * considered to have been specified as dynamic. * * The static type of a function literal of the form (T1 a1, …, * Tn an, {Tn+1 xn+1 : d1, …, Tn+k * xn+k : dk}) => e is (T1, …, Tn, {Tn+1 * xn+1, …, Tn+k xn+k}) → T0, where * T0 is the static type of e. In any case where Ti, 1 * <= i <= n, is not specified, it is considered to have been specified as dynamic. * * The static type of a function literal of the form (T1 a1, …, * Tn an, [Tn+1 xn+1 = d1, …, Tn+k * xn+k = dk]) {s} is (T1, …, Tn, [Tn+1 * xn+1, …, Tn+k xn+k]) → dynamic. In any case * where Ti, 1 <= i <= n, is not specified, it is considered to have been * specified as dynamic. * * The static type of a function literal of the form (T1 a1, …, * Tn an, {Tn+1 xn+1 : d1, …, Tn+k * xn+k : dk}) {s} is (T1, …, Tn, {Tn+1 * xn+1, …, Tn+k xn+k}) → dynamic. In any case * where Ti, 1 <= i <= n, is not specified, it is considered to have been * specified as dynamic. */ @override Object visitFunctionExpression(FunctionExpression node) { if (node.parent is FunctionDeclaration) { // The function type will be resolved and set when we visit the parent // node. return null; } ExecutableElementImpl functionElement = node.element as ExecutableElementImpl; functionElement.returnType = _computeStaticReturnTypeOfFunctionExpression(node); _recordPropagatedTypeOfFunction(functionElement, node.body); _recordStaticType(node, node.element.type); return null; } /** * The Dart Language Specification, 12.14.4: A function expression invocation i * has the form ef(a1, …, an, xn+1: * an+1, …, xn+k: an+k), where ef is * an expression. * * It is a static type warning if the static type F of ef may not be * assigned to a function type. * * If F is not a function type, the static type of i is dynamic. Otherwise the * static type of i is the declared return type of F. */ @override Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { DartType functionStaticType = _getStaticType(node.function); DartType staticType; if (functionStaticType is FunctionType) { staticType = functionStaticType.returnType; } else { staticType = _dynamicType; } _recordStaticType(node, staticType); DartType functionPropagatedType = node.function.propagatedType; if (functionPropagatedType is FunctionType) { DartType propagatedType = functionPropagatedType.returnType; _resolver.recordPropagatedTypeIfBetter(node, propagatedType); } return null; } /** * The Dart Language Specification, 12.29: An assignable expression of the form * e1[e2] is evaluated as a method invocation of the operator method * [] on e1 with argument e2. */ @override Object visitIndexExpression(IndexExpression node) { if (node.inSetterContext()) { ExecutableElement staticMethodElement = node.staticElement; DartType staticType = _computeArgumentType(staticMethodElement); _recordStaticType(node, staticType); MethodElement propagatedMethodElement = node.propagatedElement; if (!identical(propagatedMethodElement, staticMethodElement)) { DartType propagatedType = _computeArgumentType(propagatedMethodElement); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); } } else { ExecutableElement staticMethodElement = node.staticElement; DartType staticType = _computeStaticReturnType(staticMethodElement); _recordStaticType(node, staticType); MethodElement propagatedMethodElement = node.propagatedElement; if (!identical(propagatedMethodElement, staticMethodElement)) { DartType propagatedType = _computeStaticReturnType(propagatedMethodElement); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); } } return null; } /** * The Dart Language Specification, 12.11.1: The static type of a new expression of * either the form new T.id(a1, …, an) or the form new * T(a1, …, an) is T. * * The Dart Language Specification, 12.11.2: The static type of a constant object * expression of either the form const T.id(a1, …, an) or the * form const T(a1, …, an) is T. */ @override Object visitInstanceCreationExpression(InstanceCreationExpression node) { _recordStaticType(node, node.constructorName.type.type); ConstructorElement element = node.staticElement; if (element != null && "Element" == element.enclosingElement.name) { LibraryElement library = element.library; if (_isHtmlLibrary(library)) { String constructorName = element.name; if ("tag" == constructorName) { DartType returnType = _getFirstArgumentAsTypeWithMap( library, node.argumentList, _HTML_ELEMENT_TO_CLASS_MAP); _resolver.recordPropagatedTypeIfBetter(node, returnType); } else { DartType returnType = _getElementNameAsType( library, constructorName, _HTML_ELEMENT_TO_CLASS_MAP); _resolver.recordPropagatedTypeIfBetter(node, returnType); } } } return null; } /** * The Dart Language Specification, 12.3: The static type of an integer literal is * `int`. */ @override Object visitIntegerLiteral(IntegerLiteral node) { _recordStaticType(node, _typeProvider.intType); return null; } /** * The Dart Language Specification, 12.31: It is a static warning if T does not * denote a type available in the current lexical scope. * * The static type of an is-expression is `bool`. */ @override Object visitIsExpression(IsExpression node) { _recordStaticType(node, _typeProvider.boolType); return null; } /** * The Dart Language Specification, 12.6: The static type of a list literal of the * form const <E>[e1, …, en] or the form * <E>[e1, …, en] is `List<E>`. The static * type a list literal of the form const [e1, …, en] or * the form [e1, …, en] is `List<dynamic>` * . */ @override Object visitListLiteral(ListLiteral node) { DartType staticType = _dynamicType; TypeArgumentList typeArguments = node.typeArguments; if (typeArguments != null) { NodeList arguments = typeArguments.arguments; if (arguments != null && arguments.length == 1) { TypeName argumentTypeName = arguments[0]; DartType argumentType = _getType(argumentTypeName); if (argumentType != null) { staticType = argumentType; } } } _recordStaticType( node, _typeProvider.listType.substitute4([staticType])); return null; } /** * The Dart Language Specification, 12.7: The static type of a map literal of the form * const <K, V> {k1:e1, …, * kn:en} or the form <K, V> {k1:e1, * …, kn:en} is `Map<K, V>`. The static type a map * literal of the form const {k1:e1, …, * kn:en} or the form {k1:e1, …, * kn:en} is `Map<dynamic, dynamic>`. * * It is a compile-time error if the first type argument to a map literal is not * String. */ @override Object visitMapLiteral(MapLiteral node) { DartType staticKeyType = _dynamicType; DartType staticValueType = _dynamicType; TypeArgumentList typeArguments = node.typeArguments; if (typeArguments != null) { NodeList arguments = typeArguments.arguments; if (arguments != null && arguments.length == 2) { TypeName entryKeyTypeName = arguments[0]; DartType entryKeyType = _getType(entryKeyTypeName); if (entryKeyType != null) { staticKeyType = entryKeyType; } TypeName entryValueTypeName = arguments[1]; DartType entryValueType = _getType(entryValueTypeName); if (entryValueType != null) { staticValueType = entryValueType; } } } _recordStaticType(node, _typeProvider.mapType .substitute4([staticKeyType, staticValueType])); return null; } /** * The Dart Language Specification, 12.15.1: An ordinary method invocation i * has the form o.m(a1, …, an, xn+1: an+1, * …, xn+k: an+k). * * Let T be the static type of o. It is a static type warning if T does not * have an accessible instance member named m. If T.m exists, it is a static warning * if the type F of T.m may not be assigned to a function type. * * If T.m does not exist, or if F is not a function type, the static type of * i is dynamic. Otherwise the static type of i is the declared return type of * F. * * The Dart Language Specification, 11.15.3: A static method invocation i has * the form C.m(a1, …, an, xn+1: an+1, * …, xn+k: an+k). * * It is a static type warning if the type F of C.m may not be assigned to a * function type. * * If F is not a function type, or if C.m does not exist, the static type of i is * dynamic. Otherwise the static type of i is the declared return type of * F. * * The Dart Language Specification, 11.15.4: A super method invocation i has * the form super.m(a1, …, an, xn+1: an+1, * …, xn+k: an+k). * * It is a static type warning if S does not have an accessible instance member named m. If * S.m exists, it is a static warning if the type F of S.m may not be * assigned to a function type. * * If S.m does not exist, or if F is not a function type, the static type of * i is dynamic. Otherwise the static type of i is the declared return type of * F. */ @override Object visitMethodInvocation(MethodInvocation node) { SimpleIdentifier methodNameNode = node.methodName; Element staticMethodElement = methodNameNode.staticElement; // Record types of the variable invoked as a function. if (staticMethodElement is VariableElement) { VariableElement variable = staticMethodElement; DartType staticType = variable.type; _recordStaticType(methodNameNode, staticType); DartType propagatedType = _overrideManager.getType(variable); _resolver.recordPropagatedTypeIfBetter(methodNameNode, propagatedType); } // Record static return type of the static element. DartType staticStaticType = _computeStaticReturnType(staticMethodElement); _recordStaticType(node, staticStaticType); // Record propagated return type of the static element. DartType staticPropagatedType = _computePropagatedReturnType(staticMethodElement); _resolver.recordPropagatedTypeIfBetter(node, staticPropagatedType); // Check for special cases. bool needPropagatedType = true; String methodName = methodNameNode.name; if (methodName == "then") { Expression target = node.realTarget; if (target != null) { DartType targetType = target.bestType; if (_isAsyncFutureType(targetType)) { // Future.then(closure) return type is: // 1) the returned Future type, if the closure returns a Future; // 2) Future, if the closure returns a value. NodeList arguments = node.argumentList.arguments; if (arguments.length == 1) { // TODO(brianwilkerson) Handle the case where both arguments are // provided. Expression closureArg = arguments[0]; if (closureArg is FunctionExpression) { FunctionExpression closureExpr = closureArg; DartType returnType = _computePropagatedReturnType(closureExpr.element); if (returnType != null) { // prepare the type of the returned Future InterfaceTypeImpl newFutureType; if (_isAsyncFutureType(returnType)) { newFutureType = returnType as InterfaceTypeImpl; } else { InterfaceType futureType = targetType as InterfaceType; newFutureType = new InterfaceTypeImpl.con1(futureType.element); newFutureType.typeArguments = [returnType]; } // set the 'then' invocation type _recordPropagatedType(node, newFutureType); needPropagatedType = false; return null; } } } } } } else if (methodName == "\$dom_createEvent") { Expression target = node.realTarget; if (target != null) { DartType targetType = target.bestType; if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { LibraryElement library = targetType.element.library; if (_isHtmlLibrary(library)) { DartType returnType = _getFirstArgumentAsType(library, node.argumentList); if (returnType != null) { _recordPropagatedType(node, returnType); needPropagatedType = false; } } } } } else if (methodName == "query") { Expression target = node.realTarget; if (target == null) { Element methodElement = methodNameNode.bestElement; if (methodElement != null) { LibraryElement library = methodElement.library; if (_isHtmlLibrary(library)) { DartType returnType = _getFirstArgumentAsQuery(library, node.argumentList); if (returnType != null) { _recordPropagatedType(node, returnType); needPropagatedType = false; } } } } else { DartType targetType = target.bestType; if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { LibraryElement library = targetType.element.library; if (_isHtmlLibrary(library)) { DartType returnType = _getFirstArgumentAsQuery(library, node.argumentList); if (returnType != null) { _recordPropagatedType(node, returnType); needPropagatedType = false; } } } } } else if (methodName == "\$dom_createElement") { Expression target = node.realTarget; if (target != null) { DartType targetType = target.bestType; if (targetType is InterfaceType && (targetType.name == "HtmlDocument" || targetType.name == "Document")) { LibraryElement library = targetType.element.library; if (_isHtmlLibrary(library)) { DartType returnType = _getFirstArgumentAsQuery(library, node.argumentList); if (returnType != null) { _recordPropagatedType(node, returnType); needPropagatedType = false; } } } } } else if (methodName == "JS") { DartType returnType = _getFirstArgumentAsType( _typeProvider.objectType.element.library, node.argumentList); if (returnType != null) { _recordPropagatedType(node, returnType); needPropagatedType = false; } } else if (methodName == "getContext") { Expression target = node.realTarget; if (target != null) { DartType targetType = target.bestType; if (targetType is InterfaceType && (targetType.name == "CanvasElement")) { NodeList arguments = node.argumentList.arguments; if (arguments.length == 1) { Expression argument = arguments[0]; if (argument is StringLiteral) { String value = argument.stringValue; if ("2d" == value) { PropertyAccessorElement getter = targetType.element.getGetter("context2D"); if (getter != null) { DartType returnType = getter.returnType; if (returnType != null) { _recordPropagatedType(node, returnType); needPropagatedType = false; } } } } } } } } if (needPropagatedType) { Element propagatedElement = methodNameNode.propagatedElement; // HACK: special case for object methods ([toString]) on dynamic // expressions. More special cases in [visitPrefixedIdentfier]. if (propagatedElement == null) { propagatedElement = _typeProvider.objectType.getMethod(methodNameNode.name); } if (!identical(propagatedElement, staticMethodElement)) { // Record static return type of the propagated element. DartType propagatedStaticType = _computeStaticReturnType(propagatedElement); _resolver.recordPropagatedTypeIfBetter( node, propagatedStaticType, true); // Record propagated return type of the propagated element. DartType propagatedPropagatedType = _computePropagatedReturnType(propagatedElement); _resolver.recordPropagatedTypeIfBetter( node, propagatedPropagatedType, true); } } return null; } @override Object visitNamedExpression(NamedExpression node) { Expression expression = node.expression; _recordStaticType(node, _getStaticType(expression)); _resolver.recordPropagatedTypeIfBetter(node, expression.propagatedType); return null; } /** * The Dart Language Specification, 12.2: The static type of `null` is bottom. * */ @override Object visitNullLiteral(NullLiteral node) { _recordStaticType(node, _typeProvider.bottomType); return null; } @override Object visitParenthesizedExpression(ParenthesizedExpression node) { Expression expression = node.expression; _recordStaticType(node, _getStaticType(expression)); _resolver.recordPropagatedTypeIfBetter(node, expression.propagatedType); return null; } /** * The Dart Language Specification, 12.28: A postfix expression of the form * v++, where v is an identifier, is equivalent to (){var r = v; v = r + 1; * return r}(). * * A postfix expression of the form C.v++ is equivalent to (){var r = C.v; C.v = r + 1; * return r}(). * * A postfix expression of the form e1.v++ is equivalent to (x){var r = x.v; x.v = r + * 1; return r}(e1). * * A postfix expression of the form e1[e2]++ is equivalent to (a, i){var r = a[i]; a[i] * = r + 1; return r}(e1, e2) * * A postfix expression of the form v--, where v is an identifier, is equivalent to * (){var r = v; v = r - 1; return r}(). * * A postfix expression of the form C.v-- is equivalent to (){var r = C.v; C.v = r - 1; * return r}(). * * A postfix expression of the form e1.v-- is equivalent to (x){var r = x.v; x.v = r - * 1; return r}(e1). * * A postfix expression of the form e1[e2]-- is equivalent to (a, i){var r = a[i]; a[i] * = r - 1; return r}(e1, e2) */ @override Object visitPostfixExpression(PostfixExpression node) { Expression operand = node.operand; DartType staticType = _getStaticType(operand); sc.TokenType operator = node.operator.type; if (operator == sc.TokenType.MINUS_MINUS || operator == sc.TokenType.PLUS_PLUS) { DartType intType = _typeProvider.intType; if (identical(_getStaticType(node.operand), intType)) { staticType = intType; } } _recordStaticType(node, staticType); _resolver.recordPropagatedTypeIfBetter(node, operand.propagatedType); return null; } /** * See [visitSimpleIdentifier]. */ @override Object visitPrefixedIdentifier(PrefixedIdentifier node) { SimpleIdentifier prefixedIdentifier = node.identifier; Element staticElement = prefixedIdentifier.staticElement; DartType staticType = _dynamicType; DartType propagatedType = null; if (staticElement is ClassElement) { if (_isNotTypeLiteral(node)) { staticType = staticElement.type; } else { staticType = _typeProvider.typeType; } } else if (staticElement is FunctionTypeAliasElement) { if (_isNotTypeLiteral(node)) { staticType = staticElement.type; } else { staticType = _typeProvider.typeType; } } else if (staticElement is MethodElement) { staticType = staticElement.type; } else if (staticElement is PropertyAccessorElement) { staticType = _getTypeOfProperty(staticElement, node.prefix.staticType); propagatedType = _getPropertyPropagatedType(staticElement, propagatedType); } else if (staticElement is ExecutableElement) { staticType = staticElement.type; } else if (staticElement is TypeParameterElement) { staticType = staticElement.type; } else if (staticElement is VariableElement) { staticType = staticElement.type; } _recordStaticType(prefixedIdentifier, staticType); _recordStaticType(node, staticType); Element propagatedElement = prefixedIdentifier.propagatedElement; // HACK: special case for object getters ([hashCode] and [runtimeType]) on // dynamic expressions. More special cases in [visitMethodInvocation]. if (propagatedElement == null) { propagatedElement = _typeProvider.objectType.getGetter(prefixedIdentifier.name); } if (propagatedElement is ClassElement) { if (_isNotTypeLiteral(node)) { propagatedType = propagatedElement.type; } else { propagatedType = _typeProvider.typeType; } } else if (propagatedElement is FunctionTypeAliasElement) { propagatedType = propagatedElement.type; } else if (propagatedElement is MethodElement) { propagatedType = propagatedElement.type; } else if (propagatedElement is PropertyAccessorElement) { propagatedType = _getTypeOfProperty(propagatedElement, node.prefix.staticType); propagatedType = _getPropertyPropagatedType(propagatedElement, propagatedType); } else if (propagatedElement is ExecutableElement) { propagatedType = propagatedElement.type; } else if (propagatedElement is TypeParameterElement) { propagatedType = propagatedElement.type; } else if (propagatedElement is VariableElement) { propagatedType = propagatedElement.type; } DartType overriddenType = _overrideManager.getType(propagatedElement); if (propagatedType == null || (overriddenType != null && overriddenType.isMoreSpecificThan(propagatedType))) { propagatedType = overriddenType; } _resolver.recordPropagatedTypeIfBetter(prefixedIdentifier, propagatedType); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); return null; } /** * The Dart Language Specification, 12.27: A unary expression u of the form * op e is equivalent to a method invocation expression e.op(). An expression of the * form op super is equivalent to the method invocation super.op(). */ @override Object visitPrefixExpression(PrefixExpression node) { sc.TokenType operator = node.operator.type; if (operator == sc.TokenType.BANG) { _recordStaticType(node, _typeProvider.boolType); } else { // The other cases are equivalent to invoking a method. ExecutableElement staticMethodElement = node.staticElement; DartType staticType = _computeStaticReturnType(staticMethodElement); if (operator == sc.TokenType.MINUS_MINUS || operator == sc.TokenType.PLUS_PLUS) { DartType intType = _typeProvider.intType; if (identical(_getStaticType(node.operand), intType)) { staticType = intType; } } _recordStaticType(node, staticType); MethodElement propagatedMethodElement = node.propagatedElement; if (!identical(propagatedMethodElement, staticMethodElement)) { DartType propagatedType = _computeStaticReturnType(propagatedMethodElement); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); } } return null; } /** * The Dart Language Specification, 12.13: Property extraction allows for a member of * an object to be concisely extracted from the object. If o is an object, and if m * is the name of a method member of o, then * * o.m is defined to be equivalent to: (r1, …, rn, * {p1 : d1, …, pk : dk}){return * o.m(r1, …, rn, p1: p1, …, * pk: pk);} if m has required parameters r1, * …, rn, and named parameters p1 … pk * with defaults d1, …, dk. * * (r1, …, rn, [p1 = d1, …, * pk = dk]){return o.m(r1, …, rn, * p1, …, pk);} if m has required parameters * r1, …, rn, and optional positional parameters * p1 … pk with defaults d1, …, * dk. * Otherwise, if m is the name of a getter member of o (declared implicitly or * explicitly) then o.m evaluates to the result of invoking the getter. * * The Dart Language Specification, 12.17: ... a getter invocation i of the * form e.m ... * * Let T be the static type of e. It is a static type warning if T does not * have a getter named m. * * The static type of i is the declared return type of T.m, if T.m exists; * otherwise the static type of i is dynamic. * * ... a getter invocation i of the form C.m ... * * It is a static warning if there is no class C in the enclosing lexical scope of * i, or if C does not declare, implicitly or explicitly, a getter named m. * * The static type of i is the declared return type of C.m if it exists or dynamic * otherwise. * * ... a top-level getter invocation i of the form m, where m is an * identifier ... * * The static type of i is the declared return type of m. */ @override Object visitPropertyAccess(PropertyAccess node) { SimpleIdentifier propertyName = node.propertyName; Element staticElement = propertyName.staticElement; DartType staticType = _dynamicType; if (staticElement is MethodElement) { staticType = staticElement.type; } else if (staticElement is PropertyAccessorElement) { Expression realTarget = node.realTarget; staticType = _getTypeOfProperty(staticElement, realTarget != null ? _getStaticType(realTarget) : null); } else { // TODO(brianwilkerson) Report this internal error. } _recordStaticType(propertyName, staticType); _recordStaticType(node, staticType); Element propagatedElement = propertyName.propagatedElement; DartType propagatedType = _overrideManager.getType(propagatedElement); if (propagatedElement is MethodElement) { propagatedType = propagatedElement.type; } else if (propagatedElement is PropertyAccessorElement) { Expression realTarget = node.realTarget; propagatedType = _getTypeOfProperty( propagatedElement, realTarget != null ? realTarget.bestType : null); } else { // TODO(brianwilkerson) Report this internal error. } _resolver.recordPropagatedTypeIfBetter(propertyName, propagatedType); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); return null; } /** * The Dart Language Specification, 12.9: The static type of a rethrow expression is * bottom. */ @override Object visitRethrowExpression(RethrowExpression node) { _recordStaticType(node, _typeProvider.bottomType); return null; } /** * The Dart Language Specification, 12.30: Evaluation of an identifier expression * e of the form id proceeds as follows: * * Let d be the innermost declaration in the enclosing lexical scope whose name is * id. If no such declaration exists in the lexical scope, let d be the declaration * of the inherited member named id if it exists. * * If d is a class or type alias T, the value of e is the unique instance * of class `Type` reifying T. * * If d is a type parameter T, then the value of e is the value of the * actual type argument corresponding to T that was passed to the generative constructor * that created the current binding of this. We are assured that this is well defined, because if * we were in a static member the reference to T would be a compile-time error. * * If d is a library variable then: * * If d is of one of the forms var v = ei;, T v = * ei;, final v = ei;, final T v = ei;, and no * value has yet been stored into v then the initializer expression ei is * evaluated. If, during the evaluation of ei, the getter for v is * referenced, a CyclicInitializationError is thrown. If the evaluation succeeded yielding an * object o, let r = o, otherwise let r = null. In any case, r is * stored into v. The value of e is r. * * If d is of one of the forms const v = e; or const T v = e; the result * of the getter is the value of the compile time constant e. Otherwise * * e evaluates to the current binding of id. * * If d is a local variable or formal parameter then e evaluates to the current * binding of id. * * If d is a static method, top level function or local function then e * evaluates to the function defined by d. * * If d is the declaration of a static variable or static getter declared in class * C, then e is equivalent to the getter invocation C.id. * * If d is the declaration of a top level getter, then e is equivalent to the * getter invocation id. * * Otherwise, if e occurs inside a top level or static function (be it function, * method, getter, or setter) or variable initializer, evaluation of e causes a NoSuchMethodError * to be thrown. * * Otherwise e is equivalent to the property extraction this.id. * */ @override Object visitSimpleIdentifier(SimpleIdentifier node) { Element element = node.staticElement; DartType staticType = _dynamicType; if (element is ClassElement) { if (_isNotTypeLiteral(node)) { staticType = element.type; } else { staticType = _typeProvider.typeType; } } else if (element is FunctionTypeAliasElement) { if (_isNotTypeLiteral(node)) { staticType = element.type; } else { staticType = _typeProvider.typeType; } } else if (element is MethodElement) { staticType = element.type; } else if (element is PropertyAccessorElement) { staticType = _getTypeOfProperty(element, null); } else if (element is ExecutableElement) { staticType = element.type; } else if (element is TypeParameterElement) { staticType = _typeProvider.typeType; } else if (element is VariableElement) { VariableElement variable = element; staticType = _promoteManager.getStaticType(variable); } else if (element is PrefixElement) { return null; } else if (element is DynamicElementImpl) { staticType = _typeProvider.typeType; } else { staticType = _dynamicType; } _recordStaticType(node, staticType); // TODO(brianwilkerson) I think we want to repeat the logic above using the // propagated element to get another candidate for the propagated type. DartType propagatedType = _getPropertyPropagatedType(element, null); if (propagatedType == null) { DartType overriddenType = _overrideManager.getType(element); if (propagatedType == null || overriddenType != null && overriddenType.isMoreSpecificThan(propagatedType)) { propagatedType = overriddenType; } } _resolver.recordPropagatedTypeIfBetter(node, propagatedType); return null; } /** * The Dart Language Specification, 12.5: The static type of a string literal is * `String`. */ @override Object visitSimpleStringLiteral(SimpleStringLiteral node) { _recordStaticType(node, _typeProvider.stringType); return null; } /** * The Dart Language Specification, 12.5: The static type of a string literal is * `String`. */ @override Object visitStringInterpolation(StringInterpolation node) { _recordStaticType(node, _typeProvider.stringType); return null; } @override Object visitSuperExpression(SuperExpression node) { if (thisType == null) { // TODO(brianwilkerson) Report this error if it hasn't already been // reported. _recordStaticType(node, _dynamicType); } else { _recordStaticType(node, thisType); } return null; } @override Object visitSymbolLiteral(SymbolLiteral node) { _recordStaticType(node, _typeProvider.symbolType); return null; } /** * The Dart Language Specification, 12.10: The static type of `this` is the * interface of the immediately enclosing class. */ @override Object visitThisExpression(ThisExpression node) { if (thisType == null) { // TODO(brianwilkerson) Report this error if it hasn't already been // reported. _recordStaticType(node, _dynamicType); } else { _recordStaticType(node, thisType); } return null; } /** * The Dart Language Specification, 12.8: The static type of a throw expression is * bottom. */ @override Object visitThrowExpression(ThrowExpression node) { _recordStaticType(node, _typeProvider.bottomType); return null; } @override Object visitVariableDeclaration(VariableDeclaration node) { Expression initializer = node.initializer; if (initializer != null) { DartType rightType = initializer.bestType; SimpleIdentifier name = node.name; _resolver.recordPropagatedTypeIfBetter(name, rightType); VariableElement element = name.staticElement as VariableElement; if (element != null) { _resolver.overrideVariable(element, rightType, true); } } return null; } /** * Set the static (propagated) type of [node] to be the least upper bound * of the static (propagated) types of subexpressions [expr1] and [expr2]. */ void _analyzeLeastUpperBound( Expression node, Expression expr1, Expression expr2) { DartType staticType1 = _getStaticType(expr1); DartType staticType2 = _getStaticType(expr2); if (staticType1 == null) { // TODO(brianwilkerson) Determine whether this can still happen. staticType1 = _dynamicType; } if (staticType2 == null) { // TODO(brianwilkerson) Determine whether this can still happen. staticType2 = _dynamicType; } DartType staticType = staticType1.getLeastUpperBound(staticType2); if (staticType == null) { staticType = _dynamicType; } _recordStaticType(node, staticType); DartType propagatedType1 = expr1.propagatedType; DartType propagatedType2 = expr2.propagatedType; if (propagatedType1 != null || propagatedType2 != null) { if (propagatedType1 == null) { propagatedType1 = staticType1; } if (propagatedType2 == null) { propagatedType2 = staticType2; } DartType propagatedType = propagatedType1.getLeastUpperBound(propagatedType2); _resolver.recordPropagatedTypeIfBetter(node, propagatedType); } } /** * Record that the static type of the given node is the type of the second argument to the method * represented by the given element. * * @param element the element representing the method invoked by the given node */ DartType _computeArgumentType(ExecutableElement element) { if (element != null) { List parameters = element.parameters; if (parameters != null && parameters.length == 2) { return parameters[1].type; } } return _dynamicType; } /** * Compute the propagated return type of the method or function represented by the given element. * * @param element the element representing the method or function invoked by the given node * @return the propagated return type that was computed */ DartType _computePropagatedReturnType(Element element) { if (element is ExecutableElement) { return _propagatedReturnTypes[element]; } return null; } /** * Given a function body, compute the propagated return type of the function. The propagated * return type of functions with a block body is the least upper bound of all * [ReturnStatement] expressions, with an expression body it is the type of the expression. * * @param body the boy of the function whose propagated return type is to be computed * @return the propagated return type that was computed */ DartType _computePropagatedReturnTypeOfFunction(FunctionBody body) { if (body is ExpressionFunctionBody) { ExpressionFunctionBody expressionBody = body; return expressionBody.expression.bestType; } if (body is BlockFunctionBody) { _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction visitor = new _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction(); body.accept(visitor); return visitor.result; } return null; } /** * Compute the static return type of the method or function represented by the given element. * * @param element the element representing the method or function invoked by the given node * @return the static return type that was computed */ DartType _computeStaticReturnType(Element element) { if (element is PropertyAccessorElement) { // // This is a function invocation expression disguised as something else. // We are invoking a getter and then invoking the returned function. // FunctionType propertyType = element.type; if (propertyType != null) { DartType returnType = propertyType.returnType; if (returnType.isDartCoreFunction) { return _dynamicType; } else if (returnType is InterfaceType) { MethodElement callMethod = returnType.lookUpMethod( FunctionElement.CALL_METHOD_NAME, _resolver.definingLibrary); if (callMethod != null) { return callMethod.type.returnType; } } else if (returnType is FunctionType) { DartType innerReturnType = returnType.returnType; if (innerReturnType != null) { return innerReturnType; } } if (returnType != null) { return returnType; } } } else if (element is ExecutableElement) { FunctionType type = element.type; if (type != null) { // TODO(brianwilkerson) Figure out the conditions under which the type // is null. return type.returnType; } } else if (element is VariableElement) { VariableElement variable = element; DartType variableType = _promoteManager.getStaticType(variable); if (variableType is FunctionType) { return variableType.returnType; } } return _dynamicType; } /** * Given a function declaration, compute the return static type of the function. The return type * of functions with a block body is `dynamicType`, with an expression body it is the type * of the expression. * * @param node the function expression whose static return type is to be computed * @return the static return type that was computed */ DartType _computeStaticReturnTypeOfFunctionDeclaration( FunctionDeclaration node) { TypeName returnType = node.returnType; if (returnType == null) { return _dynamicType; } return returnType.type; } /** * Given a function expression, compute the return type of the function. The return type of * functions with a block body is `dynamicType`, with an expression body it is the type of * the expression. * * @param node the function expression whose return type is to be computed * @return the return type that was computed */ DartType _computeStaticReturnTypeOfFunctionExpression( FunctionExpression node) { FunctionBody body = node.body; if (body.isGenerator) { if (body.isAsynchronous) { return _typeProvider.streamDynamicType; } else { return _typeProvider.iterableDynamicType; } } DartType type; if (body is ExpressionFunctionBody) { type = _getStaticType(body.expression); } else { type = _dynamicType; } if (body.isAsynchronous) { return _typeProvider.futureType .substitute4([flattenFutures(_typeProvider, type)]); } else { return type; } } /** * If the given element name can be mapped to the name of a class defined within the given * library, return the type specified by the argument. * * @param library the library in which the specified type would be defined * @param elementName the name of the element for which a type is being sought * @param nameMap an optional map used to map the element name to a type name * @return the type specified by the first argument in the argument list */ DartType _getElementNameAsType(LibraryElement library, String elementName, HashMap nameMap) { if (elementName != null) { if (nameMap != null) { elementName = nameMap[elementName.toLowerCase()]; } ClassElement returnType = library.getType(elementName); if (returnType != null) { return returnType.type; } } return null; } /** * If the given argument list contains at least one argument, and if the argument is a simple * string literal, then parse that argument as a query string and return the type specified by the * argument. * * @param library the library in which the specified type would be defined * @param argumentList the list of arguments from which a type is to be extracted * @return the type specified by the first argument in the argument list */ DartType _getFirstArgumentAsQuery( LibraryElement library, ArgumentList argumentList) { String argumentValue = _getFirstArgumentAsString(argumentList); if (argumentValue != null) { // // If the query has spaces, full parsing is required because it might be: // E[text='warning text'] // if (StringUtilities.indexOf1(argumentValue, 0, 0x20) >= 0) { return null; } // // Otherwise, try to extract the tag based on // http://www.w3.org/TR/CSS2/selector.html. // String tag = argumentValue; tag = StringUtilities.substringBeforeChar(tag, 0x3A); tag = StringUtilities.substringBeforeChar(tag, 0x5B); tag = StringUtilities.substringBeforeChar(tag, 0x2E); tag = StringUtilities.substringBeforeChar(tag, 0x23); tag = _HTML_ELEMENT_TO_CLASS_MAP[tag.toLowerCase()]; ClassElement returnType = library.getType(tag); if (returnType != null) { return returnType.type; } } return null; } /** * If the given argument list contains at least one argument, and if the argument is a simple * string literal, return the String value of the argument. * * @param argumentList the list of arguments from which a string value is to be extracted * @return the string specified by the first argument in the argument list */ String _getFirstArgumentAsString(ArgumentList argumentList) { NodeList arguments = argumentList.arguments; if (arguments.length > 0) { Expression argument = arguments[0]; if (argument is SimpleStringLiteral) { return argument.value; } } return null; } /** * If the given argument list contains at least one argument, and if the argument is a simple * string literal, and if the value of the argument is the name of a class defined within the * given library, return the type specified by the argument. * * @param library the library in which the specified type would be defined * @param argumentList the list of arguments from which a type is to be extracted * @return the type specified by the first argument in the argument list */ DartType _getFirstArgumentAsType( LibraryElement library, ArgumentList argumentList) => _getFirstArgumentAsTypeWithMap(library, argumentList, null); /** * If the given argument list contains at least one argument, and if the argument is a simple * string literal, and if the value of the argument is the name of a class defined within the * given library, return the type specified by the argument. * * @param library the library in which the specified type would be defined * @param argumentList the list of arguments from which a type is to be extracted * @param nameMap an optional map used to map the element name to a type name * @return the type specified by the first argument in the argument list */ DartType _getFirstArgumentAsTypeWithMap(LibraryElement library, ArgumentList argumentList, HashMap nameMap) => _getElementNameAsType( library, _getFirstArgumentAsString(argumentList), nameMap); /** * Return the propagated type of the given [Element], or `null`. */ DartType _getPropertyPropagatedType(Element element, DartType currentType) { if (element is PropertyAccessorElement) { PropertyAccessorElement accessor = element; if (accessor.isGetter) { PropertyInducingElement variable = accessor.variable; DartType propagatedType = variable.propagatedType; if (currentType == null || propagatedType != null && propagatedType.isMoreSpecificThan(currentType)) { return propagatedType; } } } return currentType; } /** * Return the static type of the given expression. * * @param expression the expression whose type is to be returned * @return the static type of the given expression */ DartType _getStaticType(Expression expression) { DartType type = expression.staticType; if (type == null) { // TODO(brianwilkerson) Determine the conditions for which the static type // is null. return _dynamicType; } return type; } /** * Return the type represented by the given type name. * * @param typeName the type name representing the type to be returned * @return the type represented by the type name */ DartType _getType(TypeName typeName) { DartType type = typeName.type; if (type == null) { //TODO(brianwilkerson) Determine the conditions for which the type is // null. return _dynamicType; } return type; } /** * Return the type that should be recorded for a node that resolved to the given accessor. * * @param accessor the accessor that the node resolved to * @param context if the accessor element has context [by being the RHS of a * [PrefixedIdentifier] or [PropertyAccess]], and the return type of the * accessor is a parameter type, then the type of the LHS can be used to get more * specific type information * @return the type that should be recorded for a node that resolved to the given accessor */ DartType _getTypeOfProperty( PropertyAccessorElement accessor, DartType context) { FunctionType functionType = accessor.type; if (functionType == null) { // TODO(brianwilkerson) Report this internal error. This happens when we // are analyzing a reference to a property before we have analyzed the // declaration of the property or when the property does not have a // defined type. return _dynamicType; } if (accessor.isSetter) { List parameterTypes = functionType.normalParameterTypes; if (parameterTypes != null && parameterTypes.length > 0) { return parameterTypes[0]; } PropertyAccessorElement getter = accessor.variable.getter; if (getter != null) { functionType = getter.type; if (functionType != null) { return functionType.returnType; } } return _dynamicType; } DartType returnType = functionType.returnType; if (returnType is TypeParameterType && context is InterfaceType) { // if the return type is a TypeParameter, we try to use the context [that // the function is being called on] to get a more accurate returnType type InterfaceType interfaceTypeContext = context; // Type[] argumentTypes = interfaceTypeContext.getTypeArguments(); List typeParameterElements = interfaceTypeContext.element != null ? interfaceTypeContext.element.typeParameters : null; if (typeParameterElements != null) { for (int i = 0; i < typeParameterElements.length; i++) { TypeParameterElement typeParameterElement = typeParameterElements[i]; if (returnType.name == typeParameterElement.name) { return interfaceTypeContext.typeArguments[i]; } } // TODO(jwren) troubleshoot why call to substitute doesn't work // Type[] parameterTypes = TypeParameterTypeImpl.getTypes(parameterElements); // return returnType.substitute(argumentTypes, parameterTypes); } } return returnType; } /** * Return `true` if the given [Type] is the `Future` form the 'dart:async' * library. */ bool _isAsyncFutureType(DartType type) => type is InterfaceType && type.name == "Future" && _isAsyncLibrary(type.element.library); /** * Return `true` if the given library is the 'dart:async' library. * * @param library the library being tested * @return `true` if the library is 'dart:async' */ bool _isAsyncLibrary(LibraryElement library) => library.name == "dart.async"; /** * Return `true` if the given library is the 'dart:html' library. * * @param library the library being tested * @return `true` if the library is 'dart:html' */ bool _isHtmlLibrary(LibraryElement library) => library != null && "dart.dom.html" == library.name; /** * Return `true` if the given node is not a type literal. * * @param node the node being tested * @return `true` if the given node is not a type literal */ bool _isNotTypeLiteral(Identifier node) { AstNode parent = node.parent; return parent is TypeName || (parent is PrefixedIdentifier && (parent.parent is TypeName || identical(parent.prefix, node))) || (parent is PropertyAccess && identical(parent.target, node) && parent.operator.type == TokenType.PERIOD) || (parent is MethodInvocation && identical(node, parent.target) && parent.operator.type == TokenType.PERIOD); } /** * Record that the propagated type of the given node is the given type. * * @param expression the node whose type is to be recorded * @param type the propagated type of the node */ void _recordPropagatedType(Expression expression, DartType type) { if (type != null && !type.isDynamic && !type.isBottom) { expression.propagatedType = type; } } /** * Given a function element and its body, compute and record the propagated return type of the * function. * * @param functionElement the function element to record propagated return type for * @param body the boy of the function whose propagated return type is to be computed * @return the propagated return type that was computed, may be `null` if it is not more * specific than the static return type. */ void _recordPropagatedTypeOfFunction( ExecutableElement functionElement, FunctionBody body) { DartType propagatedReturnType = _computePropagatedReturnTypeOfFunction(body); if (propagatedReturnType == null) { return; } // Ignore 'bottom' type. if (propagatedReturnType.isBottom) { return; } // Record only if we inferred more specific type. DartType staticReturnType = functionElement.returnType; if (!propagatedReturnType.isMoreSpecificThan(staticReturnType)) { return; } // OK, do record. _propagatedReturnTypes[functionElement] = propagatedReturnType; } /** * Record that the static type of the given node is the given type. * * @param expression the node whose type is to be recorded * @param type the static type of the node */ void _recordStaticType(Expression expression, DartType type) { if (type == null) { expression.staticType = _dynamicType; } else { expression.staticType = type; } } /** * Attempts to make a better guess for the static type of the given binary expression. * * @param node the binary expression to analyze * @param staticType the static type of the expression as resolved * @return the better type guess, or the same static type as given */ DartType _refineBinaryExpressionType( BinaryExpression node, DartType staticType) { sc.TokenType operator = node.operator.type; // bool if (operator == sc.TokenType.AMPERSAND_AMPERSAND || operator == sc.TokenType.BAR_BAR || operator == sc.TokenType.EQ_EQ || operator == sc.TokenType.BANG_EQ) { return _typeProvider.boolType; } DartType intType = _typeProvider.intType; if (_getStaticType(node.leftOperand) == intType) { // int op double if (operator == sc.TokenType.MINUS || operator == sc.TokenType.PERCENT || operator == sc.TokenType.PLUS || operator == sc.TokenType.STAR) { DartType doubleType = _typeProvider.doubleType; if (_getStaticType(node.rightOperand) == doubleType) { return doubleType; } } // int op int if (operator == sc.TokenType.MINUS || operator == sc.TokenType.PERCENT || operator == sc.TokenType.PLUS || operator == sc.TokenType.STAR || operator == sc.TokenType.TILDE_SLASH) { if (_getStaticType(node.rightOperand) == intType) { staticType = intType; } } } // default return staticType; } /** * Implements the function "flatten" defined in the spec: * * If T = Future then flatten(T) = flatten(S). * * Otherwise if T <: Future then let S be a type such that T << Future * and for all R, if T << Future then S << R. Then flatten(T) = S. * * In any other circumstance, flatten(T) = T. */ static DartType flattenFutures(TypeProvider typeProvider, DartType type) { if (type is InterfaceType) { // Implement the case: "If T = Future then flatten(T) = flatten(S)." if (type.element == typeProvider.futureType.element && type.typeArguments.length > 0) { return flattenFutures(typeProvider, type.typeArguments[0]); } // Implement the case: "Otherwise if T <: Future then let S be a type // such that T << Future and for all R, if T << Future then S << R. // Then flatten(T) = S." // // In other words, given the set of all types R such that T << Future, // let S be the most specific of those types, if any such S exists. // // Since we only care about the most specific type, it is sufficent to // look at the types appearing as a parameter to Future in the type // hierarchy of T. We don't need to consider the supertypes of those // types, since they are by definition less specific. List candidateTypes = _searchTypeHierarchyForFutureParameters(typeProvider, type); DartType flattenResult = _findMostSpecificType(candidateTypes); if (flattenResult != null) { return flattenResult; } } // Implement the case: "In any other circumstance, flatten(T) = T." return type; } /** * Create a table mapping HTML tag names to the names of the classes (in 'dart:html') that * implement those tags. * * @return the table that was created */ static HashMap _createHtmlTagToClassMap() { HashMap map = new HashMap(); map["a"] = "AnchorElement"; map["area"] = "AreaElement"; map["br"] = "BRElement"; map["base"] = "BaseElement"; map["body"] = "BodyElement"; map["button"] = "ButtonElement"; map["canvas"] = "CanvasElement"; map["content"] = "ContentElement"; map["dl"] = "DListElement"; map["datalist"] = "DataListElement"; map["details"] = "DetailsElement"; map["div"] = "DivElement"; map["embed"] = "EmbedElement"; map["fieldset"] = "FieldSetElement"; map["form"] = "FormElement"; map["hr"] = "HRElement"; map["head"] = "HeadElement"; map["h1"] = "HeadingElement"; map["h2"] = "HeadingElement"; map["h3"] = "HeadingElement"; map["h4"] = "HeadingElement"; map["h5"] = "HeadingElement"; map["h6"] = "HeadingElement"; map["html"] = "HtmlElement"; map["iframe"] = "IFrameElement"; map["img"] = "ImageElement"; map["input"] = "InputElement"; map["keygen"] = "KeygenElement"; map["li"] = "LIElement"; map["label"] = "LabelElement"; map["legend"] = "LegendElement"; map["link"] = "LinkElement"; map["map"] = "MapElement"; map["menu"] = "MenuElement"; map["meter"] = "MeterElement"; map["ol"] = "OListElement"; map["object"] = "ObjectElement"; map["optgroup"] = "OptGroupElement"; map["output"] = "OutputElement"; map["p"] = "ParagraphElement"; map["param"] = "ParamElement"; map["pre"] = "PreElement"; map["progress"] = "ProgressElement"; map["script"] = "ScriptElement"; map["select"] = "SelectElement"; map["source"] = "SourceElement"; map["span"] = "SpanElement"; map["style"] = "StyleElement"; map["caption"] = "TableCaptionElement"; map["td"] = "TableCellElement"; map["col"] = "TableColElement"; map["table"] = "TableElement"; map["tr"] = "TableRowElement"; map["textarea"] = "TextAreaElement"; map["title"] = "TitleElement"; map["track"] = "TrackElement"; map["ul"] = "UListElement"; map["video"] = "VideoElement"; return map; } /** * If there is a single type which is at least as specific as all of the * types in [types], return it. Otherwise return `null`. */ static DartType _findMostSpecificType(List types) { // The << relation ("more specific than") is a partial ordering on types, // so to find the most specific type of a set, we keep a bucket of the most // specific types seen so far such that no type in the bucket is more // specific than any other type in the bucket. List bucket = []; // Then we consider each type in turn. for (DartType type in types) { // If any existing type in the bucket is more specific than this type, // then we can ignore this type. if (bucket.any((DartType t) => t.isMoreSpecificThan(type))) { continue; } // Otherwise, we need to add this type to the bucket and remove any types // that are less specific than it. bool added = false; int i = 0; while (i < bucket.length) { if (type.isMoreSpecificThan(bucket[i])) { if (added) { if (i < bucket.length - 1) { bucket[i] = bucket.removeLast(); } else { bucket.removeLast(); } } else { bucket[i] = type; i++; added = true; } } else { i++; } } if (!added) { bucket.add(type); } } // Now that we are finished, if there is exactly one type left in the // bucket, it is the most specific type. if (bucket.length == 1) { return bucket[0]; } // Otherwise, there is no single type that is more specific than the // others. return null; } /** * Given a seed type [type], search its class hierarchy for types of the form * Future, and return a list of the resulting R's. */ static List _searchTypeHierarchyForFutureParameters( TypeProvider typeProvider, InterfaceType type) { List result = []; HashSet visitedClasses = new HashSet(); void recurse(InterfaceType type) { if (type.element == typeProvider.futureType.element && type.typeArguments.length > 0) { result.add(type.typeArguments[0]); } if (visitedClasses.add(type.element)) { if (type.superclass != null) { recurse(type.superclass); } for (InterfaceType interface in type.interfaces) { recurse(interface); } visitedClasses.remove(type.element); } } recurse(type); return result; } } class _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction extends GeneralizingAstVisitor { DartType result = null; _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction(); @override Object visitExpression(Expression node) => null; @override Object visitReturnStatement(ReturnStatement node) { // prepare this 'return' type DartType type; Expression expression = node.expression; if (expression != null) { type = expression.bestType; } else { type = BottomTypeImpl.instance; } // merge types if (result == null) { result = type; } else { result = result.getLeastUpperBound(type); } return null; } }