switch (expr.getType()) {
case Token.THIS: {
if (currentScope.hasThis()) {
return new LValueResultFwd(inEnv, envGetType(inEnv, "this"),
currentScope.getDeclaredTypeOf("this"),
new QualifiedName("this"));
} else {
warnings.add(JSError.make(expr, CheckGlobalThis.GLOBAL_THIS));
return new LValueResultFwd(inEnv, JSType.UNKNOWN, null, null);
}
}
case Token.NAME: {
String varName = expr.getString();
JSType varType = analyzeExprFwd(expr, inEnv).type;
return new LValueResultFwd(inEnv, varType,
currentScope.getDeclaredTypeOf(varName),
varType.hasNonScalar() ? new QualifiedName(varName) : null);
}
case Token.GETPROP: {
Node obj = expr.getFirstChild();
QualifiedName pname =
new QualifiedName(expr.getLastChild().getString());
return analyzePropLValFwd(obj, pname, inEnv, type, insideQualifiedName);
}
case Token.GETELEM: {
Node obj = expr.getFirstChild();
Node prop = expr.getLastChild();
// (1) A getelem where the prop is a string literal is like a getprop
if (prop.isString()) {
QualifiedName pname = new QualifiedName(prop.getString());
return analyzePropLValFwd(
obj, pname, inEnv, type, insideQualifiedName);
}
// (2) A getelem where the receiver is an array
LValueResultFwd lvalue =
analyzeLValueFwd(obj, inEnv, JSType.UNKNOWN, true);
if (isArrayType(lvalue.type)) {
return analyzeArrayElmLvalFwd(obj, prop, lvalue);
}
// (3) All other getelems
EnvTypePair pair = analyzeExprFwd(expr, inEnv, type);
return new LValueResultFwd(pair.env, pair.type, null, null);
}
case Token.VAR: { // Can happen iff its parent is a for/in.
Preconditions.checkState(NodeUtil.isForIn(expr.getParent()));
Node vdecl = expr.getFirstChild();
String name = vdecl.getString();
// For/in can never have rhs of its VAR
Preconditions.checkState(!vdecl.hasChildren());
return new LValueResultFwd(
inEnv, JSType.STRING, null, new QualifiedName(name));
}
default: {
// Expressions that aren't lvalues should be handled because they may
// be, e.g., the left child of a getprop.
// We must check that they are not the direct lvalues.