/// Insert a diagnostic piece at function exit /// if a function parameter is annotated as "os_consumed", /// but it does not actually consume the reference. static std::shared_ptr<PathDiagnosticEventPiece> annotateConsumedSummaryMismatch(const ExplodedNode *N, CallExitBegin &CallExitLoc, const SourceManager &SM, CallEventManager &CEMgr) { const ExplodedNode *CN = getCalleeNode(N); if (!CN) return nullptr; CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState()); std::string sbuf; llvm::raw_string_ostream os(sbuf); ArrayRef<const ParmVarDecl *> Parameters = Call->parameters(); for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) { const ParmVarDecl *PVD = Parameters[I]; if (!PVD->hasAttr<OSConsumedAttr>()) continue; if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) { const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR); const RefVal *CountAtExit = getRefBinding(N->getState(), SR); if (!CountBeforeCall || !CountAtExit) continue; unsigned CountBefore = CountBeforeCall->getCount(); unsigned CountAfter = CountAtExit->getCount(); bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1; if (!AsExpected) { os << "Parameter '"; PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), /*Qualified=*/false); os << "' is marked as consuming, but the function did not consume " << "the reference\n"; } } } if (os.str().empty()) return nullptr; // FIXME: remove the code duplication with NoStoreFuncVisitor. PathDiagnosticLocation L; if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext()); } else { L = PathDiagnosticLocation( Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM); } return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); }
void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &CallTemplate, const EvalCallOptions &CallOpts) { // Make sure we have the most recent state attached to the call. ProgramStateRef State = Pred->getState(); CallEventRef<> Call = CallTemplate.cloneWithState(State); // Special-case trivial assignment operators. if (isTrivialObjectAssignment(*Call)) { performTrivialCopy(Bldr, Pred, *Call); return; } // Try to inline the call. // The origin expression here is just used as a kind of checksum; // this should still be safe even for CallEvents that don't come from exprs. const Expr *E = Call->getOriginExpr(); ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); if (InlinedFailedState) { // If we already tried once and failed, make sure we don't retry later. State = InlinedFailedState; } else { RuntimeDefinition RD = Call->getRuntimeDefinition(); const Decl *D = RD.getDecl(); if (shouldInlineCall(*Call, D, Pred, CallOpts)) { if (RD.mayHaveOtherDefinitions()) { AnalyzerOptions &Options = getAnalysisManager().options; // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { conservativeEvalCall(*Call, Bldr, Pred, State); return; } } // We are not bifurcating and we do have a Decl, so just inline. if (inlineCall(*Call, D, Bldr, Pred, State)) return; } } // If we can't inline it, handle the return value and invalidate the regions. conservativeEvalCall(*Call, Bldr, Pred, State); }