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
19using namespace clang;
20using namespace ento;
21
22namespace {
23class RefCntblBaseVirtualDtorChecker
24    : public Checker<check::ASTDecl<TranslationUnitDecl>> {
25private:
26  BugType Bug;
27  mutable BugReporter *BR;
28
29public:
30  RefCntblBaseVirtualDtorChecker()
31      : Bug(this,
32            "Reference-countable base class doesn't have virtual destructor",
33            "WebKit coding guidelines") {}
34
35  void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
36                    BugReporter &BRArg) const {
37    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    struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
43      const RefCntblBaseVirtualDtorChecker *Checker;
44      explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
45          : Checker(Checker) {
46        assert(Checker);
47      }
48
49      bool shouldVisitTemplateInstantiations() const { return true; }
50      bool shouldVisitImplicitCode() const { return false; }
51
52      bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
53        Checker->visitCXXRecordDecl(RD);
54        return true;
55      }
56    };
57
58    LocalVisitor visitor(this);
59    visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
60  }
61
62  void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
63    if (shouldSkipDecl(RD))
64      return;
65
66    CXXBasePaths Paths;
67    Paths.setOrigin(RD);
68
69    const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
70    const CXXRecordDecl *ProblematicBaseClass = nullptr;
71
72    const auto IsPublicBaseRefCntblWOVirtualDtor =
73        [RD, &ProblematicBaseSpecifier,
74         &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) {
75          const auto AccSpec = Base->getAccessSpecifier();
76          if (AccSpec == AS_protected || AccSpec == AS_private ||
77              (AccSpec == AS_none && RD->isClass()))
78            return false;
79
80          std::optional<const CXXRecordDecl*> RefCntblBaseRD = 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
156void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
157  Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
158}
159
160bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
161    const CheckerManager &mgr) {
162  return true;
163}
164