2017-01-11 14:19:47 +02:00
|
|
|
/* -*- 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/.
|
|
|
|
*/
|
|
|
|
|
2017-04-14 08:42:15 +10:00
|
|
|
#include <memory>
|
2017-01-11 14:19:47 +02:00
|
|
|
#include <cassert>
|
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <set>
|
|
|
|
#include "plugin.hxx"
|
2017-07-14 14:36:52 +02:00
|
|
|
#include "check.hxx"
|
2017-01-11 14:19:47 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
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 RecursiveASTVisitor<UseUniquePtr>, public loplugin::Plugin
|
|
|
|
{
|
|
|
|
public:
|
2017-11-07 11:50:47 +01:00
|
|
|
explicit UseUniquePtr(loplugin::InstantiationData const & data):
|
|
|
|
Plugin(data) {}
|
2017-01-11 14:19:47 +02:00
|
|
|
|
|
|
|
virtual void run() override
|
|
|
|
{
|
|
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
|
|
}
|
|
|
|
|
2017-06-26 09:48:30 +02:00
|
|
|
bool VisitCXXDestructorDecl(const CXXDestructorDecl* );
|
|
|
|
bool VisitCompoundStmt(const CompoundStmt* );
|
|
|
|
private:
|
2018-01-10 11:29:36 +02:00
|
|
|
void CheckForUnconditionalDelete(const CXXDestructorDecl*, const CompoundStmt* );
|
2018-01-17 13:14:21 +02:00
|
|
|
void CheckForSimpleDelete(const CXXDestructorDecl*, const CompoundStmt* );
|
|
|
|
void CheckRangedLoopDelete(const CXXDestructorDecl*, const CXXForRangeStmt* );
|
|
|
|
void CheckLoopDelete(const CXXDestructorDecl*, const Stmt* );
|
|
|
|
void CheckLoopDelete(const CXXDestructorDecl*, const CXXDeleteExpr* );
|
|
|
|
void CheckDeleteExpr(const CXXDestructorDecl*, const CXXDeleteExpr*);
|
|
|
|
void CheckDeleteExpr(const CXXDestructorDecl*, const CXXDeleteExpr*,
|
|
|
|
const MemberExpr*, StringRef message);
|
2017-01-11 14:19:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
bool UseUniquePtr::VisitCXXDestructorDecl(const CXXDestructorDecl* destructorDecl)
|
|
|
|
{
|
|
|
|
if (ignoreLocation(destructorDecl))
|
|
|
|
return true;
|
|
|
|
if (isInUnoIncludeFile(destructorDecl))
|
|
|
|
return true;
|
|
|
|
|
2017-05-22 13:25:58 +02:00
|
|
|
const CompoundStmt* compoundStmt = dyn_cast_or_null< CompoundStmt >( destructorDecl->getBody() );
|
2018-01-10 11:29:36 +02:00
|
|
|
if (!compoundStmt || compoundStmt->size() == 0)
|
2017-01-11 14:19:47 +02:00
|
|
|
return true;
|
2017-05-12 15:32:36 +02:00
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
CheckForSimpleDelete(destructorDecl, compoundStmt);
|
|
|
|
|
|
|
|
for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i)
|
|
|
|
{
|
|
|
|
if (auto cxxForRangeStmt = dyn_cast<CXXForRangeStmt>(*i))
|
|
|
|
CheckRangedLoopDelete(destructorDecl, cxxForRangeStmt);
|
|
|
|
else if (auto forStmt = dyn_cast<ForStmt>(*i))
|
|
|
|
CheckLoopDelete(destructorDecl, forStmt->getBody());
|
|
|
|
else if (auto whileStmt = dyn_cast<WhileStmt>(*i))
|
|
|
|
CheckLoopDelete(destructorDecl, whileStmt->getBody());
|
|
|
|
}
|
2017-06-26 09:48:30 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
/**
|
|
|
|
* check for simple call to delete in a destructor i.e. direct unconditional call, or if-guarded call
|
|
|
|
*/
|
|
|
|
void UseUniquePtr::CheckForSimpleDelete(const CXXDestructorDecl* destructorDecl, const CompoundStmt* compoundStmt)
|
2017-06-26 09:48:30 +02:00
|
|
|
{
|
2018-01-10 11:29:36 +02:00
|
|
|
for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i)
|
|
|
|
{
|
|
|
|
auto deleteExpr = dyn_cast<CXXDeleteExpr>(*i);
|
2018-01-10 16:23:41 +02:00
|
|
|
if (deleteExpr)
|
|
|
|
{
|
|
|
|
CheckDeleteExpr(destructorDecl, deleteExpr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Check for conditional deletes like:
|
|
|
|
// if (m_pField != nullptr) delete m_pField;
|
|
|
|
auto ifStmt = dyn_cast<IfStmt>(*i);
|
|
|
|
if (!ifStmt)
|
|
|
|
continue;
|
|
|
|
auto cond = ifStmt->getCond()->IgnoreImpCasts();
|
|
|
|
if (auto ifCondMemberExpr = dyn_cast<MemberExpr>(cond))
|
|
|
|
{
|
|
|
|
// ignore "if (bMine)"
|
|
|
|
if (!loplugin::TypeCheck(ifCondMemberExpr->getType()).Pointer())
|
|
|
|
continue;
|
2018-01-17 13:14:21 +02:00
|
|
|
// good
|
2018-01-10 16:23:41 +02:00
|
|
|
}
|
|
|
|
else if (auto binaryOp = dyn_cast<BinaryOperator>(cond))
|
|
|
|
{
|
|
|
|
if (!isa<MemberExpr>(binaryOp->getLHS()->IgnoreImpCasts()))
|
|
|
|
continue;
|
2018-01-12 08:22:39 +02:00
|
|
|
if (!isa<CXXNullPtrLiteralExpr>(binaryOp->getRHS()->IgnoreImpCasts()))
|
|
|
|
continue;
|
2018-01-17 13:14:21 +02:00
|
|
|
// good
|
2018-01-10 16:23:41 +02:00
|
|
|
}
|
2018-01-17 13:14:21 +02:00
|
|
|
else // ignore anything more complicated
|
2018-01-10 16:23:41 +02:00
|
|
|
continue;
|
2018-01-17 13:14:21 +02:00
|
|
|
|
2018-01-10 16:23:41 +02:00
|
|
|
deleteExpr = dyn_cast<CXXDeleteExpr>(ifStmt->getThen());
|
|
|
|
if (deleteExpr)
|
|
|
|
{
|
|
|
|
CheckDeleteExpr(destructorDecl, deleteExpr);
|
|
|
|
continue;
|
|
|
|
}
|
2018-01-17 13:14:21 +02:00
|
|
|
|
2018-01-10 16:23:41 +02:00
|
|
|
auto ifThenCompoundStmt = dyn_cast<CompoundStmt>(ifStmt->getThen());
|
|
|
|
if (!ifThenCompoundStmt)
|
|
|
|
continue;
|
|
|
|
for (auto j = ifThenCompoundStmt->body_begin(); j != ifThenCompoundStmt->body_end(); ++j)
|
|
|
|
{
|
|
|
|
auto ifDeleteExpr = dyn_cast<CXXDeleteExpr>(*j);
|
|
|
|
if (ifDeleteExpr)
|
|
|
|
CheckDeleteExpr(destructorDecl, ifDeleteExpr);
|
|
|
|
}
|
2018-01-10 11:29:36 +02:00
|
|
|
}
|
2017-01-11 14:19:47 +02:00
|
|
|
}
|
|
|
|
|
2018-01-12 08:22:39 +02:00
|
|
|
/**
|
|
|
|
* Check the delete expression in a destructor.
|
|
|
|
*/
|
2018-01-10 16:23:41 +02:00
|
|
|
void UseUniquePtr::CheckDeleteExpr(const CXXDestructorDecl* destructorDecl, const CXXDeleteExpr* deleteExpr)
|
|
|
|
{
|
2018-01-17 13:14:21 +02:00
|
|
|
const ImplicitCastExpr* castExpr = dyn_cast<ImplicitCastExpr>(deleteExpr->getArgument());
|
|
|
|
if (!castExpr)
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
2018-01-17 13:14:21 +02:00
|
|
|
const MemberExpr* memberExpr = dyn_cast<MemberExpr>(castExpr->getSubExpr());
|
|
|
|
if (!memberExpr)
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
2018-01-17 13:14:21 +02:00
|
|
|
CheckDeleteExpr(destructorDecl, deleteExpr, memberExpr,
|
|
|
|
"unconditional call to delete on a member, should be using std::unique_ptr");
|
|
|
|
}
|
2018-01-10 16:23:41 +02:00
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
/**
|
|
|
|
* Check the delete expression in a destructor.
|
|
|
|
*/
|
|
|
|
void UseUniquePtr::CheckDeleteExpr(const CXXDestructorDecl* destructorDecl, const CXXDeleteExpr* deleteExpr,
|
|
|
|
const MemberExpr* memberExpr, StringRef message)
|
|
|
|
{
|
2018-01-10 16:23:41 +02:00
|
|
|
// ignore union games
|
2018-01-17 13:14:21 +02:00
|
|
|
const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
|
|
|
|
if (!fieldDecl)
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
2018-01-17 13:14:21 +02:00
|
|
|
TagDecl const * td = dyn_cast<TagDecl>(fieldDecl->getDeclContext());
|
2018-01-10 16:23:41 +02:00
|
|
|
if (td->isUnion())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// ignore calling delete on someone else's field
|
2018-01-17 13:14:21 +02:00
|
|
|
if (fieldDecl->getParent() != destructorDecl->getParent() )
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
if (ignoreLocation(fieldDecl))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// to ignore things like the CPPUNIT macros
|
2018-01-17 13:14:21 +02:00
|
|
|
StringRef aFileName = compiler.getSourceManager().getFilename(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocStart()));
|
2018-03-26 13:37:06 +02:00
|
|
|
if (loplugin::hasPathnamePrefix(aFileName, WORKDIR "/"))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// passes and stores pointers to member fields
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/sot/source/sdstor/stgdir.hxx"))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// something platform-specific
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/hwpfilter/source/htags.h"))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// passes pointers to member fields
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/sd/inc/sdpptwrp.hxx"))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// @TODO intrusive linked-lists here, with some trickiness
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/sw/source/filter/html/parcss1.hxx"))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// @TODO SwDoc has some weird ref-counting going on
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/sw/inc/shellio.hxx"))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// @TODO it's sharing pointers with another class
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/sc/inc/formulacell.hxx"))
|
2018-01-10 16:23:41 +02:00
|
|
|
return;
|
|
|
|
// some weird stuff going on here around struct Entity
|
|
|
|
if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/sax/"))
|
|
|
|
return;
|
|
|
|
if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/include/sax/"))
|
|
|
|
return;
|
|
|
|
// manipulation of tree structures ie. StgAvlNode, don't lend themselves to std::unique_ptr
|
|
|
|
if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/sot/"))
|
|
|
|
return;
|
|
|
|
if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/include/sot/"))
|
|
|
|
return;
|
2018-01-17 13:14:21 +02:00
|
|
|
// the std::vector is being passed to another class
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/sfx2/source/explorer/nochaos.cxx"))
|
2018-01-17 13:14:21 +02:00
|
|
|
return;
|
|
|
|
// ignore std::map and std::unordered_map, MSVC 2015 has problems with mixing these with std::unique_ptr
|
|
|
|
auto tc = loplugin::TypeCheck(fieldDecl->getType());
|
|
|
|
if (tc.Class("map").StdNamespace() || tc.Class("unordered_map").StdNamespace())
|
|
|
|
return;
|
|
|
|
// there is a loop in ~ImplPrnQueueList deleting stuff on a global data structure
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/vcl/inc/print.h"))
|
2018-01-17 13:14:21 +02:00
|
|
|
return;
|
|
|
|
// painful linked list
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/basic/source/inc/runtime.hxx"))
|
2018-01-17 13:14:21 +02:00
|
|
|
return;
|
2018-01-22 14:04:06 +02:00
|
|
|
// not sure how the node management is working here
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/i18npool/source/localedata/saxparser.cxx"))
|
2018-01-22 14:04:06 +02:00
|
|
|
return;
|
2018-01-29 12:13:56 +02:00
|
|
|
// has a pointer that it only sometimes owns
|
2018-03-26 13:26:46 +02:00
|
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/editeng/source/editeng/impedit.hxx"))
|
2018-01-29 12:13:56 +02:00
|
|
|
return;
|
2018-01-10 16:23:41 +02:00
|
|
|
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
2018-01-17 13:14:21 +02:00
|
|
|
message,
|
2018-01-10 16:23:41 +02:00
|
|
|
deleteExpr->getLocStart())
|
|
|
|
<< deleteExpr->getSourceRange();
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Note,
|
|
|
|
"member is here",
|
2018-01-17 13:14:21 +02:00
|
|
|
fieldDecl->getLocStart())
|
|
|
|
<< fieldDecl->getSourceRange();
|
2018-01-10 16:23:41 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
void UseUniquePtr::CheckLoopDelete(const CXXDestructorDecl* destructorDecl, const Stmt* bodyStmt)
|
2017-08-23 08:49:02 +02:00
|
|
|
{
|
2018-01-17 13:14:21 +02:00
|
|
|
if (auto deleteExpr = dyn_cast<CXXDeleteExpr>(bodyStmt))
|
|
|
|
CheckLoopDelete(destructorDecl, deleteExpr);
|
|
|
|
else if (auto compoundStmt = dyn_cast<CompoundStmt>(bodyStmt))
|
2017-08-23 08:49:02 +02:00
|
|
|
{
|
2018-01-17 13:14:21 +02:00
|
|
|
for (auto i = compoundStmt->body_begin(); i != compoundStmt->body_end(); ++i)
|
2017-08-23 08:49:02 +02:00
|
|
|
{
|
2018-01-17 13:14:21 +02:00
|
|
|
if (auto deleteExpr = dyn_cast<CXXDeleteExpr>(*i))
|
|
|
|
CheckLoopDelete(destructorDecl, deleteExpr);
|
2017-08-23 08:49:02 +02:00
|
|
|
}
|
2018-01-10 11:29:36 +02:00
|
|
|
}
|
2017-08-23 08:49:02 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
void UseUniquePtr::CheckLoopDelete(const CXXDestructorDecl* destructorDecl, const CXXDeleteExpr* deleteExpr)
|
2017-08-23 08:49:02 +02:00
|
|
|
{
|
2018-01-17 13:14:21 +02:00
|
|
|
const MemberExpr* memberExpr = nullptr;
|
|
|
|
const Expr* subExpr = deleteExpr->getArgument();
|
|
|
|
for (;;)
|
2018-01-10 11:29:36 +02:00
|
|
|
{
|
2018-01-17 13:14:21 +02:00
|
|
|
subExpr = subExpr->IgnoreImpCasts();
|
|
|
|
if ((memberExpr = dyn_cast<MemberExpr>(subExpr)))
|
|
|
|
break;
|
|
|
|
else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(subExpr))
|
|
|
|
subExpr = arraySubscriptExpr->getBase();
|
|
|
|
else if (auto cxxOperatorCallExpr = dyn_cast<CXXOperatorCallExpr>(subExpr))
|
|
|
|
{
|
|
|
|
if (cxxOperatorCallExpr->getOperator() == OO_Subscript)
|
|
|
|
{
|
|
|
|
memberExpr = dyn_cast<MemberExpr>(cxxOperatorCallExpr->getArg(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-01-15 14:56:29 +02:00
|
|
|
else
|
2018-01-17 13:14:21 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!memberExpr)
|
|
|
|
return;
|
2018-01-10 11:29:36 +02:00
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
CheckDeleteExpr(destructorDecl, deleteExpr, memberExpr, "rather manage with std::some_container<std::unique_ptr<T>>");
|
|
|
|
}
|
2018-01-10 11:29:36 +02:00
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
void UseUniquePtr::CheckRangedLoopDelete(const CXXDestructorDecl* destructorDecl, const CXXForRangeStmt* cxxForRangeStmt)
|
|
|
|
{
|
|
|
|
CXXDeleteExpr const * deleteExpr = nullptr;
|
|
|
|
if (auto compoundStmt = dyn_cast<CompoundStmt>(cxxForRangeStmt->getBody()))
|
|
|
|
deleteExpr = dyn_cast<CXXDeleteExpr>(*compoundStmt->body_begin());
|
|
|
|
else
|
|
|
|
deleteExpr = dyn_cast<CXXDeleteExpr>(cxxForRangeStmt->getBody());
|
|
|
|
if (!deleteExpr)
|
|
|
|
return;
|
|
|
|
auto memberExpr = dyn_cast<MemberExpr>(cxxForRangeStmt->getRangeInit());
|
|
|
|
if (!memberExpr)
|
|
|
|
return;
|
|
|
|
auto fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
|
|
|
|
if (!fieldDecl)
|
|
|
|
return;
|
2018-01-10 11:29:36 +02:00
|
|
|
|
2018-01-17 13:14:21 +02:00
|
|
|
CheckDeleteExpr(destructorDecl, deleteExpr, memberExpr, "rather manage with std::some_container<std::unique_ptr<T>>");
|
2017-08-23 08:49:02 +02:00
|
|
|
}
|
|
|
|
|
2017-01-31 14:46:38 +02:00
|
|
|
bool UseUniquePtr::VisitCompoundStmt(const CompoundStmt* compoundStmt)
|
|
|
|
{
|
|
|
|
if (ignoreLocation(compoundStmt))
|
|
|
|
return true;
|
|
|
|
if (isInUnoIncludeFile(compoundStmt->getLocStart()))
|
|
|
|
return true;
|
|
|
|
if (compoundStmt->size() == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-06 09:46:06 +02:00
|
|
|
auto lastStmt = compoundStmt->body_back();
|
|
|
|
if (compoundStmt->size() > 1) {
|
|
|
|
if (isa<ReturnStmt>(lastStmt))
|
|
|
|
lastStmt = *(++compoundStmt->body_rbegin());
|
|
|
|
}
|
|
|
|
auto deleteExpr = dyn_cast<CXXDeleteExpr>(lastStmt);
|
2017-01-31 14:46:38 +02:00
|
|
|
if (deleteExpr == nullptr) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-06 09:46:06 +02:00
|
|
|
auto pCastExpr = dyn_cast<ImplicitCastExpr>(deleteExpr->getArgument());
|
2017-01-31 14:46:38 +02:00
|
|
|
if (!pCastExpr)
|
|
|
|
return true;
|
2017-04-06 09:46:06 +02:00
|
|
|
auto declRefExpr = dyn_cast<DeclRefExpr>(pCastExpr->getSubExpr());
|
2017-01-31 14:46:38 +02:00
|
|
|
if (!declRefExpr)
|
|
|
|
return true;
|
2017-04-06 09:46:06 +02:00
|
|
|
auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
|
2017-01-31 14:46:38 +02:00
|
|
|
if (!varDecl)
|
|
|
|
return true;
|
2017-04-06 15:20:15 +02:00
|
|
|
if (!varDecl->hasInit()
|
|
|
|
|| !isa<CXXNewExpr>(
|
|
|
|
varDecl->getInit()->IgnoreImplicit()->IgnoreParenImpCasts()))
|
2017-01-31 14:46:38 +02:00
|
|
|
return true;
|
|
|
|
// determine if the var is declared inside the same block as the delete.
|
|
|
|
// @TODO there should surely be a better way to do this
|
|
|
|
if (varDecl->getLocStart() < compoundStmt->getLocStart())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"deleting a local variable at the end of a block, is a sure sign it should be using std::unique_ptr for that var",
|
|
|
|
deleteExpr->getLocStart())
|
|
|
|
<< deleteExpr->getSourceRange();
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Note,
|
|
|
|
"var is here",
|
|
|
|
varDecl->getLocStart())
|
|
|
|
<< varDecl->getSourceRange();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-01-10 11:29:36 +02:00
|
|
|
loplugin::Plugin::Registration< UseUniquePtr > X("useuniqueptr", false);
|
2017-01-11 14:19:47 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|