return null;
}
@Override
public Void visitMethodInvocation(MethodInvocation node) {
SimpleIdentifier methodName = node.getMethodName();
//
// Synthetic identifiers have been already reported during parsing.
//
if (methodName.isSynthetic()) {
return null;
}
//
// We have a method invocation of one of two forms: 'e.m(a1, ..., an)' or 'm(a1, ..., an)'. The
// first step is to figure out which executable is being invoked, using both the static and the
// propagated type information.
//
Expression target = node.getRealTarget();
if (target instanceof SuperExpression && !isSuperInValidContext((SuperExpression) target)) {
return null;
}
Element staticElement;
Element propagatedElement;
Type staticType = null;
Type propagatedType = null;
if (target == null) {
staticElement = resolveInvokedElement(methodName);
propagatedElement = null;
} else if (methodName.getName().equals(FunctionElement.LOAD_LIBRARY_NAME)
&& isDeferredPrefix(target)) {
LibraryElement importedLibrary = getImportedLibrary(target);
methodName.setStaticElement(importedLibrary.getLoadLibraryFunction());
return null;
} else {
staticType = getStaticType(target);
propagatedType = getPropagatedType(target);
//
// If this method invocation is of the form 'C.m' where 'C' is a class, then we don't call
// resolveInvokedElement(..) which walks up the class hierarchy, instead we just look for the
// member in the type only.
//
ClassElementImpl typeReference = getTypeReference(target);
if (typeReference != null) {
staticElement = propagatedElement = resolveElement(typeReference, methodName);
} else {
staticElement = resolveInvokedElementWithTarget(target, staticType, methodName);
propagatedElement = resolveInvokedElementWithTarget(target, propagatedType, methodName);
}
}
staticElement = convertSetterToGetter(staticElement);
propagatedElement = convertSetterToGetter(propagatedElement);
//
// Record the results.
//
methodName.setStaticElement(staticElement);
methodName.setPropagatedElement(propagatedElement);
ArgumentList argumentList = node.getArgumentList();
if (staticElement != null) {
ParameterElement[] parameters = computeCorrespondingParameters(argumentList, staticElement);
if (parameters != null) {
argumentList.setCorrespondingStaticParameters(parameters);
}
}
if (propagatedElement != null) {
ParameterElement[] parameters = computeCorrespondingParameters(
argumentList,
propagatedElement);
if (parameters != null) {
argumentList.setCorrespondingPropagatedParameters(parameters);
}
}
//
// Then check for error conditions.
//
ErrorCode errorCode = checkForInvocationError(target, true, staticElement);
boolean generatedWithTypePropagation = false;
if (enableHints && errorCode == null && staticElement == null) {
// The method lookup may have failed because there were multiple
// incompatible choices. In this case we don't want to generate a hint.
if (propagatedElement == null && propagatedType instanceof UnionType) {
// TODO(collinsn): an improvement here is to make the propagated type of the method call
// the union of the propagated types of all possible calls.
if (lookupMethods(target, (UnionType) propagatedType, methodName.getName()).size() > 1) {
return null;
}
}
errorCode = checkForInvocationError(target, false, propagatedElement);
if (errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) {
ClassElement classElementContext = null;
if (target == null) {
classElementContext = resolver.getEnclosingClass();
} else {
Type type = target.getBestType();
if (type != null) {
if (type.getElement() instanceof ClassElement) {
classElementContext = (ClassElement) type.getElement();
}
}
}
if (classElementContext != null) {
subtypeManager.ensureLibraryVisited(definingLibrary);
HashSet<ClassElement> subtypeElements = subtypeManager.computeAllSubtypes(classElementContext);
for (ClassElement subtypeElement : subtypeElements) {
if (subtypeElement.getMethod(methodName.getName()) != null) {
errorCode = null;
}
}
}
}
generatedWithTypePropagation = true;
}
if (errorCode == null) {
return null;
}
if (errorCode == StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION) {
resolver.reportErrorForNode(
StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION,
methodName,
methodName.getName());
} else if (errorCode == StaticTypeWarningCode.UNDEFINED_FUNCTION) {
resolver.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_FUNCTION,
methodName,
methodName.getName());
} else if (errorCode == StaticTypeWarningCode.UNDEFINED_METHOD) {
String targetTypeName;
if (target == null) {
ClassElement enclosingClass = resolver.getEnclosingClass();
targetTypeName = enclosingClass.getDisplayName();
ErrorCode proxyErrorCode = generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD
: StaticTypeWarningCode.UNDEFINED_METHOD;
if (doesntHaveProxy(resolver.getEnclosingClass())) {
resolver.reportErrorForNode(
proxyErrorCode,
methodName,
methodName.getName(),
targetTypeName);
}
} else {
// ignore Function "call"
// (if we are about to create a hint using type propagation, then we can use type
// propagation here as well)
Type targetType = null;
if (!generatedWithTypePropagation) {
targetType = getStaticType(target);
} else {
// choose the best type
targetType = getPropagatedType(target);
if (targetType == null) {
targetType = getStaticType(target);
}
}
if (targetType != null && targetType.isDartCoreFunction()
&& methodName.getName().equals(FunctionElement.CALL_METHOD_NAME)) {
// TODO(brianwilkerson) Can we ever resolve the function being invoked?
//resolveArgumentsToParameters(node.getArgumentList(), invokedFunction);
return null;
}
targetTypeName = targetType == null ? null : targetType.getDisplayName();
ErrorCode proxyErrorCode = generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD
: StaticTypeWarningCode.UNDEFINED_METHOD;
if (doesntHaveProxy(targetType.getElement())) {
resolver.reportErrorForNode(
proxyErrorCode,
methodName,
methodName.getName(),
targetTypeName);
}
}
} else if (errorCode == StaticTypeWarningCode.UNDEFINED_SUPER_METHOD) {
// Generate the type name.
// The error code will never be generated via type propagation
Type targetType = getStaticType(target);
if (targetType instanceof InterfaceType && !targetType.isObject()) {
targetType = ((InterfaceType) targetType).getSuperclass();
}
String targetTypeName = targetType == null ? null : targetType.getName();
resolver.reportErrorForNode(
StaticTypeWarningCode.UNDEFINED_SUPER_METHOD,
methodName,
methodName.getName(),
targetTypeName);
}
return null;
}