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