Skip to content

Commit

Permalink
[-Wcompletion-handler] Support checks with builtins
Browse files Browse the repository at this point in the history
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
  • Loading branch information
SavchenkoValeriy committed Feb 9, 2021
1 parent 5b8db12 commit d1522d3
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 0 deletions.
24 changes: 24 additions & 0 deletions clang/lib/Analysis/CalledOnceCheck.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -330,6 +331,29 @@ class DeclRefFinder
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.
Expand Down
69 changes: 69 additions & 0 deletions clang/test/SemaObjC/warn-called-once.m
Expand Up @@ -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
Expand Down Expand Up @@ -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) {

Expand Down

0 comments on commit d1522d3

Please sign in to comment.