diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 19d6e62c1655..db84102983af 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -1044,6 +1044,14 @@ public: } }; +enum CallDescriptionFlags : int { + /// Describes a C standard function that is sometimes implemented as a macro + /// that expands to a compiler builtin with some __builtin prefix. + /// The builtin may as well have a few extra arguments on top of the requested + /// number of arguments. + CDF_MaybeBuiltin = 1 << 0, +}; + /// This class represents a description of a function call using the number of /// arguments and the name of the function. class CallDescription { @@ -1055,6 +1063,7 @@ class CallDescription { // e.g. "{a, b}" represent the qualified names, like "a::b". std::vector QualifiedName; Optional RequiredArgs; + int Flags; public: /// Constructs a CallDescription object. @@ -1067,9 +1076,15 @@ public: /// @param RequiredArgs The number of arguments that is expected to match a /// call. Omit this parameter to match every occurrence of call with a given /// name regardless the number of arguments. + CallDescription(int Flags, ArrayRef QualifiedName, + Optional RequiredArgs = None) + : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs), + Flags(Flags) {} + + /// Construct a CallDescription with default flags. CallDescription(ArrayRef QualifiedName, Optional RequiredArgs = None) - : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs) {} + : CallDescription(0, QualifiedName, RequiredArgs) {} /// Get the name of the function that this object matches. StringRef getFunctionName() const { return QualifiedName.back(); } diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 6339423f1125..81a9ee4d90b0 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -356,20 +356,32 @@ bool CallEvent::isCalled(const CallDescription &CD) const { // FIXME: Add ObjC Message support. if (getKind() == CE_ObjCMessage) return false; + + const IdentifierInfo *II = getCalleeIdentifier(); + if (!II) + return false; + const FunctionDecl *FD = dyn_cast_or_null(getDecl()); + if (!FD) + return false; + + if (CD.Flags & CDF_MaybeBuiltin) { + return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && + (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()); + } + if (!CD.IsLookupDone) { CD.IsLookupDone = true; CD.II = &getState()->getStateManager().getContext().Idents.get( CD.getFunctionName()); } - const IdentifierInfo *II = getCalleeIdentifier(); - if (!II || II != CD.II) + + if (II != CD.II) return false; - const Decl *D = getDecl(); // If CallDescription provides prefix names, use them to improve matching // accuracy. - if (CD.QualifiedName.size() > 1 && D) { - const DeclContext *Ctx = D->getDeclContext(); + if (CD.QualifiedName.size() > 1 && FD) { + const DeclContext *Ctx = FD->getDeclContext(); // See if we'll be able to match them all. size_t NumUnmatched = CD.QualifiedName.size() - 1; for (; Ctx && isa(Ctx); Ctx = Ctx->getParent()) { diff --git a/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp index 573b6909b180..9201922f5be0 100644 --- a/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp +++ b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp @@ -143,6 +143,18 @@ TEST(CallEvent, CallDescription) { {{{"bar", "foo"}}, false}, {{"foo"}, true}, }), "void foo(); struct bar { void foo(); }; void test() { foo(); }")); + + // Test CDF_MaybeBuiltin - a flag that allows matching weird builtins. + EXPECT_TRUE(tooling::runToolOnCode( + new CallDescriptionAction({ + {{"memset", 3}, false}, + {{CDF_MaybeBuiltin, "memset", 3}, true} + }), + "void foo() {" + " int x;" + " __builtin___memset_chk(&x, 0, sizeof(x)," + " __builtin_object_size(&x, 0));" + "}")); } } // namespace