1*e038c9c4Sjoerg //=======- NoUncountedMembersChecker.cpp -------------------------*- C++ -*-==//
2*e038c9c4Sjoerg //
3*e038c9c4Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e038c9c4Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*e038c9c4Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e038c9c4Sjoerg //
7*e038c9c4Sjoerg //===----------------------------------------------------------------------===//
8*e038c9c4Sjoerg
9*e038c9c4Sjoerg #include "ASTUtils.h"
10*e038c9c4Sjoerg #include "DiagOutputUtils.h"
11*e038c9c4Sjoerg #include "PtrTypesSemantics.h"
12*e038c9c4Sjoerg #include "clang/AST/CXXInheritance.h"
13*e038c9c4Sjoerg #include "clang/AST/Decl.h"
14*e038c9c4Sjoerg #include "clang/AST/DeclCXX.h"
15*e038c9c4Sjoerg #include "clang/AST/RecursiveASTVisitor.h"
16*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
20*e038c9c4Sjoerg #include "llvm/ADT/DenseSet.h"
21*e038c9c4Sjoerg #include "llvm/Support/Casting.h"
22*e038c9c4Sjoerg
23*e038c9c4Sjoerg using namespace clang;
24*e038c9c4Sjoerg using namespace ento;
25*e038c9c4Sjoerg
26*e038c9c4Sjoerg namespace {
27*e038c9c4Sjoerg
28*e038c9c4Sjoerg class NoUncountedMemberChecker
29*e038c9c4Sjoerg : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30*e038c9c4Sjoerg private:
31*e038c9c4Sjoerg BugType Bug;
32*e038c9c4Sjoerg mutable BugReporter *BR;
33*e038c9c4Sjoerg
34*e038c9c4Sjoerg public:
NoUncountedMemberChecker()35*e038c9c4Sjoerg NoUncountedMemberChecker()
36*e038c9c4Sjoerg : Bug(this,
37*e038c9c4Sjoerg "Member variable is a raw-poiner/reference to reference-countable "
38*e038c9c4Sjoerg "type",
39*e038c9c4Sjoerg "WebKit coding guidelines") {}
40*e038c9c4Sjoerg
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const41*e038c9c4Sjoerg void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42*e038c9c4Sjoerg BugReporter &BRArg) const {
43*e038c9c4Sjoerg BR = &BRArg;
44*e038c9c4Sjoerg
45*e038c9c4Sjoerg // The calls to checkAST* from AnalysisConsumer don't
46*e038c9c4Sjoerg // visit template instantiations or lambda classes. We
47*e038c9c4Sjoerg // want to visit those, so we make our own RecursiveASTVisitor.
48*e038c9c4Sjoerg struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
49*e038c9c4Sjoerg const NoUncountedMemberChecker *Checker;
50*e038c9c4Sjoerg explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
51*e038c9c4Sjoerg : Checker(Checker) {
52*e038c9c4Sjoerg assert(Checker);
53*e038c9c4Sjoerg }
54*e038c9c4Sjoerg
55*e038c9c4Sjoerg bool shouldVisitTemplateInstantiations() const { return true; }
56*e038c9c4Sjoerg bool shouldVisitImplicitCode() const { return false; }
57*e038c9c4Sjoerg
58*e038c9c4Sjoerg bool VisitRecordDecl(const RecordDecl *RD) {
59*e038c9c4Sjoerg Checker->visitRecordDecl(RD);
60*e038c9c4Sjoerg return true;
61*e038c9c4Sjoerg }
62*e038c9c4Sjoerg };
63*e038c9c4Sjoerg
64*e038c9c4Sjoerg LocalVisitor visitor(this);
65*e038c9c4Sjoerg visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
66*e038c9c4Sjoerg }
67*e038c9c4Sjoerg
visitRecordDecl(const RecordDecl * RD) const68*e038c9c4Sjoerg void visitRecordDecl(const RecordDecl *RD) const {
69*e038c9c4Sjoerg if (shouldSkipDecl(RD))
70*e038c9c4Sjoerg return;
71*e038c9c4Sjoerg
72*e038c9c4Sjoerg for (auto Member : RD->fields()) {
73*e038c9c4Sjoerg const Type *MemberType = Member->getType().getTypePtrOrNull();
74*e038c9c4Sjoerg if (!MemberType)
75*e038c9c4Sjoerg continue;
76*e038c9c4Sjoerg
77*e038c9c4Sjoerg if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
78*e038c9c4Sjoerg // If we don't see the definition we just don't know.
79*e038c9c4Sjoerg if (MemberCXXRD->hasDefinition()) {
80*e038c9c4Sjoerg llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD);
81*e038c9c4Sjoerg if (isRCAble && *isRCAble)
82*e038c9c4Sjoerg reportBug(Member, MemberType, MemberCXXRD, RD);
83*e038c9c4Sjoerg }
84*e038c9c4Sjoerg }
85*e038c9c4Sjoerg }
86*e038c9c4Sjoerg }
87*e038c9c4Sjoerg
shouldSkipDecl(const RecordDecl * RD) const88*e038c9c4Sjoerg bool shouldSkipDecl(const RecordDecl *RD) const {
89*e038c9c4Sjoerg if (!RD->isThisDeclarationADefinition())
90*e038c9c4Sjoerg return true;
91*e038c9c4Sjoerg
92*e038c9c4Sjoerg if (RD->isImplicit())
93*e038c9c4Sjoerg return true;
94*e038c9c4Sjoerg
95*e038c9c4Sjoerg if (RD->isLambda())
96*e038c9c4Sjoerg return true;
97*e038c9c4Sjoerg
98*e038c9c4Sjoerg // If the construct doesn't have a source file, then it's not something
99*e038c9c4Sjoerg // we want to diagnose.
100*e038c9c4Sjoerg const auto RDLocation = RD->getLocation();
101*e038c9c4Sjoerg if (!RDLocation.isValid())
102*e038c9c4Sjoerg return true;
103*e038c9c4Sjoerg
104*e038c9c4Sjoerg const auto Kind = RD->getTagKind();
105*e038c9c4Sjoerg // FIMXE: Should we check union members too?
106*e038c9c4Sjoerg if (Kind != TTK_Struct && Kind != TTK_Class)
107*e038c9c4Sjoerg return true;
108*e038c9c4Sjoerg
109*e038c9c4Sjoerg // Ignore CXXRecords that come from system headers.
110*e038c9c4Sjoerg if (BR->getSourceManager().isInSystemHeader(RDLocation))
111*e038c9c4Sjoerg return true;
112*e038c9c4Sjoerg
113*e038c9c4Sjoerg // Ref-counted smartpointers actually have raw-pointer to uncounted type as
114*e038c9c4Sjoerg // a member but we trust them to handle it correctly.
115*e038c9c4Sjoerg auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
116*e038c9c4Sjoerg if (CXXRD)
117*e038c9c4Sjoerg return isRefCounted(CXXRD);
118*e038c9c4Sjoerg
119*e038c9c4Sjoerg return false;
120*e038c9c4Sjoerg }
121*e038c9c4Sjoerg
reportBug(const FieldDecl * Member,const Type * MemberType,const CXXRecordDecl * MemberCXXRD,const RecordDecl * ClassCXXRD) const122*e038c9c4Sjoerg void reportBug(const FieldDecl *Member, const Type *MemberType,
123*e038c9c4Sjoerg const CXXRecordDecl *MemberCXXRD,
124*e038c9c4Sjoerg const RecordDecl *ClassCXXRD) const {
125*e038c9c4Sjoerg assert(Member);
126*e038c9c4Sjoerg assert(MemberType);
127*e038c9c4Sjoerg assert(MemberCXXRD);
128*e038c9c4Sjoerg
129*e038c9c4Sjoerg SmallString<100> Buf;
130*e038c9c4Sjoerg llvm::raw_svector_ostream Os(Buf);
131*e038c9c4Sjoerg
132*e038c9c4Sjoerg Os << "Member variable ";
133*e038c9c4Sjoerg printQuotedName(Os, Member);
134*e038c9c4Sjoerg Os << " in ";
135*e038c9c4Sjoerg printQuotedQualifiedName(Os, ClassCXXRD);
136*e038c9c4Sjoerg Os << " is a "
137*e038c9c4Sjoerg << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
138*e038c9c4Sjoerg << " to ref-countable type ";
139*e038c9c4Sjoerg printQuotedQualifiedName(Os, MemberCXXRD);
140*e038c9c4Sjoerg Os << "; member variables must be ref-counted.";
141*e038c9c4Sjoerg
142*e038c9c4Sjoerg PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
143*e038c9c4Sjoerg BR->getSourceManager());
144*e038c9c4Sjoerg auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
145*e038c9c4Sjoerg Report->addRange(Member->getSourceRange());
146*e038c9c4Sjoerg BR->emitReport(std::move(Report));
147*e038c9c4Sjoerg }
148*e038c9c4Sjoerg };
149*e038c9c4Sjoerg } // namespace
150*e038c9c4Sjoerg
registerNoUncountedMemberChecker(CheckerManager & Mgr)151*e038c9c4Sjoerg void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
152*e038c9c4Sjoerg Mgr.registerChecker<NoUncountedMemberChecker>();
153*e038c9c4Sjoerg }
154*e038c9c4Sjoerg
shouldRegisterNoUncountedMemberChecker(const CheckerManager & Mgr)155*e038c9c4Sjoerg bool ento::shouldRegisterNoUncountedMemberChecker(
156*e038c9c4Sjoerg const CheckerManager &Mgr) {
157*e038c9c4Sjoerg return true;
158*e038c9c4Sjoerg }
159