Java Code Examples for com.google.javascript.rhino.Token#GETPROP

The following examples show how to use com.google.javascript.rhino.Token#GETPROP . 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_96_TypeCheck_t.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();
  FunctionType type = getFunctionType(constructor);
  if (type != null && type.isConstructor()) {
    visitParameterList(t, n, type);
    ensureTyped(t, n, type.getInstanceType());
  } else {
    // TODO(user): add support for namespaced objects.
    if (constructor.getType() != Token.GETPROP) {
      // TODO(user): make the constructor node have lineno/charno
      // and use constructor for a more precise error indication.
      // It seems that GETPROP nodes are missing this information.
      Node line;
      if (constructor.getLineno() < 0 || constructor.getCharno() < 0) {
        line = n;
      } else {
        line = constructor;
      }
      report(t, line, NOT_A_CONSTRUCTOR);
    }
    ensureTyped(t, n);
  }
}
 
Example 2
Source File: Cardumen_0024_t.java    From coming with MIT License 6 votes vote down vote up
/**
 * Declares a refined type in {@code scope} for the name represented by
 * {@code node}. It must be possible to refine the type of the given node in
 * the given scope, as determined by {@link #getTypeIfRefinable}.
 */
protected void declareNameInScope(FlowScope scope, Node node, JSType type) {
  switch (node.getType()) {
    case Token.NAME:
      scope.inferSlotType(node.getString(), type);
      break;

    case Token.GETPROP:
      String qualifiedName = node.getQualifiedName();
      Preconditions.checkNotNull(qualifiedName);

      JSType origType = node.getJSType();
      origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType;
      scope.inferQualifiedSlot(node, qualifiedName, origType, type);
      break;

    case Token.THIS:
      // "this" references aren't currently modeled in the CFG.
      break;

    default:
      throw new IllegalArgumentException("Node cannot be refined. \n" +
          node.toStringTree());
  }
}
 
Example 3
Source File: Nopol2017_0051_t.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();
  FunctionType type = getFunctionType(constructor);
  if (type != null && type.isConstructor()) {
    visitParameterList(t, n, type);
    ensureTyped(t, n, type.getInstanceType());
  } else {
    // TODO(user): add support for namespaced objects.
    if (constructor.getType() != Token.GETPROP) {
      // TODO(user): make the constructor node have lineno/charno
      // and use constructor for a more precise error indication.
      // It seems that GETPROP nodes are missing this information.
      Node line;
      if (constructor.getLineno() < 0 || constructor.getCharno() < 0) {
        line = n;
      } else {
        line = constructor;
      }
      report(t, line, NOT_A_CONSTRUCTOR);
    }
    ensureTyped(t, n);
  }
}
 
Example 4
Source File: 1_ClosureCodingConvention.java    From SimFix with GNU General Public License v2.0 6 votes vote down vote up
/**
 * Determines whether the given node is a class-defining name, like
 * "inherits" or "mixin."
 * @return The type of class-defining name, or null.
 */
private SubclassType typeofClassDefiningName(Node callName) {
  // Check if the method name matches one of the class-defining methods.
  String methodName = null;
  if (callName.getType() == Token.GETPROP) {
    methodName = callName.getLastChild().getString();
  } else if (callName.getType() == Token.NAME) {
    String name = callName.getString();
    int dollarIndex = name.lastIndexOf('$');
    if (dollarIndex != -1) {
      methodName = name.substring(dollarIndex + 1);
    }
  }

  if (methodName != null) {
    if (methodName.equals("inherits")) {
      return SubclassType.INHERITS;
    } else if (methodName.equals("mixin")) {
      return SubclassType.MIXIN;
    }
  }
  return null;
}
 
Example 5
Source File: Closure_37_IRFactory_s.java    From coming with MIT License 5 votes vote down vote up
private boolean validAssignmentTarget(Node target) {
  switch (target.getType()) {
    case Token.NAME:
    case Token.GETPROP:
    case Token.GETELEM:
      return true;
  }
  return false;
}
 
Example 6
Source File: ExternExportsPass.java    From astor with GNU General Public License v2.0 5 votes vote down vote up
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
  switch (n.getType()) {

    case Token.NAME:
    case Token.GETPROP:
      String name = n.getQualifiedName();
      if (name == null) {
        return;
      }

      if (parent.isAssign() || parent.isVar()) {
        definitionMap.put(name, parent);
      }

      // Only handle function calls. This avoids assignments
      // that do not export items directly.
      if (!parent.isCall()) {
        return;
      }

      if (exportPropertyFunctionNames.contains(name)) {
        handlePropertyExport(parent);
      }

      if (exportSymbolFunctionNames.contains(name)) {
        handleSymbolExport(parent);
      }
  }
}
 
Example 7
Source File: Closure_10_NodeUtil_s.java    From coming with MIT License 5 votes vote down vote up
/**
 * @param knownConstants A set of names known to be constant value at
 * node 'n' (such as locals that are last written before n can execute).
 * @return Whether the tree can be affected by side-effects or
 * has side-effects.
 */
static boolean canBeSideEffected(Node n, Set<String> knownConstants) {
  switch (n.getType()) {
    case Token.CALL:
    case Token.NEW:
      // Function calls or constructor can reference changed values.
      // TODO(johnlenz): Add some mechanism for determining that functions
      // are unaffected by side effects.
      return true;
    case Token.NAME:
      // Non-constant names values may have been changed.
      return !isConstantName(n)
          && !knownConstants.contains(n.getString());

    // Properties on constant NAMEs can still be side-effected.
    case Token.GETPROP:
    case Token.GETELEM:
      return true;

    case Token.FUNCTION:
      // Function expression are not changed by side-effects,
      // and function declarations are not part of expressions.
      Preconditions.checkState(isFunctionExpression(n));
      return false;
  }

  for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
    if (canBeSideEffected(c, knownConstants)) {
      return true;
    }
  }

  return false;
}
 
Example 8
Source File: Closure_86_NodeUtil_s.java    From coming with MIT License 5 votes vote down vote up
/**
 * A "simple" operator is one whose children are expressions,
 * has no direct side-effects (unlike '+='), and has no
 * conditional aspects (unlike '||').
 */
static boolean isSimpleOperatorType(int type) {
  switch (type) {
    case Token.ADD:
    case Token.BITAND:
    case Token.BITNOT:
    case Token.BITOR:
    case Token.BITXOR:
    case Token.COMMA:
    case Token.DIV:
    case Token.EQ:
    case Token.GE:
    case Token.GETELEM:
    case Token.GETPROP:
    case Token.GT:
    case Token.INSTANCEOF:
    case Token.LE:
    case Token.LSH:
    case Token.LT:
    case Token.MOD:
    case Token.MUL:
    case Token.NE:
    case Token.NOT:
    case Token.RSH:
    case Token.SHEQ:
    case Token.SHNE:
    case Token.SUB:
    case Token.TYPEOF:
    case Token.VOID:
    case Token.POS:
    case Token.NEG:
    case Token.URSH:
      return true;

    default:
      return false;
  }
}
 
Example 9
Source File: Closure_80_NodeUtil_s.java    From coming with MIT License 5 votes vote down vote up
/** Whether the given name is constant by coding convention. */
static boolean isConstantByConvention(
    CodingConvention convention, Node node, Node parent) {
  String name = node.getString();
  if (parent.getType() == Token.GETPROP &&
      node == parent.getLastChild()) {
    return convention.isConstantKey(name);
  } else if (isObjectLitKey(node, parent)) {
    return convention.isConstantKey(name);
  } else {
    return convention.isConstant(name);
  }
}
 
Example 10
Source File: Cardumen_0014_s.java    From coming with MIT License 5 votes vote down vote up
/**
 * @param knownConstants A set of names known to be constant value at
 * node 'n' (such as locals that are last written before n can execute).
 * @return Whether the tree can be affected by side-effects or
 * has side-effects.
 */
static boolean canBeSideEffected(Node n, Set<String> knownConstants) {
  switch (n.getType()) {
    case Token.CALL:
    case Token.NEW:
      // Function calls or constructor can reference changed values.
      // TODO(johnlenz): Add some mechanism for determining that functions
      // are unaffected by side effects.
      return true;
    case Token.NAME:
      // Non-constant names values may have been changed.
      return !isConstantName(n)
          && !knownConstants.contains(n.getString());

    // Properties on constant NAMEs can still be side-effected.
    case Token.GETPROP:
    case Token.GETELEM:
      return true;

    case Token.FUNCTION:
      // Function expression are not changed by side-effects,
      // and function declarations are not part of expressions.
      Preconditions.checkState(isFunctionExpression(n));
      return false;
  }

  for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
    if (canBeSideEffected(c, knownConstants)) {
      return true;
    }
  }

  return false;
}
 
Example 11
Source File: Closure_66_TypeCheck_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Visits a CALL 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 visitCall(NodeTraversal t, Node n) {
  Node child = n.getFirstChild();
  JSType childType = getJSType(child).restrictByNotNullOrUndefined();

  if (!childType.canBeCalled()) {
    report(t, n, NOT_CALLABLE, childType.toString());
    ensureTyped(t, n);
    return;
  }

  // A couple of types can be called as if they were functions.
  // If it is a function type, then validate parameters.
  if (childType instanceof FunctionType) {
    FunctionType functionType = (FunctionType) childType;

    boolean isExtern = false;
    JSDocInfo functionJSDocInfo = functionType.getJSDocInfo();
    if(functionJSDocInfo != null) {
      String sourceName = functionJSDocInfo.getSourceName();
      CompilerInput functionSource = compiler.getInput(sourceName);
      isExtern = functionSource.isExtern();
    }

    // Non-native constructors should not be called directly
    // unless they specify a return type and are defined
    // in an extern.
    if (functionType.isConstructor() &&
        !functionType.isNativeObjectType() &&
        (functionType.getReturnType().isUnknownType() ||
         functionType.getReturnType().isVoidType() ||
         !isExtern)) {
      report(t, n, CONSTRUCTOR_NOT_CALLABLE, childType.toString());
    }

    // Functions with explcit 'this' types must be called in a GETPROP
    // or GETELEM.
    if (functionType.isOrdinaryFunction() &&
        !functionType.getTypeOfThis().isUnknownType() &&
        !functionType.getTypeOfThis().isNativeObjectType() &&
        !(child.getType() == Token.GETELEM ||
          child.getType() == Token.GETPROP)) {
      report(t, n, EXPECTED_THIS_TYPE, functionType.toString());
    }

    visitParameterList(t, n, functionType);
    ensureTyped(t, n, functionType.getReturnType());
  } else {
    ensureTyped(t, n);
  }

  // TODO: Add something to check for calls of RegExp objects, which is not
  // supported by IE.  Either say something about the return type or warn
  // about the non-portability of the call or both.
}
 
Example 12
Source File: Closure_94_NodeUtil_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Is this a GETPROP node?
 */
static boolean isGetProp(Node n) {
  return n.getType() == Token.GETPROP;
}
 
Example 13
Source File: Closure_70_TypedScopeCreator_s.java    From coming with MIT License 4 votes vote down vote up
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
  attachLiteralTypes(t, n);

  switch (n.getType()) {
    case Token.CALL:
      checkForClassDefiningCalls(t, n, parent);
      break;

    case Token.FUNCTION:
      if (t.getInput() == null || !t.getInput().isExtern()) {
        nonExternFunctions.add(n);
      }

      // Hoisted functions are handled during pre-traversal.
      if (!NodeUtil.isHoistedFunctionDeclaration(n)) {
        defineFunctionLiteral(n, parent);
      }
      break;

    case Token.ASSIGN:
      // Handle initialization of properties.
      Node firstChild = n.getFirstChild();
      if (firstChild.getType() == Token.GETPROP &&
          firstChild.isQualifiedName()) {
        maybeDeclareQualifiedName(t, n.getJSDocInfo(),
            firstChild, n, firstChild.getNext());
      }
      break;

    case Token.CATCH:
      defineCatch(n, parent);
      break;

    case Token.VAR:
      defineVar(n, parent);
      break;

    case Token.GETPROP:
      // Handle stubbed properties.
      if (parent.getType() == Token.EXPR_RESULT &&
          n.isQualifiedName()) {
        maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null);
      }
      break;
  }
}
 
Example 14
Source File: CrossModuleCodeMotion.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Determines whether the given NAME node belongs to a declaration that
 * can be moved across modules. If it is, registers it properly.
 *
 * There are four types of movable declarations:
 * 1) var NAME = [movable object];
 * 2) function NAME() {}
 * 3) NAME = [movable object];
 *    NAME.prop = [movable object];
 *    NAME.prop.prop2 = [movable object];
 *    etc.
 * 4) Class-defining function calls, like "inherits" and "mixin".
 *    NAME.inherits([some other name]);
 * where "movable object" is a literal or a function.
 */
private boolean maybeProcessDeclaration(NodeTraversal t, Node name,
    Node parent, NamedInfo info) {
  Node gramps = parent.getParent();
  switch (parent.getType()) {
    case Token.VAR:
      if (canMoveValue(name.getFirstChild())) {
        return info.addDeclaration(
            new Declaration(t.getModule(), name, parent, gramps));
      }
      return false;

    case Token.FUNCTION:
      if (NodeUtil.isFunctionDeclaration(parent)) {
        return info.addDeclaration(
            new Declaration(t.getModule(), name, parent, gramps));
      }
      return false;

    case Token.ASSIGN:
    case Token.GETPROP:
      Node child = name;

      // Look for assignment expressions where the name is the root
      // of a qualified name on the left hand side of the assignment.
      for (Node current : name.getAncestors()) {
        if (current.isGetProp()) {
          // fallthrough
        } else if (current.isAssign() &&
                   current.getFirstChild() == child) {
          Node currentParent = current.getParent();
          if (currentParent.isExprResult() &&
              canMoveValue(current.getLastChild())) {
            return info.addDeclaration(
                new Declaration(t.getModule(), current, currentParent,
                    currentParent.getParent()));
          }
        } else {
          return false;
        }

        child = current;
      }
      return false;

    case Token.CALL:
      if (NodeUtil.isExprCall(gramps)) {
        SubclassRelationship relationship =
            compiler.getCodingConvention().getClassesDefinedByCall(parent);
        if (relationship != null &&
            name.getString().equals(relationship.subclassName)) {
          return info.addDeclaration(
              new Declaration(t.getModule(), parent, gramps,
                  gramps.getParent()));
        }
      }
      return false;

    default:
      return false;
  }
}
 
Example 15
Source File: Closure_80_NodeUtil_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * @param locals A predicate to apply to unknown local values.
 * @return Whether the node is known to be a value that is not a reference
 *     outside the expression scope.
 */
static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) {
  switch (value.getType()) {
    case Token.ASSIGN:
      // A result that is aliased by a non-local name, is the effectively the
      // same as returning a non-local name, but this doesn't matter if the
      // value is immutable.
      return NodeUtil.isImmutableValue(value.getLastChild())
          || (locals.apply(value)
              && evaluatesToLocalValue(value.getLastChild(), locals));
    case Token.COMMA:
      return evaluatesToLocalValue(value.getLastChild(), locals);
    case Token.AND:
    case Token.OR:
      return evaluatesToLocalValue(value.getFirstChild(), locals)
         && evaluatesToLocalValue(value.getLastChild(), locals);
    case Token.HOOK:
      return evaluatesToLocalValue(value.getFirstChild().getNext(), locals)
         && evaluatesToLocalValue(value.getLastChild(), locals);
    case Token.INC:
    case Token.DEC:
      if (value.getBooleanProp(Node.INCRDECR_PROP)) {
        return evaluatesToLocalValue(value.getFirstChild(), locals);
      } else {
        return true;
      }
    case Token.THIS:
      return locals.apply(value);
    case Token.NAME:
      return isImmutableValue(value) || locals.apply(value);
    case Token.GETELEM:
    case Token.GETPROP:
      // There is no information about the locality of object properties.
      return locals.apply(value);
    case Token.CALL:
      return callHasLocalResult(value)
          || isToStringMethodCall(value)
          || locals.apply(value);
    case Token.NEW:
      return newHasLocalResult(value)
             || locals.apply(value);
    case Token.FUNCTION:
    case Token.REGEXP:
    case Token.ARRAYLIT:
    case Token.OBJECTLIT:
      // Literals objects with non-literal children are allowed.
      return true;
    case Token.DELPROP:
    case Token.IN:
      // TODO(johnlenz): should IN operator be included in #isSimpleOperator?
      return true;
    default:
      // Other op force a local value:
      //  x = '' + g (x is now an local string)
      //  x -= g (x is now an local number)
      if (isAssignmentOp(value)
          || isSimpleOperator(value)
          || isImmutableValue(value)) {
        return true;
      }

      throw new IllegalStateException(
          "Unexpected expression node" + value +
          "\n parent:" + value.getParent());
  }
}
 
Example 16
Source File: Closure_61_NodeUtil_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * Is this a GETPROP or GETELEM node?
 */
static boolean isGet(Node n) {
  return n.getType() == Token.GETPROP
      || n.getType() == Token.GETELEM;
}
 
Example 17
Source File: Closure_75_NodeUtil_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * Determines whether the given value may be assigned to a define.
 *
 * @param val The value being assigned.
 * @param defines The list of names of existing defines.
 */
static boolean isValidDefineValue(Node val, Set<String> defines) {
  switch (val.getType()) {
    case Token.STRING:
    case Token.NUMBER:
    case Token.TRUE:
    case Token.FALSE:
      return true;

    // Binary operators are only valid if both children are valid.
    case Token.ADD:
    case Token.BITAND:
    case Token.BITNOT:
    case Token.BITOR:
    case Token.BITXOR:
    case Token.DIV:
    case Token.EQ:
    case Token.GE:
    case Token.GT:
    case Token.LE:
    case Token.LSH:
    case Token.LT:
    case Token.MOD:
    case Token.MUL:
    case Token.NE:
    case Token.RSH:
    case Token.SHEQ:
    case Token.SHNE:
    case Token.SUB:
    case Token.URSH:
      return isValidDefineValue(val.getFirstChild(), defines)
          && isValidDefineValue(val.getLastChild(), defines);

    // Uniary operators are valid if the child is valid.
    case Token.NOT:
    case Token.NEG:
    case Token.POS:
      return isValidDefineValue(val.getFirstChild(), defines);

    // Names are valid if and only if they are defines themselves.
    case Token.NAME:
    case Token.GETPROP:
      if (val.isQualifiedName()) {
        return defines.contains(val.getQualifiedName());
      }
  }
  return false;
}
 
Example 18
Source File: jMutRepair_003_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Determines whether the given value may be assigned to a define.
 *
 * @param val The value being assigned.
 * @param defines The list of names of existing defines.
 */
static boolean isValidDefineValue(Node val, Set<String> defines) {
  switch (val.getType()) {
    case Token.STRING:
    case Token.NUMBER:
    case Token.TRUE:
    case Token.FALSE:
      return true;

    // Binary operators are only valid if both children are valid.
    case Token.ADD:
    case Token.BITAND:
    case Token.BITNOT:
    case Token.BITOR:
    case Token.BITXOR:
    case Token.DIV:
    case Token.EQ:
    case Token.GE:
    case Token.GT:
    case Token.LE:
    case Token.LSH:
    case Token.LT:
    case Token.MOD:
    case Token.MUL:
    case Token.NE:
    case Token.RSH:
    case Token.SHEQ:
    case Token.SHNE:
    case Token.SUB:
    case Token.URSH:
      return isValidDefineValue(val.getFirstChild(), defines)
          && isValidDefineValue(val.getLastChild(), defines);

    // Unary operators are valid if the child is valid.
    case Token.NOT:
    case Token.NEG:
    case Token.POS:
      return isValidDefineValue(val.getFirstChild(), defines);

    // Names are valid if and only if they are defines themselves.
    case Token.NAME:
    case Token.GETPROP:
      if (val.isQualifiedName()) {
        return defines.contains(val.getQualifiedName());
      }
  }
  return false;
}
 
