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 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 
28 class NoUncountedMemberChecker
29     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30 private:
31   BugType Bug;
32   mutable BugReporter *BR;
33 
34 public:
35   NoUncountedMemberChecker()
36       : Bug(this,
37             "Member variable is a raw-poiner/reference to reference-countable "
38             "type",
39             "WebKit coding guidelines") {}
40 
41   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
42                     BugReporter &BRArg) const {
43     BR = &BRArg;
44 
45     // The calls to checkAST* from AnalysisConsumer don't
46     // visit template instantiations or lambda classes. We
47     // want to visit those, so we make our own RecursiveASTVisitor.
48     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
49       const NoUncountedMemberChecker *Checker;
50       explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
51           : Checker(Checker) {
52         assert(Checker);
53       }
54 
55       bool shouldVisitTemplateInstantiations() const { return true; }
56       bool shouldVisitImplicitCode() const { return false; }
57 
58       bool VisitRecordDecl(const RecordDecl *RD) {
59         Checker->visitRecordDecl(RD);
60         return true;
61       }
62     };
63 
64     LocalVisitor visitor(this);
65     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
66   }
67 
68   void visitRecordDecl(const RecordDecl *RD) const {
69     if (shouldSkipDecl(RD))
70       return;
71 
72     for (auto Member : RD->fields()) {
73       const Type *MemberType = Member->getType().getTypePtrOrNull();
74       if (!MemberType)
75         continue;
76 
77       if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
78         if (isRefCountable(MemberCXXRD))
79           reportBug(Member, MemberType, MemberCXXRD, RD);
80       }
81     }
82   }
83 
84   bool shouldSkipDecl(const RecordDecl *RD) const {
85     if (!RD->isThisDeclarationADefinition())
86       return true;
87 
88     if (RD->isImplicit())
89       return true;
90 
91     if (RD->isLambda())
92       return true;
93 
94     // If the construct doesn't have a source file, then it's not something
95     // we want to diagnose.
96     const auto RDLocation = RD->getLocation();
97     if (!RDLocation.isValid())
98       return true;
99 
100     const auto Kind = RD->getTagKind();
101     // FIMXE: Should we check union members too?
102     if (Kind != TTK_Struct && Kind != TTK_Class)
103       return true;
104 
105     // Ignore CXXRecords that come from system headers.
106     if (BR->getSourceManager().isInSystemHeader(RDLocation))
107       return true;
108 
109     // Ref-counted smartpointers actually have raw-pointer to uncounted type as
110     // a member but we trust them to handle it correctly.
111     return isRefCounted(llvm::dyn_cast_or_null<CXXRecordDecl>(RD));
112   }
113 
114   void reportBug(const FieldDecl *Member, const Type *MemberType,
115                  const CXXRecordDecl *MemberCXXRD,
116                  const RecordDecl *ClassCXXRD) const {
117     assert(Member);
118     assert(MemberType);
119     assert(MemberCXXRD);
120 
121     SmallString<100> Buf;
122     llvm::raw_svector_ostream Os(Buf);
123 
124     Os << "Member variable ";
125     printQuotedName(Os, Member);
126     Os << " in ";
127     printQuotedQualifiedName(Os, ClassCXXRD);
128     Os << " is a "
129        << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
130        << " to ref-countable type ";
131     printQuotedQualifiedName(Os, MemberCXXRD);
132     Os << "; member variables must be ref-counted.";
133 
134     PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
135                                  BR->getSourceManager());
136     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
137     Report->addRange(Member->getSourceRange());
138     BR->emitReport(std::move(Report));
139   }
140 };
141 } // namespace
142 
143 void ento::registerWebKitNoUncountedMemberChecker(CheckerManager &Mgr) {
144   Mgr.registerChecker<NoUncountedMemberChecker>();
145 }
146 
147 bool ento::shouldRegisterWebKitNoUncountedMemberChecker(
148     const CheckerManager &Mgr) {
149   return true;
150 }
151