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