diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -49,22 +49,21 @@ } }; -class StreamChecker : public Checker { +class StreamChecker + : public Checker { mutable std::unique_ptr BT_nullfp, BT_illegalwhence, BT_doubleclose, BT_ResourceLeak; public: - bool evalCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; private: using FnCheck = std::function; - CallDescriptionMap Callbacks = { - {{"fopen"}, &StreamChecker::evalFopen}, - {{"tmpfile"}, &StreamChecker::evalFopen}, + CallDescriptionMap PreCallbacks = { {{"fclose", 1}, &StreamChecker::evalFclose}, {{"fread", 4}, std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)}, @@ -89,10 +88,17 @@ std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)}, }; + CallDescriptionMap PostCallbacks = { + {{"fopen"}, &StreamChecker::evalFopen}, + {{"tmpfile"}, &StreamChecker::evalFopen}, + }; + void evalFopen(const CallEvent &Call, CheckerContext &C) const; void evalFclose(const CallEvent &Call, CheckerContext &C) const; void evalFseek(const CallEvent &Call, CheckerContext &C) const; + void checkCall(const CallEvent &Call, CheckerContext &C, + const CallDescriptionMap &Callbacks) const; void checkArgNullStream(const CallEvent &Call, CheckerContext &C, unsigned ArgI) const; bool checkNullStream(SVal SV, CheckerContext &C, @@ -107,29 +113,38 @@ REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) +void StreamChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + checkCall(Call, C, PreCallbacks); +} + +void StreamChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + checkCall(Call, C, PostCallbacks); +} -bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { +void StreamChecker::checkCall( + const CallEvent &Call, CheckerContext &C, + const CallDescriptionMap &Callbacks) const { const auto *FD = dyn_cast_or_null(Call.getDecl()); if (!FD || FD->getKind() != Decl::Function) - return false; + return; // Recognize "global C functions" with only integral or pointer arguments // (and matching name) as stream functions. if (!Call.isGlobalCFunction()) - return false; + return; for (auto P : Call.parameters()) { QualType T = P->getType(); if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) - return false; + return; } const FnCheck *Callback = Callbacks.lookup(Call); if (!Callback) - return false; + return; (*Callback)(this, Call, C); - - return C.isDifferent(); } void StreamChecker::evalFopen(const CallEvent &Call, CheckerContext &C) const {