bool OSAtomicChecker::inlineCall(const CallExpr *CE, ExprEngine &Eng, ExplodedNode *Pred, ExplodedNodeSet &Dst) const { const ProgramState *state = Pred->getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; const IdentifierInfo *II = FD->getIdentifier(); if (!II) return false; StringRef FName(II->getName()); // Check for compare and swap. if (FName.startswith("OSAtomicCompareAndSwap") || FName.startswith("objc_atomicCompareAndSwap")) return evalOSAtomicCompareAndSwap(CE, Eng, Pred, Dst); // FIXME: Other atomics. return false; }
static StringRef getCalleeName(ProgramStateRef State, const CallExpr *CE, const LocationContext *LCtx) { const Expr *Callee = CE->getCallee(); SVal L = State->getSVal(Callee, LCtx); const FunctionDecl *funDecl = L.getAsFunctionDecl(); if (!funDecl) return StringRef(); IdentifierInfo *funI = funDecl->getIdentifier(); if (!funI) return StringRef(); return funI->getName(); }
const Decl *CallOrObjCMessage::getDecl() const { if (isCXXCall()) { const CXXMemberCallExpr *CE = cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>()); assert(CE); return CE->getMethodDecl(); } else if (isObjCMessage()) { return Msg.getMethodDecl(); } else if (isFunctionCall()) { // In case of a C style call, use the path sensitive information to find // the function declaration. SVal CalleeVal = getFunctionCallee(); return CalleeVal.getAsFunctionDecl(); } return 0; }
void NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); if (!BuildSinks) { SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return; if (FD->getAttr<AnalyzerNoReturnAttr>()) BuildSinks = true; else if (const IdentifierInfo *II = FD->getIdentifier()) { // HACK: Some functions are not marked noreturn, and don't return. // Here are a few hardwired ones. If this takes too long, we can // potentially cache these results. BuildSinks = llvm::StringSwitch<bool>(StringRef(II->getName())) .Case("exit", true) .Case("panic", true) .Case("error", true) .Case("Assert", true) // FIXME: This is just a wrapper around throwing an exception. // Eventually inter-procedural analysis should handle this easily. .Case("ziperr", true) .Case("assfail", true) .Case("db_error", true) .Case("__assert", true) .Case("__assert_rtn", true) .Case("__assert_fail", true) .Case("dtrace_assfail", true) .Case("yy_fatal_error", true) .Case("_XCAssertionFailureHandler", true) .Case("_DTAssertionFailureHandler", true) .Case("_TSAssertionFailureHandler", true) .Default(false); } } if (BuildSinks) C.generateSink(); }
bool CallInliner::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; if (!FD->getBody(FD)) return false; // Now we have the definition of the callee, create a CallEnter node. CallEnter Loc(CE, FD, C.getPredecessor()->getLocationContext()); C.addTransition(state, Loc); return true; }
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); // First, try to inline the call. if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) return; // First handle the return value. StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); // Get the callee. const Expr *Callee = CE->getCallee()->IgnoreParens(); if (state == 0) state = Pred->getState(); SVal L = state->getSVal(Callee, Pred->getLocationContext()); // Figure out the result type. We do this dance to handle references. QualType ResultTy; if (const FunctionDecl *FD = L.getAsFunctionDecl()) ResultTy = FD->getResultType(); else ResultTy = CE->getType(); if (CE->isLValue()) ResultTy = Eng.getContext().getPointerType(ResultTy); // Conjure a symbol value to use as the result. SValBuilder &SVB = Eng.getSValBuilder(); unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount(); const LocationContext *LCtx = Pred->getLocationContext(); SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); // Generate a new state with the return value set. state = state->BindExpr(CE, LCtx, RetVal); // Invalidate the arguments. state = Eng.invalidateArguments(state, CallOrObjCMessage(CE, state, LCtx), LCtx); // And make the result node. Bldr.generateNode(CE, Pred, state); }
bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) { const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return false; // Check if we called CFRetain/CFRelease. const GRState* state = N->getState(); SVal X = state->getSVal(CE->getCallee()); const FunctionDecl* FD = X.getAsFunctionDecl(); if (!FD) return false; const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release)) return false; // Finally, check if the argument is NULL. // FIXME: We should be able to bifurcate the state here, as a successful // check will result in the value not being NULL afterwards. // FIXME: Need a way to register vistors for the BugReporter. Would like // to benefit from the same diagnostics that regular null dereference // reporting has. if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) { if (!BT) BT = new APIMisuse("null passed to CFRetain/CFRelease"); const char *description = (FuncII == Retain) ? "Null pointer argument in call to CFRetain" : "Null pointer argument in call to CFRelease"; RangedBugReport *report = new RangedBugReport(*BT, description, N); report->addRange(CE->getArg(0)->getSourceRange()); BR.EmitReport(report); return true; } return false; }
void CallInliner::EvalCall(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, CallExpr* CE, SVal L, ExplodedNode* Pred) { FunctionDecl const *FD = L.getAsFunctionDecl(); if (!FD) return; // GRExprEngine is responsible for the autotransition. // Make a new LocationContext. StackFrameContext const *LocCtx = Engine.getAnalysisManager().getStackFrame(FD, Pred->getLocationContext(), CE); CFGBlock const *Entry = &(LocCtx->getCFG()->getEntry()); assert (Entry->empty() && "Entry block must be empty."); assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); // Get the solitary successor. CFGBlock const *SuccB = *(Entry->succ_begin()); // Construct an edge representing the starting location in the function. BlockEdge Loc(Entry, SuccB, LocCtx); GRState const *state = Builder.GetState(Pred); state = Engine.getStoreManager().EnterStackFrame(state, LocCtx); bool isNew; ExplodedNode *SuccN = Engine.getGraph().getNode(Loc, state, &isNew); SuccN->addPredecessor(Pred, Engine.getGraph()); Builder.Deferred.erase(Pred); // This is a hack. We really should not use the GRStmtNodeBuilder. if (isNew) Builder.getWorkList()->Enqueue(SuccN); Builder.HasGeneratedNode = true; }
bool OSAtomicChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { const ProgramState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; const IdentifierInfo *II = FD->getIdentifier(); if (!II) return false; StringRef FName(II->getName()); // Check for compare and swap. if (FName.startswith("OSAtomicCompareAndSwap") || FName.startswith("objc_atomicCompareAndSwap")) return evalOSAtomicCompareAndSwap(C, CE); // FIXME: Other atomics. return false; }
bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; ASTContext &Ctx = C.getASTContext(); if (!II_fopen) II_fopen = &Ctx.Idents.get("fopen"); if (!II_tmpfile) II_tmpfile = &Ctx.Idents.get("tmpfile"); if (!II_fclose) II_fclose = &Ctx.Idents.get("fclose"); if (!II_fread) II_fread = &Ctx.Idents.get("fread"); if (!II_fwrite) II_fwrite = &Ctx.Idents.get("fwrite"); if (!II_fseek) II_fseek = &Ctx.Idents.get("fseek"); if (!II_ftell) II_ftell = &Ctx.Idents.get("ftell"); if (!II_rewind) II_rewind = &Ctx.Idents.get("rewind"); if (!II_fgetpos) II_fgetpos = &Ctx.Idents.get("fgetpos"); if (!II_fsetpos) II_fsetpos = &Ctx.Idents.get("fsetpos"); if (!II_clearerr) II_clearerr = &Ctx.Idents.get("clearerr"); if (!II_feof) II_feof = &Ctx.Idents.get("feof"); if (!II_ferror) II_ferror = &Ctx.Idents.get("ferror"); if (!II_fileno) II_fileno = &Ctx.Idents.get("fileno"); if (FD->getIdentifier() == II_fopen) { Fopen(C, CE); return true; } if (FD->getIdentifier() == II_tmpfile) { Tmpfile(C, CE); return true; } if (FD->getIdentifier() == II_fclose) { Fclose(C, CE); return true; } if (FD->getIdentifier() == II_fread) { Fread(C, CE); return true; } if (FD->getIdentifier() == II_fwrite) { Fwrite(C, CE); return true; } if (FD->getIdentifier() == II_fseek) { Fseek(C, CE); return true; } if (FD->getIdentifier() == II_ftell) { Ftell(C, CE); return true; } if (FD->getIdentifier() == II_rewind) { Rewind(C, CE); return true; } if (FD->getIdentifier() == II_fgetpos) { Fgetpos(C, CE); return true; } if (FD->getIdentifier() == II_fsetpos) { Fsetpos(C, CE); return true; } if (FD->getIdentifier() == II_clearerr) { Clearerr(C, CE); return true; } if (FD->getIdentifier() == II_feof) { Feof(C, CE); return true; } if (FD->getIdentifier() == II_ferror) { Ferror(C, CE); return true; } if (FD->getIdentifier() == II_fileno) { Fileno(C, CE); return true; } return false; }
const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { ProgramStateRef State = getState(); const Expr *Callee = CE->getCallee(); SVal L = State->getSVal(Callee, Pred->getLocationContext()); return L.getAsFunctionDecl(); }
void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); const GRState *originalState = state; // Check if the callee has a 'nonnull' attribute. SVal X = state->getSVal(CE->getCallee()); const FunctionDecl* FD = X.getAsFunctionDecl(); if (!FD) return; const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); if (!Att) return; // Iterate through the arguments of CE and check them for null. unsigned idx = 0; for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; ++I, ++idx) { if (!Att->isNonNull(idx)) continue; const SVal &V = state->getSVal(*I); const DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); if (!DV) continue; ConstraintManager &CM = C.getConstraintManager(); const GRState *stateNotNull, *stateNull; llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); if (stateNull && !stateNotNull) { // Generate an error node. Check for a null node in case // we cache out. if (ExplodedNode *errorNode = C.GenerateNode(CE, stateNull, true)) { // Lazily allocate the BugType object if it hasn't already been // created. Ownership is transferred to the BugReporter object once // the BugReport is passed to 'EmitWarning'. if (!BT) BT = new BugType("Argument with 'nonnull' attribute passed null", "API"); EnhancedBugReport *R = new EnhancedBugReport(*BT, "Null pointer passed as an argument to a " "'nonnull' parameter", errorNode); // Highlight the range of the argument that was null. const Expr *arg = *I; R->addRange(arg->getSourceRange()); R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); // Emit the bug report. C.EmitReport(R); } // Always return. Either we cached out or we just emitted an error. return; } // If a pointer value passed the check we should assume that it is // indeed not null from this point forward. assert(stateNotNull); state = stateNotNull; } // If we reach here all of the arguments passed the nonnull check. // If 'state' has been updated generated a new node. if (state != originalState) C.addTransition(C.GenerateNode(CE, state)); }
bool MallocChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); SVal L = state->getSVal(Callee); const FunctionDecl *FD = L.getAsFunctionDecl(); if (!FD) return false; ASTContext &Ctx = C.getASTContext(); if (!II_malloc) II_malloc = &Ctx.Idents.get("malloc"); if (!II_free) II_free = &Ctx.Idents.get("free"); if (!II_realloc) II_realloc = &Ctx.Idents.get("realloc"); if (!II_calloc) II_calloc = &Ctx.Idents.get("calloc"); if (FD->getIdentifier() == II_malloc) { MallocMem(C, CE); return true; } if (FD->getIdentifier() == II_free) { FreeMem(C, CE); return true; } if (FD->getIdentifier() == II_realloc) { ReallocMem(C, CE); return true; } if (FD->getIdentifier() == II_calloc) { CallocMem(C, CE); return true; } // Check all the attributes, if there are any. // There can be multiple of these attributes. bool rv = false; if (FD->hasAttrs()) { for (specific_attr_iterator<OwnershipAttr> i = FD->specific_attr_begin<OwnershipAttr>(), e = FD->specific_attr_end<OwnershipAttr>(); i != e; ++i) { switch ((*i)->getOwnKind()) { case OwnershipAttr::Returns: { MallocMemReturnsAttr(C, CE, *i); rv = true; break; } case OwnershipAttr::Takes: case OwnershipAttr::Holds: { FreeMemAttr(C, CE, *i); rv = true; break; } default: break; } } } return rv; }
const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { const ProgramState *State = getState(); const Expr *Callee = CE->getCallee(); SVal L = State->getSVal(Callee); return L.getAsFunctionDecl(); }
bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); const Expr* Callee = CE->getCallee(); SVal CallV = N->getState()->getSVal(Callee); const FunctionDecl* FD = CallV.getAsFunctionDecl(); if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3) return false; // Get the value of the "theType" argument. SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1)); // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); if (!V) return false; uint64_t NumberKind = V->getValue().getLimitedValue(); Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!TargetSize.isKnown()) return false; // Look at the value of the integer being passed by reference. Essentially // we want to catch cases where the value passed in is not equal to the // size of the type being created. SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2)); // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); if (!LV) return false; const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); if (!R) return false; QualType T = Ctx.getCanonicalType(R->getValueType(Ctx)); // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. if (!T->isIntegerType()) return false; uint64_t SourceSize = Ctx.getTypeSize(T); // CHECK: is SourceSize == TargetSize if (SourceSize == TargetSize) return false; AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind); // FIXME: We can actually create an abstract "CFNumber" object that has // the bits initialized to the provided values. return SourceSize < TargetSize; }
void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { const Expr* Callee = CE->getCallee(); const GRState *state = C.getState(); SVal CallV = state->getSVal(Callee); const FunctionDecl* FD = CallV.getAsFunctionDecl(); if (!FD) return; ASTContext &Ctx = C.getASTContext(); if (!II) II = &Ctx.Idents.get("CFNumberCreate"); if (FD->getIdentifier() != II || CE->getNumArgs() != 3) return; // Get the value of the "theType" argument. SVal TheTypeVal = state->getSVal(CE->getArg(1)); // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!TargetSize.isKnown()) return; // Look at the value of the integer being passed by reference. Essentially // we want to catch cases where the value passed in is not equal to the // size of the type being created. SVal TheValueExpr = state->getSVal(CE->getArg(2)); // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); if (!LV) return; const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); if (!R) return; QualType T = Ctx.getCanonicalType(R->getValueType()); // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. if (!T->isIntegerType()) return; uint64_t SourceSize = Ctx.getTypeSize(T); // CHECK: is SourceSize == TargetSize if (SourceSize == TargetSize) return; // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; // otherwise generate a regular node. // // FIXME: We can actually create an abstract "CFNumber" object that has // the bits initialized to the provided values. // if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() : C.generateNode()) { llvm::SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); os << (SourceSize == 8 ? "An " : "A ") << SourceSize << " bit integer is used to initialize a CFNumber " "object that represents " << (TargetSize == 8 ? "an " : "a ") << TargetSize << " bit integer. "; if (SourceSize < TargetSize) os << (TargetSize - SourceSize) << " bits of the CFNumber value will be garbage." ; else os << (SourceSize - TargetSize) << " bits of the input integer will be lost."; if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.EmitReport(report); } }
void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, const CallExpr* CE) { // If the CallExpr doesn't have exactly 1 argument just give up checking. if (CE->getNumArgs() != 1) return; // Get the function declaration of the callee. const GRState* state = C.getState(); SVal X = state->getSVal(CE->getCallee()); const FunctionDecl* FD = X.getAsFunctionDecl(); if (!FD) return; if (!BT) { ASTContext &Ctx = C.getASTContext(); Retain = &Ctx.Idents.get("CFRetain"); Release = &Ctx.Idents.get("CFRelease"); BT = new APIMisuse("null passed to CFRetain/CFRelease"); } // Check if we called CFRetain/CFRelease. const IdentifierInfo *FuncII = FD->getIdentifier(); if (!(FuncII == Retain || FuncII == Release)) return; // FIXME: The rest of this just checks that the argument is non-null. // It should probably be refactored and combined with AttrNonNullChecker. // Get the argument's value. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); if (!DefArgVal) return; // Get a NULL value. SValBuilder &svalBuilder = C.getSValBuilder(); DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); // Make an expression asserting that they're equal. DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); // Are they equal? const GRState *stateTrue, *stateFalse; llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { ExplodedNode *N = C.generateSink(stateTrue); if (!N) return; const char *description = (FuncII == Retain) ? "Null pointer argument in call to CFRetain" : "Null pointer argument in call to CFRelease"; EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); report->addRange(Arg->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); C.EmitReport(report); return; } // From here on, we know the argument is non-null. C.addTransition(stateFalse); }
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const LocationContext *LCtx, const RefVal &CurrV, SymbolRef &Sym, const Stmt *S, llvm::raw_string_ostream &os) { CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Get the name of the callee (if it is available) // from the tracked SVal. SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); const FunctionDecl *FD = X.getAsFunctionDecl(); // If failed, try to get it from AST. if (!FD) FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; } else if (FD) { os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; } else { os << "function call"; } } else if (isa<CXXNewExpr>(S)) { os << "Operator 'new'"; } else { assert(isa<ObjCMessageExpr>(S)); CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); switch (Call->getMessageKind()) { case OCM_Message: os << "Method"; break; case OCM_PropertyAccess: os << "Property"; break; case OCM_Subscript: os << "Subscript"; break; } } Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); // If index is not found, we assume that the symbol was returned. if (!Idx) { os << " returns "; } else { os << " writes "; } if (CurrV.getObjKind() == ObjKind::CF) { os << "a Core Foundation object of type '" << Sym->getType().getAsString() << "' with a "; } else if (CurrV.getObjKind() == ObjKind::OS) { os << "an OSObject of type '" << getPrettyTypeName(Sym->getType()) << "' with a "; } else if (CurrV.getObjKind() == ObjKind::Generalized) { os << "an object of type '" << Sym->getType().getAsString() << "' with a "; } else { assert(CurrV.getObjKind() == ObjKind::ObjC); QualType T = Sym->getType(); if (!isa<ObjCObjectPointerType>(T)) { os << "an Objective-C object with a "; } else { const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); os << "an instance of " << PT->getPointeeType().getAsString() << " with a "; } } if (CurrV.isOwned()) { os << "+1 retain count"; } else { assert(CurrV.isNotOwned()); os << "+0 retain count"; } if (Idx) { os << " into an out parameter '"; const ParmVarDecl *PVD = (*CE)->parameters()[*Idx]; PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), /*Qualified=*/false); os << "'"; QualType RT = (*CE)->getResultType(); if (!RT.isNull() && !RT->isVoidType()) { SVal RV = (*CE)->getReturnValue(); if (CurrSt->isNull(RV).isConstrainedTrue()) { os << " (assuming the call returns zero)"; } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) { os << " (assuming the call returns non-zero)"; } } } }