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