Example 19
Source File: Nopol2017_0051_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Visits a CALL 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 visitCall(NodeTraversal t, Node n) {
  Node child = n.getFirstChild();
  JSType childType = getJSType(child).restrictByNotNullOrUndefined();

  if (!childType.canBeCalled()) {
    report(t, n, NOT_CALLABLE, childType.toString());
    ensureTyped(t, n);
    return;
  }

  // A couple of types can be called as if they were functions.
  // If it is a function type, then validate parameters.
  if (childType instanceof FunctionType) {
    FunctionType functionType = (FunctionType) childType;

    boolean isExtern = false;
    JSDocInfo functionJSDocInfo = functionType.getJSDocInfo();
    if(functionJSDocInfo != null) {
      String sourceName = functionJSDocInfo.getSourceName();
      CompilerInput functionSource = compiler.getInput(sourceName);
      isExtern = functionSource.isExtern();
    }

    // Non-native constructors should not be called directly
    // unless they specify a return type and are defined
    // in an extern.
    if (functionType.isConstructor() &&
        !functionType.isNativeObjectType() &&
        (functionType.getReturnType().isUnknownType() ||
         functionType.getReturnType().isVoidType() ||
         !isExtern)) {
      report(t, n, CONSTRUCTOR_NOT_CALLABLE, childType.toString());
    }

    // Functions with explcit 'this' types must be called in a GETPROP
    // or GETELEM.
    if (functionType.isOrdinaryFunction() &&
        !functionType.getTypeOfThis().isUnknownType() &&
        !functionType.getTypeOfThis().isNativeObjectType() &&
        !(child.getType() == Token.GETELEM ||
          child.getType() == Token.GETPROP)) {
      report(t, n, EXPECTED_THIS_TYPE, functionType.toString());
    }

    visitParameterList(t, n, functionType);
    ensureTyped(t, n, functionType.getReturnType());
  } else {
    ensureTyped(t, n);
  }

  // TODO: Add something to check for calls of RegExp objects, which is not
  // supported by IE.  Either say something about the return type or warn
  // about the non-portability of the call or both.
}
 
Example 20
Source File: Closure_57_ClosureCodingConvention_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * {@inheritDoc}
 *
 * <p>Understands several different inheritance patterns that occur in
 * Google code (various uses of {@code inherits} and {@code mixin}).
 */
@Override
public SubclassRelationship getClassesDefinedByCall(Node callNode) {
  Node callName = callNode.getFirstChild();
  SubclassType type = typeofClassDefiningName(callName);
  if (type != null) {
    Node subclass = null;
    Node superclass = callNode.getLastChild();

    // There are six possible syntaxes for a class-defining method:
    // SubClass.inherits(SuperClass)
    // goog.inherits(SubClass, SuperClass)
    // goog$inherits(SubClass, SuperClass)
    // SubClass.mixin(SuperClass.prototype)
    // goog.mixin(SubClass.prototype, SuperClass.prototype)
    // goog$mixin(SubClass.prototype, SuperClass.prototype)
    boolean isDeprecatedCall = callNode.getChildCount() == 2 &&
        callName.getType() == Token.GETPROP;
    if (isDeprecatedCall) {
      // SubClass.inherits(SuperClass)
      subclass = callName.getFirstChild();
    } else if (callNode.getChildCount() == 3) {
      // goog.inherits(SubClass, SuperClass)
      subclass = callName.getNext();
    } else {
      return null;
    }

    if (type == SubclassType.MIXIN) {
      // Only consider mixins that mix two prototypes as related to
      // inheritance.
      if (!endsWithPrototype(superclass)) {
        return null;
      }
      if (!isDeprecatedCall) {
        if (!endsWithPrototype(subclass)) {
          return null;
        }
        // Strip off the prototype from the name.
        subclass = subclass.getFirstChild();
      }
      superclass = superclass.getFirstChild();
    }

    // bail out if either of the side of the "inherits"
    // isn't a real class name. This prevents us from
    // doing something weird in cases like:
    // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2)
    if (subclass != null &&
        subclass.isUnscopedQualifiedName() &&
        superclass.isUnscopedQualifiedName()) {
      return new SubclassRelationship(type, subclass, superclass);
    }
  }

  return null;
}