clang 20.0.0git
NoUncountedMembersChecker.cpp
Go to the documentation of this file.
1//=======- NoUncountedMembersChecker.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 NoUncountedMemberChecker
29 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30private:
31 BugType Bug;
32 mutable BugReporter *BR;
33
34public:
35 NoUncountedMemberChecker()
36 : Bug(this,
37 "Member variable is a raw-pointer/reference to reference-countable "
38 "type",
39 "WebKit coding guidelines") {}
40
41 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42 BugReporter &BRArg) const {
43 BR = &BRArg;
44
45 // The calls to checkAST* from AnalysisConsumer don't
46 // visit template instantiations or lambda classes. We
47 // want to visit those, so we make our own RecursiveASTVisitor.
48 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
49 const NoUncountedMemberChecker *Checker;
50 explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
51 : Checker(Checker) {
52 assert(Checker);
53 }
54
55 bool shouldVisitTemplateInstantiations() const { return true; }
56 bool shouldVisitImplicitCode() const { return false; }
57
58 bool VisitRecordDecl(const RecordDecl *RD) {
59 Checker->visitRecordDecl(RD);
60 return true;
61 }
62 };
63
64 LocalVisitor visitor(this);
65 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
66 }
67
68 void visitRecordDecl(const RecordDecl *RD) const {
69 if (shouldSkipDecl(RD))
70 return;
71
72 for (auto *Member : RD->fields()) {
73 const Type *MemberType = Member->getType().getTypePtrOrNull();
74 if (!MemberType)
75 continue;
76
77 if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
78 // If we don't see the definition we just don't know.
79 if (MemberCXXRD->hasDefinition()) {
80 std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
81 if (isRCAble && *isRCAble)
82 reportBug(Member, MemberType, MemberCXXRD, RD);
83 }
84 }
85 }
86 }
87
88 bool shouldSkipDecl(const RecordDecl *RD) const {
90 return true;
91
92 if (RD->isImplicit())
93 return true;
94
95 if (RD->isLambda())
96 return true;
97
98 // If the construct doesn't have a source file, then it's not something
99 // we want to diagnose.
100 const auto RDLocation = RD->getLocation();
101 if (!RDLocation.isValid())
102 return true;
103
104 const auto Kind = RD->getTagKind();
105 // FIMXE: Should we check union members too?
106 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
107 return true;
108
109 // Ignore CXXRecords that come from system headers.
110 if (BR->getSourceManager().isInSystemHeader(RDLocation))
111 return true;
112
113 // Ref-counted smartpointers actually have raw-pointer to uncounted type as
114 // a member but we trust them to handle it correctly.
115 auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
116 if (CXXRD)
117 return isRefCounted(CXXRD);
118
119 return false;
120 }
121
122 void reportBug(const FieldDecl *Member, const Type *MemberType,
123 const CXXRecordDecl *MemberCXXRD,
124 const RecordDecl *ClassCXXRD) const {
125 assert(Member);
126 assert(MemberType);
127 assert(MemberCXXRD);
128
130 llvm::raw_svector_ostream Os(Buf);
131
132 Os << "Member variable ";
134 Os << " in ";
135 printQuotedQualifiedName(Os, ClassCXXRD);
136 Os << " is a "
137 << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
138 << " to ref-countable type ";
139 printQuotedQualifiedName(Os, MemberCXXRD);
140 Os << "; member variables must be ref-counted.";
141
142 PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
143 BR->getSourceManager());
144 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
145 Report->addRange(Member->getSourceRange());
146 BR->emitReport(std::move(Report));
147 }
148};
149} // namespace
150
151void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
152 Mgr.registerChecker<NoUncountedMemberChecker>();
153}
154
155bool ento::shouldRegisterNoUncountedMemberChecker(
156 const CheckerManager &Mgr) {
157 return true;
158}
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:600
SourceLocation getLocation() const
Definition: DeclBase.h:446
Represents a member of a struct/union/class.
Definition: Decl.h:3030
Represents a struct/union/class.
Definition: Decl.h:4145
bool isLambda() const
Determine whether this record is a class describing a lambda function object.
Definition: Decl.cpp:5039
field_range fields() const
Definition: Decl.h:4351
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
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:3659
TagKind getTagKind() const
Definition: Decl.h:3756
The top declaration context.
Definition: Decl.h:84
The base class of the type hierarchy.
Definition: Type.h:1829
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:1867
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.
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
std::optional< bool > isRefCountable(const CXXRecordDecl *R)
bool isRefCounted(const CXXRecordDecl *R)