Index: clang/lib/Analysis/CFG.cpp =================================================================== --- clang/lib/Analysis/CFG.cpp +++ clang/lib/Analysis/CFG.cpp @@ -1907,7 +1907,8 @@ Decls.push_back(*I); for (VarDecl *VD : llvm::reverse(Decls)) { - if (hasTrivialDestructor(VD)) { + bool HasCleanupAttr = VD->hasAttr(); + if (hasTrivialDestructor(VD) && !HasCleanupAttr) { // If AddScopes is enabled and *I is a first variable in a scope, add a // ScopeEnd marker in a Block. if (BuildOpts.AddScopes && DeclsWithEndedScope.count(VD)) { @@ -1925,7 +1926,8 @@ } Ty = Context->getBaseElementType(Ty); - if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn()) + bool IsCXXRecordType = Ty->getAsCXXRecordDecl() != nullptr; + if (IsCXXRecordType && Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn()) Block = createNoReturnBlock(); else autoCreateBlock(); @@ -1933,7 +1935,28 @@ // Add ScopeEnd just after automatic obj destructor. if (BuildOpts.AddScopes && DeclsWithEndedScope.count(VD)) appendScopeEnd(Block, VD, S); - appendAutomaticObjDtor(Block, VD, S); + + if (HasCleanupAttr) { + // Create a fake CallExpr for the cleanup function. + const CleanupAttr *CA = VD->getAttr(); + FunctionDecl *FD = CA->getFunctionDecl(); + assert(FD); + auto DRE = + DeclRefExpr::Create(*Context, {}, {}, VD, false, SourceLocation(), + VD->getType(), VK_PRValue); + + auto F = DeclRefExpr::Create(*Context, {}, {}, FD, false, + SourceLocation(), FD->getType(), VK_LValue); + + SmallVector Args; + Args.push_back(DRE); + auto A = CallExpr::Create(*Context, F, Args, FD->getType(), VK_PRValue, + {}, FPOptionsOverride()); + appendCall(Block, A); + } + + if (IsCXXRecordType) + appendAutomaticObjDtor(Block, VD, S); } } @@ -2090,7 +2113,8 @@ return Scope; if (BuildOpts.AddImplicitDtors) { - if (!hasTrivialDestructor(VD) || BuildOpts.AddScopes) { + if (!hasTrivialDestructor(VD) || VD->hasAttr() || + BuildOpts.AddScopes) { // Add the variable to scope Scope = createOrReuseLocalScope(Scope); Scope->addVar(VD); Index: clang/test/Sema/warn-thread-safety-analysis.c =================================================================== --- clang/test/Sema/warn-thread-safety-analysis.c +++ clang/test/Sema/warn-thread-safety-analysis.c @@ -22,6 +22,7 @@ #define SHARED_LOCKS_REQUIRED(...) \ __attribute__ ((shared_locks_required(__VA_ARGS__))) #define NO_THREAD_SAFETY_ANALYSIS __attribute__ ((no_thread_safety_analysis)) +#define CLEANUP(A) __attribute__ ((cleanup(A))) // Define the mutex struct. // Simplified only for test purpose. @@ -72,6 +73,11 @@ return *p; } +void cleanup_int(int *unused) __attribute__((release_capability(mu1))) { + (void)unused; + mutex_exclusive_unlock(&mu1); +} + int main(void) { Foo_fun1(1); // expected-warning{{calling function 'Foo_fun1' requires holding mutex 'mu2'}} \ @@ -127,6 +133,15 @@ // expected-note@-1{{mutex released here}} mutex_shared_unlock(&mu1); // expected-warning {{releasing mutex 'mu1' that was not held}} + + { + mutex_exclusive_lock(&mu1); + int CLEANUP(cleanup_int) i; + + Bar_fun1(3); + } + Bar_fun1(4); // expected-warning {{calling function 'Bar_fun1' requires holding mutex 'mu1' exclusively}} + return 0; }