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/.
17 #include "clang/AST/CXXInheritance.h"
19 // Check for local variables that we are calling delete on
25 public loplugin::FilteringPlugin
<MemoryVar
>
28 explicit MemoryVar(loplugin::InstantiationData
const & data
): FilteringPlugin(data
), mbChecking(false) {}
30 virtual void run() override
{
31 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
34 bool TraverseFunctionDecl(FunctionDecl
*);
35 bool VisitCXXDeleteExpr(const CXXDeleteExpr
*);
36 bool VisitCXXNewExpr(const CXXNewExpr
* );
37 bool VisitBinaryOperator(const BinaryOperator
*);
38 bool VisitReturnStmt(const ReturnStmt
*);
42 std::set
<SourceLocation
> maVarUsesSet
;
43 std::set
<SourceLocation
> maVarNewSet
;
44 std::set
<SourceLocation
> maVarIgnoreSet
;
45 std::map
<SourceLocation
,SourceRange
> maVarDeclSourceRangeMap
;
46 std::map
<SourceLocation
,SourceRange
> maVarDeleteSourceRangeMap
;
47 StringRef
getFilename(SourceLocation loc
);
50 StringRef
MemoryVar::getFilename(SourceLocation loc
)
52 SourceLocation spellingLocation
= compiler
.getSourceManager().getSpellingLoc(loc
);
53 StringRef name
{ getFilenameOfLocation(spellingLocation
) };
57 bool MemoryVar::TraverseFunctionDecl(FunctionDecl
* decl
)
59 if (ignoreLocation(decl
)) {
62 if (!decl
->hasBody() || !decl
->isThisDeclarationADefinition()) {
68 maVarIgnoreSet
.clear();
69 maVarDeclSourceRangeMap
.clear();
70 maVarDeleteSourceRangeMap
.clear();
74 TraverseStmt(decl
->getBody());
77 for (const auto& varLoc
: maVarUsesSet
)
79 // checking the location of the var instead of the function because for some reason
80 // I'm not getting accurate results from clang right now
81 StringRef aFileName
= getFilename(varLoc
);
82 // TODO these files are doing some weird stuff I don't know how to ignore yet
83 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/vcl/source/filter/")) {
86 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/sw/source/core/layout/frmtool.cxx")) {
91 if (maVarNewSet
.find(varLoc
) == maVarNewSet
.end())
93 if (maVarIgnoreSet
.find(varLoc
) != maVarIgnoreSet
.end())
96 report(DiagnosticsEngine::Warning
,
97 "calling new and delete on a local var, rather use std::unique_ptr",
99 << maVarDeclSourceRangeMap
[varLoc
];
100 report(DiagnosticsEngine::Note
,
101 "delete called here",
102 maVarDeleteSourceRangeMap
[varLoc
].getBegin())
103 << maVarDeleteSourceRangeMap
[varLoc
];
108 bool MemoryVar::VisitCXXDeleteExpr(const CXXDeleteExpr
*deleteExpr
)
112 if (ignoreLocation(deleteExpr
)) {
115 const Expr
* argumentExpr
= deleteExpr
->getArgument();
116 if (isa
<CastExpr
>(argumentExpr
)) {
117 argumentExpr
= dyn_cast
<CastExpr
>(argumentExpr
)->getSubExpr();
119 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(argumentExpr
);
122 const Decl
* decl
= declRefExpr
->getDecl();
123 if (!isa
<VarDecl
>(decl
) || isa
<ParmVarDecl
>(decl
)) {
126 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(decl
)->getCanonicalDecl();
127 if (varDecl
->hasGlobalStorage()) {
131 SourceLocation loc
= varDecl
->getLocation();
133 if (maVarUsesSet
.insert(loc
).second
) {
134 maVarDeclSourceRangeMap
[loc
] = varDecl
->getSourceRange();
135 maVarDeleteSourceRangeMap
[loc
] = declRefExpr
->getSourceRange();
140 bool MemoryVar::VisitCXXNewExpr(const CXXNewExpr
*newExpr
)
144 if (ignoreLocation(newExpr
)) {
147 const Stmt
* stmt
= getParentStmt(newExpr
);
149 const DeclStmt
* declStmt
= dyn_cast
<DeclStmt
>(stmt
);
151 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declStmt
->getSingleDecl());
153 varDecl
= varDecl
->getCanonicalDecl();
154 SourceLocation loc
= varDecl
->getLocation();
155 maVarNewSet
.insert(loc
);
160 const BinaryOperator
* binaryOp
= dyn_cast
<BinaryOperator
>(stmt
);
161 if (binaryOp
&& binaryOp
->getOpcode() == BO_Assign
) {
162 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(binaryOp
->getLHS());
164 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
166 varDecl
= varDecl
->getCanonicalDecl();
167 SourceLocation loc
= varDecl
->getLocation();
168 maVarNewSet
.insert(loc
);
175 // Ignore cases where the variable in question is assigned to another variable
176 bool MemoryVar::VisitBinaryOperator(const BinaryOperator
*binaryOp
)
180 if (ignoreLocation(binaryOp
)) {
183 if (binaryOp
->getOpcode() != BO_Assign
) {
186 const Expr
* expr
= binaryOp
->getRHS();
188 while (isa
<CastExpr
>(expr
)) {
189 expr
= dyn_cast
<CastExpr
>(expr
)->getSubExpr();
191 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(expr
);
195 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
199 varDecl
= varDecl
->getCanonicalDecl();
200 maVarIgnoreSet
.insert(varDecl
->getLocation());
204 // Ignore cases where the variable in question is returned from a function
205 bool MemoryVar::VisitReturnStmt(const ReturnStmt
*returnStmt
)
209 if (ignoreLocation(returnStmt
)) {
212 const Expr
* expr
= returnStmt
->getRetValue();
217 while (isa
<CastExpr
>(expr
)) {
218 expr
= dyn_cast
<CastExpr
>(expr
)->getSubExpr();
220 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(expr
);
224 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
228 varDecl
= varDecl
->getCanonicalDecl();
229 maVarIgnoreSet
.insert(varDecl
->getLocation());
233 loplugin::Plugin::Registration
< MemoryVar
> X("memoryvar", false);
237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */