Program: 27798
Project: 27798
Buggy source:
/** 
 * @param requiredType The context requires this type; warn if the expressiondoesn't have this type.
 * @param specializedType Used in boolean contexts to infer types of names.Invariant: specializedType is a subtype of requiredType.
 */
private EnvTypePair analyzeExprFwd(Node expr,TypeEnv inEnv,JSType requiredType,JSType specializedType){
  Preconditions.checkArgument(requiredType != null && !requiredType.isBottom());
  int exprKind=expr.getType();
switch (exprKind) {
case Token.EMPTY:
    return new EnvTypePair(inEnv,JSType.UNKNOWN);
case Token.FUNCTION:
{
    String fnName=symbolTable.getFunInternalName(expr);
    return new EnvTypePair(inEnv,envGetType(inEnv,fnName));
  }
case Token.FALSE:
case Token.NULL:
case Token.NUMBER:
case Token.STRING:
case Token.TRUE:
return new EnvTypePair(inEnv,scalarValueToType(exprKind));
case Token.OBJECTLIT:
{
JSType result=JSType.TOP_OBJECT;
TypeEnv env=inEnv;
for (Node key : expr.children()) {
  String pname=NodeUtil.getObjectLitKeyName(key);
  JSType reqPtype=requiredType.mayHaveProp(pname) ? requiredType.getProp(pname) : JSType.TOP;
  JSType specPtype=specializedType.mayHaveProp(pname) ? specializedType.getProp(pname) : JSType.TOP;
  EnvTypePair pair=analyzeExprFwd(key.getLastChild(),env,reqPtype,specPtype);
  result=result.withProperty(pname,pair.type);
  env=pair.env;
}
return new EnvTypePair(env,result);
}
case Token.THIS:
{
if (!currentScope.hasThis()) {
warnings.add(JSError.make(expr,CheckGlobalThis.GLOBAL_THIS));
return new EnvTypePair(inEnv,JSType.UNKNOWN);
}
JSType thisType=currentScope.getDeclaredTypeOf("String_Node_Str");
return new EnvTypePair(inEnv,thisType);
}
case Token.NAME:
{
String varName=expr.getQualifiedName();
if (varName.equals("String_Node_Str")) {
return new EnvTypePair(inEnv,JSType.UNDEFINED);
}
if (currentScope.isLocalVar(varName) || currentScope.isFormalParam(varName) || currentScope.isLocalFunDef(varName)|| currentScope.isOuterVar(varName)) {
JSType inferredType=envGetType(inEnv,varName);
println(varName,"String_Node_Str",inferredType,"String_Node_Str",requiredType);
if (!inferredType.isSubtypeOf(requiredType)) {
if (currentScope.getDeclaredTypeOf(varName) == null && inferredType.equals(JSType.NUM_OR_STR) && requiredType.isSubtypeOf(inferredType)) {
  inferredType=requiredType;
}
 else {
  return new EnvTypePair(inEnv,inferredType);
}
}
JSType preciseType=inferredType.specialize(specializedType);
println(varName,"String_Node_Str",preciseType);
if (!preciseType.isBottom() && currentScope.isUndeclaredFormal(varName) && requiredType.hasNonScalar()) {
preciseType=preciseType.withLoose();
}
return EnvTypePair.addBinding(inEnv,varName,preciseType);
}
println("String_Node_Str",varName);
return new EnvTypePair(inEnv,JSType.UNKNOWN);
}
case Token.AND:
case Token.OR:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
if ((specializedType.isTruthy() && exprKind == Token.AND) || (specializedType.isFalsy() && exprKind == Token.OR)) {
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.TOP,specializedType);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,specializedType);
return rhsPair;
}
 else if ((specializedType.isFalsy() && exprKind == Token.AND) || (specializedType.isTruthy() && exprKind == Token.OR)) {
EnvTypePair shortCircuitPair=analyzeExprFwd(lhs,inEnv,JSType.TOP,specializedType);
JSType negatedType=specializedType.isTruthy() ? JSType.FALSY : JSType.TRUTHY;
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.TOP,negatedType);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,specializedType);
return EnvTypePair.join(rhsPair,shortCircuitPair);
}
 else {
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env);
return rhsPair;
}
}
case Token.INC:
case Token.DEC:
case Token.BITNOT:
case Token.POS:
case Token.NEG:
{
Node child=expr.getFirstChild();
EnvTypePair pair=analyzeExprFwd(child,inEnv,JSType.NUMBER);
if (!pair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(child,expr.getType(),JSType.NUMBER,pair.type);
}
pair.type=JSType.NUMBER;
return pair;
}
case Token.TYPEOF:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv);
pair.type=JSType.STRING;
return pair;
}
case Token.INSTANCEOF:
{
Node obj=expr.getFirstChild();
Node ctor=expr.getLastChild();
EnvTypePair objPair, ctorPair;
objPair=analyzeExprFwd(obj,inEnv);
JSType objType=objPair.type;
if (!objType.equals(JSType.TOP) && !objType.equals(JSType.UNKNOWN) && !objType.hasNonScalar()) {
warnInvalidOperand(obj,Token.INSTANCEOF,"String_Node_Str",objPair.type);
}
ctorPair=analyzeExprFwd(ctor,objPair.env,JSType.topFunction());
JSType ctorType=ctorPair.type;
FunctionType ctorFunType=ctorType.getFunType();
if (!ctorType.isUnknown() && (!ctorType.isSubtypeOf(JSType.topFunction()) || !ctorFunType.isConstructor())) {
warnInvalidOperand(ctor,Token.INSTANCEOF,"String_Node_Str",ctorType);
}
if (ctorFunType == null || !ctorFunType.isConstructor() || (!specializedType.isTruthy() && !specializedType.isFalsy())) {
ctorPair.type=JSType.BOOLEAN;
return ctorPair;
}
JSType instanceType=ctorFunType.getTypeOfThis();
objPair=analyzeExprFwd(obj,inEnv,JSType.TOP,specializedType.isTruthy() ? objPair.type.specialize(instanceType) : objPair.type.removeType(instanceType));
ctorPair=analyzeExprFwd(ctor,objPair.env,JSType.topFunction());
ctorPair.type=JSType.BOOLEAN;
return ctorPair;
}
case Token.ADD:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.NUM_OR_STR);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.NUM_OR_STR);
JSType lhsType=lhsPair.type;
JSType rhsType=rhsPair.type;
if (!lhsType.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(lhs,expr.getType(),JSType.NUM_OR_STR,lhsType);
}
if (!rhsType.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(rhs,expr.getType(),JSType.NUM_OR_STR,rhsType);
}
return new EnvTypePair(rhsPair.env,JSType.plus(lhsType,rhsType));
}
case Token.BITOR:
case Token.BITAND:
case Token.BITXOR:
case Token.DIV:
case Token.LSH:
case Token.MOD:
case Token.MUL:
case Token.RSH:
case Token.SUB:
case Token.URSH:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.NUMBER);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.NUMBER);
if (!lhsPair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(lhs,expr.getType(),JSType.NUMBER,lhsPair.type);
}
if (!rhsPair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(rhs,expr.getType(),JSType.NUMBER,rhsPair.type);
}
rhsPair.type=JSType.NUMBER;
return rhsPair;
}
case Token.ASSIGN:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
LValueResult lvalue=analyzeLValueFwd(lhs,inEnv,requiredType);
JSType declType=lvalue.declType;
EnvTypePair rhsPair=analyzeExprFwd(rhs,lvalue.env,requiredType,specializedType);
JSType rhsType=rhsPair.type;
if (declType != null && !rhsType.isSubtypeOf(declType)) {
warnings.add(JSError.make(expr,MISTYPED_ASSIGN_RHS,declType.toString(),rhsType.toString()));
rhsType=declType;
}
return new EnvTypePair(updateLvalueTypeInEnv(rhsPair.env,lhs,lvalue.ptr,rhsType),rhsType);
}
case Token.ASSIGN_ADD:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
JSType lhsReqType=specializeWithCorrection(requiredType,JSType.NUM_OR_STR);
LValueResult lvalue=analyzeLValueFwd(lhs,inEnv,lhsReqType);
JSType lhsType=lvalue.type;
if (!lhsType.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(lhs,Token.ASSIGN_ADD,JSType.NUM_OR_STR,lhsType);
}
JSType rhsReqType=lhsType.equals(JSType.NUMBER) ? JSType.NUMBER : JSType.NUM_OR_STR;
EnvTypePair pair=analyzeExprFwd(rhs,lvalue.env,rhsReqType);
if (!pair.type.isSubtypeOf(rhsReqType)) {
warnInvalidOperand(rhs,Token.ASSIGN_ADD,rhsReqType,pair.type);
}
return pair;
}
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
LValueResult lvalue=analyzeLValueFwd(lhs,inEnv,JSType.NUMBER);
JSType lhsType=lvalue.type;
if (!lhsType.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(lhs,expr.getType(),JSType.NUMBER,lhsType);
}
EnvTypePair pair=analyzeExprFwd(rhs,lvalue.env,JSType.NUMBER);
if (!pair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(rhs,expr.getType(),JSType.NUMBER,pair.type);
}
return new EnvTypePair(updateLvalueTypeInEnv(pair.env,lhs,lvalue.ptr,JSType.NUMBER),JSType.NUMBER);
}
case Token.SHEQ:
case Token.SHNE:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env);
if ((exprKind == Token.SHEQ && specializedType.isTruthy()) || (exprKind == Token.SHNE && specializedType.isFalsy())) {
JSType meetType=JSType.meet(lhsPair.type,rhsPair.type);
lhsPair=analyzeExprFwd(lhs,rhsPair.env,JSType.TOP,meetType);
rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,meetType);
}
 else if ((exprKind == Token.SHEQ && specializedType.isFalsy()) || (exprKind == Token.SHNE && specializedType.isTruthy())) {
JSType lhsType=lhsPair.type;
JSType rhsType=rhsPair.type;
if (lhsType.equals(JSType.NULL) || lhsType.equals(JSType.UNDEFINED)) {
rhsType=rhsType.removeType(lhsType);
}
 else if (rhsType.equals(JSType.NULL) || rhsType.equals(JSType.UNDEFINED)) {
lhsType=lhsType.removeType(rhsType);
}
lhsPair=analyzeExprFwd(lhs,rhsPair.env,JSType.TOP,lhsType);
rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,rhsType);
}
rhsPair.type=JSType.BOOLEAN;
return rhsPair;
}
case Token.EQ:
case Token.NE:
return analyzeNonStrictComparisonFwd(expr,inEnv,requiredType,specializedType);
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env);
if (lhsPair.type.isScalar() && rhsPair.type.isUnknown()) {
rhsPair=analyzeExprFwd(rhs,lhsPair.env,lhsPair.type);
}
 else if (lhsPair.type.isUnknown() && rhsPair.type.isScalar()) {
lhsPair=analyzeExprFwd(lhs,inEnv,rhsPair.type);
rhsPair=analyzeExprFwd(rhs,lhsPair.env,rhsPair.type);
}
 else if (lhs.isVar() && lhsPair.type.isUnknown() && rhs.isVar()&& rhsPair.type.isUnknown()) {
TypeEnv env=envPutType(rhsPair.env,lhs.getQualifiedName(),JSType.TOP_SCALAR);
env=envPutType(rhsPair.env,rhs.getQualifiedName(),JSType.TOP_SCALAR);
return new EnvTypePair(env,JSType.BOOLEAN);
}
JSType lhsType=lhsPair.type;
JSType rhsType=rhsPair.type;
if (!lhsType.isSubtypeOf(JSType.TOP_SCALAR) || !rhsType.isSubtypeOf(JSType.TOP_SCALAR) || !JSType.areCompatibleScalarTypes(lhsType,rhsType)) {
warnInvalidOperand(expr,exprKind,"String_Node_Str",lhsType.toString() + "String_Node_Str" + rhsType.toString());
}
rhsPair.type=JSType.BOOLEAN;
return rhsPair;
}
case Token.GETPROP:
Preconditions.checkState(!NodeUtil.isAssignmentOp(expr.getParent()) || !NodeUtil.isLValue(expr));
return analyzePropAccessFwd(expr.getFirstChild(),expr.getLastChild().getString(),inEnv,requiredType,specializedType);
case Token.HOOK:
{
Node cond=expr.getFirstChild();
Node thenBranch=cond.getNext();
Node elseBranch=thenBranch.getNext();
TypeEnv trueEnv=analyzeExprFwd(cond,inEnv,JSType.TOP,JSType.TRUE_TYPE).env;
TypeEnv falseEnv=analyzeExprFwd(cond,inEnv,JSType.TOP,JSType.FALSE_TYPE).env;
EnvTypePair thenPair=analyzeExprFwd(thenBranch,trueEnv,requiredType,specializedType);
EnvTypePair elsePair=analyzeExprFwd(elseBranch,falseEnv,requiredType,specializedType);
return EnvTypePair.join(thenPair,elsePair);
}
case Token.CALL:
case Token.NEW:
{
Node callee=expr.getFirstChild();
EnvTypePair calleePair=analyzeExprFwd(callee,inEnv,JSType.topFunction());
JSType calleeType=calleePair.type;
if (!calleeType.isSubtypeOf(JSType.topFunction())) {
warnings.add(JSError.make(expr,TypeCheck.NOT_CALLABLE,calleeType.toString()));
}
FunctionType funType=calleeType.getFunType();
if (funType == null || funType.isTopFunction()) {
return new EnvTypePair(inEnv,requiredType);
}
 else if (funType.isLoose()) {
return analyzeLooseCallNodeFwd(expr,inEnv,requiredType);
}
 else if (expr.isCall() && funType.isConstructor()) {
warnings.add(JSError.make(expr,TypeCheck.CONSTRUCTOR_NOT_CALLABLE,funType.toString()));
return new EnvTypePair(inEnv,requiredType);
}
 else if (expr.isNew() && !funType.isConstructor()) {
warnings.add(JSError.make(expr,TypeCheck.NOT_A_CONSTRUCTOR,funType.toString()));
return new EnvTypePair(inEnv,requiredType);
}
int maxArity=funType.getMaxArity();
int minArity=funType.getMinArity();
int numArgs=expr.getChildCount() - 1;
if (numArgs < minArity || numArgs > maxArity) {
warnings.add(JSError.make(expr,TypeCheck.WRONG_ARGUMENT_COUNT,"String_Node_Str",Integer.toString(numArgs),Integer.toString(minArity),"String_Node_Str" + maxArity));
}
List<JSType> argTypes=Lists.newArrayList();
TypeEnv tmpEnv=inEnv;
for (int i=0; i < numArgs; i++) {
JSType formalType=(i < maxArity) ? funType.getFormalType(i) : JSType.TOP;
if (formalType.isBottom()) {
warnings.add(JSError.make(expr,CALL_FUNCTION_WITH_BOTTOM_FORMAL,Integer.toString(i)));
formalType=JSType.TOP;
}
Node arg=expr.getChildAtIndex(i + 1);
EnvTypePair pair=analyzeExprFwd(arg,tmpEnv,formalType);
if (!pair.type.isSubtypeOf(formalType)) {
warnings.add(JSError.make(arg,INVALID_ARGUMENT_TYPE,Integer.toString(i + 1),"String_Node_Str",formalType.toString(),pair.type.toString()));
pair.type=JSType.UNKNOWN;
}
if (i < maxArity) {
Preconditions.checkState(!pair.type.equals(JSType.topFunction()));
argTypes.add(pair.type);
}
tmpEnv=pair.env;
}
JSType retType=funType.getReturnType();
if (callee.isName()) {
String calleeName=callee.getQualifiedName();
if (currentScope.isKnownFunction(calleeName)) {
if (currentScope.isLocalFunDef(calleeName)) {
collectTypesForFreeVarsFwd(callee,tmpEnv);
}
 else {
JSType expectedRetType=requiredType;
println("String_Node_Str",expectedRetType,"String_Node_Str",argTypes);
DeferredCheck dc;
if (expr.isCall()) {
dc=deferredChecks.get(expr);
dc.updateReturn(expectedRetType);
}
 else {
dc=new DeferredCheck(expr,currentScope,currentScope.getScope(calleeName));
dc.updateReturn(JSType.TOP);
deferredChecks.put(expr,dc);
}
dc.updateArgTypes(argTypes);
}
}
}
return new EnvTypePair(tmpEnv,retType);
}
case Token.COMMA:
return analyzeExprFwd(expr.getLastChild(),analyzeExprFwd(expr.getFirstChild(),inEnv).env,requiredType,specializedType);
case Token.NOT:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv,JSType.TOP,specializedType.negate());
pair.type=pair.type.negate().toBoolean();
return pair;
}
case Token.GETELEM:
{
Node receiver=expr.getFirstChild();
Node index=expr.getLastChild();
if (index.isString()) {
return analyzePropAccessFwd(receiver,index.getString(),inEnv,requiredType,specializedType);
}
EnvTypePair pair=analyzeExprFwd(index,inEnv);
pair=analyzeExprFwd(receiver,pair.env,JSType.TOP_OBJECT);
pair.type=requiredType;
return pair;
}
case Token.VOID:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv);
pair.type=JSType.UNDEFINED;
return pair;
}
case Token.IN:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair pair;
pair=analyzeExprFwd(lhs,inEnv,JSType.NUM_OR_STR);
if (!pair.type.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(lhs,Token.IN,JSType.NUM_OR_STR,pair.type);
}
pair=analyzeExprFwd(rhs,pair.env,JSType.TOP_OBJECT);
if (!pair.type.isSubtypeOf(JSType.TOP_OBJECT)) {
warnInvalidOperand(rhs,Token.IN,"String_Node_Str",pair.type);
pair.type=JSType.BOOLEAN;
return pair;
}
JSType resultType=JSType.BOOLEAN;
if (lhs.isString()) {
String pname=lhs.getString();
if (specializedType.isTruthy()) {
pair=analyzeExprFwd(rhs,inEnv,JSType.TOP_OBJECT,JSType.TOP_OBJECT.withPropertyRequired(pname));
resultType=JSType.TRUE_TYPE;
}
 else if (specializedType.isFalsy()) {
pair=analyzeExprFwd(rhs,inEnv,JSType.TOP_OBJECT);
pair=analyzeExprFwd(rhs,inEnv,JSType.TOP_OBJECT,pair.type.withoutProperty(pname));
resultType=JSType.FALSE_TYPE;
}
}
pair.type=resultType;
return pair;
}
case Token.DELPROP:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv);
pair.type=JSType.BOOLEAN;
return pair;
}
case Token.VAR:
{
Node vdecl=expr.getFirstChild();
String name=vdecl.getQualifiedName();
Node rhs=vdecl.getFirstChild();
TypeEnv env=inEnv;
if (rhs != null) {
env=analyzeExprFwd(rhs,inEnv).env;
}
return new EnvTypePair(envPutType(env,name,JSType.STRING),JSType.STRING);
}
case Token.REGEXP:
return new EnvTypePair(inEnv,regexpType);
case Token.ARRAYLIT:
TypeEnv env=inEnv;
for (Node arrayElm=expr.getFirstChild(); arrayElm != null; arrayElm=arrayElm.getNext()) {
env=analyzeExprFwd(arrayElm,env).env;
}
return new EnvTypePair(env,arrayType);
default :
throw new RuntimeException("String_Node_Str" + Token.name(expr.getType()));
}
}

