1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
15 #include "clang/AST/CXXInheritance.h"
18 // Check for calls to virtual methods from destructors. These are dangerous because intention might be to call
19 // a method on a subclass, while in actual fact, it only calls the method on the current or super class.
24 class FragileDestructor
:
25 public loplugin::FilteringPlugin
<FragileDestructor
>
28 explicit FragileDestructor(loplugin::InstantiationData
const & data
):
29 FilteringPlugin(data
) {}
31 virtual void run() override
{ TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
33 bool TraverseCXXDestructorDecl(CXXDestructorDecl
*);
35 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr
*);
38 bool mbChecking
= false;
41 bool FragileDestructor::TraverseCXXDestructorDecl(CXXDestructorDecl
* pCXXDestructorDecl
)
43 if (ignoreLocation(pCXXDestructorDecl
)) {
44 return RecursiveASTVisitor::TraverseCXXDestructorDecl(pCXXDestructorDecl
);
46 if (!pCXXDestructorDecl
->isThisDeclarationADefinition()) {
47 return RecursiveASTVisitor::TraverseCXXDestructorDecl(pCXXDestructorDecl
);
49 // ignore this for now, too tricky for me to work out
50 StringRef aFileName
= getFileNameOfSpellingLoc(
51 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(pCXXDestructorDecl
)));
52 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/include/comphelper/")
53 || loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/include/cppuhelper/")
54 || loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/cppuhelper/")
55 || loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/comphelper/")
56 // don't know how to detect this in clang - it is making an explicit call to its own method, so presumably OK
57 || loplugin::isSamePathname(aFileName
, SRCDIR
"/basic/source/sbx/sbxvalue.cxx")
59 return RecursiveASTVisitor::TraverseCXXDestructorDecl(pCXXDestructorDecl
);
61 bool ret
= RecursiveASTVisitor::TraverseCXXDestructorDecl(pCXXDestructorDecl
);
66 bool FragileDestructor::VisitCXXMemberCallExpr(const CXXMemberCallExpr
* callExpr
)
68 if (!mbChecking
|| ignoreLocation(callExpr
)) {
71 const CXXMethodDecl
* methodDecl
= callExpr
->getMethodDecl();
72 if (!methodDecl
->isVirtual() || methodDecl
->hasAttr
<FinalAttr
>()) {
75 const CXXRecordDecl
* parentRecordDecl
= methodDecl
->getParent();
76 if (parentRecordDecl
->hasAttr
<FinalAttr
>()) {
79 if (!callExpr
->getImplicitObjectArgument()->IgnoreImpCasts()->isImplicitCXXThis()) {
82 // if we see an explicit call to its own method, that's OK
83 auto s1
= compiler
.getSourceManager().getCharacterData(compat::getBeginLoc(callExpr
));
84 auto s2
= compiler
.getSourceManager().getCharacterData(compat::getEndLoc(callExpr
));
85 std::string
tok(s1
, s2
-s1
);
86 if (tok
.find("::") != std::string::npos
) {
89 // e.g. osl/thread.hxx and cppuhelper/compbase.hxx
90 StringRef aFileName
= getFileNameOfSpellingLoc(
91 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(methodDecl
)));
92 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/include/osl/")
93 || loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/include/comphelper/")
94 || loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/include/cppuhelper/"))
97 DiagnosticsEngine::Warning
,
98 "calling virtual method from destructor, either make the virtual method final, or make this class final",
99 compat::getBeginLoc(callExpr
))
100 << callExpr
->getSourceRange();
102 DiagnosticsEngine::Note
,
103 "callee method here",
104 compat::getBeginLoc(methodDecl
))
105 << methodDecl
->getSourceRange();
110 loplugin::Plugin::Registration
< FragileDestructor
> X("fragiledestructor", false);
114 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */