bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / memoryvar.cxx
blob9cd723cf2bf841479ff75d2a7e56647f4939f2ff
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 <string>
12 #include <iostream>
13 #include <map>
14 #include <set>
16 #include "plugin.hxx"
17 #include "clang/AST/CXXInheritance.h"
19 // Check for local variables that we are calling delete on
21 namespace
24 class MemoryVar:
25 public loplugin::FilteringPlugin<MemoryVar>
27 public:
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*);
40 private:
41 bool mbChecking;
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 { getFileNameOfSpellingLoc(spellingLocation) };
54 return name;
57 bool MemoryVar::TraverseFunctionDecl(FunctionDecl * decl)
59 if (ignoreLocation(decl)) {
60 return true;
62 if (!decl->hasBody() || !decl->isThisDeclarationADefinition()) {
63 return true;
66 maVarUsesSet.clear();
67 maVarNewSet.clear();
68 maVarIgnoreSet.clear();
69 maVarDeclSourceRangeMap.clear();
70 maVarDeleteSourceRangeMap.clear();
72 assert(!mbChecking);
73 mbChecking = true;
74 TraverseStmt(decl->getBody());
75 mbChecking = false;
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/")) {
84 return true;
86 if (loplugin::isSamePathname(aFileName, SRCDIR "/sw/source/core/layout/frmtool.cxx")) {
87 return true;
91 if (maVarNewSet.find(varLoc) == maVarNewSet.end())
92 continue;
93 if (maVarIgnoreSet.find(varLoc) != maVarIgnoreSet.end())
94 continue;
96 report(DiagnosticsEngine::Warning,
97 "calling new and delete on a local var, rather use std::unique_ptr",
98 varLoc)
99 << maVarDeclSourceRangeMap[varLoc];
100 report(DiagnosticsEngine::Note,
101 "delete called here",
102 maVarDeleteSourceRangeMap[varLoc].getBegin())
103 << maVarDeleteSourceRangeMap[varLoc];
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.insert(loc).second) {
134 maVarDeclSourceRangeMap[loc] = varDecl->getSourceRange();
135 maVarDeleteSourceRangeMap[loc] = declRefExpr->getSourceRange();
137 return true;
140 bool MemoryVar::VisitCXXNewExpr(const CXXNewExpr *newExpr)
142 if (!mbChecking)
143 return true;
144 if (ignoreLocation(newExpr)) {
145 return true;
147 const Stmt* stmt = getParentStmt(newExpr);
149 const DeclStmt* declStmt = dyn_cast<DeclStmt>(stmt);
150 if (declStmt) {
151 const VarDecl* varDecl = dyn_cast<VarDecl>(declStmt->getSingleDecl());
152 if (varDecl) {
153 varDecl = varDecl->getCanonicalDecl();
154 SourceLocation loc = varDecl->getLocation();
155 maVarNewSet.insert(loc);
157 return true;
160 const BinaryOperator* binaryOp = dyn_cast<BinaryOperator>(stmt);
161 if (binaryOp && binaryOp->getOpcode() == BO_Assign) {
162 const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(binaryOp->getLHS());
163 if (declRefExpr) {
164 const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
165 if (varDecl) {
166 varDecl = varDecl->getCanonicalDecl();
167 SourceLocation loc = varDecl->getLocation();
168 maVarNewSet.insert(loc);
172 return true;
175 // Ignore cases where the variable in question is assigned to another variable
176 bool MemoryVar::VisitBinaryOperator(const BinaryOperator *binaryOp)
178 if (!mbChecking)
179 return true;
180 if (ignoreLocation(binaryOp)) {
181 return true;
183 if (binaryOp->getOpcode() != BO_Assign) {
184 return true;
186 const Expr* expr = binaryOp->getRHS();
187 // unwrap casts
188 while (isa<CastExpr>(expr)) {
189 expr = dyn_cast<CastExpr>(expr)->getSubExpr();
191 const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(expr);
192 if (!declRefExpr) {
193 return true;
195 const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
196 if (!varDecl) {
197 return true;
199 varDecl = varDecl->getCanonicalDecl();
200 maVarIgnoreSet.insert(varDecl->getLocation());
201 return true;
204 // Ignore cases where the variable in question is returned from a function
205 bool MemoryVar::VisitReturnStmt(const ReturnStmt *returnStmt)
207 if (!mbChecking)
208 return true;
209 if (ignoreLocation(returnStmt)) {
210 return true;
212 const Expr* expr = returnStmt->getRetValue();
213 if (!expr) {
214 return true;
216 // unwrap casts
217 while (isa<CastExpr>(expr)) {
218 expr = dyn_cast<CastExpr>(expr)->getSubExpr();
220 const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(expr);
221 if (!declRefExpr) {
222 return true;
224 const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl());
225 if (!varDecl) {
226 return true;
228 varDecl = varDecl->getCanonicalDecl();
229 maVarIgnoreSet.insert(varDecl->getLocation());
230 return true;
233 loplugin::Plugin::Registration< MemoryVar > X("memoryvar", false);
237 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */