1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
20 Find destructors that only contain a single call to delete of a field. In which
21 case that field should really be managed by unique_ptr.
27 public RecursiveASTVisitor
<UseUniquePtr
>, public loplugin::Plugin
30 explicit UseUniquePtr(loplugin::InstantiationData
const & data
):
33 virtual void run() override
35 std::string
fn(compiler
.getSourceManager()
36 .getFileEntryForID(compiler
.getSourceManager().getMainFileID())
38 loplugin::normalizeDotDotInFilePath(fn
);
39 // can't change these because we pass them down to the SfxItemPool stuff
40 if (fn
== SRCDIR
"/sc/source/core/data/docpool.cxx")
42 // this just too clever for me
43 if (fn
== SRCDIR
"/sc/source/core/tool/chgtrack.cxx")
46 if (fn
== SRCDIR
"/pyuno/source/module/pyuno_runtime.cxx")
48 // m_pExampleSet here is very badly managed. sometimes it is owning, sometimes not,
49 // and the logic depends on overriding methods.
50 if (fn
== SRCDIR
"/sfx2/source/dialog/tabdlg.cxx")
52 // pLongArr is being deleted here because we temporarily overwrite a pointer to someone else's buffer, with a pointer
54 if (fn
== SRCDIR
"/editeng/source/misc/txtrange.cxx")
56 // can't use std::set<std::unique_ptr<>> until C++14
57 if (fn
== SRCDIR
"/editeng/source/misc/svxacorr.cxx")
60 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
63 bool VisitCXXMethodDecl(const CXXMethodDecl
* );
64 bool VisitCompoundStmt(const CompoundStmt
* );
66 void CheckCompoundStmt(const CXXMethodDecl
*, const CompoundStmt
* );
67 void CheckForUnconditionalDelete(const CXXMethodDecl
*, const CompoundStmt
* );
68 void CheckForSimpleDelete(const CXXMethodDecl
*, const CompoundStmt
* );
69 void CheckRangedLoopDelete(const CXXMethodDecl
*, const CXXForRangeStmt
* );
70 void CheckLoopDelete(const CXXMethodDecl
*, const Stmt
* );
71 void CheckLoopDelete(const CXXMethodDecl
*, const CXXDeleteExpr
* );
72 void CheckDeleteExpr(const CXXMethodDecl
*, const CXXDeleteExpr
*);
73 void CheckParenExpr(const CXXMethodDecl
*, const ParenExpr
*);
74 void CheckDeleteExpr(const CXXMethodDecl
*, const CXXDeleteExpr
*,
75 const MemberExpr
*, StringRef message
);
78 bool UseUniquePtr::VisitCXXMethodDecl(const CXXMethodDecl
* methodDecl
)
80 if (ignoreLocation(methodDecl
))
82 if (isInUnoIncludeFile(methodDecl
))
85 const CompoundStmt
* compoundStmt
= dyn_cast_or_null
< CompoundStmt
>( methodDecl
->getBody() );
86 if (!compoundStmt
|| compoundStmt
->size() == 0)
89 CheckCompoundStmt(methodDecl
, compoundStmt
);
94 void UseUniquePtr::CheckCompoundStmt(const CXXMethodDecl
* methodDecl
, const CompoundStmt
* compoundStmt
)
96 CheckForSimpleDelete(methodDecl
, compoundStmt
);
98 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
100 if (auto cxxForRangeStmt
= dyn_cast
<CXXForRangeStmt
>(*i
))
101 CheckRangedLoopDelete(methodDecl
, cxxForRangeStmt
);
102 else if (auto forStmt
= dyn_cast
<ForStmt
>(*i
))
103 CheckLoopDelete(methodDecl
, forStmt
->getBody());
104 else if (auto whileStmt
= dyn_cast
<WhileStmt
>(*i
))
105 CheckLoopDelete(methodDecl
, whileStmt
->getBody());
106 // check for unconditional inner compound statements
107 else if (auto innerCompoundStmt
= dyn_cast
<CompoundStmt
>(*i
))
108 CheckCompoundStmt(methodDecl
, innerCompoundStmt
);
113 * check for simple call to delete in a destructor i.e. direct unconditional call, or if-guarded call
115 void UseUniquePtr::CheckForSimpleDelete(const CXXMethodDecl
* methodDecl
, const CompoundStmt
* compoundStmt
)
117 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
119 auto deleteExpr
= dyn_cast
<CXXDeleteExpr
>(*i
);
122 CheckDeleteExpr(methodDecl
, deleteExpr
);
125 auto parenExpr
= dyn_cast
<ParenExpr
>(*i
);
128 CheckParenExpr(methodDecl
, parenExpr
);
131 // Check for conditional deletes like:
132 // if (m_pField != nullptr) delete m_pField;
133 auto ifStmt
= dyn_cast
<IfStmt
>(*i
);
136 auto cond
= ifStmt
->getCond()->IgnoreImpCasts();
137 if (auto ifCondMemberExpr
= dyn_cast
<MemberExpr
>(cond
))
139 // ignore "if (bMine)"
140 if (!loplugin::TypeCheck(ifCondMemberExpr
->getType()).Pointer())
144 else if (auto binaryOp
= dyn_cast
<BinaryOperator
>(cond
))
146 if (!isa
<MemberExpr
>(binaryOp
->getLHS()->IgnoreImpCasts()))
148 if (!isa
<CXXNullPtrLiteralExpr
>(binaryOp
->getRHS()->IgnoreImpCasts()))
152 else // ignore anything more complicated
155 deleteExpr
= dyn_cast
<CXXDeleteExpr
>(ifStmt
->getThen());
158 CheckDeleteExpr(methodDecl
, deleteExpr
);
162 parenExpr
= dyn_cast
<ParenExpr
>(ifStmt
->getThen());
165 CheckParenExpr(methodDecl
, parenExpr
);
169 auto ifThenCompoundStmt
= dyn_cast
<CompoundStmt
>(ifStmt
->getThen());
170 if (!ifThenCompoundStmt
)
172 for (auto j
= ifThenCompoundStmt
->body_begin(); j
!= ifThenCompoundStmt
->body_end(); ++j
)
174 auto ifDeleteExpr
= dyn_cast
<CXXDeleteExpr
>(*j
);
176 CheckDeleteExpr(methodDecl
, ifDeleteExpr
);
177 ParenExpr
const * parenExpr
= dyn_cast
<ParenExpr
>(*i
);
179 CheckParenExpr(methodDecl
, parenExpr
);
185 * Check the delete expression in a destructor.
187 void UseUniquePtr::CheckDeleteExpr(const CXXMethodDecl
* methodDecl
, const CXXDeleteExpr
* deleteExpr
)
189 const ImplicitCastExpr
* castExpr
= dyn_cast
<ImplicitCastExpr
>(deleteExpr
->getArgument());
192 const MemberExpr
* memberExpr
= dyn_cast
<MemberExpr
>(castExpr
->getSubExpr());
195 CheckDeleteExpr(methodDecl
, deleteExpr
, memberExpr
,
196 "unconditional call to delete on a member, should be using std::unique_ptr");
200 * Look for DELETEZ expressions.
202 void UseUniquePtr::CheckParenExpr(const CXXMethodDecl
* methodDecl
, const ParenExpr
* parenExpr
)
204 auto binaryOp
= dyn_cast
<BinaryOperator
>(parenExpr
->getSubExpr());
205 if (!binaryOp
|| binaryOp
->getOpcode() != BO_Comma
)
207 auto deleteExpr
= dyn_cast
<CXXDeleteExpr
>(binaryOp
->getLHS());
210 CheckDeleteExpr(methodDecl
, deleteExpr
);
214 * Check the delete expression in a destructor.
216 void UseUniquePtr::CheckDeleteExpr(const CXXMethodDecl
* methodDecl
, const CXXDeleteExpr
* deleteExpr
,
217 const MemberExpr
* memberExpr
, StringRef message
)
219 // ignore union games
220 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(memberExpr
->getMemberDecl());
223 TagDecl
const * td
= dyn_cast
<TagDecl
>(fieldDecl
->getDeclContext());
227 // ignore calling delete on someone else's field
228 if (fieldDecl
->getParent() != methodDecl
->getParent() )
231 if (ignoreLocation(fieldDecl
))
233 // to ignore things like the CPPUNIT macros
234 StringRef aFileName
= compiler
.getSourceManager().getFilename(compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocStart()));
235 if (loplugin::hasPathnamePrefix(aFileName
, WORKDIR
"/"))
237 // passes and stores pointers to member fields
238 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/sot/source/sdstor/stgdir.hxx"))
240 // something platform-specific
241 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/hwpfilter/source/htags.h"))
243 // passes pointers to member fields
244 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/sd/inc/sdpptwrp.hxx"))
246 // @TODO intrusive linked-lists here, with some trickiness
247 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/sw/source/filter/html/parcss1.hxx"))
249 // @TODO SwDoc has some weird ref-counting going on
250 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/sw/inc/shellio.hxx"))
252 // @TODO it's sharing pointers with another class
253 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/sc/inc/formulacell.hxx"))
255 // some weird stuff going on here around struct Entity
256 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/sax/"))
258 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/include/sax/"))
260 // manipulation of tree structures ie. StgAvlNode, don't lend themselves to std::unique_ptr
261 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/sot/"))
263 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/include/sot/"))
265 // the std::vector is being passed to another class
266 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/sfx2/source/explorer/nochaos.cxx"))
268 // ignore std::map and std::unordered_map, MSVC 2015 has problems with mixing these with std::unique_ptr
269 auto tc
= loplugin::TypeCheck(fieldDecl
->getType());
270 if (tc
.Class("map").StdNamespace() || tc
.Class("unordered_map").StdNamespace())
272 // there is a loop in ~ImplPrnQueueList deleting stuff on a global data structure
273 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/vcl/inc/print.h"))
275 // painful linked list
276 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/basic/source/inc/runtime.hxx"))
278 // not sure how the node management is working here
279 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/i18npool/source/localedata/saxparser.cxx"))
281 // has a pointer that it only sometimes owns
282 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/editeng/source/editeng/impedit.hxx"))
286 DiagnosticsEngine::Warning
,
288 deleteExpr
->getLocStart())
289 << deleteExpr
->getSourceRange();
291 DiagnosticsEngine::Note
,
293 fieldDecl
->getLocStart())
294 << fieldDecl
->getSourceRange();
297 void UseUniquePtr::CheckLoopDelete(const CXXMethodDecl
* methodDecl
, const Stmt
* bodyStmt
)
299 if (auto deleteExpr
= dyn_cast
<CXXDeleteExpr
>(bodyStmt
))
300 CheckLoopDelete(methodDecl
, deleteExpr
);
301 else if (auto compoundStmt
= dyn_cast
<CompoundStmt
>(bodyStmt
))
303 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
305 if (auto deleteExpr
= dyn_cast
<CXXDeleteExpr
>(*i
))
306 CheckLoopDelete(methodDecl
, deleteExpr
);
311 void UseUniquePtr::CheckLoopDelete(const CXXMethodDecl
* methodDecl
, const CXXDeleteExpr
* deleteExpr
)
313 const MemberExpr
* memberExpr
= nullptr;
314 const Expr
* subExpr
= deleteExpr
->getArgument();
317 subExpr
= subExpr
->IgnoreImpCasts();
318 if ((memberExpr
= dyn_cast
<MemberExpr
>(subExpr
)))
320 else if (auto arraySubscriptExpr
= dyn_cast
<ArraySubscriptExpr
>(subExpr
))
321 subExpr
= arraySubscriptExpr
->getBase();
322 else if (auto cxxOperatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(subExpr
))
324 if (cxxOperatorCallExpr
->getOperator() == OO_Subscript
)
326 memberExpr
= dyn_cast
<MemberExpr
>(cxxOperatorCallExpr
->getArg(0));
337 CheckDeleteExpr(methodDecl
, deleteExpr
, memberExpr
, "rather manage with std::some_container<std::unique_ptr<T>>");
340 void UseUniquePtr::CheckRangedLoopDelete(const CXXMethodDecl
* methodDecl
, const CXXForRangeStmt
* cxxForRangeStmt
)
342 CXXDeleteExpr
const * deleteExpr
= nullptr;
343 if (auto compoundStmt
= dyn_cast
<CompoundStmt
>(cxxForRangeStmt
->getBody()))
344 deleteExpr
= dyn_cast
<CXXDeleteExpr
>(*compoundStmt
->body_begin());
346 deleteExpr
= dyn_cast
<CXXDeleteExpr
>(cxxForRangeStmt
->getBody());
349 auto memberExpr
= dyn_cast
<MemberExpr
>(cxxForRangeStmt
->getRangeInit());
352 auto fieldDecl
= dyn_cast
<FieldDecl
>(memberExpr
->getMemberDecl());
356 CheckDeleteExpr(methodDecl
, deleteExpr
, memberExpr
, "rather manage with std::some_container<std::unique_ptr<T>>");
359 bool UseUniquePtr::VisitCompoundStmt(const CompoundStmt
* compoundStmt
)
361 if (ignoreLocation(compoundStmt
))
363 if (isInUnoIncludeFile(compoundStmt
->getLocStart()))
365 if (compoundStmt
->size() == 0) {
369 auto lastStmt
= compoundStmt
->body_back();
370 if (compoundStmt
->size() > 1) {
371 if (isa
<ReturnStmt
>(lastStmt
))
372 lastStmt
= *(++compoundStmt
->body_rbegin());
374 auto deleteExpr
= dyn_cast
<CXXDeleteExpr
>(lastStmt
);
375 if (deleteExpr
== nullptr) {
379 auto pCastExpr
= dyn_cast
<ImplicitCastExpr
>(deleteExpr
->getArgument());
382 auto declRefExpr
= dyn_cast
<DeclRefExpr
>(pCastExpr
->getSubExpr());
385 auto varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
388 if (!varDecl
->hasInit()
390 varDecl
->getInit()->IgnoreImplicit()->IgnoreParenImpCasts()))
392 // determine if the var is declared inside the same block as the delete.
393 // @TODO there should surely be a better way to do this
394 if (varDecl
->getLocStart() < compoundStmt
->getLocStart())
398 DiagnosticsEngine::Warning
,
399 "deleting a local variable at the end of a block, is a sure sign it should be using std::unique_ptr for that var",
400 deleteExpr
->getLocStart())
401 << deleteExpr
->getSourceRange();
403 DiagnosticsEngine::Note
,
405 varDecl
->getLocStart())
406 << varDecl
->getSourceRange();
410 loplugin::Plugin::Registration
< UseUniquePtr
> X("useuniqueptr", false);
414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */