Java Code Examples for com.google.javascript.rhino.jstype.JSType#toMaybeFunctionType()

The following examples show how to use com.google.javascript.rhino.jstype.JSType#toMaybeFunctionType() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: Closure_117_TypeValidator_s.java    From coming with MIT License 6 votes vote down vote up
private void registerMismatch(JSType found, JSType required, JSError error) {
  // Don't register a mismatch for differences in null or undefined or if the
  // code didn't downcast.
  found = found.restrictByNotNullOrUndefined();
  required = required.restrictByNotNullOrUndefined();
  if (found.isSubtype(required) || required.isSubtype(found)) {
    return;
  }

  mismatches.add(new TypeMismatch(found, required, error));
  if (found.isFunctionType() &&
      required.isFunctionType()) {
    FunctionType fnTypeA = found.toMaybeFunctionType();
    FunctionType fnTypeB = required.toMaybeFunctionType();
    Iterator<Node> paramItA = fnTypeA.getParameters().iterator();
    Iterator<Node> paramItB = fnTypeB.getParameters().iterator();
    while (paramItA.hasNext() && paramItB.hasNext()) {
      registerIfMismatch(paramItA.next().getJSType(),
          paramItB.next().getJSType(), error);
    }

    registerIfMismatch(
        fnTypeA.getReturnType(), fnTypeB.getReturnType(), error);
  }
}
 
Example 2
Source File: Closure_35_TypeInference_t.java    From coming with MIT License 6 votes vote down vote up
/**
 * Traverse a return value.
 */
private FlowScope traverseReturn(Node n, FlowScope scope) {
  scope = traverseChildren(n, scope);

  Node retValue = n.getFirstChild();
  if (retValue != null) {
    JSType type = functionScope.getRootNode().getJSType();
    if (type != null) {
      FunctionType fnType = type.toMaybeFunctionType();
      if (fnType != null) {
        inferPropertyTypesToMatchConstraint(
            retValue.getJSType(), fnType.getReturnType());
      }
    }
  }
  return scope;
}
 
Example 3
Source File: Closure_117_TypeValidator_t.java    From coming with MIT License 6 votes vote down vote up
private void registerMismatch(JSType found, JSType required, JSError error) {
  // Don't register a mismatch for differences in null or undefined or if the
  // code didn't downcast.
  found = found.restrictByNotNullOrUndefined();
  required = required.restrictByNotNullOrUndefined();
  if (found.isSubtype(required) || required.isSubtype(found)) {
    return;
  }

  mismatches.add(new TypeMismatch(found, required, error));
  if (found.isFunctionType() &&
      required.isFunctionType()) {
    FunctionType fnTypeA = found.toMaybeFunctionType();
    FunctionType fnTypeB = required.toMaybeFunctionType();
    Iterator<Node> paramItA = fnTypeA.getParameters().iterator();
    Iterator<Node> paramItB = fnTypeB.getParameters().iterator();
    while (paramItA.hasNext() && paramItB.hasNext()) {
      registerIfMismatch(paramItA.next().getJSType(),
          paramItB.next().getJSType(), error);
    }

    registerIfMismatch(
        fnTypeA.getReturnType(), fnTypeB.getReturnType(), error);
  }
}
 
Example 4
Source File: Closure_125_TypeCheck_s.java    From coming with MIT License 6 votes vote down vote up
/**
 * Visits a NEW node.
 */
private void visitNew(NodeTraversal t, Node n) {
  Node constructor = n.getFirstChild();
  JSType type = getJSType(constructor).restrictByNotNullOrUndefined();
  if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) {
    FunctionType fnType = type.toMaybeFunctionType();
    if (fnType != null) {
      visitParameterList(t, n, fnType);
      ensureTyped(t, n, fnType.getInstanceType());
    } else {
      ensureTyped(t, n);
    }
  } else {
    report(t, n, NOT_A_CONSTRUCTOR);
    ensureTyped(t, n);
  }
}
 
Example 5
Source File: Nopol2017_0027_t.java    From coming with MIT License 6 votes vote down vote up
/**
 * Find the function that's being overridden on this type, if any.
 */
private FunctionType findOverriddenFunction(
    ObjectType ownerType, String propName) {
  // First, check to see if the property is implemented
  // on a superclass.
  JSType propType = ownerType.getPropertyType(propName);
  if (propType != null && propType.isFunctionType()) {
    return propType.toMaybeFunctionType();
  } else {
    // If it's not, then check to see if it's implemented
    // on an implemented interface.
    for (ObjectType iface :
             ownerType.getCtorImplementedInterfaces()) {
      propType = iface.getPropertyType(propName);
      if (propType != null && propType.isFunctionType()) {
        return propType.toMaybeFunctionType();
      }
    }
  }

  return null;
}
 
Example 6
Source File: Closure_25_TypeInference_t.java    From coming with MIT License 6 votes vote down vote up
private FlowScope traverseCall(Node n, FlowScope scope) {
  scope = traverseChildren(n, scope);

  Node left = n.getFirstChild();
  JSType functionType = getJSType(left).restrictByNotNullOrUndefined();
  if (functionType != null) {
    if (functionType.isFunctionType()) {
      FunctionType fnType = functionType.toMaybeFunctionType();
      n.setJSType(fnType.getReturnType());
      backwardsInferenceFromCallSite(n, fnType);
    } else if (functionType.equals(getNativeType(CHECKED_UNKNOWN_TYPE))) {
      n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE));
    }
  }

  scope = tightenTypesAfterAssertions(scope, n);
  return scope;
}
 
Example 7
Source File: SemanticReverseAbstractInterpreter.java    From astor with GNU General Public License v2.0 6 votes vote down vote up
private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope,
    boolean outcome) {
  JSType leftType = getTypeIfRefinable(left, blindScope);
  if (leftType == null) {
    return blindScope;
  }
  JSType rightType = right.getJSType();
  ObjectType targetType =
      typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
  if (rightType != null && rightType.isFunctionType()) {
    targetType = rightType.toMaybeFunctionType();
  }
  Visitor<JSType> visitor;
  if (outcome) {
    visitor = new RestrictByTrueInstanceOfResultVisitor(targetType);
  } else {
    visitor = new RestrictByFalseInstanceOfResultVisitor(targetType);
  }
  return maybeRestrictName(
      blindScope, left, leftType, leftType.visit(visitor));
}
 
Example 8
Source File: CheckMissingReturn.java    From astor with GNU General Public License v2.0 6 votes vote down vote up
/**
 * Determines if the given scope should explicitly return. All functions
 * with non-void or non-unknown return types must have explicit returns.
 * @return If a return type is expected, returns it. Otherwise, returns null.
 */
private JSType explicitReturnExpected(Node scope) {
  FunctionType scopeType = JSType.toMaybeFunctionType(scope.getJSType());

  if (scopeType == null) {
    return null;
  }

  if (isEmptyFunction(scope)) {
    return null;
  }

  JSType returnType = scopeType.getReturnType();

  if (returnType == null) {
    return null;
  }

  if (!isVoidOrUnknown(returnType)) {
    return returnType;
  }

  return null;
}
 
Example 9
Source File: Nopol2017_0029_s.java    From coming with MIT License 5 votes vote down vote up
/**
 * Visits a RETURN node.
 *
 * @param t The node traversal object that supplies context, such as the
 * scope chain to use in name lookups as well as error reporting.
 * @param n The node being visited.
 */
private void visitReturn(NodeTraversal t, Node n) {
  JSType jsType = getJSType(t.getEnclosingFunction());

  if (jsType.isFunctionType()) {
    FunctionType functionType = jsType.toMaybeFunctionType();

    JSType returnType = functionType.getReturnType();

    // if no return type is specified, undefined must be returned
    // (it's a void function)
    if (returnType == null) {
      returnType = getNativeType(VOID_TYPE);
    }

    // fetching the returned value's type
    Node valueNode = n.getFirstChild();
    JSType actualReturnType;
    if (valueNode == null) {
      actualReturnType = getNativeType(VOID_TYPE);
      valueNode = n;
    } else {
      actualReturnType = getJSType(valueNode);
    }

    // verifying
    validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType,
        "inconsistent return type");
  }
}
 
Example 10
Source File: Closure_25_TypeInference_s.java    From coming with MIT License 5 votes vote down vote up
/**
 * For functions with function parameters, type inference will set the type of
 * a function literal argument from the function parameter type.
 */
private void updateTypeOfParameters(Node n, FunctionType fnType) {
  int i = 0;
  int childCount = n.getChildCount();
  for (Node iParameter : fnType.getParameters()) {
    if (i + 1 >= childCount) {
      // TypeCheck#visitParametersList will warn so we bail.
      return;
    }

    JSType iParameterType = getJSType(iParameter);
    Node iArgument = n.getChildAtIndex(i + 1);
    JSType iArgumentType = getJSType(iArgument);
    inferPropertyTypesToMatchConstraint(iArgumentType, iParameterType);

    if (iParameterType.isFunctionType()) {
      FunctionType iParameterFnType = iParameterType.toMaybeFunctionType();

      if (iArgument.isFunction() &&
          iArgumentType.isFunctionType() &&
          iArgument.getJSDocInfo() == null) {
        iArgument.setJSType(iParameterFnType);
      }
    }
    i++;
  }
}
 
Example 11
Source File: NameReferenceGraphConstruction.java    From astor with GNU General Public License v2.0 5 votes vote down vote up
/**
 * @param assign The assignment node, null if it is just a "forward"
 *     declaration for recording the rValue's type.
 */
private Name recordPrototypePropDefinition(
    NodeTraversal t, Node qName, JSType type,
    @Nullable Node assign, @Nullable Node parent, @Nullable Node gParent) {
  JSType constructor = getType(NodeUtil.getPrototypeClassName(qName));
  FunctionType classType = null;
  String className = null;

  if (constructor != null && constructor.isConstructor()) {
    // Case where the class has been properly declared with @constructor
    classType = constructor.toMaybeFunctionType();
    className = classType.getReferenceName();
  } else {
    // We'll guess it is a constructor even if it didn't have @constructor
    classType = compiler.getTypeRegistry().getNativeFunctionType(
        JSTypeNative.U2U_CONSTRUCTOR_TYPE);
    className = NodeUtil.getPrototypeClassName(qName).getQualifiedName();
  }
  // In case we haven't seen the function yet.
  recordClassConstructorOrInterface(
      className, classType, null, null, null, null);

  String qNameStr = className + ".prototype." +
      NodeUtil.getPrototypePropertyName(qName);
  Name prototypeProp = graph.defineNameIfNotExists(qNameStr, isExtern);
  Preconditions.checkNotNull(prototypeProp,
      "%s should be in the name graph as a node.", qNameStr);
  if (assign != null) {
    prototypeProp.addAssignmentDeclaration(assign);
  }
  prototypeProp.setType(type);
  return prototypeProp;
}
 
Example 12
Source File: Closure_125_TypeCheck_s.java    From coming with MIT License 5 votes vote down vote up
private void checkPropertyInheritanceOnGetpropAssign(
    NodeTraversal t, Node assign, Node object, String property,
    JSDocInfo info, JSType propertyType) {
  // Inheritance checks for prototype properties.
  //
  // TODO(nicksantos): This isn't the right place to do this check. We
  // really want to do this when we're looking at the constructor.
  // We'd find all its properties and make sure they followed inheritance
  // rules, like we currently do for @implements to make sure
  // all the methods are implemented.
  //
  // As-is, this misses many other ways to override a property.
  //
  // object.prototype.property = ...;
  if (object.isGetProp()) {
    Node object2 = object.getFirstChild();
    String property2 = NodeUtil.getStringValue(object.getLastChild());

    if ("prototype".equals(property2)) {
      JSType jsType = getJSType(object2);
      if (jsType.isFunctionType()) {
        FunctionType functionType = jsType.toMaybeFunctionType();
        if (functionType.isConstructor() || functionType.isInterface()) {
          checkDeclaredPropertyInheritance(
              t, assign, functionType, property, info, propertyType);
        }
      }
    }
  }
}
 
Example 13
Source File: Closure_2_TypeCheck_t.java    From coming with MIT License 5 votes vote down vote up
/**
 * Visits a RETURN node.
 *
 * @param t The node traversal object that supplies context, such as the
 * scope chain to use in name lookups as well as error reporting.
 * @param n The node being visited.
 */
private void visitReturn(NodeTraversal t, Node n) {
  JSType jsType = getJSType(t.getEnclosingFunction());

  if (jsType.isFunctionType()) {
    FunctionType functionType = jsType.toMaybeFunctionType();

    JSType returnType = functionType.getReturnType();

    // if no return type is specified, undefined must be returned
    // (it's a void function)
    if (returnType == null) {
      returnType = getNativeType(VOID_TYPE);
    }

    // fetching the returned value's type
    Node valueNode = n.getFirstChild();
    JSType actualReturnType;
    if (valueNode == null) {
      actualReturnType = getNativeType(VOID_TYPE);
      valueNode = n;
    } else {
      actualReturnType = getJSType(valueNode);
    }

    // verifying
    validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType,
        "inconsistent return type");
  }
}
 
