cURL: follow redirects
[LibreOffice.git] / compilerplugins / clang / memoryvar.cxx
blob2c8f3fd193b3cef422be3253b7de332c724d9ce8
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 <string>
11 #include <iostream>
12 #include <map>
13 #include <set>
15 #include "plugin.hxx"
16 #include "clang/AST/CXXInheritance.h"
18 // Check for local variables that we are calling delete on
20 namespace
23 class MemoryVar:
24 public RecursiveASTVisitor<MemoryVar>, public loplugin::Plugin
26 public:
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*);
39 private:
40 bool mbChecking;
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) };
53 return name;
56 bool MemoryVar::TraverseFunctionDecl(FunctionDecl * decl)
58 if (ignoreLocation(decl)) {
59 return true;
61 if (!decl->hasBody() || !decl->isThisDeclarationADefinition()) {
62 return true;
65 maVarUsesSet.clear();
66 maVarNewSet.clear();
67 maVarIgnoreSet.clear();
68 maVarDeclSourceRangeMap.clear();
69 maVarDeleteSourceRangeMap.clear();
71 assert(!mbChecking);
72 mbChecking = true;
73 TraverseStmt(decl->getBody());
74 mbChecking = false;
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")) {
83 return true;
85 if (aFileName.startswith(SRCDIR "/sw/source/core/layout/frmtool.cxx")) {
86 return true;
90 if (maVarNewSet.find(varLoc) == maVarNewSet.end())
91 continue;
92 if (maVarIgnoreSet.find(varLoc) != maVarIgnoreSet.end())
93 continue;
95 report(DiagnosticsEngine::Warning,
96 "calling new and delete on a local var, rather use std::unique_ptr",
97 varLoc)
98 << maVarDeclSourceRangeMap[varLoc];
99 report(DiagnosticsEngine::Note,
100 "delete called here",
101 maVarDeleteSourceRangeMap[varLoc].getBegin())
102 << maVarDeleteSourceRangeMap[varLoc];
103 cout << "xxxx " << aFileName.str() << endl;
105 return true;
108 bool MemoryVar::VisitCXXDeleteExpr(const CXXDeleteExpr *deleteExpr)
110 if (!mbChecking)
111 return true;
112 if (ignoreLocation(deleteExpr)) {
113 return true;
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);
120 if (!declRefExpr)
121 return true;
122 const Decl* decl = declRefExpr->getDecl();
123 if (!isa<VarDecl>(decl) || isa<ParmVarDecl>(decl)) {
124 return true;
126 const VarDecl * varDecl = dyn_cast<VarDecl>(decl)->getCanonicalDecl();
127 if (varDecl->hasGlobalStorage()) {
128 return true;
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();
138 return true;
141 bool MemoryVar::VisitCXXNewExpr(const CXXNewExpr *newExpr)
143 if (!mbChecking)
144 return true;
145 if (ignoreLocation(newExpr)) {
146 return true;
148 const Stmt* stmt = parentStmt(newExpr);
150 const DeclStmt* declStmt = dyn_cast<DeclStmt>(stmt);
151 if (declStmt) {
152 const VarDecl* varDecl = dyn_cast<VarDecl>(declStmt->getSingleDecl());
153 if (varDecl) {
154 varDecl = varDecl->getCanonicalDecl();
155 SourceLocation loc = varDecl->getLocation();
156 maVarNewSet.insert(loc);
158 return true;
161 const BinaryOperator* binaryOp = dyn_cast<BinaryOperator>(stmt);
162 if (binaryOp && binaryOp->getOpcode() == BO_Assign) {
163 const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(binaryOp->getLHS());
164 if (declRefExpr) {
165 const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
166 if (varDecl) {
167 varDecl = varDecl->getCanonicalDecl();
168 SourceLocation loc = varDecl->getLocation();
169 maVarNewSet.insert(loc);
173 return true;
176 // Ignore cases where the variable in question is assigned to another variable
177 bool MemoryVar::VisitBinaryOperator(const BinaryOperator *binaryOp)
179 if (!mbChecking)
180 return true;
181 if (ignoreLocation(binaryOp)) {
182 return true;
184 if (binaryOp->getOpcode() != BO_Assign) {
185 return true;
187 const Expr* expr = binaryOp->getRHS();
188 // unwrap casts
189 while (isa<CastExpr>(expr)) {
190 expr = dyn_cast<CastExpr>(expr)->getSubExpr();
192 const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(expr);
193 if (!declRefExpr) {
194 return true;
196 const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
197 if (!varDecl) {
198 return true;
200 varDecl = varDecl->getCanonicalDecl();
201 maVarIgnoreSet.insert(varDecl->getLocation());
202 return true;
205 // Ignore cases where the variable in question is returned from a function
206 bool MemoryVar::VisitReturnStmt(const ReturnStmt *returnStmt)
208 if (!mbChecking)
209 return true;
210 if (ignoreLocation(returnStmt)) {
211 return true;
213 const Expr* expr = returnStmt->getRetValue();
214 if (!expr) {
215 return true;
217 // unwrap casts
218 while (isa<CastExpr>(expr)) {
219 expr = dyn_cast<CastExpr>(expr)->getSubExpr();
221 const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(expr);
222 if (!declRefExpr) {
223 return true;
225 const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
226 if (!varDecl) {
227 return true;
229 varDecl = varDecl->getCanonicalDecl();
230 maVarIgnoreSet.insert(varDecl->getLocation());
231 return true;
234 loplugin::Plugin::Registration< MemoryVar > X("memoryvar", false);
238 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */