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