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 we don't see the definition we just don't know.
79         if (MemberCXXRD->hasDefinition() && isRefCountable(MemberCXXRD))
80           reportBug(Member, MemberType, MemberCXXRD, RD);
81       }
82     }
83   }
84 
85   bool shouldSkipDecl(const RecordDecl *RD) const {
86     if (!RD->isThisDeclarationADefinition())
87       return true;
88 
89     if (RD->isImplicit())
90       return true;
91 
92     if (RD->isLambda())
93       return true;
94 
95     // If the construct doesn't have a source file, then it's not something
96     // we want to diagnose.
97     const auto RDLocation = RD->getLocation();
98     if (!RDLocation.isValid())
99       return true;
100 
101     const auto Kind = RD->getTagKind();
102     // FIMXE: Should we check union members too?
103     if (Kind != TTK_Struct && Kind != TTK_Class)
104       return true;
105 
106     // Ignore CXXRecords that come from system headers.
107     if (BR->getSourceManager().isInSystemHeader(RDLocation))
108       return true;
109 
110     // Ref-counted smartpointers actually have raw-pointer to uncounted type as
111     // a member but we trust them to handle it correctly.
112     auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
113     if (CXXRD)
114       return isRefCounted(CXXRD);
115 
116     return false;
117   }
118 
119   void reportBug(const FieldDecl *Member, const Type *MemberType,
120                  const CXXRecordDecl *MemberCXXRD,
121                  const RecordDecl *ClassCXXRD) const {
122     assert(Member);
123     assert(MemberType);
124     assert(MemberCXXRD);
125 
126     SmallString<100> Buf;
127     llvm::raw_svector_ostream Os(Buf);
128 
129     Os << "Member variable ";
130     printQuotedName(Os, Member);
131     Os << " in ";
132     printQuotedQualifiedName(Os, ClassCXXRD);
133     Os << " is a "
134        << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
135        << " to ref-countable type ";
136     printQuotedQualifiedName(Os, MemberCXXRD);
137     Os << "; member variables must be ref-counted.";
138 
139     PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
140                                  BR->getSourceManager());
141     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
142     Report->addRange(Member->getSourceRange());
143     BR->emitReport(std::move(Report));
144   }
145 };
146 } // namespace
147 
148 void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
149   Mgr.registerChecker<NoUncountedMemberChecker>();
150 }
151 
152 bool ento::shouldRegisterNoUncountedMemberChecker(
153     const CheckerManager &Mgr) {
154   return true;
155 }
156