Fixed version (ground truth):
/** 
 * @param requiredType The context requires this type; warn if the expressiondoesn't have this type.
 * @param specializedType Used in boolean contexts to infer types of names.Invariant: specializedType is a subtype of requiredType.
 */
private EnvTypePair analyzeExprFwd(Node expr,TypeEnv inEnv,JSType requiredType,JSType specializedType){
  Preconditions.checkArgument(requiredType != null && !requiredType.isBottom());
  int exprKind=expr.getType();
switch (exprKind) {
case Token.EMPTY:
    return new EnvTypePair(inEnv,JSType.UNKNOWN);
case Token.FUNCTION:
{
    String fnName=symbolTable.getFunInternalName(expr);
    return new EnvTypePair(inEnv,envGetType(inEnv,fnName));
  }
case Token.FALSE:
case Token.NULL:
case Token.NUMBER:
case Token.STRING:
case Token.TRUE:
return new EnvTypePair(inEnv,scalarValueToType(exprKind));
case Token.OBJECTLIT:
{
JSType result=JSType.TOP_OBJECT;
TypeEnv env=inEnv;
for (Node key : expr.children()) {
  String pname=NodeUtil.getObjectLitKeyName(key);
  JSType reqPtype=requiredType.mayHaveProp(pname) ? requiredType.getProp(pname) : JSType.TOP;
  JSType specPtype=specializedType.mayHaveProp(pname) ? specializedType.getProp(pname) : JSType.TOP;
  EnvTypePair pair=analyzeExprFwd(key.getLastChild(),env,reqPtype,specPtype);
  result=result.withProperty(pname,pair.type);
  env=pair.env;
}
return new EnvTypePair(env,result);
}
case Token.THIS:
{
if (!currentScope.hasThis()) {
warnings.add(JSError.make(expr,CheckGlobalThis.GLOBAL_THIS));
return new EnvTypePair(inEnv,JSType.UNKNOWN);
}
JSType thisType=currentScope.getDeclaredTypeOf("String_Node_Str");
return new EnvTypePair(inEnv,thisType);
}
case Token.NAME:
{
String varName=expr.getQualifiedName();
if (varName.equals("String_Node_Str")) {
return new EnvTypePair(inEnv,JSType.UNDEFINED);
}
if (currentScope.isLocalVar(varName) || currentScope.isFormalParam(varName) || currentScope.isLocalFunDef(varName)|| currentScope.isOuterVar(varName)) {
JSType inferredType=envGetType(inEnv,varName);
println(varName,"String_Node_Str",inferredType,"String_Node_Str",requiredType);
if (!inferredType.isSubtypeOf(requiredType)) {
if (currentScope.getDeclaredTypeOf(varName) == null && inferredType.equals(JSType.NUM_OR_STR) && requiredType.isSubtypeOf(inferredType)) {
  inferredType=requiredType;
}
 else {
  return new EnvTypePair(inEnv,inferredType);
}
}
JSType preciseType=inferredType.specialize(specializedType);
println(varName,"String_Node_Str",preciseType);
if (!preciseType.isBottom() && currentScope.isUndeclaredFormal(varName) && requiredType.hasNonScalar()) {
preciseType=preciseType.withLoose();
}
return EnvTypePair.addBinding(inEnv,varName,preciseType);
}
println("String_Node_Str",varName);
return new EnvTypePair(inEnv,JSType.UNKNOWN);
}
case Token.AND:
case Token.OR:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
if ((specializedType.isTruthy() && exprKind == Token.AND) || (specializedType.isFalsy() && exprKind == Token.OR)) {
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.TOP,specializedType);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,specializedType);
return rhsPair;
}
 else if ((specializedType.isFalsy() && exprKind == Token.AND) || (specializedType.isTruthy() && exprKind == Token.OR)) {
EnvTypePair shortCircuitPair=analyzeExprFwd(lhs,inEnv,JSType.TOP,specializedType);
JSType negatedType=specializedType.isTruthy() ? JSType.FALSY : JSType.TRUTHY;
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.TOP,negatedType);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,specializedType);
return EnvTypePair.join(rhsPair,shortCircuitPair);
}
 else {
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env);
return rhsPair;
}
}
case Token.INC:
case Token.DEC:
case Token.BITNOT:
case Token.POS:
case Token.NEG:
{
Node child=expr.getFirstChild();
EnvTypePair pair=analyzeExprFwd(child,inEnv,JSType.NUMBER);
if (!pair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(child,expr.getType(),JSType.NUMBER,pair.type);
}
pair.type=JSType.NUMBER;
return pair;
}
case Token.TYPEOF:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv);
pair.type=JSType.STRING;
return pair;
}
case Token.INSTANCEOF:
{
Node obj=expr.getFirstChild();
Node ctor=expr.getLastChild();
EnvTypePair objPair, ctorPair;
objPair=analyzeExprFwd(obj,inEnv);
JSType objType=objPair.type;
if (!objType.equals(JSType.TOP) && !objType.equals(JSType.UNKNOWN) && !objType.hasNonScalar()) {
warnInvalidOperand(obj,Token.INSTANCEOF,"String_Node_Str",objPair.type);
}
ctorPair=analyzeExprFwd(ctor,objPair.env,JSType.topFunction());
JSType ctorType=ctorPair.type;
FunctionType ctorFunType=ctorType.getFunType();
if (!ctorType.isUnknown() && (!ctorType.isSubtypeOf(JSType.topFunction()) || !ctorFunType.isConstructor())) {
warnInvalidOperand(ctor,Token.INSTANCEOF,"String_Node_Str",ctorType);
}
if (ctorFunType == null || !ctorFunType.isConstructor() || (!specializedType.isTruthy() && !specializedType.isFalsy())) {
ctorPair.type=JSType.BOOLEAN;
return ctorPair;
}
JSType instanceType=ctorFunType.getTypeOfThis();
objPair=analyzeExprFwd(obj,inEnv,JSType.TOP,specializedType.isTruthy() ? objPair.type.specialize(instanceType) : objPair.type.removeType(instanceType));
ctorPair=analyzeExprFwd(ctor,objPair.env,JSType.topFunction());
ctorPair.type=JSType.BOOLEAN;
return ctorPair;
}
case Token.ADD:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.NUM_OR_STR);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.NUM_OR_STR);
JSType lhsType=lhsPair.type;
JSType rhsType=rhsPair.type;
if (!lhsType.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(lhs,expr.getType(),JSType.NUM_OR_STR,lhsType);
}
if (!rhsType.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(rhs,expr.getType(),JSType.NUM_OR_STR,rhsType);
}
return new EnvTypePair(rhsPair.env,JSType.plus(lhsType,rhsType));
}
case Token.BITOR:
case Token.BITAND:
case Token.BITXOR:
case Token.DIV:
case Token.LSH:
case Token.MOD:
case Token.MUL:
case Token.RSH:
case Token.SUB:
case Token.URSH:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv,JSType.NUMBER);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.NUMBER);
if (!lhsPair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(lhs,expr.getType(),JSType.NUMBER,lhsPair.type);
}
if (!rhsPair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(rhs,expr.getType(),JSType.NUMBER,rhsPair.type);
}
rhsPair.type=JSType.NUMBER;
return rhsPair;
}
case Token.ASSIGN:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
LValueResult lvalue=analyzeLValueFwd(lhs,inEnv,requiredType);
JSType declType=lvalue.declType;
EnvTypePair rhsPair=analyzeExprFwd(rhs,lvalue.env,requiredType,specializedType);
JSType rhsType=rhsPair.type;
if (declType != null && !rhsType.isSubtypeOf(declType)) {
warnings.add(JSError.make(expr,MISTYPED_ASSIGN_RHS,declType.toString(),rhsType.toString()));
rhsType=declType;
}
return new EnvTypePair(updateLvalueTypeInEnv(rhsPair.env,lhs,lvalue.ptr,rhsType),rhsType);
}
case Token.ASSIGN_ADD:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
JSType lhsReqType=specializeWithCorrection(requiredType,JSType.NUM_OR_STR);
LValueResult lvalue=analyzeLValueFwd(lhs,inEnv,lhsReqType);
JSType lhsType=lvalue.type;
if (!lhsType.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(lhs,Token.ASSIGN_ADD,JSType.NUM_OR_STR,lhsType);
}
JSType rhsReqType=lhsType.equals(JSType.NUMBER) ? JSType.NUMBER : JSType.NUM_OR_STR;
EnvTypePair pair=analyzeExprFwd(rhs,lvalue.env,rhsReqType);
if (!pair.type.isSubtypeOf(rhsReqType)) {
warnInvalidOperand(rhs,Token.ASSIGN_ADD,rhsReqType,pair.type);
}
return pair;
}
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
LValueResult lvalue=analyzeLValueFwd(lhs,inEnv,JSType.NUMBER);
JSType lhsType=lvalue.type;
if (!lhsType.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(lhs,expr.getType(),JSType.NUMBER,lhsType);
}
EnvTypePair pair=analyzeExprFwd(rhs,lvalue.env,JSType.NUMBER);
if (!pair.type.isSubtypeOf(JSType.NUMBER)) {
warnInvalidOperand(rhs,expr.getType(),JSType.NUMBER,pair.type);
}
return new EnvTypePair(updateLvalueTypeInEnv(pair.env,lhs,lvalue.ptr,JSType.NUMBER),JSType.NUMBER);
}
case Token.SHEQ:
case Token.SHNE:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env);
if ((exprKind == Token.SHEQ && specializedType.isTruthy()) || (exprKind == Token.SHNE && specializedType.isFalsy())) {
JSType meetType=JSType.meet(lhsPair.type,rhsPair.type);
lhsPair=analyzeExprFwd(lhs,rhsPair.env,JSType.TOP,meetType);
rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,meetType);
}
 else if ((exprKind == Token.SHEQ && specializedType.isFalsy()) || (exprKind == Token.SHNE && specializedType.isTruthy())) {
JSType lhsType=lhsPair.type;
JSType rhsType=rhsPair.type;
if (lhsType.equals(JSType.NULL) || lhsType.equals(JSType.UNDEFINED)) {
rhsType=rhsType.removeType(lhsType);
}
 else if (rhsType.equals(JSType.NULL) || rhsType.equals(JSType.UNDEFINED)) {
lhsType=lhsType.removeType(rhsType);
}
lhsPair=analyzeExprFwd(lhs,rhsPair.env,JSType.TOP,lhsType);
rhsPair=analyzeExprFwd(rhs,lhsPair.env,JSType.TOP,rhsType);
}
rhsPair.type=JSType.BOOLEAN;
return rhsPair;
}
case Token.EQ:
case Token.NE:
return analyzeNonStrictComparisonFwd(expr,inEnv,requiredType,specializedType);
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair lhsPair=analyzeExprFwd(lhs,inEnv);
EnvTypePair rhsPair=analyzeExprFwd(rhs,lhsPair.env);
if (lhsPair.type.isScalar() && rhsPair.type.isUnknown()) {
rhsPair=analyzeExprFwd(rhs,lhsPair.env,lhsPair.type);
}
 else if (lhsPair.type.isUnknown() && rhsPair.type.isScalar()) {
lhsPair=analyzeExprFwd(lhs,inEnv,rhsPair.type);
rhsPair=analyzeExprFwd(rhs,lhsPair.env,rhsPair.type);
}
 else if (lhs.isName() && lhsPair.type.isUnknown() && rhs.isName()&& rhsPair.type.isUnknown()) {
TypeEnv env=envPutType(rhsPair.env,lhs.getQualifiedName(),JSType.TOP_SCALAR);
env=envPutType(rhsPair.env,rhs.getQualifiedName(),JSType.TOP_SCALAR);
return new EnvTypePair(env,JSType.BOOLEAN);
}
JSType lhsType=lhsPair.type;
JSType rhsType=rhsPair.type;
if (!lhsType.isSubtypeOf(JSType.TOP_SCALAR) || !rhsType.isSubtypeOf(JSType.TOP_SCALAR) || !JSType.areCompatibleScalarTypes(lhsType,rhsType)) {
warnInvalidOperand(expr,exprKind,"String_Node_Str",lhsType.toString() + "String_Node_Str" + rhsType.toString());
}
rhsPair.type=JSType.BOOLEAN;
return rhsPair;
}
case Token.GETPROP:
Preconditions.checkState(!NodeUtil.isAssignmentOp(expr.getParent()) || !NodeUtil.isLValue(expr));
return analyzePropAccessFwd(expr.getFirstChild(),expr.getLastChild().getString(),inEnv,requiredType,specializedType);
case Token.HOOK:
{
Node cond=expr.getFirstChild();
Node thenBranch=cond.getNext();
Node elseBranch=thenBranch.getNext();
TypeEnv trueEnv=analyzeExprFwd(cond,inEnv,JSType.TOP,JSType.TRUE_TYPE).env;
TypeEnv falseEnv=analyzeExprFwd(cond,inEnv,JSType.TOP,JSType.FALSE_TYPE).env;
EnvTypePair thenPair=analyzeExprFwd(thenBranch,trueEnv,requiredType,specializedType);
EnvTypePair elsePair=analyzeExprFwd(elseBranch,falseEnv,requiredType,specializedType);
return EnvTypePair.join(thenPair,elsePair);
}
case Token.CALL:
case Token.NEW:
{
Node callee=expr.getFirstChild();
EnvTypePair calleePair=analyzeExprFwd(callee,inEnv,JSType.topFunction());
JSType calleeType=calleePair.type;
if (!calleeType.isSubtypeOf(JSType.topFunction())) {
warnings.add(JSError.make(expr,TypeCheck.NOT_CALLABLE,calleeType.toString()));
}
FunctionType funType=calleeType.getFunType();
if (funType == null || funType.isTopFunction()) {
return new EnvTypePair(inEnv,requiredType);
}
 else if (funType.isLoose()) {
return analyzeLooseCallNodeFwd(expr,inEnv,requiredType);
}
 else if (expr.isCall() && funType.isConstructor()) {
warnings.add(JSError.make(expr,TypeCheck.CONSTRUCTOR_NOT_CALLABLE,funType.toString()));
return new EnvTypePair(inEnv,requiredType);
}
 else if (expr.isNew() && !funType.isConstructor()) {
warnings.add(JSError.make(expr,TypeCheck.NOT_A_CONSTRUCTOR,funType.toString()));
return new EnvTypePair(inEnv,requiredType);
}
int maxArity=funType.getMaxArity();
int minArity=funType.getMinArity();
int numArgs=expr.getChildCount() - 1;
if (numArgs < minArity || numArgs > maxArity) {
warnings.add(JSError.make(expr,TypeCheck.WRONG_ARGUMENT_COUNT,"String_Node_Str",Integer.toString(numArgs),Integer.toString(minArity),"String_Node_Str" + maxArity));
}
List<JSType> argTypes=Lists.newArrayList();
TypeEnv tmpEnv=inEnv;
for (int i=0; i < numArgs; i++) {
JSType formalType=(i < maxArity) ? funType.getFormalType(i) : JSType.TOP;
if (formalType.isBottom()) {
warnings.add(JSError.make(expr,CALL_FUNCTION_WITH_BOTTOM_FORMAL,Integer.toString(i)));
formalType=JSType.TOP;
}
Node arg=expr.getChildAtIndex(i + 1);
EnvTypePair pair=analyzeExprFwd(arg,tmpEnv,formalType);
if (!pair.type.isSubtypeOf(formalType)) {
warnings.add(JSError.make(arg,INVALID_ARGUMENT_TYPE,Integer.toString(i + 1),"String_Node_Str",formalType.toString(),pair.type.toString()));
pair.type=JSType.UNKNOWN;
}
if (i < maxArity) {
Preconditions.checkState(!pair.type.equals(JSType.topFunction()));
argTypes.add(pair.type);
}
tmpEnv=pair.env;
}
JSType retType=funType.getReturnType();
if (callee.isName()) {
String calleeName=callee.getQualifiedName();
if (currentScope.isKnownFunction(calleeName)) {
if (currentScope.isLocalFunDef(calleeName)) {
collectTypesForFreeVarsFwd(callee,tmpEnv);
}
 else {
JSType expectedRetType=requiredType;
println("String_Node_Str",expectedRetType,"String_Node_Str",argTypes);
DeferredCheck dc;
if (expr.isCall()) {
dc=deferredChecks.get(expr);
dc.updateReturn(expectedRetType);
}
 else {
dc=new DeferredCheck(expr,currentScope,currentScope.getScope(calleeName));
dc.updateReturn(JSType.TOP);
deferredChecks.put(expr,dc);
}
dc.updateArgTypes(argTypes);
}
}
}
return new EnvTypePair(tmpEnv,retType);
}
case Token.COMMA:
return analyzeExprFwd(expr.getLastChild(),analyzeExprFwd(expr.getFirstChild(),inEnv).env,requiredType,specializedType);
case Token.NOT:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv,JSType.TOP,specializedType.negate());
pair.type=pair.type.negate().toBoolean();
return pair;
}
case Token.GETELEM:
{
Node receiver=expr.getFirstChild();
Node index=expr.getLastChild();
if (index.isString()) {
return analyzePropAccessFwd(receiver,index.getString(),inEnv,requiredType,specializedType);
}
EnvTypePair pair=analyzeExprFwd(index,inEnv);
pair=analyzeExprFwd(receiver,pair.env,JSType.TOP_OBJECT);
pair.type=requiredType;
return pair;
}
case Token.VOID:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv);
pair.type=JSType.UNDEFINED;
return pair;
}
case Token.IN:
{
Node lhs=expr.getFirstChild();
Node rhs=expr.getLastChild();
EnvTypePair pair;
pair=analyzeExprFwd(lhs,inEnv,JSType.NUM_OR_STR);
if (!pair.type.isSubtypeOf(JSType.NUM_OR_STR)) {
warnInvalidOperand(lhs,Token.IN,JSType.NUM_OR_STR,pair.type);
}
pair=analyzeExprFwd(rhs,pair.env,JSType.TOP_OBJECT);
if (!pair.type.isSubtypeOf(JSType.TOP_OBJECT)) {
warnInvalidOperand(rhs,Token.IN,"String_Node_Str",pair.type);
pair.type=JSType.BOOLEAN;
return pair;
}
JSType resultType=JSType.BOOLEAN;
if (lhs.isString()) {
String pname=lhs.getString();
if (specializedType.isTruthy()) {
pair=analyzeExprFwd(rhs,inEnv,JSType.TOP_OBJECT,JSType.TOP_OBJECT.withPropertyRequired(pname));
resultType=JSType.TRUE_TYPE;
}
 else if (specializedType.isFalsy()) {
pair=analyzeExprFwd(rhs,inEnv,JSType.TOP_OBJECT);
pair=analyzeExprFwd(rhs,inEnv,JSType.TOP_OBJECT,pair.type.withoutProperty(pname));
resultType=JSType.FALSE_TYPE;
}
}
pair.type=resultType;
return pair;
}
case Token.DELPROP:
{
EnvTypePair pair=analyzeExprFwd(expr.getFirstChild(),inEnv);
pair.type=JSType.BOOLEAN;
return pair;
}
case Token.VAR:
{
Node vdecl=expr.getFirstChild();
String name=vdecl.getQualifiedName();
Preconditions.checkState(!vdecl.hasChildren());
return new EnvTypePair(envPutType(inEnv,name,JSType.STRING),JSType.STRING);
}
case Token.REGEXP:
return new EnvTypePair(inEnv,regexpType);
case Token.ARRAYLIT:
TypeEnv env=inEnv;
for (Node arrayElm=expr.getFirstChild(); arrayElm != null; arrayElm=arrayElm.getNext()) {
env=analyzeExprFwd(arrayElm,env).env;
}
return new EnvTypePair(env,arrayType);
default :
throw new RuntimeException("String_Node_Str" + Token.name(expr.getType()));
}
}