diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index 883629a300dcb..92d68d85fbc28 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -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 @@ 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. diff --git a/clang/lib/Analysis/RetainSummaryManager.cpp b/clang/lib/Analysis/RetainSummaryManager.cpp index 9f45a8efe546f..00bc854a88049 100644 --- a/clang/lib/Analysis/RetainSummaryManager.cpp +++ b/clang/lib/Analysis/RetainSummaryManager.cpp @@ -146,9 +146,7 @@ static bool isSubclass(const Decl *D, } static bool isOSObjectSubclass(const Decl *D) { - // OSSymbols are particular OSObjects that are allocated globally - // and therefore aren't really refcounted, so we ignore them. - return D && isSubclass(D, "OSMetaClassBase") && !isSubclass(D, "OSSymbol"); + return D && isSubclass(D, "OSMetaClassBase"); } static bool isOSObjectDynamicCast(StringRef S) { diff --git a/clang/test/Analysis/osobject-retain-release.cpp b/clang/test/Analysis/osobject-retain-release.cpp index d88349dcd807e..41606a30c39f4 100644 --- a/clang/test/Analysis/osobject-retain-release.cpp +++ b/clang/test/Analysis/osobject-retain-release.cpp @@ -53,9 +53,6 @@ struct MyArray : public OSArray { OSObject *generateObject(OSObject *input) override; }; -// These are never refcounted. -struct OSSymbol : OSObject {}; - struct OtherStruct { static void doNothingToArray(OSArray *array); OtherStruct(OSArray *arr); @@ -757,10 +754,3 @@ void test() { b(0); } } // namespace inherited_constructor_crash - -namespace ossymbol_suppression { -OSSymbol *createSymbol(); -void test() { - OSSymbol *sym = createSymbol(); // no-warning -} -} // namespace ossymbol_suppression diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index 094f92a49935b..1014e17301638 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -1,9 +1,14 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -fblocks -fobjc-exceptions -Wcompletion-handler %s +// RUN: %clang_cc1 -verify -fsyntax-only -fblocks -fobjc-exceptions -Wcompletion-handler -Wno-pointer-to-int-cast %s #define NULL (void *)0 #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) {