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/.
16 #include "clang/AST/CXXInheritance.h"
18 // Check for local variables that we are calling delete on
24 public RecursiveASTVisitor
<MemoryVar
>, public loplugin::Plugin
27 explicit MemoryVar(InstantiationData
const & data
): Plugin(data
), mbChecking(false) {}
29 virtual void run() override
{
30 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
33 bool TraverseFunctionDecl(FunctionDecl
*);
34 bool VisitCXXDeleteExpr(const CXXDeleteExpr
*);
35 bool VisitCXXNewExpr(const CXXNewExpr
* );
36 bool VisitBinaryOperator(const BinaryOperator
*);
37 bool VisitReturnStmt(const ReturnStmt
*);
41 std::set
<SourceLocation
> maVarUsesSet
;
42 std::set
<SourceLocation
> maVarNewSet
;
43 std::set
<SourceLocation
> maVarIgnoreSet
;
44 std::map
<SourceLocation
,SourceRange
> maVarDeclSourceRangeMap
;
45 std::map
<SourceLocation
,SourceRange
> maVarDeleteSourceRangeMap
;
46 StringRef
getFilename(SourceLocation loc
);
49 StringRef
MemoryVar::getFilename(SourceLocation loc
)
51 SourceLocation spellingLocation
= compiler
.getSourceManager().getSpellingLoc(loc
);
52 StringRef name
{ compiler
.getSourceManager().getFilename(spellingLocation
) };
56 bool MemoryVar::TraverseFunctionDecl(FunctionDecl
* decl
)
58 if (ignoreLocation(decl
)) {
61 if (!decl
->hasBody() || !decl
->isThisDeclarationADefinition()) {
67 maVarIgnoreSet
.clear();
68 maVarDeclSourceRangeMap
.clear();
69 maVarDeleteSourceRangeMap
.clear();
73 TraverseStmt(decl
->getBody());
76 for (const auto& varLoc
: maVarUsesSet
)
78 // checking the location of the var instead of the function because for some reason
79 // I'm not getting accurate results from clang right now
80 StringRef aFileName
= getFilename(varLoc
);
81 // TODO these files are doing some weird stuff I don't know how to ignore yet
82 if (aFileName
.startswith(SRCDIR
"/vcl/source/filter")) {
85 if (aFileName
.startswith(SRCDIR
"/sw/source/core/layout/frmtool.cxx")) {
90 if (maVarNewSet
.find(varLoc
) == maVarNewSet
.end())
92 if (maVarIgnoreSet
.find(varLoc
) != maVarIgnoreSet
.end())
95 report(DiagnosticsEngine::Warning
,
96 "calling new and delete on a local var, rather use std::unique_ptr",
98 << maVarDeclSourceRangeMap
[varLoc
];
99 report(DiagnosticsEngine::Note
,
100 "delete called here",
101 maVarDeleteSourceRangeMap
[varLoc
].getBegin())
102 << maVarDeleteSourceRangeMap
[varLoc
];
103 cout
<< "xxxx " << aFileName
.str() << endl
;
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
.find(loc
) == maVarUsesSet
.end()) {
134 maVarUsesSet
.insert(loc
);
135 maVarDeclSourceRangeMap
[loc
] = varDecl
->getSourceRange();
136 maVarDeleteSourceRangeMap
[loc
] = declRefExpr
->getSourceRange();
141 bool MemoryVar::VisitCXXNewExpr(const CXXNewExpr
*newExpr
)
145 if (ignoreLocation(newExpr
)) {
148 const Stmt
* stmt
= parentStmt(newExpr
);
150 const DeclStmt
* declStmt
= dyn_cast
<DeclStmt
>(stmt
);
152 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declStmt
->getSingleDecl());
154 varDecl
= varDecl
->getCanonicalDecl();
155 SourceLocation loc
= varDecl
->getLocation();
156 maVarNewSet
.insert(loc
);
161 const BinaryOperator
* binaryOp
= dyn_cast
<BinaryOperator
>(stmt
);
162 if (binaryOp
&& binaryOp
->getOpcode() == BO_Assign
) {
163 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(binaryOp
->getLHS());
165 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
167 varDecl
= varDecl
->getCanonicalDecl();
168 SourceLocation loc
= varDecl
->getLocation();
169 maVarNewSet
.insert(loc
);
176 // Ignore cases where the variable in question is assigned to another variable
177 bool MemoryVar::VisitBinaryOperator(const BinaryOperator
*binaryOp
)
181 if (ignoreLocation(binaryOp
)) {
184 if (binaryOp
->getOpcode() != BO_Assign
) {
187 const Expr
* expr
= binaryOp
->getRHS();
189 while (isa
<CastExpr
>(expr
)) {
190 expr
= dyn_cast
<CastExpr
>(expr
)->getSubExpr();
192 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(expr
);
196 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
200 varDecl
= varDecl
->getCanonicalDecl();
201 maVarIgnoreSet
.insert(varDecl
->getLocation());
205 // Ignore cases where the variable in question is returned from a function
206 bool MemoryVar::VisitReturnStmt(const ReturnStmt
*returnStmt
)
210 if (ignoreLocation(returnStmt
)) {
213 const Expr
* expr
= returnStmt
->getRetValue();
218 while (isa
<CastExpr
>(expr
)) {
219 expr
= dyn_cast
<CastExpr
>(expr
)->getSubExpr();
221 const DeclRefExpr
* declRefExpr
= dyn_cast
<DeclRefExpr
>(expr
);
225 const VarDecl
* varDecl
= dyn_cast
<VarDecl
>(declRefExpr
->getDecl());
229 varDecl
= varDecl
->getCanonicalDecl();
230 maVarIgnoreSet
.insert(varDecl
->getLocation());
234 loplugin::Plugin::Registration
< MemoryVar
> X("memoryvar", false);
238 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */