/* -*- 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 #include #include #include #include #include #include "plugin.hxx" #include "check.hxx" /** Find destructors that only contain a single call to delete of a field. In which case that field should really be managed by unique_ptr. */ namespace { class UseUniquePtr: public loplugin::FilteringPlugin { public: explicit UseUniquePtr(loplugin::InstantiationData const & data): FilteringPlugin(data) {} virtual void run() override { fn = handler.getMainFileName().str(); loplugin::normalizeDotDotInFilePath(fn); // can't change these because we pass them down to the SfxItemPool stuff if (fn == SRCDIR "/sc/source/core/data/docpool.cxx") return; // this just too clever for me if (fn == SRCDIR "/sc/source/core/tool/chgtrack.cxx") return; // too clever if (fn == SRCDIR "/pyuno/source/module/pyuno_runtime.cxx") return; // m_pExampleSet here is very badly managed. sometimes it is owning, sometimes not, // and the logic depends on overriding methods. if (fn == SRCDIR "/sfx2/source/dialog/tabdlg.cxx") return; // pLongArr is being deleted here because we temporarily overwrite a pointer to someone else's buffer, with a pointer // to our own buffer if (fn == SRCDIR "/editeng/source/misc/txtrange.cxx") return; // can't use std::set> until C++14 if (fn == SRCDIR "/editeng/source/misc/svxacorr.cxx") return; // horrible horrible spawn of evil ownership and deletion here if (fn == SRCDIR "/sfx2/source/view/ipclient.cxx") return; // sometimes it owns, sometimes it doesn't if (fn == SRCDIR "/editeng/source/misc/svxacorr.cxx") return; // SwDoc::m_PageDescs has weird handling if (fn == SRCDIR "/sw/source/core/doc/docnew.cxx") return; // SwRedlineData::pNext and pExtraData have complex handling if (fn == SRCDIR "/sw/source/core/doc/docredln.cxx") return; // ScTempDocSource::pTempDoc if (fn == SRCDIR "/sc/source/ui/unoobj/funcuno.cxx") return; // SwAttrIter::m_pFont if (fn == SRCDIR "/sw/source/core/text/itratr.cxx" || fn == SRCDIR "/sw/source/core/text/redlnitr.cxx") return; // SwWrongList if (fn == SRCDIR "/sw/source/core/text/wrong.cxx") return; // SwLineLayout::m_pNext if (fn == SRCDIR "/sw/source/core/text/porlay.cxx") return; // ODatabaseExport::m_aDestColumns if (fn == SRCDIR "/dbaccess/source/ui/misc/DExport.cxx") return; // ScTabView::pDrawActual and pDrawOld if (fn == SRCDIR "/sc/source/ui/view/tabview5.cxx") return; // SwHTMLParser::m_pPendStack if (fn == SRCDIR "/sw/source/filter/html/htmlcss1.cxx") return; // Visual Studio 2017 has trouble with these if (fn == SRCDIR "/comphelper/source/property/MasterPropertySet.cxx" || fn == SRCDIR "/comphelper/source/property/MasterPropertySetInfo.cxx") return; // SwTableLine::m_aBoxes if (fn == SRCDIR "/sw/source/core/table/swtable.cxx") return; // SwHTMLParser::m_pFormImpl if (fn == SRCDIR "/sw/source/filter/html/htmlform.cxx") return; // SwHTMLParser::m_pPendStack, pNext if (fn == SRCDIR "/sw/source/filter/html/htmltab.cxx") return; // SaveLine::pBox, pNext if (fn == SRCDIR "/sw/source/core/undo/untbl.cxx") return; // RedlineInfo::pNextRedline if (fn == SRCDIR "/sw/source/filter/xml/XMLRedlineImportHelper.cxx") return; // SfxObjectShell::pMedium if (fn == SRCDIR "/sfx2/source/doc/objxtor.cxx") return; // various if (fn == SRCDIR "/sw/source/filter/ww8/wrtww8.cxx") return; // WW8TabBandDesc if (fn == SRCDIR "/sw/source/filter/ww8/ww8par2.cxx") return; // ZipOutputStream, ownership of ZipEntry is horribly complicated here if (fn == SRCDIR "/package/source/zipapi/ZipOutputStream.cxx") return; // custom deleter if (fn == SRCDIR "/sal/rtl/locale.cxx") return; // std::vector is tricky, changing it would require moving lots of class definitions around if (fn == SRCDIR "/sc/source/core/data/documen2.cxx" || fn == SRCDIR "/sc/source/core/tool/interpretercontext.cxx") return; TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); } bool VisitFunctionDecl(const FunctionDecl* ); bool VisitCXXDeleteExpr(const CXXDeleteExpr* ); bool TraverseFunctionDecl(FunctionDecl* ); bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* ); bool TraverseCXXMethodDecl(CXXMethodDecl* ); bool TraverseCXXConstructorDecl(CXXConstructorDecl* ); bool TraverseCXXConversionDecl(CXXConversionDecl* ); bool TraverseCXXDestructorDecl(CXXDestructorDecl* ); bool TraverseConstructorInitializer(CXXCtorInitializer*); private: void CheckCompoundStmt(const FunctionDecl*, const CompoundStmt* ); void CheckIfStmt(const FunctionDecl*, const IfStmt* ); void CheckCXXForRangeStmt(const FunctionDecl*, const CXXForRangeStmt* ); void CheckLoopDelete(const FunctionDecl*, const Stmt* ); void CheckLoopDelete(const FunctionDecl*, const CXXDeleteExpr* ); void CheckDeleteExpr(const FunctionDecl*, const CXXDeleteExpr*); void CheckDeleteLocalVar(const FunctionDecl*, const CXXDeleteExpr*, const VarDecl*); void CheckDeleteParmVar(const CXXDeleteExpr*, const ParmVarDecl*); void CheckParenExpr(const FunctionDecl*, const ParenExpr*); void CheckMemberDeleteExpr(const FunctionDecl*, const CXXDeleteExpr*, const MemberExpr*, StringRef message); FunctionDecl const * mpCurrentFunctionDecl = nullptr; std::string fn; }; bool UseUniquePtr::VisitFunctionDecl(const FunctionDecl* functionDecl) { if (ignoreLocation(functionDecl)) return true; if (isInUnoIncludeFile(functionDecl)) return true; const CompoundStmt* compoundStmt = dyn_cast_or_null< CompoundStmt >( functionDecl->getBody() ); if (!compoundStmt || compoundStmt->size() == 0) return true; CheckCompoundStmt(functionDecl, compoundStmt); return true; } /** * check for simple call to delete i.e. direct unconditional call, or if-guarded call */ void UseUniquePtr::CheckCompoundStmt(const FunctionDecl* functionDecl, const CompoundStmt* compoundStmt) { for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i) { if (auto cxxForRangeStmt = dyn_cast(*i)) CheckCXXForRangeStmt(functionDecl, cxxForRangeStmt); else if (auto forStmt = dyn_cast(*i)) CheckLoopDelete(functionDecl, forStmt->getBody()); else if (auto whileStmt = dyn_cast(*i)) CheckLoopDelete(functionDecl, whileStmt->getBody()); // check for unconditional inner compound statements else if (auto innerCompoundStmt = dyn_cast(*i)) CheckCompoundStmt(functionDecl, innerCompoundStmt); else if (auto deleteExpr = dyn_cast(*i)) CheckDeleteExpr(functionDecl, deleteExpr); else if (auto parenExpr = dyn_cast(*i)) CheckParenExpr(functionDecl, parenExpr); else if (auto ifStmt = dyn_cast(*i)) CheckIfStmt(functionDecl, ifStmt); } } // Check for conditional deletes like: // if (m_pField != nullptr) delete m_pField; void UseUniquePtr::CheckIfStmt(const FunctionDecl* functionDecl, const IfStmt* ifStmt) { auto cond = ifStmt->getCond()->IgnoreImplicit(); if (auto ifCondMemberExpr = dyn_cast(cond)) { // ignore "if (bMine)" if (!loplugin::TypeCheck(ifCondMemberExpr->getType()).Pointer()) return; // good } else if (auto binaryOp = dyn_cast(cond)) { if (!isa(binaryOp->getLHS()->IgnoreImplicit())) return; if (!isa(binaryOp->getRHS()->IgnoreImplicit())) return; // good } else // ignore anything more complicated return; auto deleteExpr = dyn_cast(ifStmt->getThen()); if (deleteExpr) { CheckDeleteExpr(functionDecl, deleteExpr); return; } auto parenExpr = dyn_cast(ifStmt->getThen()); if (parenExpr) { CheckParenExpr(functionDecl, parenExpr); return; } auto ifThenCompoundStmt = dyn_cast(ifStmt->getThen()); if (!ifThenCompoundStmt) return; for (auto j = ifThenCompoundStmt->body_begin(); j != ifThenCompoundStmt->body_end(); ++j) { auto ifDeleteExpr = dyn_cast(*j); if (ifDeleteExpr) CheckDeleteExpr(functionDecl, ifDeleteExpr); ParenExpr const * parenExpr = dyn_cast(*j); if (parenExpr) CheckParenExpr(functionDecl, parenExpr); } } void UseUniquePtr::CheckDeleteExpr(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr) { auto deleteExprArg = deleteExpr->getArgument()->IgnoreParens()->IgnoreImplicit(); if (const MemberExpr* memberExpr = dyn_cast(deleteExprArg)) { // ignore delete static_cast(p)->other; if (!isa(memberExpr->getBase()->IgnoreCasts())) return; // don't always own this if (fn == SRCDIR "/editeng/source/editeng/impedit2.cxx") return; // this member needs to get passed via an extern "C" API if (fn == SRCDIR "/sd/source/filter/sdpptwrp.cxx") return; // ownership complicated between this and the group if (fn == SRCDIR "/sc/source/core/data/formulacell.cxx") return; // linked list if (fn == SRCDIR "/sw/source/filter/html/parcss1.cxx") return; // linked list if (fn == SRCDIR "/sw/source/filter/writer/writer.cxx") return; // complicated if (fn == SRCDIR "/sc/source/filter/html/htmlpars.cxx") return; // complicated pimpl stuff in SalLayoutGlyphs if (fn == SRCDIR "/vcl/source/gdi/impglyphitem.cxx") return; CheckMemberDeleteExpr(functionDecl, deleteExpr, memberExpr, "unconditional call to delete on a member, should be using std::unique_ptr"); return; } const ArraySubscriptExpr* arrayExpr = dyn_cast(deleteExprArg); if (arrayExpr) { auto baseMemberExpr = dyn_cast(arrayExpr->getBase()->IgnoreParens()->IgnoreImplicit()); if (baseMemberExpr) CheckMemberDeleteExpr(functionDecl, deleteExpr, baseMemberExpr, "unconditional call to delete on an array member, should be using std::unique_ptr"); } } template bool any_equal(StringRef needle, T first) { return needle == first; } template bool any_equal(StringRef needle, T first, Args... args) { return needle == first || any_equal(needle, args...); } void UseUniquePtr::CheckDeleteLocalVar(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr, const VarDecl* varDecl) { // ignore globals for now if (varDecl->hasGlobalStorage()) return; // Ignore times when we are casting from void* to init the var, normally indicates // some complex memory management. if (varDecl->getInit()) { if (auto explicitCast = dyn_cast(varDecl->getInit())) { if (loplugin::TypeCheck(explicitCast->getSubExpr()->getType()).Pointer().Void()) return; } } if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/comphelper/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/cppuhelper/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/libreofficekit/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sfx2/qa/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/smoketest/")) return; if (loplugin::hasPathnamePrefix(fn, WORKDIR)) return; // linked lists if (fn == SRCDIR "/vcl/source/gdi/regband.cxx") return; // this thing relies on explicit delete if (loplugin::TypeCheck(varDecl->getType()).Pointer().Class("VersionCompatRead").GlobalNamespace()) return; if (loplugin::TypeCheck(varDecl->getType()).Pointer().Class("VersionCompatWrite").GlobalNamespace()) return; if (loplugin::TypeCheck(varDecl->getType()).Pointer().Class("IMapCompat").GlobalNamespace()) return; // passing data to gtk API and I can't figure out the types if (fn == SRCDIR "/vcl/unx/gtk3/gtkdata.cxx") return; // sometimes this stuff is held by tools::SvRef, sometimes by std::unique_ptr... if (fn == SRCDIR "/sot/source/unoolestorage/xolesimplestorage.cxx") return; // don't feel like messing with this chunk of sfx2 if (fn == SRCDIR "/sfx2/source/appl/appinit.cxx") return; if (fn == SRCDIR "/svx/source/svdraw/svdobj.cxx") return; if (fn == SRCDIR "/svx/source/svdraw/svdmodel.cxx") return; // linked list if (fn == SRCDIR "/basic/source/comp/parser.cxx") return; if (fn == SRCDIR "/basic/source/runtime/runtime.cxx") return; // just horrible if (fn == SRCDIR "/svx/source/form/filtnav.cxx") return; // using clucene macros if (fn == SRCDIR "/helpcompiler/source/HelpSearch.cxx") return; // linked list if (fn == SRCDIR "/filter/source/graphicfilter/ios2met/ios2met.cxx") return; // no idea what this is trying to do if (fn == SRCDIR "/cui/source/customize/SvxMenuConfigPage.cxx") return; // I cannot follow the ownership of OSQLParseNode's if (fn == SRCDIR "/dbaccess/source/core/api/SingleSelectQueryComposer.cxx") return; if (fn == SRCDIR "/dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx") return; // linked list if (fn == SRCDIR "/formula/source/core/api/FormulaCompiler.cxx") return; // smuggling data around via SvxFontListItem if (fn == SRCDIR "/extensions/source/propctrlr/fontdialog.cxx") return; // atomics if (fn == SRCDIR "/sc/source/ui/docshell/documentlinkmgr.cxx") return; // finicky if (fn == SRCDIR "/sc/source/core/data/stlpool.cxx") return; // macros if (fn == SRCDIR "/sc/source/core/tool/autoform.cxx") return; // unsure about ownership if (fn == SRCDIR "/xmlsecurity/source/framework/saxeventkeeperimpl.cxx") return; // ScTokenArray ownership complicated between this and the group if (fn == SRCDIR "/sc/source/core/data/formulacell.cxx") return; // macros if (fn == SRCDIR "/sw/source/core/doc/tblafmt.cxx") return; // more ScTokenArray if (fn == SRCDIR "/sc/source/ui/unoobj/tokenuno.cxx") return; // SwDoc::DelTextFormatColl if (fn == SRCDIR "/sw/source/core/doc/docfmt.cxx") return; // SwRootFrame::CalcFrameRects if (fn == SRCDIR "/sw/source/core/layout/trvlfrm.cxx") return; // crazy code if (fn == SRCDIR "/sw/source/core/undo/SwUndoPageDesc.cxx") return; // unsure about the SwLinePortion ownership if (fn == SRCDIR "/sw/source/core/text/itrform2.cxx") return; // can't follow the ownership if (fn == SRCDIR "/sw/source/filter/html/htmlatr.cxx") return; // SwTextFormatter::BuildMultiPortion complicated if (fn == SRCDIR "/sw/source/core/text/pormulti.cxx") return; // SwXMLExport::ExportTableLines if (fn == SRCDIR "/sw/source/filter/xml/xmltble.cxx") return; // SwPagePreview::~SwPagePreview if (fn == SRCDIR "/sw/source/uibase/uiview/pview.cxx") return; // alloc/free routines for the hand constructed virtual function table if (fn == SRCDIR "/sal/textenc/convertisciidevangari.cxx") return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/bridges/")) return; // bootstrap_map if (fn == SRCDIR "/sal/rtl/bootstrap.cxx") return; // too complicated for my small brain if (loplugin::hasPathnamePrefix(fn, SRCDIR "/cppu/")) return; // linked list if (fn == SRCDIR "/vcl/source/gdi/octree.cxx") return; // linked list if (fn == SRCDIR "/vcl/source/filter/graphicfilter.cxx") return; // linked list if (fn == SRCDIR "/svtools/source/control/ctrltool.cxx") return; // complicated if (fn == SRCDIR "/sfx2/source/control/msgpool.cxx") return; // complicated if (fn == SRCDIR "/svx/source/sdr/contact/objectcontact.cxx") return; // memory management in this module is a mess if (fn == SRCDIR "/idlc/source/aststack.cxx") return; // complicated if (fn == SRCDIR "/cui/source/customize/cfg.cxx") return; // linked list if (fn == SRCDIR "/lotuswordpro/source/filter/lwpfribptr.cxx") return; // complicated if (loplugin::hasPathnamePrefix(fn, SRCDIR "/connectivity/source/drivers/file/")) return; // complicated if (loplugin::hasPathnamePrefix(fn, SRCDIR "/unodevtools/source/skeletonmaker/")) return; llvm::StringRef parentName; if (auto cxxMethodDecl = dyn_cast(functionDecl)) { parentName = cxxMethodDecl->getParent()->getName(); } // no idea what is going on here if (parentName == "ScChangeActionLinkEntry") return; // ok if (parentName == "SfxItemSet" || parentName == "SfxItemPool") return; // linked list if (parentName == "ScFunctionList" || parentName == "SwNodes" || parentName == "SwUnoCursor" || parentName == "SortedResultSet" || parentName == "Atom" || parentName == "RegionBand" || parentName == "WMFWriter" || parentName == "Scheduler" || parentName == "OpenGLContext" || parentName == "WizardDialog") return; // manual ref counting if (parentName == "ScBroadcastAreaSlot") return; // complicated if (any_equal(parentName, "SwFormatField", "FontPropertyBox", "SdFontPropertyBox", "SwHTMLParser", "PDFWriterImpl", "SbiParser", "DictionaryList", "SwGlossaryHdl", "SwGlossaryGroupDlg")) return; // ok if (any_equal(parentName, "SbTreeListBox")) return; if (functionDecl->getIdentifier()) { auto name = functionDecl->getName(); SmallString<256> buf; if (!parentName.empty()) name = (parentName + "::" + name).toStringRef(buf); // custom deleters if (name == "Proxy_free" || name == "s_free" || name == "binuno_proxy_free") return; if (name == "SvpSalFrame::ReleaseGraphics") return; // don't feel like changing the API functions in registry if (name == "createRegistry" || name == "openRegistry" || name == "closeRegistry" || name == "destroyRegistry" || name == "reg_openRegistry") return; // linked list if (any_equal(name, "TypeWriter::createBlop", "ImplDeleteConfigData", "Config::DeleteGroup", "Config::DeleteKey", "E3dView::DoDepthArrange", "DXFBlocks::Clear", "DXFTables::Clear", "DXFEntities::Clear", "PSWriter::WritePS", "PSWriter::ImplWriteActions", "CUtList::Destroy", "ScBroadcastAreaSlotMachine::UpdateBroadcastAreas")) return; // ok if (any_equal(name, "write_uInt16s_FromOUString", "ProgressMonitor::removeText", "StgDirEntry::SetSize", "UCBStorage::CopyStorageElement_Impl" "OutputDevice::ImplDrawPolyPolygon", "OutputDevice::ImplDrawPolyPolygon", "ImplListBox::InsertEntry", "Edit::dispose", "ViewContact::deleteAllVOCs", "SfxViewFrame::ReleaseObjectShell_Impl", "SfxViewFrame::SwitchToViewShell_Impl", "OfaSmartTagOptionsTabPage::ClearListBox", "OfaSmartTagOptionsTabPage::FillItemSet", "doc_destroy", "lo_destroy", "callColumnFormatDialog")) return; // very dodgy if (any_equal(name, "UCBStorage::OpenStorage_Impl", "SdTransferable::GetData")) return; // complicated ownership if (any_equal(name, "ParseCMAP", "OpenGLSalBitmap::CreateTexture", "X11SalGraphicsImpl::drawAlphaBitmap" "SvEmbedTransferHelper::GetData", "ORoadmap::dispose", "BrowseBox::SetMode", "ExportDialog::GetFilterData", "disposeComVariablesForBasic", "ImpEditEngine::ImpRemoveParagraph", "FactoryImpl::createAdapter", "SfxStateCache::SetVisibleState", "SfxBindings::QueryState", "ViewContact::deleteAllVOCs", "SvxMSDffManager::ProcessObj", "SvEmbedTransferHelper::GetData", "SvXMLExportPropertyMapper::Filter_", "SdXMLExport::ImpGetOrCreatePageMasterInfo", "SfxDocumentDescPage::FillItemSet", "SfxCustomPropertiesPage::FillItemSet", "SfxCmisPropertiesPage::FillItemSet", "SfxObjectShell::DoSaveCompleted", "SfxObjectShell::DoSave_Impl", "SfxObjectShell::PreDoSaveAs_Impl", "SfxObjectShell::Save_Impl", "SfxFrame::DoClose_Impl", "SfxBaseModel::load", "SdrTextObj::TakeTextRect", "SdrTableObj::TakeTextRect", "SdrObjCustomShape::TakeTextRect", "CellProperties::ItemSetChanged", "CellProperties::ItemChange", "TableLayouter::SetBorder", "TableLayouter::ClearBorderLayout", "ImpXPolygon::Resize", "SvxTextEditSourceImpl::GetBackgroundTextForwarder", "Svx3DSceneObject::setPropertyValueImpl", "lcl_RemoveTextEditOutlinerViews", "SdrObjEditView::SdrEndTextEdit", "SvxShape::_setPropertyValue", "AccessibleShape::Init", "AccessibleCell::Init", "SdrTableRtfExporter::WriteCell", "GalleryItem::_getPropertyValues", "VMLExport::StartShape", "DrawingML::WriteText", "MtfTools::DrawText", "FormulaTokenArray::RewriteMissing", "OSQLParseNode::negateSearchCondition", "OSQLParseNodesContainer::clearAndDelete", "SdFilter::GetLibrarySymbol", "SdPage::SetObjText", "SdDrawDocument::InsertBookmarkAsPage", "SdDrawDocument::InsertBookmarkAsObject", "SdDrawDocument::RemoveUnnecessaryMasterPages", "ScTable::CopyConditionalFormat", "ScTable::ValidQuery", "ScTable::SetOptimalHeight", "ScTable::SetOptimalHeightOnly", "ScCompiler::CompileString", "ScProgress::DeleteInterpretProgress", "ScInterpreter::ScBase", "UCBStorage::CopyStorageElement_Impl", "X11SalGraphicsImpl::drawAlphaBitmap", "MasterPagesSelector::ClearPageSet", "View::IsPresObjSelected", "SdDrawPagesAccess::remove", "SdMasterPagesAccess::remove", "View::InsertData", "RemoteServer::execute", "Implementation::ReleaseOutlinerView", "SwFormat::CopyAttrs", "FinitCore", "SwCursorShell::MoveFieldType", "SwExtraPainter::PaintExtra", "SwMarginPortion::AdjustRight", "SwPaintQueue::Repaint", "SwTOXMgr::UpdateOrInsertTOX", "SwBaseShell::Execute", "WW8Export::WriteSdrTextObj")) return; // complicated delete if (name == "X11SalObject::CreateObject") return; } report( DiagnosticsEngine::Warning, "call to delete on a var, should be using std::unique_ptr", compat::getBeginLoc(deleteExpr)) << deleteExpr->getSourceRange(); report( DiagnosticsEngine::Note, "var is here", compat::getBeginLoc(varDecl)) << varDecl->getSourceRange(); } /** * Look for DELETEZ expressions. */ void UseUniquePtr::CheckParenExpr(const FunctionDecl* functionDecl, const ParenExpr* parenExpr) { auto binaryOp = dyn_cast(parenExpr->getSubExpr()); if (!binaryOp || binaryOp->getOpcode() != BO_Comma) return; auto deleteExpr = dyn_cast(binaryOp->getLHS()); if (!deleteExpr) return; CheckDeleteExpr(functionDecl, deleteExpr); } void UseUniquePtr::CheckLoopDelete(const FunctionDecl* functionDecl, const Stmt* bodyStmt) { if (auto deleteExpr = dyn_cast(bodyStmt)) CheckLoopDelete(functionDecl, deleteExpr); else if (auto compoundStmt = dyn_cast(bodyStmt)) { for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i) { if (auto deleteExpr = dyn_cast(*i)) CheckLoopDelete(functionDecl, deleteExpr); } } } void UseUniquePtr::CheckLoopDelete(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr) { const MemberExpr* memberExpr = nullptr; const VarDecl* varDecl = nullptr; const Expr* subExpr = deleteExpr->getArgument(); // drill down looking for a MemberExpr for (;;) { subExpr = subExpr->IgnoreParens()->IgnoreImplicit(); if ((memberExpr = dyn_cast(subExpr))) { if (memberExpr->getMemberDecl()->getName() == "first" || memberExpr->getMemberDecl()->getName() == "second") { subExpr = memberExpr->getBase(); memberExpr = nullptr; } else break; } else if (auto declRefExpr = dyn_cast(subExpr)) { if ((varDecl = dyn_cast(declRefExpr->getDecl()))) break; } else if (auto arraySubscriptExpr = dyn_cast(subExpr)) subExpr = arraySubscriptExpr->getBase(); else if (auto cxxOperatorCallExpr = dyn_cast(subExpr)) { // look for deletes of an iterator object where the iterator is over a member field if (auto declRefExpr = dyn_cast(cxxOperatorCallExpr->getArg(0)->IgnoreImplicit())) { if (auto iterVarDecl = dyn_cast(declRefExpr->getDecl())) { auto init = iterVarDecl->getInit(); if (init) { init = compat::IgnoreImplicit(init); if (!compat::CPlusPlus17(compiler.getLangOpts())) if (auto x = dyn_cast(init)) if (x->isElidable()) init = compat::IgnoreImplicit(x->getArg(0)); if (auto x = dyn_cast(init)) if (x->getNumArgs() == 1 || (x->getNumArgs() >= 2 && isa(x->getArg(1)))) { init = compat::IgnoreImplicit(x->getArg(0)); } if (auto x = dyn_cast(init)) init = x->getImplicitObjectArgument()->IgnoreParenImpCasts(); if ((memberExpr = dyn_cast(init))) break; // look for deletes of an iterator object where the iterator is over a var if (auto declRefExpr2 = dyn_cast(init)) { if ((varDecl = dyn_cast(declRefExpr2->getDecl()))) break; } } } } // look for deletes like "delete m_pField[0]" if (cxxOperatorCallExpr->getOperator() == OO_Subscript) { subExpr = cxxOperatorCallExpr->getArg(0)->IgnoreImplicit(); if ((memberExpr = dyn_cast(subExpr))) break; if (auto declRefExpr = dyn_cast(subExpr)) { if ((varDecl = dyn_cast(declRefExpr->getDecl()))) break; } } break; } else break; } if (memberExpr) { // OStorage_Impl::Commit very complicated ownership passing going on if (fn == SRCDIR "/package/source/xstor/xstorage.cxx") return; // complicated if (fn == SRCDIR "/vcl/source/gdi/print.cxx") return; // linked list if (fn == SRCDIR "/basic/source/runtime/runtime.cxx") return; // complicated if (fn == SRCDIR "/sw/source/core/bastyp/swcache.cxx") return; CheckMemberDeleteExpr(functionDecl, deleteExpr, memberExpr, "rather manage this member with std::some_container>"); } if (varDecl) { // ignore if the value for the var comes from somewhere else if (varDecl->hasInit() && isa(varDecl->getInit()->IgnoreImpCasts())) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/qa/")) return; // linked list if (fn == SRCDIR "/registry/source/reflwrit.cxx") return; // linked list if (fn == SRCDIR "/tools/source/generic/config.cxx") return; // linked lists if (fn == SRCDIR "/vcl/source/gdi/regband.cxx") return; // linked lists if (fn == SRCDIR "/vcl/source/gdi/regionband.cxx") return; // linked list if (fn == SRCDIR "/vcl/source/gdi/octree.cxx") return; // linked list if (fn == SRCDIR "/vcl/source/app/scheduler.cxx") return; // linked list if (fn == SRCDIR "/vcl/source/filter/wmf/wmfwr.cxx") return; // linked list if (fn == SRCDIR "/vcl/source/filter/graphicfilter.cxx") return; // valid code if (fn == SRCDIR "/vcl/source/app/salvtables.cxx") return; // undo code is tricky if (fn == SRCDIR "/svl/source/undo/undo.cxx") return; // subclass that messes with parent class in constructor/destructor, yuck if (fn == SRCDIR "/svtools/source/contnr/imivctl1.cxx") return; // SQLParseNode if (fn == SRCDIR "/connectivity/source/parse/sqlnode.cxx") return; // the whole svx model/contact/view thing confuses me if (fn == SRCDIR "/svx/source/sdr/contact/viewcontact.cxx") return; if (fn == SRCDIR "/svx/source/sdr/contact/objectcontact.cxx") return; // no idea if (fn == SRCDIR "/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.cxx") return; // SdrUndo stuff if (fn == SRCDIR "/svx/source/svdraw/svdundo.cxx") return; // TODO the lazydelete stuff should probably just be ripped out altogether now that we have VclPtr if (fn == SRCDIR "/vcl/source/helper/lazydelete.cxx") return; // linked list if (fn == SRCDIR "/filter/source/graphicfilter/idxf/dxfblkrd.cxx") return; if (fn == SRCDIR "/filter/source/graphicfilter/idxf/dxftblrd.cxx") return; if (fn == SRCDIR "/lotuswordpro/source/filter/utlist.cxx") return; if (fn == SRCDIR "/lotuswordpro/source/filter/lwpfribptr.cxx") return; // valid if (fn == SRCDIR "/sd/source/ui/sidebar/MasterPagesSelector.cxx") return; // linked list if (fn == SRCDIR "/sd/source/filter/ppt/pptatom.cxx") return; // linked list if (fn == SRCDIR "/sc/source/core/data/funcdesc.cxx") return; // linked list if (fn == SRCDIR "/sw/source/core/crsr/crsrsh.cxx") return; // no idea if (fn == SRCDIR "/sw/source/core/docnode/nodes.cxx") return; // linked list if (fn == SRCDIR "/sw/source/core/unocore/unocrsr.cxx") return; // linked list if (fn == SRCDIR "/filter/source/graphicfilter/idxf/dxfentrd.cxx") return; // linked list if (fn == SRCDIR "/filter/source/graphicfilter/ios2met/ios2met.cxx") return; // sometimes owning, sometimes not if (fn == SRCDIR "/sw/qa/core/Test-BigPtrArray.cxx") return; report( DiagnosticsEngine::Warning, "loopdelete: rather manage this var with std::some_container>", compat::getBeginLoc(deleteExpr)) << deleteExpr->getSourceRange(); report( DiagnosticsEngine::Note, "var is here", compat::getBeginLoc(varDecl)) << varDecl->getSourceRange(); } } void UseUniquePtr::CheckCXXForRangeStmt(const FunctionDecl* functionDecl, const CXXForRangeStmt* cxxForRangeStmt) { CXXDeleteExpr const * deleteExpr = nullptr; if (auto compoundStmt = dyn_cast(cxxForRangeStmt->getBody())) { for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i) if ((deleteExpr = dyn_cast(*i))) break; } else deleteExpr = dyn_cast(cxxForRangeStmt->getBody()); if (!deleteExpr) return; // check for delete of member if (auto memberExpr = dyn_cast(cxxForRangeStmt->getRangeInit())) { auto fieldDecl = dyn_cast(memberExpr->getMemberDecl()); if (!fieldDecl) return; // appears to just randomly leak stuff, and it involves some lex/yacc stuff if (fn == SRCDIR "/idlc/source/aststack.cxx") return; // complicated if (fn == SRCDIR "/vcl/source/gdi/print.cxx") return; // sometimes it's an owning field, sometimes not if (fn == SRCDIR "/i18npool/source/localedata/localedata.cxx") return; CheckMemberDeleteExpr(functionDecl, deleteExpr, memberExpr, "rather manage this with std::some_container>"); } // check for delete of var if (auto declRefExpr = dyn_cast(cxxForRangeStmt->getRangeInit()->IgnoreParens()->IgnoreImplicit())) { auto varDecl = dyn_cast(declRefExpr->getDecl()); if (!varDecl) return; // don't feel like messing with this part of sfx2 if (fn == SRCDIR "/sfx2/source/control/msgpool.cxx") return; if (fn == SRCDIR "/sfx2/source/doc/doctemplates.cxx") return; // lex/yacc if (fn == SRCDIR "/hwpfilter/source/grammar.cxx") return; if (fn == SRCDIR "/hwpfilter/source/formula.cxx") return; // no idea why, but ui tests crash afterwards in weird ways if (fn == SRCDIR "/svtools/source/control/roadmap.cxx") return; // sometimes it owns it, sometimes it does not if (fn == SRCDIR "/dbaccess/source/ui/misc/WCopyTable.cxx") return; // SfxPoolItem array if (fn == SRCDIR "/dbaccess/source/ui/misc/UITools.cxx") return; // SfxPoolItem array if (fn == SRCDIR "/sw/source/core/bastyp/init.cxx") return; // SfxPoolItem array if (fn == SRCDIR "/reportdesign/source/ui/misc/UITools.cxx") return; // SfxPoolItem array if (fn == SRCDIR "/reportdesign/source/ui/report/ReportController.cxx") return; // complicated if (fn == SRCDIR "/svx/source/sdr/contact/viewcontact.cxx") return; if (fn == SRCDIR "/svx/source/sdr/contact/objectcontact.cxx") return; report( DiagnosticsEngine::Warning, "rather manage this var with std::some_container>", compat::getBeginLoc(deleteExpr)) << deleteExpr->getSourceRange(); report( DiagnosticsEngine::Note, "var is here", compat::getBeginLoc(varDecl)) << varDecl->getSourceRange(); } } void UseUniquePtr::CheckMemberDeleteExpr(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr, const MemberExpr* memberExpr, StringRef message) { // ignore union games const FieldDecl* fieldDecl = dyn_cast(memberExpr->getMemberDecl()); if (!fieldDecl) return; TagDecl const * td = dyn_cast(fieldDecl->getDeclContext()); if (td->isUnion()) return; // ignore calling delete on someone else's field if (auto methodDecl = dyn_cast(functionDecl)) if (fieldDecl->getParent() != methodDecl->getParent() ) return; if (ignoreLocation(fieldDecl)) return; // to ignore things like the CPPUNIT macros if (loplugin::hasPathnamePrefix(fn, WORKDIR "/")) return; // passes and stores pointers to member fields if (fn == SRCDIR "/sot/source/sdstor/stgdir.hxx") return; // something platform-specific if (fn == SRCDIR "/hwpfilter/source/htags.h") return; // passes pointers to member fields if (fn == SRCDIR "/sd/inc/sdpptwrp.hxx") return; // @TODO intrusive linked-lists here, with some trickiness if (fn == SRCDIR "/sw/source/filter/html/parcss1.hxx") return; // @TODO SwDoc has some weird ref-counting going on if (fn == SRCDIR "/sw/inc/shellio.hxx") return; // @TODO it's sharing pointers with another class if (fn == SRCDIR "/sc/inc/formulacell.hxx") return; // some weird stuff going on here around struct Entity if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sax/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/include/sax/")) return; // manipulation of tree structures ie. StgAvlNode, don't lend themselves to std::unique_ptr if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sot/")) return; if (loplugin::hasPathnamePrefix(fn, SRCDIR "/include/sot/")) return; // the std::vector is being passed to another class if (fn == SRCDIR "/sfx2/source/explorer/nochaos.cxx") return; auto tc = loplugin::TypeCheck(fieldDecl->getType()); // these sw::Ring based classes do not lend themselves to std::unique_ptr management if (tc.Pointer().Class("SwNodeIndex").GlobalNamespace() || tc.Pointer().Class("SwShellTableCursor").GlobalNamespace() || tc.Pointer().Class("SwBlockCursor").GlobalNamespace() || tc.Pointer().Class("SwVisibleCursor").GlobalNamespace() || tc.Pointer().Class("SwShellCursor").GlobalNamespace()) return; // there is a loop in ~ImplPrnQueueList deleting stuff on a global data structure if (fn == SRCDIR "/vcl/inc/print.h") return; // painful linked list if (fn == SRCDIR "/basic/source/inc/runtime.hxx") return; // not sure how the node management is working here if (fn == SRCDIR "/i18npool/source/localedata/saxparser.cxx") return; // has a pointer that it only sometimes owns if (fn == SRCDIR "/editeng/source/editeng/impedit.hxx") return; report( DiagnosticsEngine::Warning, message, compat::getBeginLoc(deleteExpr)) << deleteExpr->getSourceRange(); report( DiagnosticsEngine::Note, "member is here", compat::getBeginLoc(fieldDecl)) << fieldDecl->getSourceRange(); } bool UseUniquePtr::TraverseFunctionDecl(FunctionDecl* functionDecl) { if (ignoreLocation(functionDecl)) return true; auto oldCurrent = mpCurrentFunctionDecl; mpCurrentFunctionDecl = functionDecl; bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl); mpCurrentFunctionDecl = oldCurrent; return ret; } bool UseUniquePtr::TraverseCXXMethodDecl(CXXMethodDecl* methodDecl) { if (ignoreLocation(methodDecl)) return true; auto oldCurrent = mpCurrentFunctionDecl; mpCurrentFunctionDecl = methodDecl; bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(methodDecl); mpCurrentFunctionDecl = oldCurrent; return ret; } bool UseUniquePtr::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* methodDecl) { if (ignoreLocation(methodDecl)) return true; auto oldCurrent = mpCurrentFunctionDecl; mpCurrentFunctionDecl = methodDecl; bool ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(methodDecl); mpCurrentFunctionDecl = oldCurrent; return ret; } bool UseUniquePtr::TraverseCXXConstructorDecl(CXXConstructorDecl* methodDecl) { if (ignoreLocation(methodDecl)) return true; auto oldCurrent = mpCurrentFunctionDecl; mpCurrentFunctionDecl = methodDecl; bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(methodDecl); mpCurrentFunctionDecl = oldCurrent; return ret; } bool UseUniquePtr::TraverseCXXConversionDecl(CXXConversionDecl* methodDecl) { if (ignoreLocation(methodDecl)) return true; auto oldCurrent = mpCurrentFunctionDecl; mpCurrentFunctionDecl = methodDecl; bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(methodDecl); mpCurrentFunctionDecl = oldCurrent; return ret; } bool UseUniquePtr::TraverseCXXDestructorDecl(CXXDestructorDecl* methodDecl) { if (ignoreLocation(methodDecl)) return true; auto oldCurrent = mpCurrentFunctionDecl; mpCurrentFunctionDecl = methodDecl; bool ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(methodDecl); mpCurrentFunctionDecl = oldCurrent; return ret; } bool UseUniquePtr::TraverseConstructorInitializer(CXXCtorInitializer * ctorInit) { if (!ctorInit->getSourceLocation().isValid() || ignoreLocation(ctorInit->getSourceLocation())) return true; if (!ctorInit->getMember()) return true; if (!loplugin::TypeCheck(ctorInit->getMember()->getType()).Class("unique_ptr").StdNamespace()) return true; auto constructExpr = dyn_cast_or_null(ctorInit->getInit()); if (!constructExpr || constructExpr->getNumArgs() == 0) return true; auto init = constructExpr->getArg(0)->IgnoreImpCasts(); if (!isa(init)) return true; StringRef fn = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(ctorInit->getSourceLocation())); // don't feel like fiddling with the yacc parser if (loplugin::hasPathnamePrefix(fn, SRCDIR "/idlc/")) return true; // cannot change URE if (loplugin::hasPathnamePrefix(fn, SRCDIR "/cppu/source/helper/purpenv/helper_purpenv_Environment.cxx")) return true; report( DiagnosticsEngine::Warning, "should be passing via std::unique_ptr param", ctorInit->getSourceLocation()) << ctorInit->getSourceRange(); return RecursiveASTVisitor::TraverseConstructorInitializer(ctorInit); } // Only checks for calls to delete on a pointer param bool UseUniquePtr::VisitCXXDeleteExpr(const CXXDeleteExpr* deleteExpr) { if (!mpCurrentFunctionDecl) return true; if (ignoreLocation(mpCurrentFunctionDecl)) return true; if (isInUnoIncludeFile(compat::getBeginLoc(mpCurrentFunctionDecl->getCanonicalDecl()))) return true; auto declRefExpr = dyn_cast(deleteExpr->getArgument()->IgnoreParenImpCasts()->IgnoreImplicit()); if (!declRefExpr) return true; if (auto parmVarDecl = dyn_cast(declRefExpr->getDecl())) CheckDeleteParmVar(deleteExpr, parmVarDecl); else if (auto varDecl = dyn_cast(declRefExpr->getDecl())) CheckDeleteLocalVar(mpCurrentFunctionDecl, deleteExpr, varDecl); return true; } void UseUniquePtr::CheckDeleteParmVar(const CXXDeleteExpr* deleteExpr, const ParmVarDecl* ) { if (mpCurrentFunctionDecl->getIdentifier()) { auto name = mpCurrentFunctionDecl->getName(); if (name == "delete_IncludesCollection" || name == "convertName" || name == "createNamedType" || name == "typelib_typedescriptionreference_release" || name == "deleteExceptions" || name == "uno_threadpool_destroy" || name == "AddRanges_Impl" || name == "DestroySalInstance" || name == "ImplHandleUserEvent" || name == "releaseDecimalPtr" // TODO, basic || name == "replaceAndReset" // TODO, connectivity || name == "intrusive_ptr_release" || name == "FreeParaList" || name == "DeleteSdrUndoAction" // TODO, sc || name == "lcl_MergeGCBox" || name == "lcl_MergeGCLine" || name == "lcl_DelHFFormat") return; } if (auto cxxMethodDecl = dyn_cast(mpCurrentFunctionDecl)) { auto parentName = cxxMethodDecl->getParent()->getName(); // include/o3tl/deleter.hxx if (parentName == "default_delete") return; // TODO Bitmap::ReleaseAccess // Tricky because it reverberates through other code and requires that BitmapWriteAccess move into /include again if (parentName == "Bitmap") return; // TODO virtual ones are much trickier, leave for later if (cxxMethodDecl->isVirtual()) return; // sw/inc/unobaseclass.hxx holds SolarMutex while deleting if (parentName == "UnoImplPtrDeleter") return; } // StgAvlNode::Remove if (fn == SRCDIR "/sot/source/sdstor/stgavl.cxx") return; // SfxItemPool::ReleaseDefaults and SfxItemPool::Free if (fn == SRCDIR "/svl/source/items/itempool.cxx") return; // SwContourCache if (fn == SRCDIR "/sw/source/core/text/txtfly.cxx") return; // too messy to cope with the SQL parser if (fn == SRCDIR "/connectivity/source/parse/sqlnode.cxx") return; // I can't figure out the ownership of the SfxMedium in the call site(s) if (fn == SRCDIR "/sfx2/source/doc/sfxbasemodel.cxx") return; // pointer passed via IMPL_LINK if (fn == SRCDIR "/sfx2/source/control/dispatch.cxx") return; // NavigatorTreeModel::Remove if (fn == SRCDIR "/svx/source/form/navigatortreemodel.cxx") return; // SdrModel::AddUndo if (fn == SRCDIR "/svx/source/svdraw/svdmodel.cxx") return; // undo callback if (fn == SRCDIR "/basctl/source/basicide/baside3.cxx") return; // ActualizeProgress::TimeoutHdl if (fn == SRCDIR "/cui/source/dialogs/cuigaldlg.cxx") return; // ToolbarSaveInData::RemoveToolbar if (fn == SRCDIR "/cui/source/customize/cfg.cxx") return; // OStorage_Impl::RemoveElement very complicated ownership passing going on if (fn == SRCDIR "/package/source/xstor/xstorage.cxx") return; // actually held via shared_ptr, uses protected deleter object if (fn == SRCDIR "/sd/source/ui/framework/tools/FrameworkHelper.cxx") return; // actually held via shared_ptr, uses protected deleter object if (fn == SRCDIR "/sd/source/ui/presenter/CanvasUpdateRequester.cxx") return; // actually held via shared_ptr, uses protected deleter object if (fn == SRCDIR "/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx") return; // actually held via shared_ptr, uses protected deleter object if (fn == SRCDIR "/sd/source/ui/sidebar/MasterPageContainer.cxx") return; // actually held via shared_ptr, uses protected deleter object if (fn == SRCDIR "/sd/source/ui/tools/TimerBasedTaskExecution.cxx") return; // actually held via shared_ptr, uses protected deleter object if (fn == SRCDIR "/sd/source/ui/view/ViewShellImplementation.cxx") return; // ScBroadcastAreaSlot::StartListeningArea manual ref-counting of ScBroadcastArea if (fn == SRCDIR "/sc/source/core/data/bcaslot.cxx") return; // ScDrawLayer::AddCalcUndo undo stuff if (fn == SRCDIR "/sc/source/core/data/drwlayer.cxx") return; // ScTable::SetFormulaCell if (fn == SRCDIR "/sc/source/core/data/table2.cxx") return; // ScDocument::SetFormulaCell if (fn == SRCDIR "/sc/source/core/data/documen2.cxx") return; // RemoveEditAttribsHandler, stored in mdds block if (fn == SRCDIR "/sc/source/core/data/column2.cxx") return; // just turns into a mess if (fn == SRCDIR "/sc/source/ui/Accessibility/AccessibleDocument.cxx") return; // SwCache::DeleteObj, linked list if (fn == SRCDIR "/sw/source/core/bastyp/swcache.cxx") return; // SAXEventKeeperImpl::smashBufferNode if (fn == SRCDIR "/xmlsecurity/source/framework/saxeventkeeperimpl.cxx") return; // SwDoc::DeleteExtTextInput if (fn == SRCDIR "/sw/source/core/doc/extinput.cxx") return; // SwDoc::DelSectionFormat if (fn == SRCDIR "/sw/source/core/docnode/ndsect.cxx") return; // SwFrame::DestroyFrame if (fn == SRCDIR "/sw/source/core/layout/ssfrm.cxx") return; // SwGluePortion::Join if (fn == SRCDIR "/sw/source/core/text/porglue.cxx") return; // SwDoc::DelFrameFormat if (fn == SRCDIR "/sw/source/core/doc/docfmt.cxx") return; // SwTextAttr::Destroy if (fn == SRCDIR "/sw/source/core/txtnode/txatbase.cxx") return; // IMPL_LINK( SwDoc, AddDrawUndo, SdrUndoAction *, pUndo, void ) if (fn == SRCDIR "/sw/source/core/undo/undraw.cxx") return; // SwHTMLParser::EndAttr if (fn == SRCDIR "/sw/source/filter/html/swhtml.cxx") return; // SwGlossaryHdl::Expand sometimes the pointer is owned, sometimes it is not if (fn == SRCDIR "/sw/source/uibase/dochdl/gloshdl.cxx") return; // SwWrtShell::Insert only owned sometimes if (fn == SRCDIR "/sw/source/uibase/wrtsh/wrtsh1.cxx") return; // NodeArrayDeleter if (fn == SRCDIR "/unoxml/source/rdf/librdf_repository.cxx") return; // SmCursor::LineToList ran out of enthusiasm to rework the node handling if (fn == SRCDIR "/starmath/source/cursor.cxx") return; // XMLEventOASISTransformerContext::FlushEventMap if (fn == SRCDIR "/xmloff/source/transform/EventOASISTContext.cxx") return; // XMLEventOOoTransformerContext::FlushEventMap if (fn == SRCDIR "/xmloff/source/transform/EventOOoTContext.cxx") return; // SbiProcDef::Match if (fn == SRCDIR "/basic/source/comp/symtbl.cxx") return; /* Sometimes we can pass the param as std::unique_ptr& or std::unique_ptr, sometimes the method just needs to be inlined, which normally exposes more simplification. */ report( DiagnosticsEngine::Warning, "calling delete on a pointer param, should be either allowlisted or simplified", compat::getBeginLoc(deleteExpr)) << deleteExpr->getSourceRange(); } loplugin::Plugin::Registration< UseUniquePtr > X("useuniqueptr", false); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */