[clang] Thread Safety Analysis: Support warning on passing/returning pointers to guarded variables (PR #127396)
Marco Elver via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 16 06:34:41 PST 2025
https://github.com/melver updated https://github.com/llvm/llvm-project/pull/127396
>From 13e86fc43071af4c926d3c2d4662423f5c5b4fe8 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Sun, 16 Feb 2025 12:42:06 +0100
Subject: [PATCH] Thread Safety Analysis: Support warning on passing/returning
pointers to guarded variables
Introduce `-Wthread-safety-pointer` (under `-Wthread-safety-beta`) to
warn when passing or returning pointers to guarded variables or guarded
data. This is is analogous to `-Wthread-safety-reference`, which
performs similar checks for C++ references.
Adding checks for pointer passing is required to avoid false negatives
in large C codebases, where data structures are typically implemented
through helpers that take pointers to instances of a data structure.
---
clang/docs/ReleaseNotes.rst | 5 +
clang/docs/ThreadSafetyAnalysis.rst | 6 +-
.../clang/Analysis/Analyses/ThreadSafety.h | 12 ++
clang/include/clang/Basic/DiagnosticGroups.td | 4 +-
.../clang/Basic/DiagnosticSemaKinds.td | 18 ++
clang/lib/Analysis/ThreadSafety.cpp | 56 ++++++-
clang/lib/Sema/AnalysisBasedWarnings.cpp | 24 +++
clang/test/Sema/warn-thread-safety-analysis.c | 4 +
.../SemaCXX/warn-thread-safety-analysis.cpp | 158 +++++++++++++++++-
9 files changed, 279 insertions(+), 8 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index efaacdf18d50a..830433853437d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -143,6 +143,11 @@ Improvements to Clang's diagnostics
- A statement attribute applied to a ``case`` label no longer suppresses
'bypassing variable initialization' diagnostics (#84072).
+- The :doc:`ThreadSafetyAnalysis` now supports ``-Wthread-safety-pointer``
+ (with ``-Wthread-safety-beta``), which enables warning on passing or
+ returning pointers to guarded variables as function arguments or return value
+ respectively.
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/docs/ThreadSafetyAnalysis.rst b/clang/docs/ThreadSafetyAnalysis.rst
index 9c1c32e46989b..1e7950067f608 100644
--- a/clang/docs/ThreadSafetyAnalysis.rst
+++ b/clang/docs/ThreadSafetyAnalysis.rst
@@ -515,7 +515,8 @@ Warning flags
+ ``-Wthread-safety-analysis``: The core analysis.
+ ``-Wthread-safety-precise``: Requires that mutex expressions match precisely.
This warning can be disabled for code which has a lot of aliases.
- + ``-Wthread-safety-reference``: Checks when guarded members are passed by reference.
+ + ``-Wthread-safety-reference``: Checks when guarded members are passed or
+ returned by reference.
:ref:`negative` are an experimental feature, which are enabled with:
@@ -528,6 +529,9 @@ for a period of time, after which they are migrated into the standard analysis.
* ``-Wthread-safety-beta``: New features. Off by default.
+ + ``-Wthread-safety-pointer``: Checks when passing or returning pointers to
+ guarded variables, or pointers to guarded data, as function argument or
+ return value respectively.
.. _negative:
diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafety.h b/clang/include/clang/Analysis/Analyses/ThreadSafety.h
index 0fcf5bed1285a..20b75c46593e0 100644
--- a/clang/include/clang/Analysis/Analyses/ThreadSafety.h
+++ b/clang/include/clang/Analysis/Analyses/ThreadSafety.h
@@ -54,6 +54,18 @@ enum ProtectedOperationKind {
/// Returning a pt-guarded variable by reference.
POK_PtReturnByRef,
+
+ /// Passing pointer to a guarded variable.
+ POK_PassPointer,
+
+ /// Passing a pt-guarded pointer.
+ POK_PtPassPointer,
+
+ /// Returning pointer to a guarded variable.
+ POK_ReturnPointer,
+
+ /// Returning a pt-guarded pointer.
+ POK_PtReturnPointer,
};
/// This enum distinguishes between different kinds of lock actions. For
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 05e39899e6f25..94c44cd199ed9 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1156,6 +1156,7 @@ def ThreadSafetyPrecise : DiagGroup<"thread-safety-precise">;
def ThreadSafetyReferenceReturn : DiagGroup<"thread-safety-reference-return">;
def ThreadSafetyReference : DiagGroup<"thread-safety-reference",
[ThreadSafetyReferenceReturn]>;
+def ThreadSafetyPointer : DiagGroup<"thread-safety-pointer">;
def ThreadSafetyNegative : DiagGroup<"thread-safety-negative">;
def ThreadSafety : DiagGroup<"thread-safety",
[ThreadSafetyAttributes,
@@ -1163,7 +1164,8 @@ def ThreadSafety : DiagGroup<"thread-safety",
ThreadSafetyPrecise,
ThreadSafetyReference]>;
def ThreadSafetyVerbose : DiagGroup<"thread-safety-verbose">;
-def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">;
+def ThreadSafetyBeta : DiagGroup<"thread-safety-beta",
+ [ThreadSafetyPointer]>;
// Warnings and notes related to the function effects system which underlies
// the nonblocking and nonallocating attributes.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f10af8f5bd6b2..ad5310145c841 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4135,6 +4135,24 @@ def warn_pt_guarded_return_by_reference : Warning<
"%select{'%2'|'%2' exclusively}3">,
InGroup<ThreadSafetyReferenceReturn>, DefaultIgnore;
+// Thread safety warnings on pointer passing
+def warn_guarded_pass_pointer : Warning<
+ "passing pointer to variable %1 requires holding %0 "
+ "%select{'%2'|'%2' exclusively}3">,
+ InGroup<ThreadSafetyPointer>, DefaultIgnore;
+def warn_pt_guarded_pass_pointer : Warning<
+ "passing pointer %1 requires holding %0 "
+ "%select{'%2'|'%2' exclusively}3">,
+ InGroup<ThreadSafetyPointer>, DefaultIgnore;
+def warn_guarded_return_pointer : Warning<
+ "returning pointer to variable %1 requires holding %0 "
+ "%select{'%2'|'%2' exclusively}3">,
+ InGroup<ThreadSafetyPointer>, DefaultIgnore;
+def warn_pt_guarded_return_pointer : Warning<
+ "returning pointer %1 requires holding %0 "
+ "%select{'%2'|'%2' exclusively}3">,
+ InGroup<ThreadSafetyPointer>, DefaultIgnore;
+
// Imprecise thread safety warnings
def warn_variable_requires_lock : Warning<
"%select{reading|writing}3 variable %1 requires holding %0 "
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
index bfaf1a0e7c7ff..5a77dffcd226b 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -1719,9 +1719,16 @@ void ThreadSafetyAnalyzer::checkAccess(const FactSet &FSet, const Expr *Exp,
}
if (const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
- // For dereferences
- if (UO->getOpcode() == UO_Deref)
+ switch (UO->getOpcode()) {
+ case UO_Deref: // dereference
checkPtAccess(FSet, UO->getSubExpr(), AK, POK);
+ break;
+ case UO_AddrOf: // addressof
+ checkAccess(FSet, UO->getSubExpr(), AK, POK);
+ break;
+ default:
+ break;
+ }
return;
}
@@ -1785,9 +1792,22 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
// Pass by reference warnings are under a different flag.
ProtectedOperationKind PtPOK = POK_VarDereference;
- if (POK == POK_PassByRef) PtPOK = POK_PtPassByRef;
- if (POK == POK_ReturnByRef)
+ switch (POK) {
+ case POK_PassByRef:
+ PtPOK = POK_PtPassByRef;
+ break;
+ case POK_ReturnByRef:
PtPOK = POK_PtReturnByRef;
+ break;
+ case POK_PassPointer:
+ PtPOK = POK_PtPassPointer;
+ break;
+ case POK_ReturnPointer:
+ PtPOK = POK_PtReturnPointer;
+ break;
+ default:
+ break;
+ }
const ValueDecl *D = getValueDecl(Exp);
if (!D || !D->hasAttrs())
@@ -2132,8 +2152,17 @@ void BuildLockset::examineArguments(const FunctionDecl *FD,
for (auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
++Param, ++Arg) {
QualType Qt = (*Param)->getType();
- if (Qt->isReferenceType())
+ if (Qt->isReferenceType()) {
checkAccess(*Arg, AK_Read, POK_PassByRef);
+ } else if (Qt->isPointerType()) {
+ const auto *Exp = (*Arg)->IgnoreImplicit()->IgnoreParenCasts();
+ if (const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
+ if (UO->getOpcode() == UO_AddrOf)
+ checkAccess(Exp, AK_Read, POK_PassPointer);
+ } else {
+ checkPtAccess(Exp, AK_Read, POK_PassPointer);
+ }
+ }
}
}
@@ -2284,6 +2313,23 @@ void BuildLockset::VisitReturnStmt(const ReturnStmt *S) {
FunctionExitFSet, RetVal,
ReturnType->getPointeeType().isConstQualified() ? AK_Read : AK_Written,
POK_ReturnByRef);
+ } else if (ReturnType->isPointerType()) {
+ const auto *Exp = RetVal->IgnoreImplicit()->IgnoreParenCasts();
+ if (const auto *UO = dyn_cast<UnaryOperator>(Exp)) {
+ if (UO->getOpcode() == UO_AddrOf) {
+ Analyzer->checkAccess(FunctionExitFSet, Exp,
+ ReturnType->getPointeeType().isConstQualified()
+ ? AK_Read
+ : AK_Written,
+ POK_ReturnPointer);
+ }
+ } else {
+ Analyzer->checkPtAccess(FunctionExitFSet, Exp,
+ ReturnType->getPointeeType().isConstQualified()
+ ? AK_Read
+ : AK_Written,
+ POK_ReturnPointer);
+ }
}
}
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 589869d018657..f0ea6cfbf33f9 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2021,6 +2021,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
case POK_PtReturnByRef:
DiagID = diag::warn_pt_guarded_return_by_reference;
break;
+ case POK_PassPointer:
+ DiagID = diag::warn_guarded_pass_pointer;
+ break;
+ case POK_PtPassPointer:
+ DiagID = diag::warn_pt_guarded_pass_pointer;
+ break;
+ case POK_ReturnPointer:
+ DiagID = diag::warn_guarded_return_pointer;
+ break;
+ case POK_PtReturnPointer:
+ DiagID = diag::warn_pt_guarded_return_pointer;
+ break;
}
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
@@ -2057,6 +2069,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
case POK_PtReturnByRef:
DiagID = diag::warn_pt_guarded_return_by_reference;
break;
+ case POK_PassPointer:
+ DiagID = diag::warn_guarded_pass_pointer;
+ break;
+ case POK_PtPassPointer:
+ DiagID = diag::warn_pt_guarded_pass_pointer;
+ break;
+ case POK_ReturnPointer:
+ DiagID = diag::warn_guarded_return_pointer;
+ break;
+ case POK_PtReturnPointer:
+ DiagID = diag::warn_pt_guarded_return_pointer;
+ break;
}
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind
<< D
diff --git a/clang/test/Sema/warn-thread-safety-analysis.c b/clang/test/Sema/warn-thread-safety-analysis.c
index 73b4e4798f644..66bfecb8d9c6d 100644
--- a/clang/test/Sema/warn-thread-safety-analysis.c
+++ b/clang/test/Sema/warn-thread-safety-analysis.c
@@ -134,7 +134,9 @@ int main(void) {
Foo_func3(5);
set_value(&a_, 0); // expected-warning{{calling function 'set_value' requires holding mutex 'foo_.mu_' exclusively}}
+ // expected-warning at -1{{passing pointer to variable 'a_' requires holding mutex 'foo_.mu_'}}
get_value(b_); // expected-warning{{calling function 'get_value' requires holding mutex 'foo_.mu_'}}
+ // expected-warning at -1{{passing pointer 'b_' requires holding mutex 'foo_.mu_'}}
mutex_exclusive_lock(foo_.mu_);
set_value(&a_, 1);
mutex_unlock(foo_.mu_);
@@ -180,6 +182,8 @@ int main(void) {
#ifdef LATE_PARSING
late_parsing.a_value_defined_before = 1; // expected-warning{{writing variable 'a_value_defined_before' requires holding mutex 'a_mutex_defined_late' exclusively}}
late_parsing.a_ptr_defined_before = 0;
+ set_value(&late_parsing.a_value_defined_before, 0); // expected-warning{{calling function 'set_value' requires holding mutex 'foo_.mu_' exclusively}}
+ // expected-warning at -1{{passing pointer to variable 'a_value_defined_before' requires holding mutex 'a_mutex_defined_late'}}
mutex_exclusive_lock(late_parsing.a_mutex_defined_late);
mutex_exclusive_lock(late_parsing.a_mutex_defined_early); // expected-warning{{mutex 'a_mutex_defined_early' must be acquired before 'a_mutex_defined_late'}}
mutex_exclusive_unlock(late_parsing.a_mutex_defined_early);
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 018d6d1bb258b..976e135d2df43 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -4863,6 +4863,10 @@ class Data {
int dat;
};
+class DataWithAddrOf : public Data {
+public:
+ const Data* operator&() const;
+};
class DataCell {
public:
@@ -4873,6 +4877,7 @@ class DataCell {
};
+void showData(const Data* d);
void showDataCell(const DataCell& dc);
@@ -4944,6 +4949,14 @@ class Foo {
(*datap2_)[0] = 0; // expected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
data_(); // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
+
+ // Calls operator&, and does not take the address.
+ (void)&data_ao_; // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}}
+ (void)__builtin_addressof(data_ao_); // expected-warning {{passing variable 'data_ao_' by reference requires holding mutex 'mu_'}}
+ showData(&data_ao_); // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}}
+ (void)&data_; // no warning
+ (void)datap2_; // no warning
+ showData(&data_); // expected-warning {{passing pointer to variable 'data_' requires holding mutex 'mu_'}}
}
// const operator tests
@@ -4955,6 +4968,10 @@ class Foo {
//showDataCell(*datap2_); // xpected-warning {{reading the value pointed to by 'datap2_' requires holding mutex 'mu_'}}
int a = data_[0]; // expected-warning {{reading variable 'data_' requires holding mutex 'mu_'}}
+
+ (void)&data_ao_; // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}}
+ showData(&data_ao_); // expected-warning {{reading variable 'data_ao_' requires holding mutex 'mu_'}}
+ showData(&data_); // expected-warning {{passing pointer to variable 'data_' requires holding mutex 'mu_'}}
}
private:
@@ -4962,6 +4979,7 @@ class Foo {
Data data_ GUARDED_BY(mu_);
Data* datap1_ GUARDED_BY(mu_);
Data* datap2_ PT_GUARDED_BY(mu_);
+ DataWithAddrOf data_ao_ GUARDED_BY(mu_);
};
} // end namespace GuardedNonPrimitiveTypeTest
@@ -5911,8 +5929,12 @@ T&& mymove(T& f);
void copy(Foo f);
void write1(Foo& f);
void write2(int a, Foo& f);
+void write3(Foo* f);
+void write4(Foo** f);
void read1(const Foo& f);
void read2(int a, const Foo& f);
+void read3(const Foo* f);
+void read4(Foo* const* f);
void destroy(Foo&& f);
void operator/(const Foo& f, const Foo& g);
@@ -5942,19 +5964,24 @@ class Bar {
Foo foo GUARDED_BY(mu);
Foo foo2 GUARDED_BY(mu);
Foo* foop PT_GUARDED_BY(mu);
+ Foo* foop2 GUARDED_BY(mu);
SmartPtr<Foo> foosp PT_GUARDED_BY(mu);
// test methods.
void mwrite1(Foo& f);
void mwrite2(int a, Foo& f);
+ void mwrite3(Foo* f);
void mread1(const Foo& f);
void mread2(int a, const Foo& f);
+ void mread3(const Foo* f);
// static methods
static void smwrite1(Foo& f);
static void smwrite2(int a, Foo& f);
+ static void smwrite3(Foo* f);
static void smread1(const Foo& f);
static void smread2(int a, const Foo& f);
+ static void smread3(const Foo* f);
void operator<<(const Foo& f);
@@ -6017,6 +6044,107 @@ class Bar {
read2(10, *foosp.get());
destroy(mymove(*foosp.get()));
}
+
+ void test_pass_pointer() {
+ (void)&foo; // no warning
+ (void)foop; // no warning
+
+ write3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ write3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ write3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ write4((Foo **)&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ write4(&foop); // no warning
+ read3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ read3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ read3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ read4((Foo **)&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ read4(&foop); // no warning
+ mwrite3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ mwrite3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ mwrite3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ mread3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ mread3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ mread3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ smwrite3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ smwrite3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ smwrite3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ smread3(&foo); // expected-warning {{passing pointer to variable 'foo' requires holding mutex 'mu'}}
+ smread3(foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+ smread3(&*foop); // expected-warning {{passing pointer 'foop' requires holding mutex 'mu'}}
+
+ write3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ write3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ write4(&foop2); // expected-warning {{passing pointer to variable 'foop2' requires holding mutex 'mu'}}
+ read3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ read3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ read4(&foop2); // expected-warning {{passing pointer to variable 'foop2' requires holding mutex 'mu'}}
+ mwrite3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ mwrite3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ mread3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ mread3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ smwrite3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ smwrite3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ smread3(foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+ smread3(&*foop2); // expected-warning {{reading variable 'foop2' requires holding mutex 'mu'}}
+
+ mu.Lock();
+ write3(&foo);
+ write3(foop);
+ write3(&*foop);
+ write3(foop2);
+ write4(&foop2);
+ read3(&foo);
+ read3(foop);
+ read3(&*foop);
+ read3(foop2);
+ read4(&foop2);
+ mwrite3(&foo);
+ mwrite3(foop);
+ mwrite3(&*foop);
+ mwrite3(foop2);
+ mread3(&foo);
+ mread3(foop);
+ mread3(&*foop);
+ mread3(foop2);
+ smwrite3(&foo);
+ smwrite3(foop);
+ smwrite3(&*foop);
+ smwrite3(foop2);
+ smread3(&foo);
+ smread3(foop);
+ smread3(&*foop);
+ smread3(foop2);
+ mu.Unlock();
+
+ mu.ReaderLock();
+ write3(&foo);
+ write3(foop);
+ write3(&*foop);
+ write3(foop2);
+ write4(&foop2);
+ read3(&foo);
+ read3(foop);
+ read3(&*foop);
+ read3(foop2);
+ read4(&foop2);
+ mwrite3(&foo);
+ mwrite3(foop);
+ mwrite3(&*foop);
+ mwrite3(foop2);
+ mread3(&foo);
+ mread3(foop);
+ mread3(&*foop);
+ mread3(foop2);
+ smwrite3(&foo);
+ smwrite3(foop);
+ smwrite3(&*foop);
+ smwrite3(foop2);
+ smread3(&foo);
+ smread3(foop);
+ smread3(&*foop);
+ smread3(foop2);
+ mu.ReaderUnlock();
+ }
};
class Return {
@@ -6087,9 +6215,37 @@ class Return {
const Foo &returns_constref_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
return foo;
}
+
+ Foo *returns_ptr_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ return &foo;
+ }
+
+ Foo *returns_pt_ptr_exclusive_locks_required() EXCLUSIVE_LOCKS_REQUIRED(mu) {
+ return foo_ptr;
+ }
+
+ Foo *returns_ptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+ return &foo; // expected-warning {{returning pointer to variable 'foo' requires holding mutex 'mu' exclusively}}
+ }
+
+ Foo *returns_pt_ptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+ return foo_ptr; // expected-warning {{returning pointer 'foo_ptr' requires holding mutex 'mu' exclusively}}
+ }
+
+ const Foo *returns_constptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+ return &foo;
+ }
+
+ const Foo *returns_pt_constptr_shared_locks_required() SHARED_LOCKS_REQUIRED(mu) {
+ return foo_ptr;
+ }
Foo *returns_ptr() {
- return &foo; // FIXME -- Do we want to warn on this ?
+ return &foo; // expected-warning {{returning pointer to variable 'foo' requires holding mutex 'mu' exclusively}}
+ }
+
+ Foo *returns_pt_ptr() {
+ return foo_ptr; // expected-warning {{returning pointer 'foo_ptr' requires holding mutex 'mu' exclusively}}
}
Foo &returns_ref2() {
More information about the cfe-commits
mailing list