private EnvTypePair analyzePropAccessFwd(Node receiver, String pname,
TypeEnv inEnv, JSType requiredType, JSType specializedType) {
QualifiedName propQname = new QualifiedName(pname);
Node propAccessNode = receiver.getParent();
EnvTypePair pair;
JSType objWithProp = pickReqObjType(receiver)
.withLoose().withProperty(propQname, requiredType);
JSType recvReqType, recvSpecType, recvType;
// First, analyze the receiver object.
if (specializedType.isTruthy() || specializedType.isFalsy()) {
recvReqType = JSType.UNKNOWN;
recvSpecType = objWithProp;
} else {
recvReqType = recvSpecType = objWithProp;
}
pair = analyzeExprFwd(receiver, inEnv, recvReqType, recvSpecType);
recvType = pair.type;
if (recvType.isUnknown() ||
mayWarnAboutNonObject(receiver, pname, recvType, specializedType)) {
return new EnvTypePair(pair.env, requiredType);
}
if (propAccessNode.isGetProp() &&
mayWarnAboutDictPropAccess(receiver, recvType)) {
return new EnvTypePair(pair.env, requiredType);
}
// Then, analyze the property access.
QualifiedName getterPname = new QualifiedName(GETTER_PREFIX + pname);
if (recvType.hasProp(getterPname)) {
return new EnvTypePair(pair.env, recvType.getProp(getterPname));
}
JSType resultType = recvType.getProp(propQname);
if (!propAccessNode.getParent().isExprResult() &&
!specializedType.isTruthy() && !specializedType.isFalsy()) {
if (!recvType.mayHaveProp(propQname)) {
// TODO(dimvar): maybe don't warn if the getprop is inside a typeof,
// see testMissingProperty8 (who relies on that for prop checking?)
warnings.add(JSError.make(propAccessNode, TypeCheck.INEXISTENT_PROPERTY,
pname, recvType.toString()));
} else if (!recvType.hasProp(propQname)) {
warnings.add(JSError.make(
propAccessNode, POSSIBLY_INEXISTENT_PROPERTY,
pname, recvType.toString()));
} else if (recvType.hasProp(propQname) &&
!resultType.isSubtypeOf(requiredType) &&
tightenTypeAndDontWarn(
receiver.isName() ? receiver.getString() : null,
recvType.getDeclaredProp(propQname),
resultType, requiredType)) {
// Tighten the inferred type and don't warn.
// See Token.NAME fwd for explanation about types as lower/upper bounds.
resultType = resultType.specialize(requiredType);
LValueResultFwd lvr =
analyzeLValueFwd(propAccessNode, inEnv, resultType);
TypeEnv updatedEnv =
updateLvalueTypeInEnv(lvr.env, propAccessNode, lvr.ptr, resultType);
return new EnvTypePair(updatedEnv, resultType);