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/.
9 #ifndef LO_CLANG_SHARED_PLUGINS
15 #include "config_clang.h"
16 #include "clang/AST/CXXInheritance.h"
19 // Check for calls to virtual methods from destructors. These are dangerous because intention might be to call
20 // a method on a subclass, while in actual fact, it only calls the method on the current or super class.
25 class FragileDestructor
:
26 public loplugin::FilteringPlugin
<FragileDestructor
>
29 explicit FragileDestructor(loplugin::InstantiationData
const & data
):
30 FilteringPlugin(data
) {}
32 virtual bool preRun() override
34 StringRef
fn(handler
.getMainFileName());
36 // TODO, these all need fixing
38 if (loplugin::isSamePathname(fn
, SRCDIR
"/comphelper/source/misc/proxyaggregation.cxx"))
40 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/svdraw/svdpntv.cxx")) // ~SdrPaintView calling ClearPageView
42 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/svdraw/svdobj.cxx")) // ~SdrObject calling GetLastBoundRect
44 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/svdraw/svdedxv.cxx")) // ~SdrObjEditView calling SdrEndTextEdit
46 if (loplugin::isSamePathname(fn
, SRCDIR
"/connectivity/source/drivers/file/FStatement.cxx")) // ~OStatement_Base calling disposing
48 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/core/CustomAnimationEffect.cxx")) // ~EffectSequenceHelper calling reset
50 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/view/sdview.cxx")) // ~View calling DeleteWindowFromPaintView
52 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/core/layout/ssfrm.cxx")) // ~SwFrame calling IsDeleteForbidden
54 if (loplugin::isSamePathname(fn
, SRCDIR
"/chart2/source/model/template/CandleStickChartType.cxx")) // to ignore <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100797> "[10/11/12 Regression] using declaration causing virtual call with wrongly adjusted this pointer" workaround
60 virtual void run() override
63 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
66 bool PreTraverseCXXDestructorDecl(CXXDestructorDecl
*);
67 bool PostTraverseCXXDestructorDecl(CXXDestructorDecl
*, bool);
68 bool TraverseCXXDestructorDecl(CXXDestructorDecl
*);
69 bool VisitCXXMemberCallExpr(const CXXMemberCallExpr
*);
72 std::vector
<CXXDestructorDecl
*> m_vDestructors
;
75 bool FragileDestructor::PreTraverseCXXDestructorDecl(CXXDestructorDecl
* cxxDestructorDecl
)
77 if (ignoreLocation(cxxDestructorDecl
))
79 if (!cxxDestructorDecl
->isThisDeclarationADefinition())
81 if (cxxDestructorDecl
->getParent()->hasAttr
<FinalAttr
>())
83 m_vDestructors
.push_back(cxxDestructorDecl
);
87 bool FragileDestructor::PostTraverseCXXDestructorDecl(CXXDestructorDecl
* cxxDestructorDecl
, bool)
89 if (!m_vDestructors
.empty() && m_vDestructors
.back() == cxxDestructorDecl
)
90 m_vDestructors
.pop_back();
94 bool FragileDestructor::TraverseCXXDestructorDecl(CXXDestructorDecl
* cxxDestructorDecl
)
96 PreTraverseCXXDestructorDecl(cxxDestructorDecl
);
97 auto ret
= FilteringPlugin::TraverseCXXDestructorDecl(cxxDestructorDecl
);
98 PostTraverseCXXDestructorDecl(cxxDestructorDecl
, ret
);
102 bool FragileDestructor::VisitCXXMemberCallExpr(const CXXMemberCallExpr
* callExpr
)
104 if (m_vDestructors
.empty() || ignoreLocation(callExpr
))
106 const CXXMethodDecl
* methodDecl
= callExpr
->getMethodDecl();
107 if (!methodDecl
->isVirtual() || methodDecl
->hasAttr
<FinalAttr
>())
109 const CXXRecordDecl
* parentRecordDecl
= methodDecl
->getParent();
110 if (parentRecordDecl
->hasAttr
<FinalAttr
>())
112 if (!callExpr
->getImplicitObjectArgument()->IgnoreImpCasts()->isImplicitCXXThis())
115 // if we see an explicit call to its own method, that's OK
116 auto s1
= compiler
.getSourceManager().getCharacterData(callExpr
->getBeginLoc());
117 auto s2
= compiler
.getSourceManager().getCharacterData(callExpr
->getEndLoc());
118 std::string
tok(s1
, s2
-s1
);
119 if (tok
.find("::") != std::string::npos
)
122 // Very common pattern that we call acquire/dispose in destructors of UNO objects
123 // to make sure they are cleaned up.
124 if (methodDecl
->getName() == "acquire" || methodDecl
->getName() == "dispose")
128 DiagnosticsEngine::Warning
,
129 "calling virtual method from destructor, either make the virtual method final, or make this class final",
130 callExpr
->getBeginLoc())
131 << callExpr
->getSourceRange();
133 DiagnosticsEngine::Note
,
134 "callee method here",
135 methodDecl
->getBeginLoc())
136 << methodDecl
->getSourceRange();
141 loplugin::Plugin::Registration
<FragileDestructor
> fragiledestructor("fragiledestructor");
145 #endif // LO_CLANG_SHARED_PLUGINS
147 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */