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