]> granicus.if.org Git - clang/commitdiff
[analyzer] NFC: CallDescription: Implement describing C library functions.
authorArtem Dergachev <artem.dergachev@gmail.com>
Mon, 1 Jul 2019 23:02:07 +0000 (23:02 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Mon, 1 Jul 2019 23:02:07 +0000 (23:02 +0000)
When matching C standard library functions in the checker, it's easy to forget
that they are often implemented as macros that are expanded to builtins.

Such builtins would have a different name, so matching the callee identifier
would fail, or may sometimes have more arguments than expected, so matching
the exact number of arguments would fail, but this is fine as long as we have
all the arguments that we need in their respective places.

This patch adds a set of flags to the CallDescription class so that to handle
various special matching rules, and adds the first flag into this set,
which enables a more fuzzy matching for functions that
may be implemented as compiler builtins.

Differential Revision: https://reviews.llvm.org/D62556

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@364867 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
lib/StaticAnalyzer/Core/CallEvent.cpp
unittests/StaticAnalyzer/CallDescriptionTest.cpp

index 19d6e62c1655d49e6d1f6486a0d2936057c4ca29..db84102983af17185b27f6bb7dee310446ea84e0 100644 (file)
@@ -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<const char *> QualifiedName;
   Optional<unsigned> 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<const char *> QualifiedName,
+                  Optional<unsigned> RequiredArgs = None)
+      : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs),
+        Flags(Flags) {}
+
+  /// Construct a CallDescription with default flags.
   CallDescription(ArrayRef<const char *> QualifiedName,
                   Optional<unsigned> 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(); }
index 6339423f1125bcf90bec470df73adfff6623eecb..81a9ee4d90b0a5cc78ee8d243e32dc4637886fcf 100644 (file)
@@ -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<FunctionDecl>(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<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
index 573b6909b1805f3689b2fd0ef97640c3c4dd8f76..9201922f5be05b3149587b117958172201fefc65 100644 (file)
@@ -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