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