From 4a7afc9a8843f4793296a260f7153fd2ef4ec497 Mon Sep 17 00:00:00 2001 From: Valeriy Savchenko Date: Thu, 11 Mar 2021 14:22:47 +0300 Subject: [PATCH] [-Wcalled-once-parameter] Fix false positives for cleanup attr Cleanup attribute allows users to attach a destructor-like functions to variable declarations to be called whenever they leave the scope. The logic of such functions is not supported by the Clang's CFG and is too hard to be reasoned about. In order to avoid false positives in this situation, we assume that we didn't see ALL of the executtion paths of the function and, thus, can warn only about multiple call violation. rdar://74441906 Differential Revision: https://reviews.llvm.org/D98694 --- clang/lib/Analysis/CalledOnceCheck.cpp | 19 ++++++++++-- clang/test/SemaObjC/warn-called-once.m | 42 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index 29021b0a9016..ab56d3e3c988 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -812,8 +812,12 @@ private: } } - // Early exit if we don't have parameters for extra analysis. - if (NotCalledOnEveryPath.none() && NotUsedOnEveryPath.none()) + // Early exit if we don't have parameters for extra analysis... + if (NotCalledOnEveryPath.none() && NotUsedOnEveryPath.none() && + // ... or if we've seen variables with cleanup functions. + // We can't reason that we've seen every path in this case, + // and thus abandon reporting any warnings that imply that. + !FunctionHasCleanupVars) return; // We are looking for a pair of blocks A, B so that the following is true: @@ -1601,6 +1605,10 @@ public: if (Var->getInit()) { checkEscapee(Var->getInit()); } + + if (Var->hasAttr()) { + FunctionHasCleanupVars = true; + } } } } @@ -1669,6 +1677,13 @@ private: // around. bool SuppressOnConventionalErrorPaths = false; + // The user can annotate variable declarations with cleanup functions, which + // essentially imposes a custom destructor logic on that variable. + // It is possible to use it, however, to call tracked parameters on all exits + // from the function. For this reason, we track the fact that the function + // actually has these. + bool FunctionHasCleanupVars = false; + State CurrentState; ParamSizedVector TrackedParams; CFGSizedVector States; diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index 825d491f53bb..ff2778d4bd0a 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -1193,4 +1193,46 @@ void suppression_3(int cond, void (^callback)(void) CALLED_ONCE) { escape(handler); } +// rdar://74441906 +typedef void (^DeferredBlock)(void); +static inline void DefferedCallback(DeferredBlock *inBlock) { (*inBlock)(); } +#define _DEFERCONCAT(a, b) a##b +#define _DEFERNAME(a) _DEFERCONCAT(__DeferredVar_, a) +#define DEFER __extension__ __attribute__((cleanup(DefferedCallback), unused)) \ + DeferredBlock _DEFERNAME(__COUNTER__) = ^ + +- (void)test_cleanup_1:(int)cond + withCompletion:(void (^)(void))handler { + int error = 0; + DEFER { + if (error) + handler(); + }; + + if (cond) { + error = 1; + } else { + // no-warning + handler(); + } +} + +- (void)test_cleanup_2:(int)cond + withCompletion:(void (^)(void))handler { + int error = 0; + DEFER { + if (error) + handler(); + }; + + if (cond) { + error = 1; + } else { + handler(); // expected-note{{previous call is here}} + } + + // We still can warn about double call even in this case. + handler(); // expected-warning{{completion handler is called twice}} +} + @end -- GitLab