Element staticMethodElement = methodNameNode.getStaticElement();
// Record types of the local variable invoked as a function.
if (staticMethodElement instanceof LocalVariableElement) {
LocalVariableElement variable = (LocalVariableElement) staticMethodElement;
Type staticType = variable.getType();
recordStaticType(methodNameNode, staticType);
Type propagatedType = overrideManager.getType(variable);
if (propagatedType != null && propagatedType.isMoreSpecificThan(staticType)) {
recordPropagatedType(methodNameNode, propagatedType);
}
}
// Record static return type of the static element.
Type staticStaticType = computeStaticReturnType(staticMethodElement);
recordStaticType(node, staticStaticType);
// Record propagated return type of the static element.
Type staticPropagatedType = computePropagatedReturnType(staticMethodElement);
if (staticPropagatedType != null
&& (staticStaticType == null || staticPropagatedType.isMoreSpecificThan(staticStaticType))) {
recordPropagatedType(node, staticPropagatedType);
}
boolean needPropagatedType = true;
String methodName = methodNameNode.getName();
if (methodName.equals("then")) {
Expression target = node.getRealTarget();
if (target != null) {
Type targetType = target.getBestType();
if (isAsyncFutureType(targetType)) {
// Future.then(closure) return type is:
// 1) the returned Future type, if the closure returns a Future;
// 2) Future<valueType>, if the closure returns a value.
NodeList<Expression> arguments = node.getArgumentList().getArguments();
if (arguments.size() == 1) {
// TODO(brianwilkerson) Handle the case where both arguments are provided.
Expression closureArg = arguments.get(0);
if (closureArg instanceof FunctionExpression) {
FunctionExpression closureExpr = (FunctionExpression) closureArg;
Type returnType = computePropagatedReturnType(closureExpr.getElement());
if (returnType != null) {
// prepare the type of the returned Future
InterfaceTypeImpl newFutureType;
if (isAsyncFutureType(returnType)) {
newFutureType = (InterfaceTypeImpl) returnType;
} else {
InterfaceType futureType = (InterfaceType) targetType;
newFutureType = new InterfaceTypeImpl(futureType.getElement());
newFutureType.setTypeArguments(new Type[] {returnType});
}
// set the 'then' invocation type
recordPropagatedType(node, newFutureType);
needPropagatedType = false;
return null;
}
}
}
}
}
} else if (methodName.equals("$dom_createEvent")) {
Expression target = node.getRealTarget();
if (target != null) {
Type targetType = target.getBestType();
if (targetType instanceof InterfaceType
&& (targetType.getName().equals("HtmlDocument") || targetType.getName().equals(
"Document"))) {
LibraryElement library = targetType.getElement().getLibrary();
if (isHtmlLibrary(library)) {
Type returnType = getFirstArgumentAsType(library, node.getArgumentList());
if (returnType != null) {
recordPropagatedType(node, returnType);
needPropagatedType = false;
}
}
}
}
} else if (methodName.equals("query")) {
Expression target = node.getRealTarget();
if (target == null) {
Element methodElement = methodNameNode.getBestElement();
if (methodElement != null) {
LibraryElement library = methodElement.getLibrary();
if (isHtmlLibrary(library)) {
Type returnType = getFirstArgumentAsQuery(library, node.getArgumentList());
if (returnType != null) {
recordPropagatedType(node, returnType);
needPropagatedType = false;
}
}
}
} else {
Type targetType = target.getBestType();
if (targetType instanceof InterfaceType
&& (targetType.getName().equals("HtmlDocument") || targetType.getName().equals(
"Document"))) {
LibraryElement library = targetType.getElement().getLibrary();
if (isHtmlLibrary(library)) {
Type returnType = getFirstArgumentAsQuery(library, node.getArgumentList());
if (returnType != null) {
recordPropagatedType(node, returnType);
needPropagatedType = false;
}
}
}
}
} else if (methodName.equals("$dom_createElement")) {
Expression target = node.getRealTarget();
if (target != null) {
Type targetType = target.getBestType();
if (targetType instanceof InterfaceType
&& (targetType.getName().equals("HtmlDocument") || targetType.getName().equals(
"Document"))) {
LibraryElement library = targetType.getElement().getLibrary();
if (isHtmlLibrary(library)) {
Type returnType = getFirstArgumentAsQuery(library, node.getArgumentList());
if (returnType != null) {
recordPropagatedType(node, returnType);
needPropagatedType = false;
}
}
}
}
} else if (methodName.equals("JS")) {
Type returnType = getFirstArgumentAsType(
typeProvider.getObjectType().getElement().getLibrary(),
node.getArgumentList());
if (returnType != null) {
recordPropagatedType(node, returnType);
needPropagatedType = false;
}
} else if (methodName.equals("getContext")) {
Expression target = node.getRealTarget();
if (target != null) {
Type targetType = target.getBestType();
if (targetType instanceof InterfaceType && (targetType.getName().equals("CanvasElement"))) {
NodeList<Expression> arguments = node.getArgumentList().getArguments();
if (arguments.size() == 1) {
Expression argument = arguments.get(0);
if (argument instanceof StringLiteral) {
String value = ((StringLiteral) argument).getStringValue();
if ("2d".equals(value)) {
PropertyAccessorElement getter = ((InterfaceType) targetType).getElement().getGetter(
"context2D");
if (getter != null) {
Type returnType = getter.getReturnType();
if (returnType != null) {
recordPropagatedType(node, returnType);
needPropagatedType = false;
}
}
}
}
}
}
}
}
if (needPropagatedType) {
Element propagatedElement = methodNameNode.getPropagatedElement();
// HACK: special case for object methods ([toString]) on dynamic expressions.
// More special cases in [visitPrefixedIdentfier].
if (propagatedElement == null) {
propagatedElement = typeProvider.getObjectType().getMethod(methodNameNode.getName());
}
if (propagatedElement != staticMethodElement) {
// Record static return type of the propagated element.
Type propagatedStaticType = computeStaticReturnType(propagatedElement);
if (propagatedStaticType != null
&& (staticStaticType == null || propagatedStaticType.isMoreSpecificThan(staticStaticType))
&& (staticPropagatedType == null || propagatedStaticType.isMoreSpecificThan(staticPropagatedType))) {
recordPropagatedType(node, propagatedStaticType);
}
// Record propagated return type of the propagated element.
Type propagatedPropagatedType = computePropagatedReturnType(propagatedElement);
if (propagatedPropagatedType != null
&& (staticStaticType == null || propagatedPropagatedType.isMoreSpecificThan(staticStaticType))
&& (staticPropagatedType == null || propagatedPropagatedType.isMoreSpecificThan(staticPropagatedType))
&& (propagatedStaticType == null || propagatedPropagatedType.isMoreSpecificThan(propagatedStaticType))) {
recordPropagatedType(node, propagatedPropagatedType);
}
}
}
return null;