Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / useuniqueptr.cxx
blobf09701c03ca680bfe450e3dc86ccef9c563b9d00
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 "plugin.hxx"
17 #include "check.hxx"
19 /**
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.
24 namespace {
26 class UseUniquePtr:
27 public RecursiveASTVisitor<UseUniquePtr>, public loplugin::Plugin
29 public:
30 explicit UseUniquePtr(loplugin::InstantiationData const & data):
31 Plugin(data) {}
33 virtual void run() override
35 std::string fn(compiler.getSourceManager()
36 .getFileEntryForID(compiler.getSourceManager().getMainFileID())
37 ->getName());
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")
41 return;
42 // this just too clever for me
43 if (fn == SRCDIR "/sc/source/core/tool/chgtrack.cxx")
44 return;
45 // too clever
46 if (fn == SRCDIR "/pyuno/source/module/pyuno_runtime.cxx")
47 return;
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")
51 return;
52 // pLongArr is being deleted here because we temporarily overwrite a pointer to someone else's buffer, with a pointer
53 // to our own buffer
54 if (fn == SRCDIR "/editeng/source/misc/txtrange.cxx")
55 return;
56 // can't use std::set<std::unique_ptr<>> until C++14
57 if (fn == SRCDIR "/editeng/source/misc/svxacorr.cxx")
58 return;
60 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
63 bool VisitCXXMethodDecl(const CXXMethodDecl* );
64 bool VisitCompoundStmt(const CompoundStmt* );
65 private:
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))
81 return true;
82 if (isInUnoIncludeFile(methodDecl))
83 return true;
85 const CompoundStmt* compoundStmt = dyn_cast_or_null< CompoundStmt >( methodDecl->getBody() );
86 if (!compoundStmt || compoundStmt->size() == 0)
87 return true;
89 CheckCompoundStmt(methodDecl, compoundStmt);
91 return true;
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);
120 if (deleteExpr)
122 CheckDeleteExpr(methodDecl, deleteExpr);
123 continue;
125 auto parenExpr = dyn_cast<ParenExpr>(*i);
126 if (parenExpr)
128 CheckParenExpr(methodDecl, parenExpr);
129 continue;
131 // Check for conditional deletes like:
132 // if (m_pField != nullptr) delete m_pField;
133 auto ifStmt = dyn_cast<IfStmt>(*i);
134 if (!ifStmt)
135 continue;
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())
141 continue;
142 // good
144 else if (auto binaryOp = dyn_cast<BinaryOperator>(cond))
146 if (!isa<MemberExpr>(binaryOp->getLHS()->IgnoreImpCasts()))
147 continue;
148 if (!isa<CXXNullPtrLiteralExpr>(binaryOp->getRHS()->IgnoreImpCasts()))
149 continue;
150 // good
152 else // ignore anything more complicated
153 continue;
155 deleteExpr = dyn_cast<CXXDeleteExpr>(ifStmt->getThen());
156 if (deleteExpr)
158 CheckDeleteExpr(methodDecl, deleteExpr);
159 continue;
162 parenExpr = dyn_cast<ParenExpr>(ifStmt->getThen());
163 if (parenExpr)
165 CheckParenExpr(methodDecl, parenExpr);
166 continue;
169 auto ifThenCompoundStmt = dyn_cast<CompoundStmt>(ifStmt->getThen());
170 if (!ifThenCompoundStmt)
171 continue;
172 for (auto j = ifThenCompoundStmt->body_begin(); j != ifThenCompoundStmt->body_end(); ++j)
174 auto ifDeleteExpr = dyn_cast<CXXDeleteExpr>(*j);
175 if (ifDeleteExpr)
176 CheckDeleteExpr(methodDecl, ifDeleteExpr);
177 ParenExpr const * parenExpr = dyn_cast<ParenExpr>(*i);
178 if (parenExpr)
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());
190 if (!castExpr)
191 return;
192 const MemberExpr* memberExpr = dyn_cast<MemberExpr>(castExpr->getSubExpr());
193 if (!memberExpr)
194 return;
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)
206 return;
207 auto deleteExpr = dyn_cast<CXXDeleteExpr>(binaryOp->getLHS());
208 if (!deleteExpr)
209 return;
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());
221 if (!fieldDecl)
222 return;
223 TagDecl const * td = dyn_cast<TagDecl>(fieldDecl->getDeclContext());
224 if (td->isUnion())
225 return;
227 // ignore calling delete on someone else's field
228 if (fieldDecl->getParent() != methodDecl->getParent() )
229 return;
231 if (ignoreLocation(fieldDecl))
232 return;
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 "/"))
236 return;
237 // passes and stores pointers to member fields
238 if (loplugin::isSamePathname(aFileName, SRCDIR "/sot/source/sdstor/stgdir.hxx"))
239 return;
240 // something platform-specific
241 if (loplugin::isSamePathname(aFileName, SRCDIR "/hwpfilter/source/htags.h"))
242 return;
243 // passes pointers to member fields
244 if (loplugin::isSamePathname(aFileName, SRCDIR "/sd/inc/sdpptwrp.hxx"))
245 return;
246 // @TODO intrusive linked-lists here, with some trickiness
247 if (loplugin::isSamePathname(aFileName, SRCDIR "/sw/source/filter/html/parcss1.hxx"))
248 return;
249 // @TODO SwDoc has some weird ref-counting going on
250 if (loplugin::isSamePathname(aFileName, SRCDIR "/sw/inc/shellio.hxx"))
251 return;
252 // @TODO it's sharing pointers with another class
253 if (loplugin::isSamePathname(aFileName, SRCDIR "/sc/inc/formulacell.hxx"))
254 return;
255 // some weird stuff going on here around struct Entity
256 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/sax/"))
257 return;
258 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/include/sax/"))
259 return;
260 // manipulation of tree structures ie. StgAvlNode, don't lend themselves to std::unique_ptr
261 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/sot/"))
262 return;
263 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/include/sot/"))
264 return;
265 // the std::vector is being passed to another class
266 if (loplugin::isSamePathname(aFileName, SRCDIR "/sfx2/source/explorer/nochaos.cxx"))
267 return;
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())
271 return;
272 // there is a loop in ~ImplPrnQueueList deleting stuff on a global data structure
273 if (loplugin::isSamePathname(aFileName, SRCDIR "/vcl/inc/print.h"))
274 return;
275 // painful linked list
276 if (loplugin::isSamePathname(aFileName, SRCDIR "/basic/source/inc/runtime.hxx"))
277 return;
278 // not sure how the node management is working here
279 if (loplugin::isSamePathname(aFileName, SRCDIR "/i18npool/source/localedata/saxparser.cxx"))
280 return;
281 // has a pointer that it only sometimes owns
282 if (loplugin::isSamePathname(aFileName, SRCDIR "/editeng/source/editeng/impedit.hxx"))
283 return;
285 report(
286 DiagnosticsEngine::Warning,
287 message,
288 deleteExpr->getLocStart())
289 << deleteExpr->getSourceRange();
290 report(
291 DiagnosticsEngine::Note,
292 "member is here",
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();
315 for (;;)
317 subExpr = subExpr->IgnoreImpCasts();
318 if ((memberExpr = dyn_cast<MemberExpr>(subExpr)))
319 break;
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));
327 break;
329 break;
331 else
332 break;
334 if (!memberExpr)
335 return;
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());
345 else
346 deleteExpr = dyn_cast<CXXDeleteExpr>(cxxForRangeStmt->getBody());
347 if (!deleteExpr)
348 return;
349 auto memberExpr = dyn_cast<MemberExpr>(cxxForRangeStmt->getRangeInit());
350 if (!memberExpr)
351 return;
352 auto fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
353 if (!fieldDecl)
354 return;
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))
362 return true;
363 if (isInUnoIncludeFile(compoundStmt->getLocStart()))
364 return true;
365 if (compoundStmt->size() == 0) {
366 return true;
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) {
376 return true;
379 auto pCastExpr = dyn_cast<ImplicitCastExpr>(deleteExpr->getArgument());
380 if (!pCastExpr)
381 return true;
382 auto declRefExpr = dyn_cast<DeclRefExpr>(pCastExpr->getSubExpr());
383 if (!declRefExpr)
384 return true;
385 auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
386 if (!varDecl)
387 return true;
388 if (!varDecl->hasInit()
389 || !isa<CXXNewExpr>(
390 varDecl->getInit()->IgnoreImplicit()->IgnoreParenImpCasts()))
391 return true;
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())
395 return true;
397 report(
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();
402 report(
403 DiagnosticsEngine::Note,
404 "var is here",
405 varDecl->getLocStart())
406 << varDecl->getSourceRange();
407 return true;
410 loplugin::Plugin::Registration< UseUniquePtr > X("useuniqueptr", false);
414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */