[-Wcompletion-handler] Support checks with builtins
authorValeriy Savchenko <vsavchenko@apple.com>
Mon, 8 Feb 2021 15:47:21 +0000 (18:47 +0300)
committerValeriy Savchenko <vsavchenko@apple.com>
Tue, 9 Feb 2021 08:32:24 +0000 (11:32 +0300)
It is very common to check callbacks and completion handlers for null.
This patch supports such checks using built-in functions:
  * __builtin_expect
  * __builtin_expect_with_probablity
  * __builtin_unpredictable

rdar://73455388

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

clang/lib/Analysis/CalledOnceCheck.cpp
clang/test/SemaObjC/warn-called-once.m

index 883629a..92d68d8 100644 (file)
@@ -22,6 +22,7 @@
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
+#include "clang/Basic/Builtins.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/BitVector.h"
@@ -330,6 +331,29 @@ public:
     return Visit(OVE->getSourceExpr());
   }
 
+  const DeclRefExpr *VisitCallExpr(const CallExpr *CE) {
+    if (!ShouldRetrieveFromComparisons)
+      return nullptr;
+
+    // We want to see through some of the boolean builtin functions
+    // that we are likely to see in conditions.
+    switch (CE->getBuiltinCallee()) {
+    case Builtin::BI__builtin_expect:
+    case Builtin::BI__builtin_expect_with_probability: {
+      assert(CE->getNumArgs() >= 2);
+
+      const DeclRefExpr *Candidate = Visit(CE->getArg(0));
+      return Candidate != nullptr ? Candidate : Visit(CE->getArg(1));
+    }
+
+    case Builtin::BI__builtin_unpredictable:
+      return Visit(CE->getArg(0));
+
+    default:
+      return nullptr;
+    }
+  }
+
   const DeclRefExpr *VisitExpr(const Expr *E) {
     // It is a fallback method that gets called whenever the actual type
     // of the given expression is not covered.
index 094f92a..0c11d0e 100644 (file)
@@ -4,6 +4,11 @@
 #define nil (id)0
 #define CALLED_ONCE __attribute__((called_once))
 #define NORETURN __attribute__((noreturn))
+#define LIKELY(X) __builtin_expect(!!(X), 1)
+#define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#define LIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 1, P)
+#define UNLIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 0, P)
+#define UNPRED(X) __builtin_unpredictable((long)(X))
 
 @protocol NSObject
 @end
@@ -547,6 +552,70 @@ int call_with_check_7(int (^callback)(void) CALLED_ONCE) {
   // no-warning
 }
 
+void call_with_builtin_check_1(int (^callback)(void) CALLED_ONCE) {
+  if (LIKELY(callback))
+    callback();
+  // no-warning
+}
+
+void call_with_builtin_check_2(int (^callback)(void) CALLED_ONCE) {
+  if (!UNLIKELY(callback)) {
+  } else {
+    callback();
+  }
+  // no-warning
+}
+
+void call_with_builtin_check_3(int (^callback)(void) CALLED_ONCE) {
+  if (__builtin_expect((long)callback, 0L)) {
+  } else {
+    callback();
+  }
+  // no-warning
+}
+
+void call_with_builtin_check_4(int (^callback)(void) CALLED_ONCE) {
+  if (__builtin_expect(0L, (long)callback)) {
+  } else {
+    callback();
+  }
+  // no-warning
+}
+
+void call_with_builtin_check_5(int (^callback)(void) CALLED_ONCE) {
+  if (LIKELY_WITH_PROBA(callback, 0.9))
+    callback();
+  // no-warning
+}
+
+void call_with_builtin_check_6(int (^callback)(void) CALLED_ONCE) {
+  if (!UNLIKELY_WITH_PROBA(callback, 0.9)) {
+  } else {
+    callback();
+  }
+  // no-warning
+}
+
+void call_with_builtin_check_7(int (^callback)(void) CALLED_ONCE) {
+  if (UNPRED(callback)) {
+  } else {
+    callback();
+  }
+  // no-warning
+}
+
+void call_with_builtin_check_8(int (^callback)(void) CALLED_ONCE) {
+  if (LIKELY(callback != nil))
+    callback();
+  // no-warning
+}
+
+void call_with_builtin_check_9(int (^callback)(void) CALLED_ONCE) {
+  if (!UNLIKELY(callback == NULL))
+    callback();
+  // no-warning
+}
+
 void unreachable_true_branch(void (^callback)(void) CALLED_ONCE) {
   if (0) {