Skip to content

Commit 3c09843

Browse files
authored
[alpha.webkit.NoUncheckedPtrMemberChecker] Introduce member variable checker for CheckedPtr/CheckedRef (#108352)
This PR introduces new WebKit checker to warn a member variable that is a raw reference or a raw pointer to an object, which is capable of creating a CheckedRef/CheckedPtr.
1 parent 880ee48 commit 3c09843

File tree

9 files changed

+257
-46
lines changed

9 files changed

+257
-46
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3442,6 +3442,27 @@ Check for non-determinism caused by sorting of pointers.
34423442
alpha.WebKit
34433443
^^^^^^^^^^^^
34443444
3445+
.. _alpha-webkit-NoUncheckedPtrMemberChecker:
3446+
3447+
alpha.webkit.NoUncheckedPtrMemberChecker
3448+
""""""""""""""""""""""""""""""""""""""""
3449+
Raw pointers and references to an object which supports CheckedPtr or CheckedRef can't be used as class members. Only CheckedPtr, CheckedRef, RefPtr, or Ref are allowed.
3450+
3451+
.. code-block:: cpp
3452+
3453+
struct CheckableObj {
3454+
void incrementPtrCount() {}
3455+
void decrementPtrCount() {}
3456+
};
3457+
3458+
struct Foo {
3459+
CheckableObj* ptr; // warn
3460+
CheckableObj& ptr; // warn
3461+
// ...
3462+
};
3463+
3464+
See `WebKit Guidelines for Safer C++ Programming <https://github.com/WebKit/WebKit/wiki/Safer-CPP-Guidelines>`_ for details.
3465+
34453466
.. _alpha-webkit-UncountedCallArgsChecker:
34463467
34473468
alpha.webkit.UncountedCallArgsChecker

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,10 @@ def UncountedLambdaCapturesChecker : Checker<"UncountedLambdaCapturesChecker">,
17641764

17651765
let ParentPackage = WebKitAlpha in {
17661766

1767+
def NoUncheckedPtrMemberChecker : Checker<"NoUncheckedPtrMemberChecker">,
1768+
HelpText<"Check for no unchecked member variables.">,
1769+
Documentation<HasDocumentation>;
1770+
17671771
def UncountedCallArgsChecker : Checker<"UncountedCallArgsChecker">,
17681772
HelpText<"Check uncounted call arguments.">,
17691773
Documentation<HasDocumentation>;

clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ add_clang_library(clangStaticAnalyzerCheckers
132132
VLASizeChecker.cpp
133133
ValistChecker.cpp
134134
VirtualCallChecker.cpp
135-
WebKit/NoUncountedMembersChecker.cpp
135+
WebKit/RawPtrRefMemberChecker.cpp
136136
WebKit/ASTUtils.cpp
137137
WebKit/PtrTypesSemantics.cpp
138138
WebKit/RefCntblBaseVirtualDtorChecker.cpp

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ using namespace clang;
1919

2020
namespace {
2121

22-
bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
23-
const char *NameToMatch) {
22+
bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, StringRef NameToMatch) {
2423
assert(R);
2524
assert(R->hasDefinition());
2625

@@ -37,7 +36,7 @@ bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
3736
namespace clang {
3837

3938
std::optional<const clang::CXXRecordDecl *>
40-
hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
39+
hasPublicMethodInBase(const CXXBaseSpecifier *Base, StringRef NameToMatch) {
4140
assert(Base);
4241

4342
const Type *T = Base->getType().getTypePtrOrNull();
@@ -53,48 +52,49 @@ hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
5352
return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
5453
}
5554

56-
std::optional<bool> isRefCountable(const CXXRecordDecl* R)
57-
{
55+
std::optional<bool> isSmartPtrCompatible(const CXXRecordDecl *R,
56+
StringRef IncMethodName,
57+
StringRef DecMethodName) {
5858
assert(R);
5959

6060
R = R->getDefinition();
6161
if (!R)
6262
return std::nullopt;
6363

64-
bool hasRef = hasPublicMethodInBaseClass(R, "ref");
65-
bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
64+
bool hasRef = hasPublicMethodInBaseClass(R, IncMethodName);
65+
bool hasDeref = hasPublicMethodInBaseClass(R, DecMethodName);
6666
if (hasRef && hasDeref)
6767
return true;
6868

6969
CXXBasePaths Paths;
7070
Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
7171

7272
bool AnyInconclusiveBase = false;
73-
const auto hasPublicRefInBase =
74-
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
75-
auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
76-
if (!hasRefInBase) {
77-
AnyInconclusiveBase = true;
78-
return false;
79-
}
80-
return (*hasRefInBase) != nullptr;
81-
};
73+
const auto hasPublicRefInBase = [&](const CXXBaseSpecifier *Base,
74+
CXXBasePath &) {
75+
auto hasRefInBase = clang::hasPublicMethodInBase(Base, IncMethodName);
76+
if (!hasRefInBase) {
77+
AnyInconclusiveBase = true;
78+
return false;
79+
}
80+
return (*hasRefInBase) != nullptr;
81+
};
8282

8383
hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
8484
/*LookupInDependent =*/true);
8585
if (AnyInconclusiveBase)
8686
return std::nullopt;
8787

8888
Paths.clear();
89-
const auto hasPublicDerefInBase =
90-
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
91-
auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
92-
if (!hasDerefInBase) {
93-
AnyInconclusiveBase = true;
94-
return false;
95-
}
96-
return (*hasDerefInBase) != nullptr;
97-
};
89+
const auto hasPublicDerefInBase = [&](const CXXBaseSpecifier *Base,
90+
CXXBasePath &) {
91+
auto hasDerefInBase = clang::hasPublicMethodInBase(Base, DecMethodName);
92+
if (!hasDerefInBase) {
93+
AnyInconclusiveBase = true;
94+
return false;
95+
}
96+
return (*hasDerefInBase) != nullptr;
97+
};
9898
hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
9999
/*LookupInDependent =*/true);
100100
if (AnyInconclusiveBase)
@@ -103,11 +103,23 @@ std::optional<bool> isRefCountable(const CXXRecordDecl* R)
103103
return hasRef && hasDeref;
104104
}
105105

106+
std::optional<bool> isRefCountable(const clang::CXXRecordDecl *R) {
107+
return isSmartPtrCompatible(R, "ref", "deref");
108+
}
109+
110+
std::optional<bool> isCheckedPtrCapable(const clang::CXXRecordDecl *R) {
111+
return isSmartPtrCompatible(R, "incrementPtrCount", "decrementPtrCount");
112+
}
113+
106114
bool isRefType(const std::string &Name) {
107115
return Name == "Ref" || Name == "RefAllowingPartiallyDestroyed" ||
108116
Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed";
109117
}
110118

119+
bool isCheckedPtr(const std::string &Name) {
120+
return Name == "CheckedPtr" || Name == "CheckedRef";
121+
}
122+
111123
bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
112124
assert(F);
113125
const std::string &FunctionName = safeGetName(F);
@@ -217,6 +229,15 @@ bool isRefCounted(const CXXRecordDecl *R) {
217229
return false;
218230
}
219231

232+
bool isCheckedPtr(const CXXRecordDecl *R) {
233+
assert(R);
234+
if (auto *TmplR = R->getTemplateInstantiationPattern()) {
235+
const auto &ClassName = safeGetName(TmplR);
236+
return isCheckedPtr(ClassName);
237+
}
238+
return false;
239+
}
240+
220241
bool isPtrConversion(const FunctionDecl *F) {
221242
assert(F);
222243
if (isCtorOfRefCounted(F))

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,23 @@ class Type;
3434
/// \returns CXXRecordDecl of the base if the type has ref as a public method,
3535
/// nullptr if not, std::nullopt if inconclusive.
3636
std::optional<const clang::CXXRecordDecl *>
37-
hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch);
37+
hasPublicMethodInBase(const CXXBaseSpecifier *Base,
38+
llvm::StringRef NameToMatch);
3839

3940
/// \returns true if \p Class is ref-countable, false if not, std::nullopt if
4041
/// inconclusive.
41-
std::optional<bool> isRefCountable(const clang::CXXRecordDecl* Class);
42+
std::optional<bool> isRefCountable(const clang::CXXRecordDecl *Class);
43+
44+
/// \returns true if \p Class is checked-pointer compatible, false if not,
45+
/// std::nullopt if inconclusive.
46+
std::optional<bool> isCheckedPtrCapable(const clang::CXXRecordDecl *Class);
4247

4348
/// \returns true if \p Class is ref-counted, false if not.
4449
bool isRefCounted(const clang::CXXRecordDecl *Class);
4550

51+
/// \returns true if \p Class is a CheckedPtr / CheckedRef, false if not.
52+
bool isCheckedPtr(const clang::CXXRecordDecl *Class);
53+
4654
/// \returns true if \p Class is ref-countable AND not ref-counted, false if
4755
/// not, std::nullopt if inconclusive.
4856
std::optional<bool> isUncounted(const clang::QualType T);

clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp renamed to clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//=======- NoUncountedMembersChecker.cpp -------------------------*- C++ -*-==//
1+
//=======- RawPtrRefMemberChecker.cpp ----------------------------*- C++ -*-==//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
@@ -25,18 +25,21 @@ using namespace ento;
2525

2626
namespace {
2727

28-
class NoUncountedMemberChecker
28+
class RawPtrRefMemberChecker
2929
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
3030
private:
3131
BugType Bug;
3232
mutable BugReporter *BR;
3333

3434
public:
35-
NoUncountedMemberChecker()
36-
: Bug(this,
37-
"Member variable is a raw-pointer/reference to reference-countable "
38-
"type",
39-
"WebKit coding guidelines") {}
35+
RawPtrRefMemberChecker(const char *description)
36+
: Bug(this, description, "WebKit coding guidelines") {}
37+
38+
virtual std::optional<bool>
39+
isPtrCompatible(const clang::CXXRecordDecl *) const = 0;
40+
virtual bool isPtrCls(const clang::CXXRecordDecl *) const = 0;
41+
virtual const char *typeName() const = 0;
42+
virtual const char *invariant() const = 0;
4043

4144
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
4245
BugReporter &BRArg) const {
@@ -46,8 +49,8 @@ class NoUncountedMemberChecker
4649
// visit template instantiations or lambda classes. We
4750
// want to visit those, so we make our own RecursiveASTVisitor.
4851
struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
49-
const NoUncountedMemberChecker *Checker;
50-
explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
52+
const RawPtrRefMemberChecker *Checker;
53+
explicit LocalVisitor(const RawPtrRefMemberChecker *Checker)
5154
: Checker(Checker) {
5255
assert(Checker);
5356
}
@@ -77,9 +80,9 @@ class NoUncountedMemberChecker
7780
if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
7881
// If we don't see the definition we just don't know.
7982
if (MemberCXXRD->hasDefinition()) {
80-
std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
81-
if (isRCAble && *isRCAble)
82-
reportBug(Member, MemberType, MemberCXXRD, RD);
83+
std::optional<bool> isRCAble = isPtrCompatible(MemberCXXRD);
84+
if (isRCAble && *isRCAble)
85+
reportBug(Member, MemberType, MemberCXXRD, RD);
8386
}
8487
}
8588
}
@@ -114,7 +117,7 @@ class NoUncountedMemberChecker
114117
// a member but we trust them to handle it correctly.
115118
auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
116119
if (CXXRD)
117-
return isRefCounted(CXXRD);
120+
return isPtrCls(CXXRD);
118121

119122
return false;
120123
}
@@ -134,10 +137,10 @@ class NoUncountedMemberChecker
134137
Os << " in ";
135138
printQuotedQualifiedName(Os, ClassCXXRD);
136139
Os << " is a "
137-
<< (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
138-
<< " to ref-countable type ";
140+
<< (isa<PointerType>(MemberType) ? "raw pointer" : "reference") << " to "
141+
<< typeName() << " ";
139142
printQuotedQualifiedName(Os, MemberCXXRD);
140-
Os << "; member variables must be ref-counted.";
143+
Os << "; " << invariant() << ".";
141144

142145
PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
143146
BR->getSourceManager());
@@ -146,13 +149,67 @@ class NoUncountedMemberChecker
146149
BR->emitReport(std::move(Report));
147150
}
148151
};
152+
153+
class NoUncountedMemberChecker final : public RawPtrRefMemberChecker {
154+
public:
155+
NoUncountedMemberChecker()
156+
: RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
157+
"reference-countable type") {}
158+
159+
std::optional<bool>
160+
isPtrCompatible(const clang::CXXRecordDecl *R) const final {
161+
return isRefCountable(R);
162+
}
163+
164+
bool isPtrCls(const clang::CXXRecordDecl *R) const final {
165+
return isRefCounted(R);
166+
}
167+
168+
const char *typeName() const final { return "ref-countable type"; }
169+
170+
const char *invariant() const final {
171+
return "member variables must be Ref, RefPtr, WeakRef, or WeakPtr";
172+
}
173+
};
174+
175+
class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker {
176+
public:
177+
NoUncheckedPtrMemberChecker()
178+
: RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
179+
"checked-pointer capable type") {}
180+
181+
std::optional<bool>
182+
isPtrCompatible(const clang::CXXRecordDecl *R) const final {
183+
return isCheckedPtrCapable(R);
184+
}
185+
186+
bool isPtrCls(const clang::CXXRecordDecl *R) const final {
187+
return isCheckedPtr(R);
188+
}
189+
190+
const char *typeName() const final { return "CheckedPtr capable type"; }
191+
192+
const char *invariant() const final {
193+
return "member variables must be a CheckedPtr, CheckedRef, WeakRef, or "
194+
"WeakPtr";
195+
}
196+
};
197+
149198
} // namespace
150199

151200
void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
152201
Mgr.registerChecker<NoUncountedMemberChecker>();
153202
}
154203

155-
bool ento::shouldRegisterNoUncountedMemberChecker(
204+
bool ento::shouldRegisterNoUncountedMemberChecker(const CheckerManager &Mgr) {
205+
return true;
206+
}
207+
208+
void ento::registerNoUncheckedPtrMemberChecker(CheckerManager &Mgr) {
209+
Mgr.registerChecker<NoUncheckedPtrMemberChecker>();
210+
}
211+
212+
bool ento::shouldRegisterNoUncheckedPtrMemberChecker(
156213
const CheckerManager &Mgr) {
157214
return true;
158215
}

0 commit comments

Comments
 (0)