[clang] 4a7afc9 - [-Wcalled-once-parameter] Fix false positives for cleanup attr

Valeriy Savchenko via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 18 02:32:29 PDT 2021


Author: Valeriy Savchenko
Date: 2021-03-18T12:32:16+03:00
New Revision: 4a7afc9a8843f4793296a260f7153fd2ef4ec497

URL: https://github.com/llvm/llvm-project/commit/4a7afc9a8843f4793296a260f7153fd2ef4ec497
DIFF: https://github.com/llvm/llvm-project/commit/4a7afc9a8843f4793296a260f7153fd2ef4ec497.diff

LOG: [-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

Added: 
    

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

Removed: 
    


################################################################################
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 @@ class CalledOnceChecker : public ConstStmtVisitor<CalledOnceChecker> {
       }
     }
 
-    // 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 @@ class CalledOnceChecker : public ConstStmtVisitor<CalledOnceChecker> {
         if (Var->getInit()) {
           checkEscapee(Var->getInit());
         }
+
+        if (Var->hasAttr<CleanupAttr>()) {
+          FunctionHasCleanupVars = true;
+        }
       }
     }
   }
@@ -1669,6 +1677,13 @@ class CalledOnceChecker : public ConstStmtVisitor<CalledOnceChecker> {
   // 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<const ParmVarDecl *> TrackedParams;
   CFGSizedVector<State> 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)test_escape_after_branch:(int)cond
   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


        


More information about the cfe-commits mailing list