1 //=======- NoUncountedMembersChecker.cpp -------------------------*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
10 #include "DiagOutputUtils.h"
11 #include "PtrTypesSemantics.h"
12 #include "clang/AST/CXXInheritance.h"
13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclCXX.h"
15 #include "clang/AST/RecursiveASTVisitor.h"
16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "llvm/ADT/DenseSet.h"
21 #include "llvm/Support/Casting.h"
24 using namespace clang
;
29 class NoUncountedMemberChecker
30 : public Checker
<check::ASTDecl
<TranslationUnitDecl
>> {
33 mutable BugReporter
*BR
;
36 NoUncountedMemberChecker()
38 "Member variable is a raw-poiner/reference to reference-countable "
40 "WebKit coding guidelines") {}
42 void checkASTDecl(const TranslationUnitDecl
*TUD
, AnalysisManager
&MGR
,
43 BugReporter
&BRArg
) const {
46 // The calls to checkAST* from AnalysisConsumer don't
47 // visit template instantiations or lambda classes. We
48 // want to visit those, so we make our own RecursiveASTVisitor.
49 struct LocalVisitor
: public RecursiveASTVisitor
<LocalVisitor
> {
50 const NoUncountedMemberChecker
*Checker
;
51 explicit LocalVisitor(const NoUncountedMemberChecker
*Checker
)
56 bool shouldVisitTemplateInstantiations() const { return true; }
57 bool shouldVisitImplicitCode() const { return false; }
59 bool VisitRecordDecl(const RecordDecl
*RD
) {
60 Checker
->visitRecordDecl(RD
);
65 LocalVisitor
visitor(this);
66 visitor
.TraverseDecl(const_cast<TranslationUnitDecl
*>(TUD
));
69 void visitRecordDecl(const RecordDecl
*RD
) const {
70 if (shouldSkipDecl(RD
))
73 for (auto *Member
: RD
->fields()) {
74 const Type
*MemberType
= Member
->getType().getTypePtrOrNull();
78 if (auto *MemberCXXRD
= MemberType
->getPointeeCXXRecordDecl()) {
79 // If we don't see the definition we just don't know.
80 if (MemberCXXRD
->hasDefinition()) {
81 std::optional
<bool> isRCAble
= isRefCountable(MemberCXXRD
);
82 if (isRCAble
&& *isRCAble
)
83 reportBug(Member
, MemberType
, MemberCXXRD
, RD
);
89 bool shouldSkipDecl(const RecordDecl
*RD
) const {
90 if (!RD
->isThisDeclarationADefinition())
99 // If the construct doesn't have a source file, then it's not something
100 // we want to diagnose.
101 const auto RDLocation
= RD
->getLocation();
102 if (!RDLocation
.isValid())
105 const auto Kind
= RD
->getTagKind();
106 // FIMXE: Should we check union members too?
107 if (Kind
!= TagTypeKind::Struct
&& Kind
!= TagTypeKind::Class
)
110 // Ignore CXXRecords that come from system headers.
111 if (BR
->getSourceManager().isInSystemHeader(RDLocation
))
114 // Ref-counted smartpointers actually have raw-pointer to uncounted type as
115 // a member but we trust them to handle it correctly.
116 auto CXXRD
= llvm::dyn_cast_or_null
<CXXRecordDecl
>(RD
);
118 return isRefCounted(CXXRD
);
123 void reportBug(const FieldDecl
*Member
, const Type
*MemberType
,
124 const CXXRecordDecl
*MemberCXXRD
,
125 const RecordDecl
*ClassCXXRD
) const {
130 SmallString
<100> Buf
;
131 llvm::raw_svector_ostream
Os(Buf
);
133 Os
<< "Member variable ";
134 printQuotedName(Os
, Member
);
136 printQuotedQualifiedName(Os
, ClassCXXRD
);
138 << (isa
<PointerType
>(MemberType
) ? "raw pointer" : "reference")
139 << " to ref-countable type ";
140 printQuotedQualifiedName(Os
, MemberCXXRD
);
141 Os
<< "; member variables must be ref-counted.";
143 PathDiagnosticLocation
BSLoc(Member
->getSourceRange().getBegin(),
144 BR
->getSourceManager());
145 auto Report
= std::make_unique
<BasicBugReport
>(Bug
, Os
.str(), BSLoc
);
146 Report
->addRange(Member
->getSourceRange());
147 BR
->emitReport(std::move(Report
));
152 void ento::registerNoUncountedMemberChecker(CheckerManager
&Mgr
) {
153 Mgr
.registerChecker
<NoUncountedMemberChecker
>();
156 bool ento::shouldRegisterNoUncountedMemberChecker(
157 const CheckerManager
&Mgr
) {