xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1ec727ea7Spatrick //=======- RefCntblBaseVirtualDtor.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 "DiagOutputUtils.h"
10ec727ea7Spatrick #include "PtrTypesSemantics.h"
11ec727ea7Spatrick #include "clang/AST/CXXInheritance.h"
12ec727ea7Spatrick #include "clang/AST/RecursiveASTVisitor.h"
13ec727ea7Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
15ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
17*12c85518Srobert #include <optional>
18ec727ea7Spatrick 
19ec727ea7Spatrick using namespace clang;
20ec727ea7Spatrick using namespace ento;
21ec727ea7Spatrick 
22ec727ea7Spatrick namespace {
23ec727ea7Spatrick class RefCntblBaseVirtualDtorChecker
24ec727ea7Spatrick     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
25ec727ea7Spatrick private:
26ec727ea7Spatrick   BugType Bug;
27ec727ea7Spatrick   mutable BugReporter *BR;
28ec727ea7Spatrick 
29ec727ea7Spatrick public:
RefCntblBaseVirtualDtorChecker()30ec727ea7Spatrick   RefCntblBaseVirtualDtorChecker()
31ec727ea7Spatrick       : Bug(this,
32ec727ea7Spatrick             "Reference-countable base class doesn't have virtual destructor",
33ec727ea7Spatrick             "WebKit coding guidelines") {}
34ec727ea7Spatrick 
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const35ec727ea7Spatrick   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
36ec727ea7Spatrick                     BugReporter &BRArg) const {
37ec727ea7Spatrick     BR = &BRArg;
38ec727ea7Spatrick 
39ec727ea7Spatrick     // The calls to checkAST* from AnalysisConsumer don't
40ec727ea7Spatrick     // visit template instantiations or lambda classes. We
41ec727ea7Spatrick     // want to visit those, so we make our own RecursiveASTVisitor.
42ec727ea7Spatrick     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
43ec727ea7Spatrick       const RefCntblBaseVirtualDtorChecker *Checker;
44ec727ea7Spatrick       explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
45ec727ea7Spatrick           : Checker(Checker) {
46ec727ea7Spatrick         assert(Checker);
47ec727ea7Spatrick       }
48ec727ea7Spatrick 
49ec727ea7Spatrick       bool shouldVisitTemplateInstantiations() const { return true; }
50ec727ea7Spatrick       bool shouldVisitImplicitCode() const { return false; }
51ec727ea7Spatrick 
52ec727ea7Spatrick       bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
53ec727ea7Spatrick         Checker->visitCXXRecordDecl(RD);
54ec727ea7Spatrick         return true;
55ec727ea7Spatrick       }
56ec727ea7Spatrick     };
57ec727ea7Spatrick 
58ec727ea7Spatrick     LocalVisitor visitor(this);
59ec727ea7Spatrick     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
60ec727ea7Spatrick   }
61ec727ea7Spatrick 
visitCXXRecordDecl(const CXXRecordDecl * RD) const62ec727ea7Spatrick   void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
63ec727ea7Spatrick     if (shouldSkipDecl(RD))
64ec727ea7Spatrick       return;
65ec727ea7Spatrick 
66ec727ea7Spatrick     CXXBasePaths Paths;
67ec727ea7Spatrick     Paths.setOrigin(RD);
68ec727ea7Spatrick 
69ec727ea7Spatrick     const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
70ec727ea7Spatrick     const CXXRecordDecl *ProblematicBaseClass = nullptr;
71ec727ea7Spatrick 
72ec727ea7Spatrick     const auto IsPublicBaseRefCntblWOVirtualDtor =
73ec727ea7Spatrick         [RD, &ProblematicBaseSpecifier,
74ec727ea7Spatrick          &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) {
75ec727ea7Spatrick           const auto AccSpec = Base->getAccessSpecifier();
76ec727ea7Spatrick           if (AccSpec == AS_protected || AccSpec == AS_private ||
77ec727ea7Spatrick               (AccSpec == AS_none && RD->isClass()))
78ec727ea7Spatrick             return false;
79ec727ea7Spatrick 
80*12c85518Srobert           std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base);
81a9ac8606Spatrick           if (!RefCntblBaseRD || !(*RefCntblBaseRD))
82ec727ea7Spatrick             return false;
83ec727ea7Spatrick 
84a9ac8606Spatrick           const auto *Dtor = (*RefCntblBaseRD)->getDestructor();
85ec727ea7Spatrick           if (!Dtor || !Dtor->isVirtual()) {
86ec727ea7Spatrick             ProblematicBaseSpecifier = Base;
87a9ac8606Spatrick             ProblematicBaseClass = *RefCntblBaseRD;
88ec727ea7Spatrick             return true;
89ec727ea7Spatrick           }
90ec727ea7Spatrick 
91ec727ea7Spatrick           return false;
92ec727ea7Spatrick         };
93ec727ea7Spatrick 
94ec727ea7Spatrick     if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
95ec727ea7Spatrick                           /*LookupInDependent =*/true)) {
96ec727ea7Spatrick       reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
97ec727ea7Spatrick     }
98ec727ea7Spatrick   }
99ec727ea7Spatrick 
shouldSkipDecl(const CXXRecordDecl * RD) const100ec727ea7Spatrick   bool shouldSkipDecl(const CXXRecordDecl *RD) const {
101ec727ea7Spatrick     if (!RD->isThisDeclarationADefinition())
102ec727ea7Spatrick       return true;
103ec727ea7Spatrick 
104ec727ea7Spatrick     if (RD->isImplicit())
105ec727ea7Spatrick       return true;
106ec727ea7Spatrick 
107ec727ea7Spatrick     if (RD->isLambda())
108ec727ea7Spatrick       return true;
109ec727ea7Spatrick 
110ec727ea7Spatrick     // If the construct doesn't have a source file, then it's not something
111ec727ea7Spatrick     // we want to diagnose.
112ec727ea7Spatrick     const auto RDLocation = RD->getLocation();
113ec727ea7Spatrick     if (!RDLocation.isValid())
114ec727ea7Spatrick       return true;
115ec727ea7Spatrick 
116ec727ea7Spatrick     const auto Kind = RD->getTagKind();
117ec727ea7Spatrick     if (Kind != TTK_Struct && Kind != TTK_Class)
118ec727ea7Spatrick       return true;
119ec727ea7Spatrick 
120ec727ea7Spatrick     // Ignore CXXRecords that come from system headers.
121ec727ea7Spatrick     if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
122ec727ea7Spatrick         SrcMgr::C_User)
123ec727ea7Spatrick       return true;
124ec727ea7Spatrick 
125ec727ea7Spatrick     return false;
126ec727ea7Spatrick   }
127ec727ea7Spatrick 
reportBug(const CXXRecordDecl * DerivedClass,const CXXBaseSpecifier * BaseSpec,const CXXRecordDecl * ProblematicBaseClass) const128ec727ea7Spatrick   void reportBug(const CXXRecordDecl *DerivedClass,
129ec727ea7Spatrick                  const CXXBaseSpecifier *BaseSpec,
130ec727ea7Spatrick                  const CXXRecordDecl *ProblematicBaseClass) const {
131ec727ea7Spatrick     assert(DerivedClass);
132ec727ea7Spatrick     assert(BaseSpec);
133ec727ea7Spatrick     assert(ProblematicBaseClass);
134ec727ea7Spatrick 
135ec727ea7Spatrick     SmallString<100> Buf;
136ec727ea7Spatrick     llvm::raw_svector_ostream Os(Buf);
137ec727ea7Spatrick 
138ec727ea7Spatrick     Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
139ec727ea7Spatrick     printQuotedQualifiedName(Os, ProblematicBaseClass);
140ec727ea7Spatrick 
141ec727ea7Spatrick     Os << " is used as a base of "
142ec727ea7Spatrick        << (DerivedClass->isClass() ? "class" : "struct") << " ";
143ec727ea7Spatrick     printQuotedQualifiedName(Os, DerivedClass);
144ec727ea7Spatrick 
145ec727ea7Spatrick     Os << " but doesn't have virtual destructor";
146ec727ea7Spatrick 
147ec727ea7Spatrick     PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
148ec727ea7Spatrick                                  BR->getSourceManager());
149ec727ea7Spatrick     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
150ec727ea7Spatrick     Report->addRange(BaseSpec->getSourceRange());
151ec727ea7Spatrick     BR->emitReport(std::move(Report));
152ec727ea7Spatrick   }
153ec727ea7Spatrick };
154ec727ea7Spatrick } // namespace
155ec727ea7Spatrick 
registerRefCntblBaseVirtualDtorChecker(CheckerManager & Mgr)156ec727ea7Spatrick void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
157ec727ea7Spatrick   Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
158ec727ea7Spatrick }
159ec727ea7Spatrick 
shouldRegisterRefCntblBaseVirtualDtorChecker(const CheckerManager & mgr)160ec727ea7Spatrick bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
161ec727ea7Spatrick     const CheckerManager &mgr) {
162ec727ea7Spatrick   return true;
163ec727ea7Spatrick }
164