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