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