xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp (revision 39a72be5e59c9a80570c1085a769593e2f26f054)
1 //=======- RawPtrRefMemberChecker.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/DynamicRecursiveASTVisitor.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/Support/Casting.h"
21 #include <optional>
22 
23 using namespace clang;
24 using namespace ento;
25 
26 namespace {
27 
28 class RawPtrRefMemberChecker
29     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
30 private:
31   BugType Bug;
32   mutable BugReporter *BR;
33 
34 public:
35   RawPtrRefMemberChecker(const char *description)
36       : Bug(this, description, "WebKit coding guidelines") {}
37 
38   virtual std::optional<bool>
39   isPtrCompatible(const clang::CXXRecordDecl *) const = 0;
40   virtual bool isPtrCls(const clang::CXXRecordDecl *) const = 0;
41   virtual const char *typeName() const = 0;
42   virtual const char *invariant() const = 0;
43 
44   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
45                     BugReporter &BRArg) const {
46     BR = &BRArg;
47 
48     // The calls to checkAST* from AnalysisConsumer don't
49     // visit template instantiations or lambda classes. We
50     // want to visit those, so we make our own RecursiveASTVisitor.
51     struct LocalVisitor : ConstDynamicRecursiveASTVisitor {
52       const RawPtrRefMemberChecker *Checker;
53       explicit LocalVisitor(const RawPtrRefMemberChecker *Checker)
54           : Checker(Checker) {
55         assert(Checker);
56         ShouldVisitTemplateInstantiations = true;
57         ShouldVisitImplicitCode = false;
58       }
59 
60       bool VisitRecordDecl(const RecordDecl *RD) override {
61         Checker->visitRecordDecl(RD);
62         return true;
63       }
64     };
65 
66     LocalVisitor visitor(this);
67     visitor.TraverseDecl(TUD);
68   }
69 
70   void visitRecordDecl(const RecordDecl *RD) const {
71     if (shouldSkipDecl(RD))
72       return;
73 
74     for (auto *Member : RD->fields()) {
75       const Type *MemberType = Member->getType().getTypePtrOrNull();
76       if (!MemberType)
77         continue;
78 
79       if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
80         // If we don't see the definition we just don't know.
81         if (MemberCXXRD->hasDefinition()) {
82           std::optional<bool> isRCAble = isPtrCompatible(MemberCXXRD);
83           if (isRCAble && *isRCAble)
84             reportBug(Member, MemberType, MemberCXXRD, RD);
85         }
86       }
87     }
88   }
89 
90   bool shouldSkipDecl(const RecordDecl *RD) const {
91     if (!RD->isThisDeclarationADefinition())
92       return true;
93 
94     if (RD->isImplicit())
95       return true;
96 
97     if (RD->isLambda())
98       return true;
99 
100     // If the construct doesn't have a source file, then it's not something
101     // we want to diagnose.
102     const auto RDLocation = RD->getLocation();
103     if (!RDLocation.isValid())
104       return true;
105 
106     const auto Kind = RD->getTagKind();
107     // FIMXE: Should we check union members too?
108     if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
109       return true;
110 
111     // Ignore CXXRecords that come from system headers.
112     if (BR->getSourceManager().isInSystemHeader(RDLocation))
113       return true;
114 
115     // Ref-counted smartpointers actually have raw-pointer to uncounted type as
116     // a member but we trust them to handle it correctly.
117     auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
118     if (CXXRD)
119       return isPtrCls(CXXRD);
120 
121     return false;
122   }
123 
124   void reportBug(const FieldDecl *Member, const Type *MemberType,
125                  const CXXRecordDecl *MemberCXXRD,
126                  const RecordDecl *ClassCXXRD) const {
127     assert(Member);
128     assert(MemberType);
129     assert(MemberCXXRD);
130 
131     SmallString<100> Buf;
132     llvm::raw_svector_ostream Os(Buf);
133 
134     Os << "Member variable ";
135     printQuotedName(Os, Member);
136     Os << " in ";
137     printQuotedQualifiedName(Os, ClassCXXRD);
138     Os << " is a "
139        << (isa<PointerType>(MemberType) ? "raw pointer" : "reference") << " to "
140        << typeName() << " ";
141     printQuotedQualifiedName(Os, MemberCXXRD);
142     Os << "; " << invariant() << ".";
143 
144     PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
145                                  BR->getSourceManager());
146     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
147     Report->addRange(Member->getSourceRange());
148     BR->emitReport(std::move(Report));
149   }
150 };
151 
152 class NoUncountedMemberChecker final : public RawPtrRefMemberChecker {
153 public:
154   NoUncountedMemberChecker()
155       : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
156                                "reference-countable type") {}
157 
158   std::optional<bool>
159   isPtrCompatible(const clang::CXXRecordDecl *R) const final {
160     return isRefCountable(R);
161   }
162 
163   bool isPtrCls(const clang::CXXRecordDecl *R) const final {
164     return isRefCounted(R);
165   }
166 
167   const char *typeName() const final { return "ref-countable type"; }
168 
169   const char *invariant() const final {
170     return "member variables must be Ref, RefPtr, WeakRef, or WeakPtr";
171   }
172 };
173 
174 class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker {
175 public:
176   NoUncheckedPtrMemberChecker()
177       : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
178                                "checked-pointer capable type") {}
179 
180   std::optional<bool>
181   isPtrCompatible(const clang::CXXRecordDecl *R) const final {
182     return isCheckedPtrCapable(R);
183   }
184 
185   bool isPtrCls(const clang::CXXRecordDecl *R) const final {
186     return isCheckedPtr(R);
187   }
188 
189   const char *typeName() const final { return "CheckedPtr capable type"; }
190 
191   const char *invariant() const final {
192     return "member variables must be a CheckedPtr, CheckedRef, WeakRef, or "
193            "WeakPtr";
194   }
195 };
196 
197 } // namespace
198 
199 void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
200   Mgr.registerChecker<NoUncountedMemberChecker>();
201 }
202 
203 bool ento::shouldRegisterNoUncountedMemberChecker(const CheckerManager &Mgr) {
204   return true;
205 }
206 
207 void ento::registerNoUncheckedPtrMemberChecker(CheckerManager &Mgr) {
208   Mgr.registerChecker<NoUncheckedPtrMemberChecker>();
209 }
210 
211 bool ento::shouldRegisterNoUncheckedPtrMemberChecker(
212     const CheckerManager &Mgr) {
213   return true;
214 }
215