diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index cbcfefdc52549..1df89802baa9c 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -135,14 +135,30 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp, Ctx.NumArgs = CE->getNumArgs(); Ctx.FunArgs = CE->getArgs(); } else if (const auto *CE = dyn_cast(DeclExp)) { - Ctx.NumArgs = CE->getNumArgs(); - Ctx.FunArgs = CE->getArgs(); + // Calls to operators that are members need to be treated like member calls. + if (isa(CE) && isa(D)) { + Ctx.SelfArg = CE->getArg(0); + Ctx.SelfArrow = false; + Ctx.NumArgs = CE->getNumArgs() - 1; + Ctx.FunArgs = CE->getArgs() + 1; + } else { + Ctx.NumArgs = CE->getNumArgs(); + Ctx.FunArgs = CE->getArgs(); + } } else if (const auto *CE = dyn_cast(DeclExp)) { Ctx.SelfArg = nullptr; // Will be set below Ctx.NumArgs = CE->getNumArgs(); Ctx.FunArgs = CE->getArgs(); } + // Usually we want to substitute the self-argument for "this", but lambdas + // are an exception: "this" on or in a lambda call operator doesn't refer + // to the lambda, but to captured "this" in the context it was created in. + // This can happen for operator calls and member calls, so fix it up here. + if (const auto *CMD = dyn_cast(D)) + if (CMD->getParent()->isLambda()) + Ctx.SelfArg = nullptr; + if (Self) { assert(!Ctx.SelfArg && "Ambiguous self argument"); assert(isa(D) && "Self argument requires function"); diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index 8477200456d98..3c52c8165d852 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -1593,8 +1593,12 @@ namespace substitution_test { void unlockData() UNLOCK_FUNCTION(mu); void doSomething() EXCLUSIVE_LOCKS_REQUIRED(mu) { } + void operator()() EXCLUSIVE_LOCKS_REQUIRED(mu) { } + + MyData operator+(const MyData& other) const SHARED_LOCKS_REQUIRED(mu, other.mu); }; + MyData operator-(const MyData& a, const MyData& b) SHARED_LOCKS_REQUIRED(a.mu, b.mu); class DataLocker { public: @@ -1607,6 +1611,27 @@ namespace substitution_test { public: void foo(MyData* d) EXCLUSIVE_LOCKS_REQUIRED(d->mu) { } + void subst(MyData& d) { + d.doSomething(); // expected-warning {{calling function 'doSomething' requires holding mutex 'd.mu' exclusively}} + d(); // expected-warning {{calling function 'operator()' requires holding mutex 'd.mu' exclusively}} + d.operator()(); // expected-warning {{calling function 'operator()' requires holding mutex 'd.mu' exclusively}} + + d.lockData(); + d.doSomething(); + d(); + d.operator()(); + d.unlockData(); + } + + void binop(MyData& a, MyData& b) EXCLUSIVE_LOCKS_REQUIRED(a.mu) { + a + b; // expected-warning {{calling function 'operator+' requires holding mutex 'b.mu'}} + // expected-note@-1 {{found near match 'a.mu'}} + b + a; // expected-warning {{calling function 'operator+' requires holding mutex 'b.mu'}} + // expected-note@-1 {{found near match 'a.mu'}} + a - b; // expected-warning {{calling function 'operator-' requires holding mutex 'b.mu'}} + // expected-note@-1 {{found near match 'a.mu'}} + } + void bar1(MyData* d) { d->lockData(); foo(d); @@ -5172,9 +5197,13 @@ class Foo { }; func1(); // expected-warning {{calling function 'operator()' requires holding mutex 'mu_' exclusively}} + func1.operator()(); // expected-warning {{calling function 'operator()' requires holding mutex 'mu_' exclusively}} func2(); + func2.operator()(); func3(); mu_.Unlock(); + func3.operator()(); + mu_.Unlock(); } };