Index: include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -66,6 +66,28 @@ #define REGISTER_LIST_WITH_PROGRAMSTATE(Name, Elem) \ REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, llvm::ImmutableList) +/// This class represents a description of a function call using the number of +/// arguments and the name of the function. It can also represent descriptions +/// of Obj-C message expressions. +class CallDescription { + friend class CheckerContext; + mutable IdentifierInfo *II; + StringRef FuncName; + unsigned RequiredArgs; + +public: + const static unsigned NoArgRequirement = ~0; + /// \brief Constructs a CallDescription object. + /// + /// @param FuncName The name of the function or the selector that will be + /// matched. + /// + /// @param RequiredArgs The number of arguments that is expected to match a + /// call. Omit this parameter to match every occurance of call with a given + /// name regardless the number of arguments. + CallDescription(StringRef FuncName, unsigned RequiredArgs = NoArgRequirement) + : FuncName(FuncName), RequiredArgs(RequiredArgs) {} +}; class CheckerContext { ExprEngine &Eng; @@ -284,6 +306,17 @@ return getCalleeName(FunDecl); } + /// \brief Returns true if the CallEvent is call to a function that matches + /// the CallDescription. + /// + /// Note that this overload is not intended to be used to match Obj-C method + /// calls. Use the corresponding overload for that case. + bool isCalled(const CallEvent &Call, const CallDescription &CD); + + /// \brief Returns true if the ObjCMethodCall is call to a method that matches + /// the CallDescription. + bool isCalled(const ObjCMethodCall &Call, const CallDescription &CD); + /// \brief Returns true if the callee is an externally-visible function in the /// top-level namespace, such as \c malloc. /// Index: lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -51,14 +51,11 @@ check::PreCall, check::DeadSymbols, check::PointerEscape> { - - mutable IdentifierInfo *IIfopen, *IIfclose; + CallDescription OpenFn, CloseFn; std::unique_ptr DoubleCloseBugType; std::unique_ptr LeakBugType; - void initIdentifierInfo(ASTContext &Ctx) const; - void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const; @@ -106,7 +103,7 @@ } // end anonymous namespace SimpleStreamChecker::SimpleStreamChecker() - : IIfopen(nullptr), IIfclose(nullptr) { + : OpenFn("fopen"), CloseFn("fclose", 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); @@ -119,12 +116,10 @@ void SimpleStreamChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - initIdentifierInfo(C.getASTContext()); - if (!Call.isGlobalCFunction()) return; - if (Call.getCalleeIdentifier() != IIfopen) + if (!C.isCalled(Call, OpenFn)) return; // Get the symbolic value corresponding to the file handle. @@ -140,15 +135,10 @@ void SimpleStreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - initIdentifierInfo(C.getASTContext()); - if (!Call.isGlobalCFunction()) return; - if (Call.getCalleeIdentifier() != IIfclose) - return; - - if (Call.getNumArgs() != 1) + if (!C.isCalled(Call, CloseFn)) return; // Get the symbolic value corresponding to the file handle. @@ -275,13 +265,6 @@ return State; } -void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const { - if (IIfopen) - return; - IIfopen = &Ctx.Idents.get("fopen"); - IIfclose = &Ctx.Idents.get("fclose"); -} - void ento::registerSimpleStreamChecker(CheckerManager &mgr) { mgr.registerChecker(); } Index: lib/StaticAnalyzer/Core/CheckerContext.cpp =================================================================== --- lib/StaticAnalyzer/Core/CheckerContext.cpp +++ lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/Basic/Builtins.h" #include "clang/Lex/Lexer.h" @@ -35,6 +36,26 @@ return funI->getName(); } +bool CheckerContext::isCalled(const CallEvent &Call, + const CallDescription &CD) { + assert(Call.getKind() != CE_ObjCMessage); + if (!CD.II) + CD.II = &getASTContext().Idents.get(CD.FuncName); + if (CD.RequiredArgs != CallDescription::NoArgRequirement && + CD.RequiredArgs != Call.getNumArgs()) + return false; + return Call.getCalleeIdentifier() == CD.II; +} + +bool CheckerContext::isCalled(const ObjCMethodCall &Call, + const CallDescription &CD) { + if (!CD.II) + CD.II = &getASTContext().Idents.get(CD.FuncName); + if (CD.RequiredArgs != CallDescription::NoArgRequirement && + CD.RequiredArgs != Call.getNumArgs()) + return false; + return Call.getSelector() == getASTContext().Selectors.getSelector(0, &CD.II); +} bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, StringRef Name) { @@ -91,4 +112,3 @@ SmallVector buf; return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts()); } -