154e91a3cSJan Korous //=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==//
254e91a3cSJan Korous //
354e91a3cSJan Korous // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
454e91a3cSJan Korous // See https://llvm.org/LICENSE.txt for license information.
554e91a3cSJan Korous // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
654e91a3cSJan Korous //
754e91a3cSJan Korous //===----------------------------------------------------------------------===//
854e91a3cSJan Korous
954e91a3cSJan Korous #include "DiagOutputUtils.h"
1054e91a3cSJan Korous #include "PtrTypesSemantics.h"
1154e91a3cSJan Korous #include "clang/AST/CXXInheritance.h"
1254e91a3cSJan Korous #include "clang/AST/RecursiveASTVisitor.h"
1354e91a3cSJan Korous #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1454e91a3cSJan Korous #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
1554e91a3cSJan Korous #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1654e91a3cSJan Korous #include "clang/StaticAnalyzer/Core/Checker.h"
1754e91a3cSJan Korous
1854e91a3cSJan Korous using namespace clang;
1954e91a3cSJan Korous using namespace ento;
2054e91a3cSJan Korous
2154e91a3cSJan Korous namespace {
2254e91a3cSJan Korous class RefCntblBaseVirtualDtorChecker
2354e91a3cSJan Korous : public Checker<check::ASTDecl<TranslationUnitDecl>> {
2454e91a3cSJan Korous private:
2554e91a3cSJan Korous BugType Bug;
2654e91a3cSJan Korous mutable BugReporter *BR;
2754e91a3cSJan Korous
2854e91a3cSJan Korous public:
RefCntblBaseVirtualDtorChecker()2954e91a3cSJan Korous RefCntblBaseVirtualDtorChecker()
3054e91a3cSJan Korous : Bug(this,
3154e91a3cSJan Korous "Reference-countable base class doesn't have virtual destructor",
3254e91a3cSJan Korous "WebKit coding guidelines") {}
3354e91a3cSJan Korous
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const3454e91a3cSJan Korous void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
3554e91a3cSJan Korous BugReporter &BRArg) const {
3654e91a3cSJan Korous BR = &BRArg;
3754e91a3cSJan Korous
3854e91a3cSJan Korous // The calls to checkAST* from AnalysisConsumer don't
3954e91a3cSJan Korous // visit template instantiations or lambda classes. We
4054e91a3cSJan Korous // want to visit those, so we make our own RecursiveASTVisitor.
4154e91a3cSJan Korous struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
4254e91a3cSJan Korous const RefCntblBaseVirtualDtorChecker *Checker;
4354e91a3cSJan Korous explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
4454e91a3cSJan Korous : Checker(Checker) {
4554e91a3cSJan Korous assert(Checker);
4654e91a3cSJan Korous }
4754e91a3cSJan Korous
4854e91a3cSJan Korous bool shouldVisitTemplateInstantiations() const { return true; }
4954e91a3cSJan Korous bool shouldVisitImplicitCode() const { return false; }
5054e91a3cSJan Korous
5154e91a3cSJan Korous bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
5254e91a3cSJan Korous Checker->visitCXXRecordDecl(RD);
5354e91a3cSJan Korous return true;
5454e91a3cSJan Korous }
5554e91a3cSJan Korous };
5654e91a3cSJan Korous
5754e91a3cSJan Korous LocalVisitor visitor(this);
5854e91a3cSJan Korous visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
5954e91a3cSJan Korous }
6054e91a3cSJan Korous
visitCXXRecordDecl(const CXXRecordDecl * RD) const6154e91a3cSJan Korous void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
6254e91a3cSJan Korous if (shouldSkipDecl(RD))
6354e91a3cSJan Korous return;
6454e91a3cSJan Korous
6554e91a3cSJan Korous CXXBasePaths Paths;
6654e91a3cSJan Korous Paths.setOrigin(RD);
6754e91a3cSJan Korous
6854e91a3cSJan Korous const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
6954e91a3cSJan Korous const CXXRecordDecl *ProblematicBaseClass = nullptr;
7054e91a3cSJan Korous
7154e91a3cSJan Korous const auto IsPublicBaseRefCntblWOVirtualDtor =
7254e91a3cSJan Korous [RD, &ProblematicBaseSpecifier,
7354e91a3cSJan Korous &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) {
7454e91a3cSJan Korous const auto AccSpec = Base->getAccessSpecifier();
7554e91a3cSJan Korous if (AccSpec == AS_protected || AccSpec == AS_private ||
7654e91a3cSJan Korous (AccSpec == AS_none && RD->isClass()))
7754e91a3cSJan Korous return false;
7854e91a3cSJan Korous
79*47e68514SJan Korous llvm::Optional<const CXXRecordDecl *> RefCntblBaseRD =
8054e91a3cSJan Korous isRefCountable(Base);
81*47e68514SJan Korous if (!RefCntblBaseRD || !(*RefCntblBaseRD))
8254e91a3cSJan Korous return false;
8354e91a3cSJan Korous
84*47e68514SJan Korous const auto *Dtor = (*RefCntblBaseRD)->getDestructor();
8554e91a3cSJan Korous if (!Dtor || !Dtor->isVirtual()) {
8654e91a3cSJan Korous ProblematicBaseSpecifier = Base;
87*47e68514SJan Korous ProblematicBaseClass = *RefCntblBaseRD;
8854e91a3cSJan Korous return true;
8954e91a3cSJan Korous }
9054e91a3cSJan Korous
9154e91a3cSJan Korous return false;
9254e91a3cSJan Korous };
9354e91a3cSJan Korous
9454e91a3cSJan Korous if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
9554e91a3cSJan Korous /*LookupInDependent =*/true)) {
9654e91a3cSJan Korous reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
9754e91a3cSJan Korous }
9854e91a3cSJan Korous }
9954e91a3cSJan Korous
shouldSkipDecl(const CXXRecordDecl * RD) const10054e91a3cSJan Korous bool shouldSkipDecl(const CXXRecordDecl *RD) const {
10154e91a3cSJan Korous if (!RD->isThisDeclarationADefinition())
10254e91a3cSJan Korous return true;
10354e91a3cSJan Korous
10454e91a3cSJan Korous if (RD->isImplicit())
10554e91a3cSJan Korous return true;
10654e91a3cSJan Korous
10754e91a3cSJan Korous if (RD->isLambda())
10854e91a3cSJan Korous return true;
10954e91a3cSJan Korous
11054e91a3cSJan Korous // If the construct doesn't have a source file, then it's not something
11154e91a3cSJan Korous // we want to diagnose.
11254e91a3cSJan Korous const auto RDLocation = RD->getLocation();
11354e91a3cSJan Korous if (!RDLocation.isValid())
11454e91a3cSJan Korous return true;
11554e91a3cSJan Korous
11654e91a3cSJan Korous const auto Kind = RD->getTagKind();
11754e91a3cSJan Korous if (Kind != TTK_Struct && Kind != TTK_Class)
11854e91a3cSJan Korous return true;
11954e91a3cSJan Korous
12054e91a3cSJan Korous // Ignore CXXRecords that come from system headers.
12154e91a3cSJan Korous if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
12254e91a3cSJan Korous SrcMgr::C_User)
12354e91a3cSJan Korous return true;
12454e91a3cSJan Korous
12554e91a3cSJan Korous return false;
12654e91a3cSJan Korous }
12754e91a3cSJan Korous
reportBug(const CXXRecordDecl * DerivedClass,const CXXBaseSpecifier * BaseSpec,const CXXRecordDecl * ProblematicBaseClass) const12854e91a3cSJan Korous void reportBug(const CXXRecordDecl *DerivedClass,
12954e91a3cSJan Korous const CXXBaseSpecifier *BaseSpec,
13054e91a3cSJan Korous const CXXRecordDecl *ProblematicBaseClass) const {
13154e91a3cSJan Korous assert(DerivedClass);
13254e91a3cSJan Korous assert(BaseSpec);
13354e91a3cSJan Korous assert(ProblematicBaseClass);
13454e91a3cSJan Korous
13554e91a3cSJan Korous SmallString<100> Buf;
13654e91a3cSJan Korous llvm::raw_svector_ostream Os(Buf);
13754e91a3cSJan Korous
13854e91a3cSJan Korous Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
13954e91a3cSJan Korous printQuotedQualifiedName(Os, ProblematicBaseClass);
14054e91a3cSJan Korous
14154e91a3cSJan Korous Os << " is used as a base of "
14254e91a3cSJan Korous << (DerivedClass->isClass() ? "class" : "struct") << " ";
14354e91a3cSJan Korous printQuotedQualifiedName(Os, DerivedClass);
14454e91a3cSJan Korous
14554e91a3cSJan Korous Os << " but doesn't have virtual destructor";
14654e91a3cSJan Korous
14754e91a3cSJan Korous PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
14854e91a3cSJan Korous BR->getSourceManager());
14954e91a3cSJan Korous auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
15054e91a3cSJan Korous Report->addRange(BaseSpec->getSourceRange());
15154e91a3cSJan Korous BR->emitReport(std::move(Report));
15254e91a3cSJan Korous }
15354e91a3cSJan Korous };
15454e91a3cSJan Korous } // namespace
15554e91a3cSJan Korous
registerRefCntblBaseVirtualDtorChecker(CheckerManager & Mgr)15654e91a3cSJan Korous void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
15754e91a3cSJan Korous Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
15854e91a3cSJan Korous }
15954e91a3cSJan Korous
shouldRegisterRefCntblBaseVirtualDtorChecker(const CheckerManager & mgr)16054e91a3cSJan Korous bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
16154e91a3cSJan Korous const CheckerManager &mgr) {
16254e91a3cSJan Korous return true;
16354e91a3cSJan Korous }
164