Example 14
Source File: Closure_2_TypeCheck_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Visits a {@link Token#FUNCTION} node.
 *
 * @param t The node traversal object that supplies context, such as the
 * scope chain to use in name lookups as well as error reporting.
 * @param n The node being visited.
 */
private void visitFunction(NodeTraversal t, Node n) {
  FunctionType functionType = JSType.toMaybeFunctionType(n.getJSType());
  String functionPrivateName = n.getFirstChild().getString();
  if (functionType.isConstructor()) {
    FunctionType baseConstructor = functionType.getSuperClassConstructor();
    if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) &&
        baseConstructor != null &&
        baseConstructor.isInterface()) {
      compiler.report(
          t.makeError(n, CONFLICTING_EXTENDED_TYPE,
                      "constructor", functionPrivateName));
    } else {
      if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE)) {
        ObjectType proto = functionType.getPrototype();
        if (functionType.makesStructs() && !proto.isStruct()) {
          compiler.report(t.makeError(n, CONFLICTING_EXTENDED_TYPE,
                                      "struct", functionPrivateName));
        } else if (functionType.makesDicts() && !proto.isDict()) {
          compiler.report(t.makeError(n, CONFLICTING_EXTENDED_TYPE,
                                      "dict", functionPrivateName));
        }
      }
      // All interfaces are properly implemented by a class
      for (JSType baseInterface : functionType.getImplementedInterfaces()) {
        boolean badImplementedType = false;
        ObjectType baseInterfaceObj = ObjectType.cast(baseInterface);
        if (baseInterfaceObj != null) {
          FunctionType interfaceConstructor =
            baseInterfaceObj.getConstructor();
          if (interfaceConstructor != null &&
              !interfaceConstructor.isInterface()) {
            badImplementedType = true;
          }
        } else {
          badImplementedType = true;
        }
        if (badImplementedType) {
          report(t, n, BAD_IMPLEMENTED_TYPE, functionPrivateName);
        }
      }
      // check properties
      validator.expectAllInterfaceProperties(t, n, functionType);
    }
  } else if (functionType.isInterface()) {
    // Interface must extend only interfaces
    for (ObjectType extInterface : functionType.getExtendedInterfaces()) {
      if (extInterface.getConstructor() != null
          && !extInterface.getConstructor().isInterface()) {
        compiler.report(
            t.makeError(n, CONFLICTING_EXTENDED_TYPE,
                        "interface", functionPrivateName));
      }
    }

    // Check whether the extended interfaces have any conflicts
    if (functionType.getExtendedInterfacesCount() > 1) {
      // Only check when extending more than one interfaces
      HashMap<String, ObjectType> properties
          = new HashMap<String, ObjectType>();
      HashMap<String, ObjectType> currentProperties
          = new HashMap<String, ObjectType>();
      for (ObjectType interfaceType : functionType.getExtendedInterfaces()) {
        currentProperties.clear();
        checkInterfaceConflictProperties(t, n, functionPrivateName,
            properties, currentProperties, interfaceType);
        properties.putAll(currentProperties);
      }
    }
  }
}
 
Example 15
Source File: Nopol2017_0027_t.java    From coming with MIT License 4 votes vote down vote up
private FunctionType getFunctionType(@Nullable Var v) {
  JSType t = v == null ? null : v.getType();
  ObjectType o = t == null ? null : t.dereference();
  return JSType.toMaybeFunctionType(o);
}
 
Example 16
Source File: Nopol2017_0027_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Creates a scope with all types declared. Declares newly discovered types
 * and type properties in the type registry.
 */
@Override
public Scope createScope(Node root, Scope parent) {
  // Constructing the global scope is very different than constructing
  // inner scopes, because only global scopes can contain named classes that
  // show up in the type registry.
  Scope newScope = null;
  AbstractScopeBuilder scopeBuilder = null;
  if (parent == null) {
    // Run a first-order analysis over the syntax tree.
    (new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults))
        .process(root.getFirstChild(), root.getLastChild());

    // Find all the classes in the global scope.
    newScope = createInitialScope(root);

    GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope);
    scopeBuilder = globalScopeBuilder;
    NodeTraversal.traverse(compiler, root, scopeBuilder);
  } else {
    newScope = new Scope(parent, root);
    LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope);
    scopeBuilder = localScopeBuilder;
    localScopeBuilder.build();
  }

  scopeBuilder.resolveStubDeclarations();
  scopeBuilder.resolveTypes();

  // Gather the properties in each function that we found in the
  // global scope, if that function has a @this type that we can
  // build properties on.
  for (Node functionNode : scopeBuilder.nonExternFunctions) {
    JSType type = functionNode.getJSType();
    if (type != null && type.isFunctionType()) {
      FunctionType fnType = type.toMaybeFunctionType();
      ObjectType fnThisType = fnType.getTypeOfThis();
      if (!fnThisType.isUnknownType()) {
        NodeTraversal.traverse(compiler, functionNode.getLastChild(),
            scopeBuilder.new CollectProperties(fnThisType));
      }
    }
  }

  if (parent == null) {
    codingConvention.defineDelegateProxyPrototypeProperties(
        typeRegistry, newScope, delegateProxyPrototypes,
        delegateCallingConventions);
  }
  return newScope;
}
 
Example 17
Source File: TypedScopeCreator.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
private FunctionType getFunctionType(@Nullable Var v) {
  JSType t = v == null ? null : v.getType();
  ObjectType o = t == null ? null : t.dereference();
  return JSType.toMaybeFunctionType(o);
}
 
Example 18
Source File: Closure_125_TypeCheck_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Visits a {@link Token#FUNCTION} node.
 *
 * @param t The node traversal object that supplies context, such as the
 * scope chain to use in name lookups as well as error reporting.
 * @param n The node being visited.
 */
private void visitFunction(NodeTraversal t, Node n) {
  FunctionType functionType = JSType.toMaybeFunctionType(n.getJSType());
  String functionPrivateName = n.getFirstChild().getString();
  if (functionType.isConstructor()) {
    FunctionType baseConstructor = functionType.getSuperClassConstructor();
    if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) &&
        baseConstructor != null &&
        baseConstructor.isInterface()) {
      compiler.report(
          t.makeError(n, CONFLICTING_EXTENDED_TYPE,
                      "constructor", functionPrivateName));
    } else {
      if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE)) {
        ObjectType proto = functionType.getPrototype();
        if (functionType.makesStructs() && !proto.isStruct()) {
          compiler.report(t.makeError(n, CONFLICTING_SHAPE_TYPE,
                                      "struct", functionPrivateName));
        } else if (functionType.makesDicts() && !proto.isDict()) {
          compiler.report(t.makeError(n, CONFLICTING_SHAPE_TYPE,
                                      "dict", functionPrivateName));
        }
      }
      // All interfaces are properly implemented by a class
      for (JSType baseInterface : functionType.getImplementedInterfaces()) {
        boolean badImplementedType = false;
        ObjectType baseInterfaceObj = ObjectType.cast(baseInterface);
        if (baseInterfaceObj != null) {
          FunctionType interfaceConstructor =
            baseInterfaceObj.getConstructor();
          if (interfaceConstructor != null &&
              !interfaceConstructor.isInterface()) {
            badImplementedType = true;
          }
        } else {
          badImplementedType = true;
        }
        if (badImplementedType) {
          report(t, n, BAD_IMPLEMENTED_TYPE, functionPrivateName);
        }
      }
      // check properties
      validator.expectAllInterfaceProperties(t, n, functionType);
    }
  } else if (functionType.isInterface()) {
    // Interface must extend only interfaces
    for (ObjectType extInterface : functionType.getExtendedInterfaces()) {
      if (extInterface.getConstructor() != null
          && !extInterface.getConstructor().isInterface()) {
        compiler.report(
            t.makeError(n, CONFLICTING_EXTENDED_TYPE,
                        "interface", functionPrivateName));
      }
    }

    // Check whether the extended interfaces have any conflicts
    if (functionType.getExtendedInterfacesCount() > 1) {
      // Only check when extending more than one interfaces
      HashMap<String, ObjectType> properties
          = new HashMap<String, ObjectType>();
      HashMap<String, ObjectType> currentProperties
          = new HashMap<String, ObjectType>();
      for (ObjectType interfaceType : functionType.getExtendedInterfaces()) {
        currentProperties.clear();
        checkInterfaceConflictProperties(t, n, functionPrivateName,
            properties, currentProperties, interfaceType);
        properties.putAll(currentProperties);
      }
    }
  }
}
 
Example 19
Source File: Nopol2017_0027_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * Creates a scope with all types declared. Declares newly discovered types
 * and type properties in the type registry.
 */
@Override
public Scope createScope(Node root, Scope parent) {
  // Constructing the global scope is very different than constructing
  // inner scopes, because only global scopes can contain named classes that
  // show up in the type registry.
  Scope newScope = null;
  AbstractScopeBuilder scopeBuilder = null;
  if (parent == null) {
    // Run a first-order analysis over the syntax tree.
    (new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults))
        .process(root.getFirstChild(), root.getLastChild());

    // Find all the classes in the global scope.
    newScope = createInitialScope(root);

    GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope);
    scopeBuilder = globalScopeBuilder;
    NodeTraversal.traverse(compiler, root, scopeBuilder);
  } else {
    newScope = new Scope(parent, root);
    LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope);
    scopeBuilder = localScopeBuilder;
    localScopeBuilder.build();
  }

  scopeBuilder.resolveStubDeclarations();
  scopeBuilder.resolveTypes();

  // Gather the properties in each function that we found in the
  // global scope, if that function has a @this type that we can
  // build properties on.
  for (Node functionNode : scopeBuilder.nonExternFunctions) {
    JSType type = functionNode.getJSType();
    if (type != null && type.isFunctionType()) {
      FunctionType fnType = type.toMaybeFunctionType();
      ObjectType fnThisType = fnType.getTypeOfThis();
      if (!fnThisType.isUnknownType()) {
        NodeTraversal.traverse(compiler, functionNode.getLastChild(),
            scopeBuilder.new CollectProperties(fnThisType));
      }
    }
  }

  if (parent == null) {
    codingConvention.defineDelegateProxyPrototypeProperties(
        typeRegistry, newScope, delegateProxyPrototypes,
        delegateCallingConventions);
  }
  return newScope;
}
 
Example 20
Source File: Closure_112_TypeInference_t.java    From coming with MIT License 4 votes vote down vote up
private void maybeResolveTemplatedType(
    JSType paramType,
    JSType argType,
    Map<TemplateType, JSType> resolvedTypes) {
  if (paramType.isTemplateType()) {
    // @param {T}
    resolvedTemplateType(
        resolvedTypes, paramType.toMaybeTemplateType(), argType);
  } else if (paramType.isUnionType()) {
    // @param {Array.<T>|NodeList|Arguments|{length:number}}
    UnionType unionType = paramType.toMaybeUnionType();
    for (JSType alernative : unionType.getAlternates()) {
      maybeResolveTemplatedType(alernative, argType, resolvedTypes);
    }
  } else if (paramType.isFunctionType()) {
    FunctionType paramFunctionType = paramType.toMaybeFunctionType();
    FunctionType argFunctionType = argType
        .restrictByNotNullOrUndefined()
        .collapseUnion()
        .toMaybeFunctionType();
    if (argFunctionType != null && argFunctionType.isSubtype(paramType)) {
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getTypeOfThis(),
          argFunctionType.getTypeOfThis(), resolvedTypes);
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getReturnType(),
          argFunctionType.getReturnType(), resolvedTypes);
      // infer from parameter types of the function type
      maybeResolveTemplateTypeFromNodes(
          paramFunctionType.getParameters(),
          argFunctionType.getParameters(), resolvedTypes);
    }
  } else if (paramType.isTemplatizedType()) {
    // @param {Array.<T>}
    ObjectType referencedParamType = paramType
        .toMaybeTemplatizedType()
        .getReferencedType();
    JSType argObjectType = argType
        .restrictByNotNullOrUndefined()
        .collapseUnion();

    if (argObjectType.isSubtype(referencedParamType)) {
      // If the argument type is a subtype of the parameter type, resolve any
      // template types amongst their templatized types.
      TemplateTypeMap paramTypeMap = paramType.getTemplateTypeMap();
      TemplateTypeMap argTypeMap = argObjectType.getTemplateTypeMap();
      for (TemplateType key : paramTypeMap.getTemplateKeys()) {
        maybeResolveTemplatedType(
            paramTypeMap.getTemplateType(key),
            argTypeMap.getTemplateType(key),
            resolvedTypes);
      }
    }
  }
}