https://github.com/aaronpuchert created 
https://github.com/llvm/llvm-project/pull/141432

The analysis already excludes functions with a zero-argument acquire or release 
attribute. According to the requirements enforced by 
-Wthread-safety-attributes, these are methods of a capability class where the 
attribute implicitly refers to `this`.

C doesn't have class methods, so the lock/unlock implementations are free 
functions. If we disable the analysis for all free functions with attributes, 
this would obviously exclude too much. But maybe we can exclude functions where 
the attribute directly refers to a parameter. Mutex implementations should 
typically do that, and I don't see why other functions should. (Typically, 
other functions acquire or release a global mutex or a member of a parameter.)

Fixes #139933.

>From 7df9ba1dca287c8ce1025160d06a04835b43f254 Mon Sep 17 00:00:00 2001
From: Aaron Puchert <aaronpuch...@alice-dsl.net>
Date: Sun, 25 May 2025 23:51:23 +0200
Subject: [PATCH] Thread safety analysis: Skip functions acquiring/releasing
 parameters

The analysis already excludes functions with a zero-argument acquire or
release attribute. According to the requirements enforced by
-Wthread-safety-attributes, these are methods of a capability class
where the attribute implicitly refers to `this`.

C doesn't have class methods, so the lock/unlock implementations are
free functions. If we disable the analysis for all free functions with
attributes, this would obviously exclude too much. But maybe we can
exclude functions where the attribute directly refers to a parameter.
Mutex implementations should typically do that, and I don't see why
other functions should. (Typically, other functions acquire or release a
global mutex or a member of a parameter.)

Fixes #139933.
---
 clang/lib/Analysis/ThreadSafety.cpp           | 18 +++++++++--
 .../SemaCXX/warn-thread-safety-analysis.cpp   | 30 +++++++++++++++++++
 2 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/ThreadSafety.cpp 
b/clang/lib/Analysis/ThreadSafety.cpp
index 528fd09958578..9f375d700b641 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -2344,6 +2344,20 @@ static bool neverReturns(const CFGBlock *B) {
   return false;
 }
 
+static bool attrArgsForImpl(llvm::iterator_range<clang::Expr **> Args) {
+  // An attribute with no arguments implicitly refers to 'this'.
+  if (Args.empty())
+    return true;
+
+  return llvm::all_of(Args, [](const Expr *E) {
+    if (isa<CXXThisExpr>(E))
+      return true;
+    if (const auto* DRE = dyn_cast<DeclRefExpr>(E))
+      return isa<ParmVarDecl>(DRE->getDecl());
+    return false;
+  });
+}
+
 /// Check a function's CFG for thread-safety violations.
 ///
 /// We traverse the blocks in the CFG, compute the set of mutexes that are held
@@ -2421,13 +2435,13 @@ void 
ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
       } else if (const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {
         // UNLOCK_FUNCTION() is used to hide the underlying lock 
implementation.
         // We must ignore such methods.
-        if (A->args_size() == 0)
+        if (attrArgsForImpl(A->args()))
           return;
         getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
                     nullptr, D);
         getMutexIDs(LocksReleased, A, nullptr, D);
       } else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
-        if (A->args_size() == 0)
+        if (attrArgsForImpl(A->args()))
           return;
         getMutexIDs(A->isShared() ? SharedLocksAcquired
                                   : ExclusiveLocksAcquired,
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp 
b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 5382023318f28..4b12d68d41ed0 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -2401,6 +2401,36 @@ class LOCKABLE MyLock2 {
 } // end namespace SelfLockingTest
 
 
+namespace ExcludeImplementation {
+
+class LOCKABLE Mutex2 {
+public:
+  // We don't require EXCLUSIVE_(UN)LOCK_FUNCTION(impl) on these methods.
+  void Lock() EXCLUSIVE_LOCK_FUNCTION() { impl.Lock(); }
+  void Unlock() EXCLUSIVE_UNLOCK_FUNCTION() { impl.Unlock(); }
+
+  void LockAlt() EXCLUSIVE_LOCK_FUNCTION(this) { impl.Lock(); }
+  void UnlockAlt() EXCLUSIVE_UNLOCK_FUNCTION(this) { impl.Unlock(); }
+
+private:
+  Mutex impl;
+};
+
+struct LOCKABLE Mutex3 {
+  Mutex impl;
+};
+
+void Lock(Mutex3* mu) EXCLUSIVE_LOCK_FUNCTION(mu) {
+  mu->impl.Lock();
+}
+
+void Unlock(Mutex3* mu) EXCLUSIVE_UNLOCK_FUNCTION(mu) {
+  mu->impl.Unlock();
+}
+
+} // namespace ExcludeImplementation
+
+
 namespace InvalidNonstatic {
 
 // Forward decl here causes bogus "invalid use of non-static data member"

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to