diff options
Diffstat (limited to 'compilerplugins/clang/fragiledestructor.cxx')
-rw-r--r-- | compilerplugins/clang/fragiledestructor.cxx | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/compilerplugins/clang/fragiledestructor.cxx b/compilerplugins/clang/fragiledestructor.cxx new file mode 100644 index 0000000000..f8ec76a561 --- /dev/null +++ b/compilerplugins/clang/fragiledestructor.cxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#ifndef LO_CLANG_SHARED_PLUGINS + +#include <string> +#include <iostream> + +#include "plugin.hxx" +#include "config_clang.h" +#include "clang/AST/CXXInheritance.h" + + +// Check for calls to virtual methods from destructors. These are dangerous because intention might be to call +// a method on a subclass, while in actual fact, it only calls the method on the current or super class. +// + +namespace { + +class FragileDestructor: + public loplugin::FilteringPlugin<FragileDestructor> +{ +public: + explicit FragileDestructor(loplugin::InstantiationData const & data): + FilteringPlugin(data) {} + + virtual bool preRun() override + { + StringRef fn(handler.getMainFileName()); + + // TODO, these all need fixing + + if (loplugin::isSamePathname(fn, SRCDIR "/comphelper/source/misc/proxyaggregation.cxx")) + return false; + if (loplugin::isSamePathname(fn, SRCDIR "/svx/source/svdraw/svdpntv.cxx")) // ~SdrPaintView calling ClearPageView + return false; + if (loplugin::isSamePathname(fn, SRCDIR "/svx/source/svdraw/svdobj.cxx")) // ~SdrObject calling GetLastBoundRect + return false; + if (loplugin::isSamePathname(fn, SRCDIR "/svx/source/svdraw/svdedxv.cxx")) // ~SdrObjEditView calling SdrEndTextEdit + return false; + if (loplugin::isSamePathname(fn, SRCDIR "/connectivity/source/drivers/file/FStatement.cxx")) // ~OStatement_Base calling disposing + return false; + if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/core/CustomAnimationEffect.cxx")) // ~EffectSequenceHelper calling reset + return false; + if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/view/sdview.cxx")) // ~View calling DeleteWindowFromPaintView + return false; + if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/core/layout/ssfrm.cxx")) // ~SwFrame calling IsDeleteForbidden + return false; + 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 + return false; + + return true; + } + + virtual void run() override + { + if (preRun()) + TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); + } + + bool PreTraverseCXXDestructorDecl(CXXDestructorDecl*); + bool PostTraverseCXXDestructorDecl(CXXDestructorDecl*, bool); + bool TraverseCXXDestructorDecl(CXXDestructorDecl*); + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *); + +private: + std::vector<CXXDestructorDecl*> m_vDestructors; +}; + +bool FragileDestructor::PreTraverseCXXDestructorDecl(CXXDestructorDecl* cxxDestructorDecl) +{ + if (ignoreLocation(cxxDestructorDecl)) + return true; + if (!cxxDestructorDecl->isThisDeclarationADefinition()) + return true; + if (cxxDestructorDecl->getParent()->hasAttr<FinalAttr>()) + return true; + m_vDestructors.push_back(cxxDestructorDecl); + return true; +} + +bool FragileDestructor::PostTraverseCXXDestructorDecl(CXXDestructorDecl* cxxDestructorDecl, bool) +{ + if (!m_vDestructors.empty() && m_vDestructors.back() == cxxDestructorDecl) + m_vDestructors.pop_back(); + return true; +} + +bool FragileDestructor::TraverseCXXDestructorDecl(CXXDestructorDecl* cxxDestructorDecl) +{ + PreTraverseCXXDestructorDecl(cxxDestructorDecl); + auto ret = FilteringPlugin::TraverseCXXDestructorDecl(cxxDestructorDecl); + PostTraverseCXXDestructorDecl(cxxDestructorDecl, ret); + return ret; +} + +bool FragileDestructor::VisitCXXMemberCallExpr(const CXXMemberCallExpr* callExpr) +{ + if (m_vDestructors.empty() || ignoreLocation(callExpr)) + return true; + const CXXMethodDecl* methodDecl = callExpr->getMethodDecl(); + if (!methodDecl->isVirtual() || methodDecl->hasAttr<FinalAttr>()) + return true; + const CXXRecordDecl* parentRecordDecl = methodDecl->getParent(); + if (parentRecordDecl->hasAttr<FinalAttr>()) + return true; + if (!callExpr->getImplicitObjectArgument()->IgnoreImpCasts()->isImplicitCXXThis()) + return true; + + // if we see an explicit call to its own method, that's OK + auto s1 = compiler.getSourceManager().getCharacterData(callExpr->getBeginLoc()); + auto s2 = compiler.getSourceManager().getCharacterData(callExpr->getEndLoc()); + std::string tok(s1, s2-s1); + if (tok.find("::") != std::string::npos) + return true; + + // Very common pattern that we call acquire/dispose in destructors of UNO objects + // to make sure they are cleaned up. + if (methodDecl->getName() == "acquire" || methodDecl->getName() == "dispose") + return true; + + report( + DiagnosticsEngine::Warning, + "calling virtual method from destructor, either make the virtual method final, or make this class final", + callExpr->getBeginLoc()) + << callExpr->getSourceRange(); + report( + DiagnosticsEngine::Note, + "callee method here", + methodDecl->getBeginLoc()) + << methodDecl->getSourceRange(); + return true; +} + + +loplugin::Plugin::Registration<FragileDestructor> fragiledestructor("fragiledestructor"); + +} + +#endif // LO_CLANG_SHARED_PLUGINS + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |