void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
                                           CheckerContext &C) const {
  // We can obtain perfect type info for return values from some calls.
  if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {

    // Get the returned value if it's a region.
    const MemRegion *RetReg = Call.getReturnValue().getAsRegion();
    if (!RetReg)
      return;

    ProgramStateRef State = C.getState();

    switch (Msg->getMethodFamily()) {
    default:
      break;

    // We assume that the type of the object returned by alloc and new are the
    // pointer to the object of the class specified in the receiver of the
    // message.
    case OMF_alloc:
    case OMF_new: {
      // Get the type of object that will get created.
      const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
      const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
      if (!ObjTy)
        return;
      QualType DynResTy =
                 C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
      C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false));
      break;
    }
    case OMF_init: {
      // Assume, the result of the init method has the same dynamic type as
      // the receiver and propagate the dynamic type info.
      const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
      if (!RecReg)
        return;
      DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
      C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType));
      break;
    }
    }

    return;
  }

  if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
    // We may need to undo the effects of our pre-call check.
    switch (Ctor->getOriginExpr()->getConstructionKind()) {
    case CXXConstructExpr::CK_Complete:
    case CXXConstructExpr::CK_Delegating:
      // No additional work necessary.
      // Note: This will leave behind the actual type of the object for
      // complete constructors, but arguably that's a good thing, since it
      // means the dynamic type info will be correct even for objects
      // constructed with operator new.
      return;
    case CXXConstructExpr::CK_NonVirtualBase:
    case CXXConstructExpr::CK_VirtualBase:
      if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) {
        // We just finished a base constructor. Now we can use the subclass's
        // type when resolving virtual calls.
        const Decl *D = C.getLocationContext()->getDecl();
        recordFixedType(Target, cast<CXXConstructorDecl>(D), C);
      }
      return;
    }
  }
}