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"
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"
22 #include <optional>
23
24 using namespace clang;
25 using namespace ento;
26
27 namespace {
28
29 class NoUncountedMemberChecker
30 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
31 private:
32 BugType Bug;
33 mutable BugReporter *BR;
34
35 public:
NoUncountedMemberChecker()36 NoUncountedMemberChecker()
37 : Bug(this,
38 "Member variable is a raw-poiner/reference to reference-countable "
39 "type",
40 "WebKit coding guidelines") {}
41
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const42 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
43 BugReporter &BRArg) const {
44 BR = &BRArg;
45
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)
52 : Checker(Checker) {
53 assert(Checker);
54 }
55
56 bool shouldVisitTemplateInstantiations() const { return true; }
57 bool shouldVisitImplicitCode() const { return false; }
58
59 bool VisitRecordDecl(const RecordDecl *RD) {
60 Checker->visitRecordDecl(RD);
61 return true;
62 }
63 };
64
65 LocalVisitor visitor(this);
66 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
67 }
68
visitRecordDecl(const RecordDecl * RD) const69 void visitRecordDecl(const RecordDecl *RD) const {
70 if (shouldSkipDecl(RD))
71 return;
72
73 for (auto *Member : RD->fields()) {
74 const Type *MemberType = Member->getType().getTypePtrOrNull();
75 if (!MemberType)
76 continue;
77
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);
84 }
85 }
86 }
87 }
88
shouldSkipDecl(const RecordDecl * RD) const89 bool shouldSkipDecl(const RecordDecl *RD) const {
90 if (!RD->isThisDeclarationADefinition())
91 return true;
92
93 if (RD->isImplicit())
94 return true;
95
96 if (RD->isLambda())
97 return true;
98
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())
103 return true;
104
105 const auto Kind = RD->getTagKind();
106 // FIMXE: Should we check union members too?
107 if (Kind != TTK_Struct && Kind != TTK_Class)
108 return true;
109
110 // Ignore CXXRecords that come from system headers.
111 if (BR->getSourceManager().isInSystemHeader(RDLocation))
112 return true;
113
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);
117 if (CXXRD)
118 return isRefCounted(CXXRD);
119
120 return false;
121 }
122
reportBug(const FieldDecl * Member,const Type * MemberType,const CXXRecordDecl * MemberCXXRD,const RecordDecl * ClassCXXRD) const123 void reportBug(const FieldDecl *Member, const Type *MemberType,
124 const CXXRecordDecl *MemberCXXRD,
125 const RecordDecl *ClassCXXRD) const {
126 assert(Member);
127 assert(MemberType);
128 assert(MemberCXXRD);
129
130 SmallString<100> Buf;
131 llvm::raw_svector_ostream Os(Buf);
132
133 Os << "Member variable ";
134 printQuotedName(Os, Member);
135 Os << " in ";
136 printQuotedQualifiedName(Os, ClassCXXRD);
137 Os << " is a "
138 << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
139 << " to ref-countable type ";
140 printQuotedQualifiedName(Os, MemberCXXRD);
141 Os << "; member variables must be ref-counted.";
142
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));
148 }
149 };
150 } // namespace
151
registerNoUncountedMemberChecker(CheckerManager & Mgr)152 void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
153 Mgr.registerChecker<NoUncountedMemberChecker>();
154 }
155
shouldRegisterNoUncountedMemberChecker(const CheckerManager & Mgr)156 bool ento::shouldRegisterNoUncountedMemberChecker(
157 const CheckerManager &Mgr) {
158 return true;
159 }
160