clang 20.0.0git
RawPtrRefMemberChecker.cpp
Go to the documentation of this file.
1//=======- RawPtrRefMemberChecker.cpp ----------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ASTUtils.h"
10#include "DiagOutputUtils.h"
11#include "PtrTypesSemantics.h"
13#include "clang/AST/Decl.h"
14#include "clang/AST/DeclCXX.h"
20#include "llvm/Support/Casting.h"
21#include <optional>
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27
28class RawPtrRefMemberChecker
29 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30private:
31 BugType Bug;
32 mutable BugReporter *BR;
33
34public:
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;
43
44 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
45 BugReporter &BRArg) const {
46 BR = &BRArg;
47
48 // The calls to checkAST* from AnalysisConsumer don't
49 // visit template instantiations or lambda classes. We
50 // want to visit those, so we make our own RecursiveASTVisitor.
51 struct LocalVisitor : DynamicRecursiveASTVisitor {
52 const RawPtrRefMemberChecker *Checker;
53 explicit LocalVisitor(const RawPtrRefMemberChecker *Checker)
54 : Checker(Checker) {
55 assert(Checker);
56 ShouldVisitTemplateInstantiations = true;
57 ShouldVisitImplicitCode = false;
58 }
59
60 bool VisitRecordDecl(RecordDecl *RD) override {
61 Checker->visitRecordDecl(RD);
62 return true;
63 }
64 };
65
66 LocalVisitor visitor(this);
67 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
68 }
69
70 void visitRecordDecl(const RecordDecl *RD) const {
71 if (shouldSkipDecl(RD))
72 return;
73
74 for (auto *Member : RD->fields()) {
75 const Type *MemberType = Member->getType().getTypePtrOrNull();
76 if (!MemberType)
77 continue;
78
79 if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
80 // If we don't see the definition we just don't know.
81 if (MemberCXXRD->hasDefinition()) {
82 std::optional<bool> isRCAble = isPtrCompatible(MemberCXXRD);
83 if (isRCAble && *isRCAble)
84 reportBug(Member, MemberType, MemberCXXRD, RD);
85 }
86 }
87 }
88 }
89
90 bool shouldSkipDecl(const RecordDecl *RD) const {
92 return true;
93
94 if (RD->isImplicit())
95 return true;
96
97 if (RD->isLambda())
98 return true;
99
100 // If the construct doesn't have a source file, then it's not something
101 // we want to diagnose.
102 const auto RDLocation = RD->getLocation();
103 if (!RDLocation.isValid())
104 return true;
105
106 const auto Kind = RD->getTagKind();
107 // FIMXE: Should we check union members too?
108 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
109 return true;
110
111 // Ignore CXXRecords that come from system headers.
112 if (BR->getSourceManager().isInSystemHeader(RDLocation))
113 return true;
114
115 // Ref-counted smartpointers actually have raw-pointer to uncounted type as
116 // a member but we trust them to handle it correctly.
117 auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
118 if (CXXRD)
119 return isPtrCls(CXXRD);
120
121 return false;
122 }
123
124 void reportBug(const FieldDecl *Member, const Type *MemberType,
125 const CXXRecordDecl *MemberCXXRD,
126 const RecordDecl *ClassCXXRD) const {
127 assert(Member);
128 assert(MemberType);
129 assert(MemberCXXRD);
130
132 llvm::raw_svector_ostream Os(Buf);
133
134 Os << "Member variable ";
136 Os << " in ";
137 printQuotedQualifiedName(Os, ClassCXXRD);
138 Os << " is a "
139 << (isa<PointerType>(MemberType) ? "raw pointer" : "reference") << " to "
140 << typeName() << " ";
141 printQuotedQualifiedName(Os, MemberCXXRD);
142 Os << "; " << invariant() << ".";
143
144 PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
145 BR->getSourceManager());
146 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
147 Report->addRange(Member->getSourceRange());
148 BR->emitReport(std::move(Report));
149 }
150};
151
152class NoUncountedMemberChecker final : public RawPtrRefMemberChecker {
153public:
154 NoUncountedMemberChecker()
155 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
156 "reference-countable type") {}
157
158 std::optional<bool>
159 isPtrCompatible(const clang::CXXRecordDecl *R) const final {
160 return isRefCountable(R);
161 }
162
163 bool isPtrCls(const clang::CXXRecordDecl *R) const final {
164 return isRefCounted(R);
165 }
166
167 const char *typeName() const final { return "ref-countable type"; }
168
169 const char *invariant() const final {
170 return "member variables must be Ref, RefPtr, WeakRef, or WeakPtr";
171 }
172};
173
174class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker {
175public:
176 NoUncheckedPtrMemberChecker()
177 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
178 "checked-pointer capable type") {}
179
180 std::optional<bool>
181 isPtrCompatible(const clang::CXXRecordDecl *R) const final {
182 return isCheckedPtrCapable(R);
183 }
184
185 bool isPtrCls(const clang::CXXRecordDecl *R) const final {
186 return isCheckedPtr(R);
187 }
188
189 const char *typeName() const final { return "CheckedPtr capable type"; }
190
191 const char *invariant() const final {
192 return "member variables must be a CheckedPtr, CheckedRef, WeakRef, or "
193 "WeakPtr";
194 }
195};
196
197} // namespace
198
199void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
200 Mgr.registerChecker<NoUncountedMemberChecker>();
201}
202
203bool ento::shouldRegisterNoUncountedMemberChecker(const CheckerManager &Mgr) {
204 return true;
205}
206
207void ento::registerNoUncheckedPtrMemberChecker(CheckerManager &Mgr) {
208 Mgr.registerChecker<NoUncheckedPtrMemberChecker>();
209}
210
211bool ento::shouldRegisterNoUncheckedPtrMemberChecker(
212 const CheckerManager &Mgr) {
213 return true;
214}
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition: DeclBase.h:596
SourceLocation getLocation() const
Definition: DeclBase.h:442
Recursive AST visitor that supports extension via dynamic dispatch.
Represents a member of a struct/union/class.
Definition: Decl.h:3033
Represents a struct/union/class.
Definition: Decl.h:4148
bool isLambda() const
Determine whether this record is a class describing a lambda function object.
Definition: Decl.cpp:5063
field_range fields() const
Definition: Decl.h:4354
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
bool isThisDeclarationADefinition() const
Return true if this declaration is a completion definition of the type.
Definition: Decl.h:3662
TagKind getTagKind() const
Definition: Decl.h:3759
The top declaration context.
Definition: Decl.h:84
The base class of the type hierarchy.
Definition: Type.h:1828
const CXXRecordDecl * getPointeeCXXRecordDecl() const
If this is a pointer or reference to a RecordType, return the CXXRecordDecl that the type refers to.
Definition: Type.cpp:1901
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
The JSON file list parser is used to communicate input to InstallAPI.
std::optional< bool > isCheckedPtrCapable(const clang::CXXRecordDecl *R)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
std::optional< bool > isRefCountable(const clang::CXXRecordDecl *R)
bool isRefCounted(const CXXRecordDecl *R)
bool isCheckedPtr(const std::string &Name)