diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -16,6 +16,7 @@ #include "clang/Analysis/Analyses/ThreadSafety.h" #include "clang/AST/Attr.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" @@ -1275,10 +1276,28 @@ // Members are in scope from methods of the same class. if (const auto *P = dyn_cast(SExp)) { + const ValueDecl *VD = P->clangDecl(); + // Public members are always accessible. + if (VD->getAccess() == AS_public) + return true; + // We are ignoring friends here. if (!CurrentMethod) return false; - const ValueDecl *VD = P->clangDecl(); - return VD->getDeclContext() == CurrentMethod->getDeclContext(); + const auto *CapRecord = cast(VD->getDeclContext()), + *MethodRecord = + cast(CurrentMethod->getDeclContext()); + // If we're in the same record, all members are accessible. + if (CapRecord == MethodRecord) + return true; + // Additionally in derived classes, protected members are accessible. + if (VD->getAccess() == AS_protected) { + CXXBasePaths Paths; + if (!MethodRecord->isDerivedFrom(CapRecord, Paths)) + return false; + for (const CXXBasePath &Path : Paths) + if (Path.Access != AS_none) + return true; + } } return false; diff --git a/clang/test/SemaCXX/warn-thread-safety-negative.cpp b/clang/test/SemaCXX/warn-thread-safety-negative.cpp --- a/clang/test/SemaCXX/warn-thread-safety-negative.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-negative.cpp @@ -108,6 +108,54 @@ ns::fq(); // expected-warning {{calling function 'fq' requires negative capability '!globalMutex'}} } +class Base { +public: + void nopub() EXCLUSIVE_LOCKS_REQUIRED(!pub); + void noprot() EXCLUSIVE_LOCKS_REQUIRED(!prot); + void nopriv() EXCLUSIVE_LOCKS_REQUIRED(!priv); + + void inClass() { + nopub(); // expected-warning {{calling function 'nopub' requires negative capability '!pub'}} + noprot(); // expected-warning {{calling function 'noprot' requires negative capability '!prot'}} + nopriv(); // expected-warning {{calling function 'nopriv' requires negative capability '!priv'}} + } + + Mutex pub; + +protected: + Mutex prot; + +private: + Mutex priv; +}; + +class Derived : private Base { +public: + void nopubDerived() EXCLUSIVE_LOCKS_REQUIRED(!pub); + void noprotDerived() EXCLUSIVE_LOCKS_REQUIRED(!prot); + + void inDerivedClass() { + nopub(); // expected-warning {{calling function 'nopub' requires negative capability '!pub'}} + noprot(); // expected-warning {{calling function 'noprot' requires negative capability '!prot'}} + nopriv(); + } +}; + +class MoreDerived : public Derived { +public: + void inDerivedClassWithInacessibleBase() { + nopubDerived(); // expected-warning {{calling function 'nopubDerived' requires negative capability '!pub'}} + noprotDerived(); + } +}; + +void outside() { + Base x; + x.nopub(); // expected-warning {{calling function 'nopub' requires negative capability '!x.pub'}} + x.noprot(); + x.nopriv(); +} + } // end namespace ScopeTest namespace DoubleAttribute {