private LValueResultFwd analyzePropLValFwd(Node obj, QualifiedName pname,
TypeEnv inEnv, JSType type, boolean insideQualifiedName) {
Preconditions.checkArgument(pname.isIdentifier());
String pnameAsString = pname.getLeftmostName();
JSType reqObjType =
pickReqObjType(obj).withLoose().withProperty(pname, type);
LValueResultFwd lvalue = analyzeLValueFwd(obj, inEnv, reqObjType, true);
TypeEnv lvalueEnv = lvalue.env;
JSType lvalueType = lvalue.type;
if (!lvalueType.isSubtypeOf(JSType.TOP_OBJECT)) {
warnPropAccessOnNonobject(obj, pnameAsString, lvalueType);
return new LValueResultFwd(lvalueEnv, type, null, null);
}
Node propAccessNode = obj.getParent();
if (propAccessNode.isGetProp() &&
propAccessNode.getParent().isAssign() &&
mayWarnAboutPropCreation(pname, propAccessNode, lvalueType)) {
return new LValueResultFwd(lvalueEnv, type, null, null);
}
if (!insideQualifiedName &&
mayWarnAboutConstProp(propAccessNode, lvalueType, pname)) {
return new LValueResultFwd(lvalueEnv, type, null, null);
}
if (!lvalueType.mayHaveProp(pname)) {
if (insideQualifiedName && lvalueType.isLoose()) {
// For loose objects, create the inner property if it doesn't exist.
lvalueType = lvalueType.withProperty(
pname, JSType.TOP_OBJECT.withLoose());
if (lvalueType.isDict() && propAccessNode.isGetProp()) {
lvalueType = lvalueType.specialize(JSType.TOP_STRUCT);
} else if (lvalueType.isStruct() && propAccessNode.isGetElem()) {
lvalueType = lvalueType.specialize(JSType.TOP_DICT);
}
lvalueEnv = updateLvalueTypeInEnv(
lvalueEnv, obj, lvalue.ptr, lvalueType);
} else {
// Warn for inexistent prop either on the non-top-level of a qualified
// name, or for assignment ops that won't create a new property.
boolean warnForInexistentProp = insideQualifiedName ||
propAccessNode.getParent().getType() != Token.ASSIGN;
if (warnForInexistentProp &&
!lvalueType.isUnknown() &&
!lvalueType.isDict()) {
warnings.add(JSError.make(obj, TypeCheck.INEXISTENT_PROPERTY,
pnameAsString, lvalueType.toString()));
return new LValueResultFwd(lvalueEnv, type, null, null);
}
}
}
if (propAccessNode.isGetElem()) {
mayWarnAboutStructPropAccess(obj, lvalueType);
} else if (propAccessNode.isGetProp()) {
mayWarnAboutDictPropAccess(obj, lvalueType);
}
QualifiedName setterPname =
new QualifiedName(SETTER_PREFIX + pnameAsString);
if (lvalueType.hasProp(setterPname)) {
FunctionType funType = lvalueType.getProp(setterPname).getFunType();
Preconditions.checkNotNull(funType);
JSType formalType = funType.getFormalType(0);
Preconditions.checkState(!formalType.isBottom());
return new LValueResultFwd(lvalueEnv, formalType, formalType, null);
}
return new LValueResultFwd(
lvalueEnv,
lvalueType.mayHaveProp(pname) ?