diff options
author | Michael Meeks <michael.meeks@collabora.com> | 2015-04-28 11:41:31 +0100 |
---|---|---|
committer | Michael Meeks <michael.meeks@collabora.com> | 2015-04-28 15:08:48 +0100 |
commit | e8b97a52c96df9c8e8055407b1e40ed7cb9cfc67 (patch) | |
tree | b322529f4a54a6427862cb20cb4cf87fb6a5d065 /compilerplugins | |
parent | 2b0be6c0e9d23f1b8535ba7033732ae2e3bfb5c9 (diff) | |
parent | 0cde74f788a054fa2b65107a030dd463b8d11c7a (diff) |
Merge remote-tracking branch 'origin/feature/vclptr'
Resolve several thousand lines of conflicts.
Conflicts:
accessibility/source/extended/accessiblelistbox.cxx
accessibility/source/standard/vclxaccessiblecombobox.cxx
accessibility/source/standard/vclxaccessibledropdowncombobox.cxx
accessibility/source/standard/vclxaccessibledropdownlistbox.cxx
accessibility/source/standard/vclxaccessiblelistbox.cxx
accessibility/source/standard/vclxaccessibletextfield.cxx
basctl/source/basicide/basidesh.cxx
cui/source/inc/chardlg.hxx
cui/source/tabpages/tpbitmap.cxx
dbaccess/source/ui/dlg/UserAdmin.cxx
dbaccess/source/ui/querydesign/ConnectionLineAccess.cxx
extensions/source/propctrlr/propertyeditor.hxx
extensions/source/scanner/sanedlg.cxx
filter/source/pdf/impdialog.cxx
include/sfx2/mgetempl.hxx
include/sfx2/sidebar/SidebarToolBox.hxx
include/sfx2/viewsh.hxx
include/svtools/brwbox.hxx
include/svtools/filectrl.hxx
include/svtools/scrwin.hxx
include/svx/dlgctrl.hxx
include/svx/sidebar/Popup.hxx
include/svx/sidebar/PopupContainer.hxx
include/svx/sidebar/PopupControl.hxx
include/svx/sidebar/SidebarDialControl.hxx
include/svx/sidebar/ValueSetWithTextControl.hxx
sc/source/ui/condformat/condformatdlgentry.cxx
sc/source/ui/navipi/navipi.cxx
sc/source/ui/sidebar/CellBorderStyleControl.hxx
sd/source/ui/animations/CustomAnimationDialog.cxx
sd/source/ui/inc/DrawViewShell.hxx
sd/source/ui/inc/Ruler.hxx
sd/source/ui/inc/SlideSorter.hxx
sd/source/ui/inc/ViewTabBar.hxx
sd/source/ui/inc/Window.hxx
sd/source/ui/inc/morphdlg.hxx
sd/source/ui/inc/sdpreslt.hxx
sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx
sd/source/ui/sidebar/LayoutMenu.hxx
sd/source/ui/sidebar/MasterPagesSelector.hxx
sd/source/ui/sidebar/NavigatorWrapper.hxx
sd/source/ui/sidebar/PanelBase.hxx
sd/source/ui/sidebar/RecentMasterPagesSelector.cxx
sd/source/ui/sidebar/RecentMasterPagesSelector.hxx
sd/source/ui/slideshow/showwindow.hxx
sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx
sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx
sd/source/ui/view/ViewShellBase.cxx
sd/source/ui/view/drviewsa.cxx
sfx2/source/appl/fileobj.hxx
sfx2/source/appl/opengrf.cxx
sfx2/source/control/thumbnailviewacc.hxx
sfx2/source/dialog/securitypage.cxx
sfx2/source/dialog/templdlg.cxx
sfx2/source/doc/docinsert.cxx
sfx2/source/doc/guisaveas.cxx
sfx2/source/inc/alienwarn.hxx
sfx2/source/sidebar/Deck.cxx
sfx2/source/sidebar/Deck.hxx
sfx2/source/sidebar/DeckTitleBar.cxx
sfx2/source/sidebar/DeckTitleBar.hxx
sfx2/source/sidebar/MenuButton.cxx
sfx2/source/sidebar/MenuButton.hxx
sfx2/source/sidebar/Panel.cxx
sfx2/source/sidebar/Panel.hxx
sfx2/source/sidebar/PanelTitleBar.hxx
sfx2/source/sidebar/SidebarDockingWindow.hxx
sfx2/source/sidebar/SidebarToolBox.cxx
sfx2/source/sidebar/TabBar.hxx
sfx2/source/sidebar/TabItem.cxx
sfx2/source/sidebar/TabItem.hxx
sfx2/source/sidebar/TitleBar.hxx
sfx2/source/toolbox/imgmgr.cxx
starmath/inc/edit.hxx
starmath/inc/smmod.hxx
starmath/qa/cppunit/test_starmath.cxx
starmath/source/edit.cxx
starmath/source/smmod.cxx
svtools/source/brwbox/brwbox1.cxx
svtools/source/brwbox/datwin.hxx
svtools/source/contnr/fileview.cxx
svtools/source/contnr/simptabl.cxx
svtools/source/control/filectrl.cxx
svtools/source/control/valueimp.hxx
svx/inc/GalleryControl.hxx
svx/source/dialog/dlgctrl.cxx
svx/source/dialog/swframeexample.cxx
svx/source/fmcomp/fmgridif.cxx
svx/source/gallery2/GalleryControl.cxx
svx/source/sidebar/EmptyPanel.hxx
svx/source/sidebar/area/AreaPropertyPanel.hxx
svx/source/sidebar/area/AreaTransparencyGradientControl.hxx
svx/source/sidebar/graphic/GraphicPropertyPanel.hxx
svx/source/sidebar/insert/InsertPropertyPanel.cxx
svx/source/sidebar/insert/InsertPropertyPanel.hxx
svx/source/sidebar/line/LinePropertyPanel.hxx
svx/source/sidebar/line/LineWidthControl.cxx
svx/source/sidebar/line/LineWidthControl.hxx
svx/source/sidebar/line/LineWidthValueSet.hxx
svx/source/sidebar/paragraph/ParaPropertyPanel.hxx
svx/source/sidebar/possize/SidebarDialControl.cxx
svx/source/sidebar/text/TextCharacterSpacingPopup.hxx
svx/source/sidebar/text/TextPropertyPanel.hxx
svx/source/sidebar/tools/PopupContainer.cxx
svx/source/sidebar/tools/PopupControl.cxx
svx/source/sidebar/tools/ValueSetWithTextControl.cxx
svx/source/svdraw/svdfmtf.hxx
svx/source/svdraw/svdibrow.cxx
svx/source/tbxctrls/colrctrl.cxx
svx/source/tbxctrls/tbcontrl.cxx
sw/source/ui/dbui/mmaddressblockpage.cxx
sw/source/ui/dialog/uiregionsw.cxx
sw/source/ui/index/cnttab.cxx
sw/source/uibase/inc/drpcps.hxx
sw/source/uibase/sidebar/PageColumnControl.hxx
sw/source/uibase/sidebar/PageMarginControl.hxx
sw/source/uibase/sidebar/PageOrientationControl.hxx
sw/source/uibase/sidebar/PagePropertyPanel.hxx
sw/source/uibase/sidebar/PageSizeControl.hxx
sw/source/uibase/uiview/view2.cxx
sw/source/uibase/utlui/navipi.cxx
vcl/inc/svdata.hxx
vcl/source/control/combobox.cxx
vcl/source/control/lstbox.cxx
vcl/source/window/dockwin.cxx
vcl/source/window/winproc.cxx
Change-Id: I056cf3026ff17d65cca0b6e6588bda4a88fa8d95
Diffstat (limited to 'compilerplugins')
-rw-r--r-- | compilerplugins/clang/vclwidgets.cxx | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/compilerplugins/clang/vclwidgets.cxx b/compilerplugins/clang/vclwidgets.cxx new file mode 100644 index 000000000000..e12e9572b385 --- /dev/null +++ b/compilerplugins/clang/vclwidgets.cxx @@ -0,0 +1,458 @@ +/* -*- 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/. + */ + +#include <string> +#include <iostream> + +#include "plugin.hxx" +#include "compat.hxx" +#include "clang/AST/CXXInheritance.h" + +// Final goal: Checker for VCL widget references. Makes sure that VCL Window subclasses are properly referenced counted and dispose()'ed. +// +// But at the moment it just finds subclasses of Window which are not heap-allocated +// +// TODO do I need to check for local and static variables, too ? +// TODO when we have a dispose() method, verify that the dispose() methods releases all of the Window references +// TODO when we have a dispose() method, verify that it calls the super-class dispose() method at some point. + +namespace { + +class VCLWidgets: + public RecursiveASTVisitor<VCLWidgets>, public loplugin::Plugin +{ +public: + explicit VCLWidgets(InstantiationData const & data): Plugin(data) {} + + virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } + + bool VisitVarDecl(const VarDecl *); + + bool VisitFieldDecl(const FieldDecl *); + + bool VisitParmVarDecl(const ParmVarDecl *); + + bool VisitFunctionDecl(const FunctionDecl *); + + bool VisitCXXDestructorDecl(const CXXDestructorDecl *); + + bool VisitCXXDeleteExpr(const CXXDeleteExpr *); + +private: + bool isDisposeCallingSuperclassDispose(const CXXMethodDecl* pMethodDecl); +}; + +static bool startsWith(const std::string& s, const char* other) +{ + return s.compare(0, strlen(other), other) == 0; +} + +bool BaseCheckNotWindowSubclass(const CXXRecordDecl *BaseDefinition, void *) { + if (BaseDefinition && BaseDefinition->getQualifiedNameAsString() == "OutputDevice") { + return false; + } + return true; +} + +bool isDerivedFromWindow(const CXXRecordDecl *decl) { + if (!decl) + return false; + if (decl->getQualifiedNameAsString() == "OutputDevice") + return true; + if (!decl->hasDefinition()) { + return false; + } + if (// not sure what hasAnyDependentBases() does, + // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1 + !decl->hasAnyDependentBases() && + !decl->forallBases(BaseCheckNotWindowSubclass, nullptr, true)) { + return true; + } + return false; +} + +bool containsWindowSubclass(const Type* pType0); + +bool containsWindowSubclass(const QualType& qType) { + if (startsWith(qType.getAsString(), "VclPtr")) + return false; + if (startsWith(qType.getAsString(), "const VclPtr")) + return false; + if (startsWith(qType.getAsString(), "class VclPtr")) + return false; + if (startsWith(qType.getAsString(), "const class VclPtr")) + return false; + if (startsWith(qType.getAsString(), "ScopedVclPtr")) + return false; + if (startsWith(qType.getAsString(), "class ScopedVclPtr")) + return false; + if (startsWith(qType.getAsString(), "const class ScopedVclPtr")) + return false; + return containsWindowSubclass(qType.getTypePtr()); +} + +bool containsWindowSubclass(const Type* pType0) { + if (!pType0) + return false; + const Type* pType = pType0->getUnqualifiedDesugaredType(); + if (!pType) + return false; + const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl(); + if (pRecordDecl) { + const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl); + if (pTemplate) { + for(unsigned i=0; i<pTemplate->getTemplateArgs().size(); ++i) { + const TemplateArgument& rArg = pTemplate->getTemplateArgs()[i]; + if (rArg.getKind() == TemplateArgument::ArgKind::Type && + containsWindowSubclass(rArg.getAsType())) + { + return true; + } + } + } + } + if (pType->isPointerType()) { + QualType pointeeType = pType->getPointeeType(); + return containsWindowSubclass(pointeeType); + } else if (pType->isArrayType()) { + const ArrayType* pArrayType = dyn_cast<ArrayType>(pType); + QualType elementType = pArrayType->getElementType(); + return containsWindowSubclass(elementType); + } else { + return isDerivedFromWindow(pRecordDecl); + } +} + +bool VCLWidgets::VisitCXXDestructorDecl(const CXXDestructorDecl* pCXXDestructorDecl) +{ + if (ignoreLocation(pCXXDestructorDecl)) { + return true; + } + if (!pCXXDestructorDecl->isThisDeclarationADefinition()) { + return true; + } + const CXXRecordDecl * pRecordDecl = pCXXDestructorDecl->getParent(); + // ignore OutputDevice class + if (pRecordDecl->getQualifiedNameAsString() == "OutputDevice") { + return true; + } + // check if this class is derived from Window + if (!isDerivedFromWindow(pRecordDecl)) { + return true; + } + bool foundVclPtrField = false; + for(auto fieldDecl : pRecordDecl->fields()) { + const RecordType *pFieldRecordType = fieldDecl->getType()->getAs<RecordType>(); + if (pFieldRecordType) { + const CXXRecordDecl *pFieldRecordTypeDecl = dyn_cast<CXXRecordDecl>(pFieldRecordType->getDecl()); + if (startsWith(pFieldRecordTypeDecl->getQualifiedNameAsString(), "VclPtr")) { + foundVclPtrField = true; + break; + } + } + } + bool foundDispose = false; + for(auto methodDecl : pRecordDecl->methods()) { + if (methodDecl->isInstance() && methodDecl->param_size()==0 && methodDecl->getNameAsString() == "dispose") { + foundDispose = true; + break; + } + } + const CompoundStmt *pCompoundStatement = dyn_cast<CompoundStmt>(pCXXDestructorDecl->getBody()); + // having an empty body and no dispose() method is fine + if (!foundVclPtrField && !foundDispose && pCompoundStatement && pCompoundStatement->size() == 0) { + return true; + } + if (foundVclPtrField && pCompoundStatement && pCompoundStatement->size() == 0) { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass with VclPtr field must call dispose() from it's destructor.", + pCXXDestructorDecl->getLocStart()) + << pCXXDestructorDecl->getSourceRange(); + return true; + } + // check that the destructor for a OutputDevice subclass does nothing except call into the dispose() method + bool ok = false; + if (pCompoundStatement && pCompoundStatement->size() == 1) { + const CXXMemberCallExpr *pCallExpr = dyn_cast<CXXMemberCallExpr>(*pCompoundStatement->body_begin()); + if (pCallExpr) { + if( const FunctionDecl* func = pCallExpr->getDirectCallee()) { + if( func->getNumParams() == 0 && func->getIdentifier() != NULL + && ( func->getName() == "disposeOnce" )) { + ok = true; + } + } + } + } + if (!ok) { + SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc( + pCXXDestructorDecl->getLocStart()); + StringRef filename = compiler.getSourceManager().getFilename(spellingLocation); + if ( !(filename.startswith(SRCDIR "/vcl/source/window/window.cxx")) + && !(filename.startswith(SRCDIR "/vcl/source/gdi/virdev.cxx")) ) + { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass should have nothing in it's destructor but a call to disposeOnce().", + pCXXDestructorDecl->getLocStart()) + << pCXXDestructorDecl->getSourceRange(); + } + } + return true; +} + + +bool VCLWidgets::VisitVarDecl(const VarDecl * pVarDecl) { + if (ignoreLocation(pVarDecl)) { + return true; + } + const RecordType *recordType = pVarDecl->getType()->getAs<RecordType>(); + if (recordType == nullptr) { + return true; + } + const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl()); + if (recordDecl == nullptr) { + return true; + } + + // check if this field is derived from Window + if (isDerivedFromWindow(recordDecl)) { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass allocated on stack, should be allocated via VclPtr or via *.", + pVarDecl->getLocation()) + << pVarDecl->getSourceRange(); + } + if ( !startsWith(pVarDecl->getType().getAsString(), "std::vector<vcl::Window *>") + && !startsWith(pVarDecl->getType().getAsString(), "std::map<vcl::Window *, Size>") + && !startsWith(pVarDecl->getType().getAsString(), "std::map<vcl::Window *, class Size>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::vector<class Button *>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::vector<Button *>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::mem_fun1_t<") + && !startsWith(pVarDecl->getType().getAsString(), "::comphelper::mem_fun1_t<") + && !startsWith(pVarDecl->getType().getAsString(), "::std::pair<formula::RefButton *, formula::RefEdit *>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::pair<RefButton *, RefEdit *>") + && !startsWith(pVarDecl->getType().getAsString(), "std::list<SwSidebarWin *>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::map<OTableWindow *, sal_Int32>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::map<class OTableWindow *, sal_Int32>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::multimap<sal_Int32, OTableWindow *>") + && !startsWith(pVarDecl->getType().getAsString(), "::std::multimap<sal_Int32, class OTableWindow *>") + && !startsWith(pVarDecl->getType().getAsString(), "::dbp::OMultiInstanceAutoRegistration< ::dbp::OUnoAutoPilot<") + && !startsWith(pVarDecl->getType().getAsString(), "SwSidebarWin_iterator") + && !startsWith(pVarDecl->getType().getAsString(), "functor_vector_type") + && !startsWith(pVarDecl->getType().getAsString(), "const functor_vector_type") + && containsWindowSubclass(pVarDecl->getType())) + { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass should be wrapped in VclPtr. " + pVarDecl->getType().getAsString(), + pVarDecl->getLocation()) + << pVarDecl->getSourceRange(); + } + return true; +} + +bool VCLWidgets::VisitFieldDecl(const FieldDecl * fieldDecl) { + if (ignoreLocation(fieldDecl)) { + return true; + } + if (fieldDecl->isBitField()) { + return true; + } + const CXXRecordDecl *pParentRecordDecl = dyn_cast<CXXRecordDecl>(fieldDecl->getParent()); + if (containsWindowSubclass(fieldDecl->getType())) { + // have to ignore this for now, nasty reverse dependency from tools->vcl + if (!(pParentRecordDecl != nullptr && pParentRecordDecl->getQualifiedNameAsString() == "ErrorContextImpl")) { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass declared as a pointer field, should be wrapped in VclPtr." + fieldDecl->getType().getAsString(), + fieldDecl->getLocation()) + << fieldDecl->getSourceRange(); + return true; + } + } + const RecordType *recordType = fieldDecl->getType()->getAs<RecordType>(); + if (recordType == nullptr) { + return true; + } + const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl()); + if (recordDecl == nullptr) { + return true; + } + + // check if this field is derived from Window + if (isDerivedFromWindow(recordDecl)) { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass allocated as a class member, should be allocated via VclPtr.", + fieldDecl->getLocation()) + << fieldDecl->getSourceRange(); + } + + // If this field is a VclPtr field, then the class MUST have a dispose method + if (pParentRecordDecl && isDerivedFromWindow(pParentRecordDecl) + && startsWith(recordDecl->getQualifiedNameAsString(), "VclPtr")) + { + bool foundDispose = false; + for(auto methodDecl : pParentRecordDecl->methods()) { + if (methodDecl->isInstance() && methodDecl->param_size()==0 && methodDecl->getNameAsString() == "dispose") { + foundDispose = true; + break; + } + } + if (!foundDispose) { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass with a VclPtr field MUST have a dispose() method.", + fieldDecl->getLocation()) + << fieldDecl->getSourceRange(); + } + if (!pParentRecordDecl->hasUserDeclaredDestructor()) { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass with a VclPtr field MUST have an explicit destructor.", + fieldDecl->getLocation()) + << fieldDecl->getSourceRange(); + } + } + + return true; +} + +bool VCLWidgets::VisitParmVarDecl(ParmVarDecl const * pvDecl) +{ + if (ignoreLocation(pvDecl)) { + return true; + } + // ignore the stuff in the VclPtr template class + const CXXMethodDecl *pMethodDecl = dyn_cast<CXXMethodDecl>(pvDecl->getDeclContext()); + if (pMethodDecl + && pMethodDecl->getParent()->getQualifiedNameAsString().find("VclPtr") != std::string::npos) { + return true; + } + // we exclude this method in VclBuilder because it's so useful to have it like this + if (pMethodDecl + && pMethodDecl->getNameAsString() == "get" + && (pMethodDecl->getParent()->getQualifiedNameAsString() == "VclBuilder" + || pMethodDecl->getParent()->getQualifiedNameAsString() == "VclBuilderContainer")) + { + return true; + } + return true; +} + +bool VCLWidgets::VisitFunctionDecl( const FunctionDecl* functionDecl ) +{ + if (ignoreLocation(functionDecl)) { + return true; + } + // ignore the stuff in the VclPtr template class + const CXXMethodDecl *pMethodDecl = dyn_cast<CXXMethodDecl>(functionDecl); + if (pMethodDecl + && pMethodDecl->getParent()->getQualifiedNameAsString() == "VclPtr") { + return true; + } + // ignore the OutputDevice::dispose() method + if (pMethodDecl + && pMethodDecl->getParent()->getQualifiedNameAsString() == "OutputDevice") { + return true; + } + if (functionDecl->hasBody() && pMethodDecl && isDerivedFromWindow(pMethodDecl->getParent())) { + // check the last thing that the dispose() method does, is to call into the superclass dispose method + if (pMethodDecl->getNameAsString() == "dispose") { + if (!isDisposeCallingSuperclassDispose(pMethodDecl)) { + report( + DiagnosticsEngine::Warning, + "OutputDevice subclass dispose() method MUST call it's superclass dispose() as the last thing it does", + functionDecl->getLocStart()) + << functionDecl->getSourceRange(); + } + } + } + + return true; +} + +bool VCLWidgets::VisitCXXDeleteExpr(const CXXDeleteExpr *pCXXDeleteExpr) +{ + if (ignoreLocation(pCXXDeleteExpr)) { + return true; + } + const CXXRecordDecl *pPointee = pCXXDeleteExpr->getArgument()->getType()->getPointeeCXXRecordDecl(); + if (pPointee && isDerivedFromWindow(pPointee)) { + SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc( + pCXXDeleteExpr->getLocStart()); + StringRef filename = compiler.getSourceManager().getFilename(spellingLocation); + if ( !(filename.startswith(SRCDIR "/include/vcl/outdev.hxx"))) + { + report( + DiagnosticsEngine::Warning, + "calling delete on instance of OutputDevice subclass, must rather call disposeAndClear()", + pCXXDeleteExpr->getLocStart()) + << pCXXDeleteExpr->getSourceRange(); + } + } + const ImplicitCastExpr* pImplicitCastExpr = dyn_cast<ImplicitCastExpr>(pCXXDeleteExpr->getArgument()); + if (!pImplicitCastExpr) { + return true; + } + if (pImplicitCastExpr->getCastKind() != CK_UserDefinedConversion) { + return true; + } + report( + DiagnosticsEngine::Warning, + "calling delete on instance of VclPtr, must rather call disposeAndClear()", + pCXXDeleteExpr->getLocStart()) + << pCXXDeleteExpr->getSourceRange(); + return true; +} + + +/** +The AST looks like: +`-CXXMemberCallExpr 0xb06d8b0 'void' + `-MemberExpr 0xb06d868 '<bound member function type>' ->dispose 0x9d34880 + `-ImplicitCastExpr 0xb06d8d8 'class SfxTabPage *' <UncheckedDerivedToBase (SfxTabPage)> + `-CXXThisExpr 0xb06d850 'class SfxAcceleratorConfigPage *' this + +*/ +bool VCLWidgets::isDisposeCallingSuperclassDispose(const CXXMethodDecl* pMethodDecl) +{ + const CompoundStmt *pCompoundStatement = dyn_cast<CompoundStmt>(pMethodDecl->getBody()); + if (!pCompoundStatement) return false; + if (pCompoundStatement->size() == 0) return false; + // find the last statement + const CXXMemberCallExpr *pCallExpr = dyn_cast<CXXMemberCallExpr>(*pCompoundStatement->body_rbegin()); + if (!pCallExpr) return false; + const MemberExpr *pMemberExpr = dyn_cast<MemberExpr>(pCallExpr->getCallee()); + if (!pMemberExpr) return false; + if (pMemberExpr->getMemberDecl()->getNameAsString() != "dispose") return false; + const CXXMethodDecl *pDirectCallee = dyn_cast<CXXMethodDecl>(pCallExpr->getDirectCallee()); + if (!pDirectCallee) return false; +/* Not working yet. Partially because sometimes the superclass does not a dispose() method, so it gets passed up the chain. + Need complex checking for that case. + if (pDirectCallee->getParent()->getTypeForDecl() != (*pMethodDecl->getParent()->bases_begin()).getType().getTypePtr()) { + report( + DiagnosticsEngine::Warning, + "dispose() method calling wrong baseclass, calling " + pDirectCallee->getParent()->getQualifiedNameAsString() + + " should be calling " + (*pMethodDecl->getParent()->bases_begin()).getType().getAsString(), + pCallExpr->getLocStart()) + << pCallExpr->getSourceRange(); + return false; + }*/ + return true; +} + + + +loplugin::Plugin::Registration< VCLWidgets > X("vclwidgets"); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |