Remove exec bits from docx
[LibreOffice.git] / compilerplugins / clang / useuniqueptr.cxx
blobcf4853f03b2b4d5d5f2c418076309aa0c14bf21d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <memory>
11 #include <cassert>
12 #include <string>
13 #include <iostream>
14 #include <fstream>
15 #include <set>
16 #include "config_clang.h"
17 #include "plugin.hxx"
18 #include "check.hxx"
20 /**
21 Find destructors that only contain a single call to delete of a field. In which
22 case that field should really be managed by unique_ptr.
25 namespace {
27 class UseUniquePtr:
28 public loplugin::FilteringPlugin<UseUniquePtr>
30 public:
31 explicit UseUniquePtr(loplugin::InstantiationData const & data):
32 FilteringPlugin(data) {}
34 virtual void run() override
36 fn = handler.getMainFileName().str();
37 loplugin::normalizeDotDotInFilePath(fn);
38 // can't change these because we pass them down to the SfxItemPool stuff
39 if (fn == SRCDIR "/sc/source/core/data/docpool.cxx")
40 return;
41 // this just too clever for me
42 if (fn == SRCDIR "/sc/source/core/tool/chgtrack.cxx")
43 return;
44 // too clever
45 if (fn == SRCDIR "/pyuno/source/module/pyuno_runtime.cxx")
46 return;
47 // m_pExampleSet here is very badly managed. sometimes it is owning, sometimes not,
48 // and the logic depends on overriding methods.
49 if (fn == SRCDIR "/sfx2/source/dialog/tabdlg.cxx")
50 return;
51 // pLongArr is being deleted here because we temporarily overwrite a pointer to someone else's buffer, with a pointer
52 // to our own buffer
53 if (fn == SRCDIR "/editeng/source/misc/txtrange.cxx")
54 return;
55 // can't use std::set<std::unique_ptr<>> until C++14
56 if (fn == SRCDIR "/editeng/source/misc/svxacorr.cxx")
57 return;
58 // horrible horrible spawn of evil ownership and deletion here
59 if (fn == SRCDIR "/sfx2/source/view/ipclient.cxx")
60 return;
61 // sometimes it owns, sometimes it doesn't
62 if (fn == SRCDIR "/editeng/source/misc/svxacorr.cxx")
63 return;
64 // SwDoc::m_PageDescs has weird handling
65 if (fn == SRCDIR "/sw/source/core/doc/docnew.cxx")
66 return;
67 // SwRedlineData::pNext and pExtraData have complex handling
68 if (fn == SRCDIR "/sw/source/core/doc/docredln.cxx")
69 return;
70 // ScTempDocSource::pTempDoc
71 if (fn == SRCDIR "/sc/source/ui/unoobj/funcuno.cxx")
72 return;
73 // SwAttrIter::m_pFont
74 if (fn == SRCDIR "/sw/source/core/text/itratr.cxx"
75 || fn == SRCDIR "/sw/source/core/text/redlnitr.cxx")
76 return;
77 // SwWrongList
78 if (fn == SRCDIR "/sw/source/core/text/wrong.cxx")
79 return;
80 // SwLineLayout::m_pNext
81 if (fn == SRCDIR "/sw/source/core/text/porlay.cxx")
82 return;
83 // ODatabaseExport::m_aDestColumns
84 if (fn == SRCDIR "/dbaccess/source/ui/misc/DExport.cxx")
85 return;
86 // ScTabView::pDrawActual and pDrawOld
87 if (fn == SRCDIR "/sc/source/ui/view/tabview5.cxx")
88 return;
89 // SwHTMLParser::m_pPendStack
90 if (fn == SRCDIR "/sw/source/filter/html/htmlcss1.cxx")
91 return;
92 // Visual Studio 2017 has trouble with these
93 if (fn == SRCDIR "/comphelper/source/property/MasterPropertySet.cxx"
94 || fn == SRCDIR "/comphelper/source/property/MasterPropertySetInfo.cxx")
95 return;
96 // SwTableLine::m_aBoxes
97 if (fn == SRCDIR "/sw/source/core/table/swtable.cxx")
98 return;
99 // SwHTMLParser::m_pFormImpl
100 if (fn == SRCDIR "/sw/source/filter/html/htmlform.cxx")
101 return;
102 // SwHTMLParser::m_pPendStack, pNext
103 if (fn == SRCDIR "/sw/source/filter/html/htmltab.cxx")
104 return;
105 // SaveLine::pBox, pNext
106 if (fn == SRCDIR "/sw/source/core/undo/untbl.cxx")
107 return;
108 // RedlineInfo::pNextRedline
109 if (fn == SRCDIR "/sw/source/filter/xml/XMLRedlineImportHelper.cxx")
110 return;
111 // SfxObjectShell::pMedium
112 if (fn == SRCDIR "/sfx2/source/doc/objxtor.cxx")
113 return;
114 // various
115 if (fn == SRCDIR "/sw/source/filter/ww8/wrtww8.cxx")
116 return;
117 // WW8TabBandDesc
118 if (fn == SRCDIR "/sw/source/filter/ww8/ww8par2.cxx")
119 return;
120 // ZipOutputStream, ownership of ZipEntry is horribly complicated here
121 if (fn == SRCDIR "/package/source/zipapi/ZipOutputStream.cxx")
122 return;
123 // custom deleter
124 if (fn == SRCDIR "/sal/rtl/locale.cxx")
125 return;
126 // std::vector<ScLookupCacheMap*> is tricky, changing it would require moving lots of class definitions around
127 if (fn == SRCDIR "/sc/source/core/data/documen2.cxx"
128 || fn == SRCDIR "/sc/source/core/tool/interpretercontext.cxx")
129 return;
131 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
134 bool VisitFunctionDecl(const FunctionDecl* );
135 bool VisitCXXDeleteExpr(const CXXDeleteExpr* );
136 bool TraverseFunctionDecl(FunctionDecl* );
137 bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* );
138 bool TraverseCXXMethodDecl(CXXMethodDecl* );
139 bool TraverseCXXConstructorDecl(CXXConstructorDecl* );
140 bool TraverseCXXConversionDecl(CXXConversionDecl* );
141 bool TraverseCXXDestructorDecl(CXXDestructorDecl* );
142 bool TraverseConstructorInitializer(CXXCtorInitializer*);
144 private:
145 void CheckCompoundStmt(const FunctionDecl*, const CompoundStmt* );
146 void CheckIfStmt(const FunctionDecl*, const IfStmt* );
147 void CheckCXXForRangeStmt(const FunctionDecl*, const CXXForRangeStmt* );
148 void CheckLoopDelete(const FunctionDecl*, const Stmt* );
149 void CheckLoopDelete(const FunctionDecl*, const CXXDeleteExpr* );
150 void CheckDeleteExpr(const FunctionDecl*, const CXXDeleteExpr*);
151 void CheckDeleteLocalVar(const FunctionDecl*, const CXXDeleteExpr*, const VarDecl*);
152 void CheckDeleteParmVar(const CXXDeleteExpr*, const ParmVarDecl*);
153 void CheckParenExpr(const FunctionDecl*, const ParenExpr*);
154 void CheckMemberDeleteExpr(const FunctionDecl*, const CXXDeleteExpr*,
155 const MemberExpr*, StringRef message);
156 FunctionDecl const * mpCurrentFunctionDecl = nullptr;
157 std::string fn;
160 bool UseUniquePtr::VisitFunctionDecl(const FunctionDecl* functionDecl)
162 if (ignoreLocation(functionDecl))
163 return true;
164 if (isInUnoIncludeFile(functionDecl))
165 return true;
167 const CompoundStmt* compoundStmt = dyn_cast_or_null< CompoundStmt >( functionDecl->getBody() );
168 if (!compoundStmt || compoundStmt->size() == 0)
169 return true;
171 CheckCompoundStmt(functionDecl, compoundStmt);
173 return true;
177 * check for simple call to delete i.e. direct unconditional call, or if-guarded call
179 void UseUniquePtr::CheckCompoundStmt(const FunctionDecl* functionDecl, const CompoundStmt* compoundStmt)
181 for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i)
183 if (auto cxxForRangeStmt = dyn_cast<CXXForRangeStmt>(*i))
184 CheckCXXForRangeStmt(functionDecl, cxxForRangeStmt);
185 else if (auto forStmt = dyn_cast<ForStmt>(*i))
186 CheckLoopDelete(functionDecl, forStmt->getBody());
187 else if (auto whileStmt = dyn_cast<WhileStmt>(*i))
188 CheckLoopDelete(functionDecl, whileStmt->getBody());
189 // check for unconditional inner compound statements
190 else if (auto innerCompoundStmt = dyn_cast<CompoundStmt>(*i))
191 CheckCompoundStmt(functionDecl, innerCompoundStmt);
192 else if (auto deleteExpr = dyn_cast<CXXDeleteExpr>(*i))
193 CheckDeleteExpr(functionDecl, deleteExpr);
194 else if (auto parenExpr = dyn_cast<ParenExpr>(*i))
195 CheckParenExpr(functionDecl, parenExpr);
196 else if (auto ifStmt = dyn_cast<IfStmt>(*i))
197 CheckIfStmt(functionDecl, ifStmt);
201 // Check for conditional deletes like:
202 // if (m_pField != nullptr) delete m_pField;
203 void UseUniquePtr::CheckIfStmt(const FunctionDecl* functionDecl, const IfStmt* ifStmt)
205 auto cond = ifStmt->getCond()->IgnoreImplicit();
206 if (auto ifCondMemberExpr = dyn_cast<MemberExpr>(cond))
208 // ignore "if (bMine)"
209 if (!loplugin::TypeCheck(ifCondMemberExpr->getType()).Pointer())
210 return;
211 // good
213 else if (auto binaryOp = dyn_cast<BinaryOperator>(cond))
215 if (!isa<MemberExpr>(binaryOp->getLHS()->IgnoreImplicit()))
216 return;
217 if (!isa<CXXNullPtrLiteralExpr>(binaryOp->getRHS()->IgnoreImplicit()))
218 return;
219 // good
221 else // ignore anything more complicated
222 return;
224 auto deleteExpr = dyn_cast<CXXDeleteExpr>(ifStmt->getThen());
225 if (deleteExpr)
227 CheckDeleteExpr(functionDecl, deleteExpr);
228 return;
231 auto parenExpr = dyn_cast<ParenExpr>(ifStmt->getThen());
232 if (parenExpr)
234 CheckParenExpr(functionDecl, parenExpr);
235 return;
238 auto ifThenCompoundStmt = dyn_cast<CompoundStmt>(ifStmt->getThen());
239 if (!ifThenCompoundStmt)
240 return;
241 for (auto j = ifThenCompoundStmt->body_begin(); j != ifThenCompoundStmt->body_end(); ++j)
243 auto ifDeleteExpr = dyn_cast<CXXDeleteExpr>(*j);
244 if (ifDeleteExpr)
245 CheckDeleteExpr(functionDecl, ifDeleteExpr);
246 ParenExpr const * parenExpr = dyn_cast<ParenExpr>(*j);
247 if (parenExpr)
248 CheckParenExpr(functionDecl, parenExpr);
252 void UseUniquePtr::CheckDeleteExpr(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr)
254 auto deleteExprArg = deleteExpr->getArgument()->IgnoreParens()->IgnoreImplicit();
257 if (const MemberExpr* memberExpr = dyn_cast<MemberExpr>(deleteExprArg))
259 // ignore delete static_cast<T>(p)->other;
260 if (!isa<CXXThisExpr>(memberExpr->getBase()->IgnoreCasts()))
261 return;
262 // don't always own this
263 if (fn == SRCDIR "/editeng/source/editeng/impedit2.cxx")
264 return;
265 // this member needs to get passed via an extern "C" API
266 if (fn == SRCDIR "/sd/source/filter/sdpptwrp.cxx")
267 return;
268 // ownership complicated between this and the group
269 if (fn == SRCDIR "/sc/source/core/data/formulacell.cxx")
270 return;
271 // linked list
272 if (fn == SRCDIR "/sw/source/filter/html/parcss1.cxx")
273 return;
274 // linked list
275 if (fn == SRCDIR "/sw/source/filter/writer/writer.cxx")
276 return;
277 // complicated
278 if (fn == SRCDIR "/sc/source/filter/html/htmlpars.cxx")
279 return;
280 // complicated pimpl stuff in SalLayoutGlyphs
281 if (fn == SRCDIR "/vcl/source/gdi/impglyphitem.cxx")
282 return;
284 CheckMemberDeleteExpr(functionDecl, deleteExpr, memberExpr,
285 "unconditional call to delete on a member, should be using std::unique_ptr");
286 return;
289 const ArraySubscriptExpr* arrayExpr = dyn_cast<ArraySubscriptExpr>(deleteExprArg);
290 if (arrayExpr)
292 auto baseMemberExpr = dyn_cast<MemberExpr>(arrayExpr->getBase()->IgnoreParens()->IgnoreImplicit());
293 if (baseMemberExpr)
294 CheckMemberDeleteExpr(functionDecl, deleteExpr, baseMemberExpr,
295 "unconditional call to delete on an array member, should be using std::unique_ptr");
299 template<typename T>
300 bool any_equal(StringRef needle, T first) {
301 return needle == first;
304 template<typename T, typename... Args>
305 bool any_equal(StringRef needle, T first, Args... args) {
306 return needle == first || any_equal(needle, args...);
309 void UseUniquePtr::CheckDeleteLocalVar(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr, const VarDecl* varDecl)
311 // ignore globals for now
312 if (varDecl->hasGlobalStorage())
313 return;
315 // Ignore times when we are casting from void* to init the var, normally indicates
316 // some complex memory management.
317 if (varDecl->getInit())
319 if (auto explicitCast = dyn_cast<ExplicitCastExpr>(varDecl->getInit()))
321 if (loplugin::TypeCheck(explicitCast->getSubExpr()->getType()).Pointer().Void())
322 return;
326 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/qa/"))
327 return;
328 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/comphelper/qa/"))
329 return;
330 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/cppuhelper/qa/"))
331 return;
332 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/libreofficekit/qa/"))
333 return;
334 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/qa/"))
335 return;
336 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sc/qa/"))
337 return;
338 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sfx2/qa/"))
339 return;
340 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/smoketest/"))
341 return;
342 if (loplugin::hasPathnamePrefix(fn, WORKDIR))
343 return;
344 // linked lists
345 if (fn == SRCDIR "/vcl/source/gdi/regband.cxx")
346 return;
347 // this thing relies on explicit delete
348 if (loplugin::TypeCheck(varDecl->getType()).Pointer().Class("VersionCompatRead").GlobalNamespace())
349 return;
350 if (loplugin::TypeCheck(varDecl->getType()).Pointer().Class("VersionCompatWrite").GlobalNamespace())
351 return;
352 if (loplugin::TypeCheck(varDecl->getType()).Pointer().Class("IMapCompat").GlobalNamespace())
353 return;
354 // passing data to gtk API and I can't figure out the types
355 if (fn == SRCDIR "/vcl/unx/gtk3/gtkdata.cxx")
356 return;
357 // sometimes this stuff is held by tools::SvRef, sometimes by std::unique_ptr...
358 if (fn == SRCDIR "/sot/source/unoolestorage/xolesimplestorage.cxx")
359 return;
360 // don't feel like messing with this chunk of sfx2
361 if (fn == SRCDIR "/sfx2/source/appl/appinit.cxx")
362 return;
363 if (fn == SRCDIR "/svx/source/svdraw/svdobj.cxx")
364 return;
365 if (fn == SRCDIR "/svx/source/svdraw/svdmodel.cxx")
366 return;
367 // linked list
368 if (fn == SRCDIR "/basic/source/comp/parser.cxx")
369 return;
370 if (fn == SRCDIR "/basic/source/runtime/runtime.cxx")
371 return;
372 // just horrible
373 if (fn == SRCDIR "/svx/source/form/filtnav.cxx")
374 return;
375 // using clucene macros
376 if (fn == SRCDIR "/helpcompiler/source/HelpSearch.cxx")
377 return;
378 // linked list
379 if (fn == SRCDIR "/filter/source/graphicfilter/ios2met/ios2met.cxx")
380 return;
381 // no idea what this is trying to do
382 if (fn == SRCDIR "/cui/source/customize/SvxMenuConfigPage.cxx")
383 return;
384 // I cannot follow the ownership of OSQLParseNode's
385 if (fn == SRCDIR "/dbaccess/source/core/api/SingleSelectQueryComposer.cxx")
386 return;
387 if (fn == SRCDIR "/dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx")
388 return;
389 // linked list
390 if (fn == SRCDIR "/formula/source/core/api/FormulaCompiler.cxx")
391 return;
392 // smuggling data around via SvxFontListItem
393 if (fn == SRCDIR "/extensions/source/propctrlr/fontdialog.cxx")
394 return;
395 // atomics
396 if (fn == SRCDIR "/sc/source/ui/docshell/documentlinkmgr.cxx")
397 return;
398 // finicky
399 if (fn == SRCDIR "/sc/source/core/data/stlpool.cxx")
400 return;
401 // macros
402 if (fn == SRCDIR "/sc/source/core/tool/autoform.cxx")
403 return;
404 // unsure about ownership
405 if (fn == SRCDIR "/xmlsecurity/source/framework/saxeventkeeperimpl.cxx")
406 return;
407 // ScTokenArray ownership complicated between this and the group
408 if (fn == SRCDIR "/sc/source/core/data/formulacell.cxx")
409 return;
410 // macros
411 if (fn == SRCDIR "/sw/source/core/doc/tblafmt.cxx")
412 return;
413 // more ScTokenArray
414 if (fn == SRCDIR "/sc/source/ui/unoobj/tokenuno.cxx")
415 return;
416 // SwDoc::DelTextFormatColl
417 if (fn == SRCDIR "/sw/source/core/doc/docfmt.cxx")
418 return;
419 // SwRootFrame::CalcFrameRects
420 if (fn == SRCDIR "/sw/source/core/layout/trvlfrm.cxx")
421 return;
422 // crazy code
423 if (fn == SRCDIR "/sw/source/core/undo/SwUndoPageDesc.cxx")
424 return;
425 // unsure about the SwLinePortion ownership
426 if (fn == SRCDIR "/sw/source/core/text/itrform2.cxx")
427 return;
428 // can't follow the ownership
429 if (fn == SRCDIR "/sw/source/filter/html/htmlatr.cxx")
430 return;
431 // SwTextFormatter::BuildMultiPortion complicated
432 if (fn == SRCDIR "/sw/source/core/text/pormulti.cxx")
433 return;
434 // SwXMLExport::ExportTableLines
435 if (fn == SRCDIR "/sw/source/filter/xml/xmltble.cxx")
436 return;
437 // SwPagePreview::~SwPagePreview
438 if (fn == SRCDIR "/sw/source/uibase/uiview/pview.cxx")
439 return;
440 // alloc/free routines for the hand constructed virtual function table
441 if (fn == SRCDIR "/sal/textenc/convertisciidevangari.cxx")
442 return;
443 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/bridges/"))
444 return;
445 // bootstrap_map
446 if (fn == SRCDIR "/sal/rtl/bootstrap.cxx")
447 return;
448 // too complicated for my small brain
449 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/cppu/"))
450 return;
451 // linked list
452 if (fn == SRCDIR "/vcl/source/gdi/octree.cxx")
453 return;
454 // linked list
455 if (fn == SRCDIR "/vcl/source/filter/graphicfilter.cxx")
456 return;
457 // linked list
458 if (fn == SRCDIR "/svtools/source/control/ctrltool.cxx")
459 return;
460 // complicated
461 if (fn == SRCDIR "/sfx2/source/control/msgpool.cxx")
462 return;
463 // complicated
464 if (fn == SRCDIR "/svx/source/sdr/contact/objectcontact.cxx")
465 return;
466 // complicated
467 if (fn == SRCDIR "/cui/source/customize/cfg.cxx")
468 return;
469 // linked list
470 if (fn == SRCDIR "/lotuswordpro/source/filter/lwpfribptr.cxx")
471 return;
472 // complicated
473 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/connectivity/source/drivers/file/"))
474 return;
475 // complicated
476 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/unodevtools/source/skeletonmaker/"))
477 return;
479 llvm::StringRef parentName;
480 if (auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(functionDecl))
482 parentName = cxxMethodDecl->getParent()->getName();
485 // no idea what is going on here
486 if (parentName == "ScChangeActionLinkEntry")
487 return;
488 // ok
489 if (parentName == "SfxItemSet" || parentName == "SfxItemPool")
490 return;
491 // linked list
492 if (parentName == "ScFunctionList" || parentName == "SwNodes"
493 || parentName == "SwUnoCursor" || parentName == "SortedResultSet"
494 || parentName == "Atom" || parentName == "RegionBand" || parentName == "WMFWriter"
495 || parentName == "Scheduler" || parentName == "OpenGLContext"
496 || parentName == "WizardDialog")
497 return;
498 // manual ref counting
499 if (parentName == "ScBroadcastAreaSlot")
500 return;
501 // complicated
502 if (any_equal(parentName, "SwFormatField", "FontPropertyBox", "SdFontPropertyBox",
503 "SwHTMLParser", "PDFWriterImpl", "SbiParser", "DictionaryList", "SwGlossaryHdl", "SwGlossaryGroupDlg"))
504 return;
505 // ok
506 if (any_equal(parentName, "SbTreeListBox"))
507 return;
509 if (functionDecl->getIdentifier())
511 auto name = functionDecl->getName();
512 SmallString<256> buf;
513 if (!parentName.empty())
514 name = (parentName + "::" + name).toStringRef(buf);
516 // custom deleters
517 if (name == "Proxy_free" || name == "s_free" || name == "binuno_proxy_free")
518 return;
519 if (name == "SvpSalFrame::ReleaseGraphics")
520 return;
521 // don't feel like changing the API functions in registry
522 if (name == "createRegistry" || name == "openRegistry" || name == "closeRegistry" || name == "destroyRegistry"
523 || name == "reg_openRegistry")
524 return;
525 // linked list
526 if (any_equal(name, "TypeWriter::createBlop", "ImplDeleteConfigData", "Config::DeleteGroup",
527 "Config::DeleteKey", "E3dView::DoDepthArrange",
528 "DXFBlocks::Clear", "DXFTables::Clear", "DXFEntities::Clear",
529 "PSWriter::WritePS", "PSWriter::ImplWriteActions", "CUtList::Destroy",
530 "ScBroadcastAreaSlotMachine::UpdateBroadcastAreas"))
531 return;
532 // ok
533 if (any_equal(name, "write_uInt16s_FromOUString", "ProgressMonitor::removeText",
534 "StgDirEntry::SetSize", "UCBStorage::CopyStorageElement_Impl"
535 "OutputDevice::ImplDrawPolyPolygon", "OutputDevice::ImplDrawPolyPolygon",
536 "ImplListBox::InsertEntry", "Edit::dispose",
537 "ViewContact::deleteAllVOCs", "SfxViewFrame::ReleaseObjectShell_Impl",
538 "SfxViewFrame::SwitchToViewShell_Impl", "OfaSmartTagOptionsTabPage::ClearListBox",
539 "OfaSmartTagOptionsTabPage::FillItemSet", "doc_destroy", "lo_destroy",
540 "callColumnFormatDialog"))
541 return;
542 // very dodgy
543 if (any_equal(name, "UCBStorage::OpenStorage_Impl", "SdTransferable::GetData"))
544 return;
545 // complicated ownership
546 if (any_equal(name, "ParseCMAP", "OpenGLSalBitmap::CreateTexture", "X11SalGraphicsImpl::drawAlphaBitmap"
547 "SvEmbedTransferHelper::GetData", "ORoadmap::dispose",
548 "BrowseBox::SetMode", "ExportDialog::GetFilterData", "disposeComVariablesForBasic",
549 "ImpEditEngine::ImpRemoveParagraph", "FactoryImpl::createAdapter",
550 "SfxStateCache::SetVisibleState", "SfxBindings::QueryState",
551 "ViewContact::deleteAllVOCs", "SvxMSDffManager::ProcessObj", "SvEmbedTransferHelper::GetData",
552 "SvXMLExportPropertyMapper::Filter_", "SdXMLExport::ImpGetOrCreatePageMasterInfo",
553 "SfxDocumentDescPage::FillItemSet", "SfxCustomPropertiesPage::FillItemSet",
554 "SfxCmisPropertiesPage::FillItemSet", "SfxObjectShell::DoSaveCompleted",
555 "SfxObjectShell::DoSave_Impl", "SfxObjectShell::PreDoSaveAs_Impl", "SfxObjectShell::Save_Impl",
556 "SfxFrame::DoClose_Impl", "SfxBaseModel::load",
557 "SdrTextObj::TakeTextRect", "SdrTableObj::TakeTextRect", "SdrObjCustomShape::TakeTextRect",
558 "CellProperties::ItemSetChanged", "CellProperties::ItemChange",
559 "TableLayouter::SetBorder", "TableLayouter::ClearBorderLayout",
560 "ImpXPolygon::Resize", "SvxTextEditSourceImpl::GetBackgroundTextForwarder",
561 "Svx3DSceneObject::setPropertyValueImpl", "lcl_RemoveTextEditOutlinerViews",
562 "SdrObjEditView::SdrEndTextEdit", "SvxShape::_setPropertyValue",
563 "AccessibleShape::Init", "AccessibleCell::Init",
564 "SdrTableRtfExporter::WriteCell", "GalleryItem::_getPropertyValues",
565 "VMLExport::StartShape", "DrawingML::WriteText",
566 "MtfTools::DrawText", "FormulaTokenArray::RewriteMissing",
567 "OSQLParseNode::negateSearchCondition", "OSQLParseNodesContainer::clearAndDelete",
568 "SdFilter::GetLibrarySymbol", "SdPage::SetObjText",
569 "SdDrawDocument::InsertBookmarkAsPage", "SdDrawDocument::InsertBookmarkAsObject",
570 "SdDrawDocument::RemoveUnnecessaryMasterPages",
571 "ScTable::CopyConditionalFormat", "ScTable::ValidQuery",
572 "ScTable::SetOptimalHeight", "ScTable::SetOptimalHeightOnly", "ScCompiler::CompileString",
573 "ScProgress::DeleteInterpretProgress", "ScInterpreter::ScBase",
574 "UCBStorage::CopyStorageElement_Impl", "X11SalGraphicsImpl::drawAlphaBitmap",
575 "MasterPagesSelector::ClearPageSet", "View::IsPresObjSelected",
576 "SdDrawPagesAccess::remove", "SdMasterPagesAccess::remove", "View::InsertData",
577 "RemoteServer::execute", "Implementation::ReleaseOutlinerView",
578 "SwFormat::CopyAttrs", "FinitCore", "SwCursorShell::MoveFieldType", "SwExtraPainter::PaintExtra",
579 "SwMarginPortion::AdjustRight", "SwPaintQueue::Repaint", "SwTOXMgr::UpdateOrInsertTOX",
580 "SwBaseShell::Execute", "WW8Export::WriteSdrTextObj"))
581 return;
582 // complicated delete
583 if (name == "X11SalObject::CreateObject")
584 return;
587 report(
588 DiagnosticsEngine::Warning,
589 "call to delete on a var, should be using std::unique_ptr",
590 deleteExpr->getBeginLoc())
591 << deleteExpr->getSourceRange();
592 report(
593 DiagnosticsEngine::Note,
594 "var is here",
595 varDecl->getBeginLoc())
596 << varDecl->getSourceRange();
600 * Look for DELETEZ expressions.
602 void UseUniquePtr::CheckParenExpr(const FunctionDecl* functionDecl, const ParenExpr* parenExpr)
604 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
605 if (!binaryOp || binaryOp->getOpcode() != BO_Comma)
606 return;
607 auto deleteExpr = dyn_cast<CXXDeleteExpr>(binaryOp->getLHS());
608 if (!deleteExpr)
609 return;
610 CheckDeleteExpr(functionDecl, deleteExpr);
613 void UseUniquePtr::CheckLoopDelete(const FunctionDecl* functionDecl, const Stmt* bodyStmt)
615 if (auto deleteExpr = dyn_cast<CXXDeleteExpr>(bodyStmt))
616 CheckLoopDelete(functionDecl, deleteExpr);
617 else if (auto compoundStmt = dyn_cast<CompoundStmt>(bodyStmt))
619 for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i)
621 if (auto deleteExpr = dyn_cast<CXXDeleteExpr>(*i))
622 CheckLoopDelete(functionDecl, deleteExpr);
627 void UseUniquePtr::CheckLoopDelete(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr)
629 const MemberExpr* memberExpr = nullptr;
630 const VarDecl* varDecl = nullptr;
631 const Expr* subExpr = deleteExpr->getArgument();
632 // drill down looking for a MemberExpr
633 for (;;)
635 subExpr = subExpr->IgnoreParens()->IgnoreImplicit();
636 if ((memberExpr = dyn_cast<MemberExpr>(subExpr)))
638 if (memberExpr->getMemberDecl()->getName() == "first" || memberExpr->getMemberDecl()->getName() == "second")
640 subExpr = memberExpr->getBase();
641 memberExpr = nullptr;
643 else
644 break;
646 else if (auto declRefExpr = dyn_cast<DeclRefExpr>(subExpr))
648 if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())))
649 break;
651 else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(subExpr))
652 subExpr = arraySubscriptExpr->getBase();
653 else if (auto cxxOperatorCallExpr = dyn_cast<CXXOperatorCallExpr>(subExpr))
655 // look for deletes of an iterator object where the iterator is over a member field
656 if (auto declRefExpr = dyn_cast<DeclRefExpr>(cxxOperatorCallExpr->getArg(0)->IgnoreImplicit()))
658 if (auto iterVarDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()))
660 auto init = iterVarDecl->getInit();
661 if (init)
663 init = init->IgnoreImplicit();
664 if (auto x = dyn_cast<CXXConstructExpr>(init))
665 if (x->getNumArgs() == 1
666 || (x->getNumArgs() >= 2 && isa<CXXDefaultArgExpr>(x->getArg(1))))
668 init = x->getArg(0)->IgnoreImplicit();
670 if (auto x = dyn_cast<CXXMemberCallExpr>(init))
671 init = x->getImplicitObjectArgument()->IgnoreParenImpCasts();
672 if ((memberExpr = dyn_cast<MemberExpr>(init)))
673 break;
674 // look for deletes of an iterator object where the iterator is over a var
675 if (auto declRefExpr2 = dyn_cast<DeclRefExpr>(init))
677 if ((varDecl = dyn_cast<VarDecl>(declRefExpr2->getDecl())))
678 break;
683 // look for deletes like "delete m_pField[0]"
684 if (cxxOperatorCallExpr->getOperator() == OO_Subscript)
686 subExpr = cxxOperatorCallExpr->getArg(0)->IgnoreImplicit();
687 if ((memberExpr = dyn_cast<MemberExpr>(subExpr)))
688 break;
689 if (auto declRefExpr = dyn_cast<DeclRefExpr>(subExpr))
691 if ((varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl())))
692 break;
695 break;
697 else
698 break;
701 if (memberExpr)
703 // OStorage_Impl::Commit very complicated ownership passing going on
704 if (fn == SRCDIR "/package/source/xstor/xstorage.cxx")
705 return;
706 // complicated
707 if (fn == SRCDIR "/vcl/source/gdi/print.cxx")
708 return;
709 // linked list
710 if (fn == SRCDIR "/basic/source/runtime/runtime.cxx")
711 return;
712 // complicated
713 if (fn == SRCDIR "/sw/source/core/bastyp/swcache.cxx")
714 return;
716 CheckMemberDeleteExpr(functionDecl, deleteExpr, memberExpr, "rather manage this member with std::some_container<std::unique_ptr<T>>");
719 if (varDecl)
721 // ignore if the value for the var comes from somewhere else
722 if (varDecl->hasInit() && isa<ExplicitCastExpr>(varDecl->getInit()->IgnoreImpCasts()))
723 return;
725 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/qa/"))
726 return;
727 // linked list
728 if (fn == SRCDIR "/tools/source/generic/config.cxx")
729 return;
730 // linked lists
731 if (fn == SRCDIR "/vcl/source/gdi/regband.cxx")
732 return;
733 // linked lists
734 if (fn == SRCDIR "/vcl/source/gdi/regionband.cxx")
735 return;
736 // linked list
737 if (fn == SRCDIR "/vcl/source/gdi/octree.cxx")
738 return;
739 // linked list
740 if (fn == SRCDIR "/vcl/source/app/scheduler.cxx")
741 return;
742 // linked list
743 if (fn == SRCDIR "/vcl/source/filter/wmf/wmfwr.cxx")
744 return;
745 // linked list
746 if (fn == SRCDIR "/vcl/source/filter/graphicfilter.cxx")
747 return;
748 // valid code
749 if (fn == SRCDIR "/vcl/source/app/salvtables.cxx")
750 return;
751 // undo code is tricky
752 if (fn == SRCDIR "/svl/source/undo/undo.cxx")
753 return;
754 // subclass that messes with parent class in constructor/destructor, yuck
755 if (fn == SRCDIR "/svtools/source/contnr/imivctl1.cxx")
756 return;
757 // SQLParseNode
758 if (fn == SRCDIR "/connectivity/source/parse/sqlnode.cxx")
759 return;
760 // the whole svx model/contact/view thing confuses me
761 if (fn == SRCDIR "/svx/source/sdr/contact/viewcontact.cxx")
762 return;
763 if (fn == SRCDIR "/svx/source/sdr/contact/objectcontact.cxx")
764 return;
765 // no idea
766 if (fn == SRCDIR "/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.cxx")
767 return;
768 // SdrUndo stuff
769 if (fn == SRCDIR "/svx/source/svdraw/svdundo.cxx")
770 return;
771 // TODO the lazydelete stuff should probably just be ripped out altogether now that we have VclPtr
772 if (fn == SRCDIR "/vcl/source/helper/lazydelete.cxx")
773 return;
774 // linked list
775 if (fn == SRCDIR "/filter/source/graphicfilter/idxf/dxfblkrd.cxx")
776 return;
777 if (fn == SRCDIR "/filter/source/graphicfilter/idxf/dxftblrd.cxx")
778 return;
779 if (fn == SRCDIR "/lotuswordpro/source/filter/utlist.cxx")
780 return;
781 if (fn == SRCDIR "/lotuswordpro/source/filter/lwpfribptr.cxx")
782 return;
783 // valid
784 if (fn == SRCDIR "/sd/source/ui/sidebar/MasterPagesSelector.cxx")
785 return;
786 // linked list
787 if (fn == SRCDIR "/sd/source/filter/ppt/pptatom.cxx")
788 return;
789 // linked list
790 if (fn == SRCDIR "/sc/source/core/data/funcdesc.cxx")
791 return;
792 // linked list
793 if (fn == SRCDIR "/sw/source/core/crsr/crsrsh.cxx")
794 return;
795 // no idea
796 if (fn == SRCDIR "/sw/source/core/docnode/nodes.cxx")
797 return;
798 // linked list
799 if (fn == SRCDIR "/sw/source/core/unocore/unocrsr.cxx")
800 return;
801 // linked list
802 if (fn == SRCDIR "/filter/source/graphicfilter/idxf/dxfentrd.cxx")
803 return;
804 // linked list
805 if (fn == SRCDIR "/filter/source/graphicfilter/ios2met/ios2met.cxx")
806 return;
807 // sometimes owning, sometimes not
808 if (fn == SRCDIR "/sw/qa/core/Test-BigPtrArray.cxx")
809 return;
811 report(
812 DiagnosticsEngine::Warning,
813 "loopdelete: rather manage this var with std::some_container<std::unique_ptr<T>>",
814 deleteExpr->getBeginLoc())
815 << deleteExpr->getSourceRange();
816 report(
817 DiagnosticsEngine::Note,
818 "var is here",
819 varDecl->getBeginLoc())
820 << varDecl->getSourceRange();
824 void UseUniquePtr::CheckCXXForRangeStmt(const FunctionDecl* functionDecl, const CXXForRangeStmt* cxxForRangeStmt)
826 CXXDeleteExpr const * deleteExpr = nullptr;
827 if (auto compoundStmt = dyn_cast<CompoundStmt>(cxxForRangeStmt->getBody()))
829 for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i)
830 if ((deleteExpr = dyn_cast<CXXDeleteExpr>(*i)))
831 break;
833 else
834 deleteExpr = dyn_cast<CXXDeleteExpr>(cxxForRangeStmt->getBody());
835 if (!deleteExpr)
836 return;
838 // check for delete of member
839 if (auto memberExpr = dyn_cast<MemberExpr>(cxxForRangeStmt->getRangeInit()))
841 auto fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
842 if (!fieldDecl)
843 return;
845 // complicated
846 if (fn == SRCDIR "/vcl/source/gdi/print.cxx")
847 return;
848 // sometimes it's an owning field, sometimes not
849 if (fn == SRCDIR "/i18npool/source/localedata/localedata.cxx")
850 return;
852 CheckMemberDeleteExpr(functionDecl, deleteExpr, memberExpr, "rather manage this with std::some_container<std::unique_ptr<T>>");
855 // check for delete of var
856 if (auto declRefExpr = dyn_cast<DeclRefExpr>(cxxForRangeStmt->getRangeInit()->IgnoreParens()->IgnoreImplicit()))
858 auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
859 if (!varDecl)
860 return;
862 // don't feel like messing with this part of sfx2
863 if (fn == SRCDIR "/sfx2/source/control/msgpool.cxx")
864 return;
865 if (fn == SRCDIR "/sfx2/source/doc/doctemplates.cxx")
866 return;
867 // lex/yacc
868 if (fn == SRCDIR "/hwpfilter/source/grammar.cxx")
869 return;
870 if (fn == SRCDIR "/hwpfilter/source/formula.cxx")
871 return;
872 // no idea why, but ui tests crash afterwards in weird ways
873 if (fn == SRCDIR "/svtools/source/control/roadmap.cxx")
874 return;
875 // sometimes it owns it, sometimes it does not
876 if (fn == SRCDIR "/dbaccess/source/ui/misc/WCopyTable.cxx")
877 return;
878 // SfxPoolItem array
879 if (fn == SRCDIR "/dbaccess/source/ui/misc/UITools.cxx")
880 return;
881 // SfxPoolItem array
882 if (fn == SRCDIR "/sw/source/core/bastyp/init.cxx")
883 return;
884 // SfxPoolItem array
885 if (fn == SRCDIR "/reportdesign/source/ui/misc/UITools.cxx")
886 return;
887 // SfxPoolItem array
888 if (fn == SRCDIR "/reportdesign/source/ui/report/ReportController.cxx")
889 return;
890 // complicated
891 if (fn == SRCDIR "/svx/source/sdr/contact/viewcontact.cxx")
892 return;
893 if (fn == SRCDIR "/svx/source/sdr/contact/objectcontact.cxx")
894 return;
896 report(
897 DiagnosticsEngine::Warning,
898 "rather manage this var with std::some_container<std::unique_ptr<T>>",
899 deleteExpr->getBeginLoc())
900 << deleteExpr->getSourceRange();
901 report(
902 DiagnosticsEngine::Note,
903 "var is here",
904 varDecl->getBeginLoc())
905 << varDecl->getSourceRange();
909 void UseUniquePtr::CheckMemberDeleteExpr(const FunctionDecl* functionDecl, const CXXDeleteExpr* deleteExpr,
910 const MemberExpr* memberExpr, StringRef message)
912 // ignore union games
913 const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
914 if (!fieldDecl)
915 return;
916 TagDecl const * td = dyn_cast<TagDecl>(fieldDecl->getDeclContext());
917 if (td->isUnion())
918 return;
920 // ignore calling delete on someone else's field
921 if (auto methodDecl = dyn_cast<CXXMethodDecl>(functionDecl))
922 if (fieldDecl->getParent() != methodDecl->getParent() )
923 return;
925 if (ignoreLocation(fieldDecl))
926 return;
927 // to ignore things like the CPPUNIT macros
928 if (loplugin::hasPathnamePrefix(fn, WORKDIR "/"))
929 return;
930 // passes and stores pointers to member fields
931 if (fn == SRCDIR "/sot/source/sdstor/stgdir.hxx")
932 return;
933 // something platform-specific
934 if (fn == SRCDIR "/hwpfilter/source/htags.h")
935 return;
936 // passes pointers to member fields
937 if (fn == SRCDIR "/sd/inc/sdpptwrp.hxx")
938 return;
939 // @TODO intrusive linked-lists here, with some trickiness
940 if (fn == SRCDIR "/sw/source/filter/html/parcss1.hxx")
941 return;
942 // @TODO SwDoc has some weird ref-counting going on
943 if (fn == SRCDIR "/sw/inc/shellio.hxx")
944 return;
945 // @TODO it's sharing pointers with another class
946 if (fn == SRCDIR "/sc/inc/formulacell.hxx")
947 return;
948 // some weird stuff going on here around struct Entity
949 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sax/"))
950 return;
951 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/include/sax/"))
952 return;
953 // manipulation of tree structures ie. StgAvlNode, don't lend themselves to std::unique_ptr
954 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sot/"))
955 return;
956 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/include/sot/"))
957 return;
958 // the std::vector is being passed to another class
959 if (fn == SRCDIR "/sfx2/source/explorer/nochaos.cxx")
960 return;
961 auto tc = loplugin::TypeCheck(fieldDecl->getType());
962 // these sw::Ring based classes do not lend themselves to std::unique_ptr management
963 if (tc.Pointer().Class("SwNodeIndex").GlobalNamespace() || tc.Pointer().Class("SwShellTableCursor").GlobalNamespace()
964 || tc.Pointer().Class("SwBlockCursor").GlobalNamespace() || tc.Pointer().Class("SwVisibleCursor").GlobalNamespace()
965 || tc.Pointer().Class("SwShellCursor").GlobalNamespace())
966 return;
967 // there is a loop in ~ImplPrnQueueList deleting stuff on a global data structure
968 if (fn == SRCDIR "/vcl/inc/print.h")
969 return;
970 // painful linked list
971 if (fn == SRCDIR "/basic/source/inc/runtime.hxx")
972 return;
973 // not sure how the node management is working here
974 if (fn == SRCDIR "/i18npool/source/localedata/saxparser.cxx")
975 return;
976 // has a pointer that it only sometimes owns
977 if (fn == SRCDIR "/editeng/source/editeng/impedit.hxx")
978 return;
980 report(
981 DiagnosticsEngine::Warning,
982 message,
983 deleteExpr->getBeginLoc())
984 << deleteExpr->getSourceRange();
985 report(
986 DiagnosticsEngine::Note,
987 "member is here",
988 fieldDecl->getBeginLoc())
989 << fieldDecl->getSourceRange();
992 bool UseUniquePtr::TraverseFunctionDecl(FunctionDecl* functionDecl)
994 if (ignoreLocation(functionDecl))
995 return true;
997 auto oldCurrent = mpCurrentFunctionDecl;
998 mpCurrentFunctionDecl = functionDecl;
999 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
1000 mpCurrentFunctionDecl = oldCurrent;
1002 return ret;
1005 bool UseUniquePtr::TraverseCXXMethodDecl(CXXMethodDecl* methodDecl)
1007 if (ignoreLocation(methodDecl))
1008 return true;
1010 auto oldCurrent = mpCurrentFunctionDecl;
1011 mpCurrentFunctionDecl = methodDecl;
1012 bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(methodDecl);
1013 mpCurrentFunctionDecl = oldCurrent;
1015 return ret;
1018 bool UseUniquePtr::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* methodDecl)
1020 if (ignoreLocation(methodDecl))
1021 return true;
1023 auto oldCurrent = mpCurrentFunctionDecl;
1024 mpCurrentFunctionDecl = methodDecl;
1025 bool ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(methodDecl);
1026 mpCurrentFunctionDecl = oldCurrent;
1028 return ret;
1031 bool UseUniquePtr::TraverseCXXConstructorDecl(CXXConstructorDecl* methodDecl)
1033 if (ignoreLocation(methodDecl))
1034 return true;
1036 auto oldCurrent = mpCurrentFunctionDecl;
1037 mpCurrentFunctionDecl = methodDecl;
1038 bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(methodDecl);
1039 mpCurrentFunctionDecl = oldCurrent;
1041 return ret;
1044 bool UseUniquePtr::TraverseCXXConversionDecl(CXXConversionDecl* methodDecl)
1046 if (ignoreLocation(methodDecl))
1047 return true;
1049 auto oldCurrent = mpCurrentFunctionDecl;
1050 mpCurrentFunctionDecl = methodDecl;
1051 bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(methodDecl);
1052 mpCurrentFunctionDecl = oldCurrent;
1054 return ret;
1057 bool UseUniquePtr::TraverseCXXDestructorDecl(CXXDestructorDecl* methodDecl)
1059 if (ignoreLocation(methodDecl))
1060 return true;
1062 auto oldCurrent = mpCurrentFunctionDecl;
1063 mpCurrentFunctionDecl = methodDecl;
1064 bool ret = RecursiveASTVisitor::TraverseCXXDestructorDecl(methodDecl);
1065 mpCurrentFunctionDecl = oldCurrent;
1067 return ret;
1070 bool UseUniquePtr::TraverseConstructorInitializer(CXXCtorInitializer * ctorInit)
1072 if (!ctorInit->getSourceLocation().isValid() || ignoreLocation(ctorInit->getSourceLocation()))
1073 return true;
1074 if (!ctorInit->getMember())
1075 return true;
1076 if (!loplugin::TypeCheck(ctorInit->getMember()->getType()).Class("unique_ptr").StdNamespace())
1077 return true;
1078 auto constructExpr = dyn_cast_or_null<CXXConstructExpr>(ctorInit->getInit());
1079 if (!constructExpr || constructExpr->getNumArgs() == 0)
1080 return true;
1081 auto init = constructExpr->getArg(0)->IgnoreImpCasts();
1082 if (!isa<DeclRefExpr>(init))
1083 return true;
1085 StringRef fn = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(ctorInit->getSourceLocation()));
1086 // cannot change URE
1087 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/cppu/source/helper/purpenv/helper_purpenv_Environment.cxx"))
1088 return true;
1090 report(
1091 DiagnosticsEngine::Warning,
1092 "should be passing via std::unique_ptr param",
1093 ctorInit->getSourceLocation())
1094 << ctorInit->getSourceRange();
1095 return RecursiveASTVisitor<UseUniquePtr>::TraverseConstructorInitializer(ctorInit);
1098 // Only checks for calls to delete on a pointer param
1099 bool UseUniquePtr::VisitCXXDeleteExpr(const CXXDeleteExpr* deleteExpr)
1101 if (!mpCurrentFunctionDecl)
1102 return true;
1103 if (ignoreLocation(mpCurrentFunctionDecl))
1104 return true;
1105 if (isInUnoIncludeFile(mpCurrentFunctionDecl->getCanonicalDecl()->getBeginLoc()))
1106 return true;
1107 auto declRefExpr = dyn_cast<DeclRefExpr>(deleteExpr->getArgument()->IgnoreParenImpCasts()->IgnoreImplicit());
1108 if (!declRefExpr)
1109 return true;
1110 if (auto parmVarDecl = dyn_cast<ParmVarDecl>(declRefExpr->getDecl()))
1111 CheckDeleteParmVar(deleteExpr, parmVarDecl);
1112 else if (auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()))
1113 CheckDeleteLocalVar(mpCurrentFunctionDecl, deleteExpr, varDecl);
1114 return true;
1117 void UseUniquePtr::CheckDeleteParmVar(const CXXDeleteExpr* deleteExpr, const ParmVarDecl* )
1119 if (mpCurrentFunctionDecl->getIdentifier())
1121 auto name = mpCurrentFunctionDecl->getName();
1122 if (name == "delete_IncludesCollection" || name == "convertName"
1123 || name == "createNamedType"
1124 || name == "typelib_typedescriptionreference_release" || name == "deleteExceptions"
1125 || name == "uno_threadpool_destroy"
1126 || name == "AddRanges_Impl"
1127 || name == "DestroySalInstance"
1128 || name == "ImplHandleUserEvent"
1129 || name == "releaseDecimalPtr" // TODO, basic
1130 || name == "replaceAndReset" // TODO, connectivity
1131 || name == "intrusive_ptr_release"
1132 || name == "FreeParaList"
1133 || name == "DeleteSdrUndoAction" // TODO, sc
1134 || name == "lcl_MergeGCBox" || name == "lcl_MergeGCLine" || name == "lcl_DelHFFormat")
1135 return;
1137 if (auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(mpCurrentFunctionDecl))
1139 auto parentName = cxxMethodDecl->getParent()->getName();
1140 // include/o3tl/deleter.hxx
1141 if (parentName == "default_delete")
1142 return;
1143 // TODO Bitmap::ReleaseAccess
1144 // Tricky because it reverberates through other code and requires that BitmapWriteAccess move into /include again
1145 if (parentName == "Bitmap")
1146 return;
1147 // TODO virtual ones are much trickier, leave for later
1148 if (cxxMethodDecl->isVirtual())
1149 return;
1150 // sw/inc/unobaseclass.hxx holds SolarMutex while deleting
1151 if (parentName == "UnoImplPtrDeleter")
1152 return;
1155 // StgAvlNode::Remove
1156 if (fn == SRCDIR "/sot/source/sdstor/stgavl.cxx")
1157 return;
1158 // SfxItemPool::ReleaseDefaults and SfxItemPool::Free
1159 if (fn == SRCDIR "/svl/source/items/itempool.cxx")
1160 return;
1161 // SwContourCache
1162 if (fn == SRCDIR "/sw/source/core/text/txtfly.cxx")
1163 return;
1164 // too messy to cope with the SQL parser
1165 if (fn == SRCDIR "/connectivity/source/parse/sqlnode.cxx")
1166 return;
1167 // I can't figure out the ownership of the SfxMedium in the call site(s)
1168 if (fn == SRCDIR "/sfx2/source/doc/sfxbasemodel.cxx")
1169 return;
1170 // pointer passed via IMPL_LINK
1171 if (fn == SRCDIR "/sfx2/source/control/dispatch.cxx")
1172 return;
1173 // NavigatorTreeModel::Remove
1174 if (fn == SRCDIR "/svx/source/form/navigatortreemodel.cxx")
1175 return;
1176 // SdrModel::AddUndo
1177 if (fn == SRCDIR "/svx/source/svdraw/svdmodel.cxx")
1178 return;
1179 // undo callback
1180 if (fn == SRCDIR "/basctl/source/basicide/baside3.cxx")
1181 return;
1182 // ActualizeProgress::TimeoutHdl
1183 if (fn == SRCDIR "/cui/source/dialogs/cuigaldlg.cxx")
1184 return;
1185 // ToolbarSaveInData::RemoveToolbar
1186 if (fn == SRCDIR "/cui/source/customize/cfg.cxx")
1187 return;
1188 // OStorage_Impl::RemoveElement very complicated ownership passing going on
1189 if (fn == SRCDIR "/package/source/xstor/xstorage.cxx")
1190 return;
1191 // actually held via shared_ptr, uses protected deleter object
1192 if (fn == SRCDIR "/sd/source/ui/framework/tools/FrameworkHelper.cxx")
1193 return;
1194 // actually held via shared_ptr, uses protected deleter object
1195 if (fn == SRCDIR "/sd/source/ui/presenter/CanvasUpdateRequester.cxx")
1196 return;
1197 // actually held via shared_ptr, uses protected deleter object
1198 if (fn == SRCDIR "/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx")
1199 return;
1200 // actually held via shared_ptr, uses protected deleter object
1201 if (fn == SRCDIR "/sd/source/ui/sidebar/MasterPageContainer.cxx")
1202 return;
1203 // actually held via shared_ptr, uses protected deleter object
1204 if (fn == SRCDIR "/sd/source/ui/tools/TimerBasedTaskExecution.cxx")
1205 return;
1206 // actually held via shared_ptr, uses protected deleter object
1207 if (fn == SRCDIR "/sd/source/ui/view/ViewShellImplementation.cxx")
1208 return;
1209 // ScBroadcastAreaSlot::StartListeningArea manual ref-counting of ScBroadcastArea
1210 if (fn == SRCDIR "/sc/source/core/data/bcaslot.cxx")
1211 return;
1212 // ScDrawLayer::AddCalcUndo undo stuff
1213 if (fn == SRCDIR "/sc/source/core/data/drwlayer.cxx")
1214 return;
1215 // ScTable::SetFormulaCell
1216 if (fn == SRCDIR "/sc/source/core/data/table2.cxx")
1217 return;
1218 // ScDocument::SetFormulaCell
1219 if (fn == SRCDIR "/sc/source/core/data/documen2.cxx")
1220 return;
1221 // RemoveEditAttribsHandler, stored in mdds block
1222 if (fn == SRCDIR "/sc/source/core/data/column2.cxx")
1223 return;
1224 // just turns into a mess
1225 if (fn == SRCDIR "/sc/source/ui/Accessibility/AccessibleDocument.cxx")
1226 return;
1227 // SwCache::DeleteObj, linked list
1228 if (fn == SRCDIR "/sw/source/core/bastyp/swcache.cxx")
1229 return;
1230 // SAXEventKeeperImpl::smashBufferNode
1231 if (fn == SRCDIR "/xmlsecurity/source/framework/saxeventkeeperimpl.cxx")
1232 return;
1233 // SwDoc::DeleteExtTextInput
1234 if (fn == SRCDIR "/sw/source/core/doc/extinput.cxx")
1235 return;
1236 // SwDoc::DelSectionFormat
1237 if (fn == SRCDIR "/sw/source/core/docnode/ndsect.cxx")
1238 return;
1239 // SwFrame::DestroyFrame
1240 if (fn == SRCDIR "/sw/source/core/layout/ssfrm.cxx")
1241 return;
1242 // SwGluePortion::Join
1243 if (fn == SRCDIR "/sw/source/core/text/porglue.cxx")
1244 return;
1245 // SwDoc::DelFrameFormat
1246 if (fn == SRCDIR "/sw/source/core/doc/docfmt.cxx")
1247 return;
1248 // SwTextAttr::Destroy
1249 if (fn == SRCDIR "/sw/source/core/txtnode/txatbase.cxx")
1250 return;
1251 // IMPL_LINK( SwDoc, AddDrawUndo, SdrUndoAction *, pUndo, void )
1252 if (fn == SRCDIR "/sw/source/core/undo/undraw.cxx")
1253 return;
1254 // SwHTMLParser::EndAttr
1255 if (fn == SRCDIR "/sw/source/filter/html/swhtml.cxx")
1256 return;
1257 // SwGlossaryHdl::Expand sometimes the pointer is owned, sometimes it is not
1258 if (fn == SRCDIR "/sw/source/uibase/dochdl/gloshdl.cxx")
1259 return;
1260 // SwWrtShell::Insert only owned sometimes
1261 if (fn == SRCDIR "/sw/source/uibase/wrtsh/wrtsh1.cxx")
1262 return;
1263 // NodeArrayDeleter
1264 if (fn == SRCDIR "/unoxml/source/rdf/librdf_repository.cxx")
1265 return;
1266 // SmCursor::LineToList ran out of enthusiasm to rework the node handling
1267 if (fn == SRCDIR "/starmath/source/cursor.cxx")
1268 return;
1269 // XMLEventOASISTransformerContext::FlushEventMap
1270 if (fn == SRCDIR "/xmloff/source/transform/EventOASISTContext.cxx")
1271 return;
1272 // XMLEventOOoTransformerContext::FlushEventMap
1273 if (fn == SRCDIR "/xmloff/source/transform/EventOOoTContext.cxx")
1274 return;
1275 // SbiProcDef::Match
1276 if (fn == SRCDIR "/basic/source/comp/symtbl.cxx")
1277 return;
1280 Sometimes we can pass the param as std::unique_ptr<T>& or std::unique_ptr, sometimes the method
1281 just needs to be inlined, which normally exposes more simplification.
1283 report(
1284 DiagnosticsEngine::Warning,
1285 "calling delete on a pointer param, should be either allowlisted or simplified",
1286 deleteExpr->getBeginLoc())
1287 << deleteExpr->getSourceRange();
1291 loplugin::Plugin::Registration< UseUniquePtr > X("useuniqueptr", false);
1295 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */