diff options
Diffstat (limited to 'compilerplugins/clang/memoryvar.cxx')
-rw-r--r-- | compilerplugins/clang/memoryvar.cxx | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/compilerplugins/clang/memoryvar.cxx b/compilerplugins/clang/memoryvar.cxx new file mode 100644 index 000000000..2354739f4 --- /dev/null +++ b/compilerplugins/clang/memoryvar.cxx @@ -0,0 +1,237 @@ +/* -*- 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/. + */ + +#include <memory> +#include <string> +#include <iostream> +#include <map> +#include <set> + +#include "plugin.hxx" +#include "clang/AST/CXXInheritance.h" + +// Check for local variables that we are calling delete on + +namespace +{ + +class MemoryVar: + public loplugin::FilteringPlugin<MemoryVar> +{ +public: + explicit MemoryVar(loplugin::InstantiationData const & data): FilteringPlugin(data), mbChecking(false) {} + + virtual void run() override { + TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); + } + + bool TraverseFunctionDecl(FunctionDecl*); + bool VisitCXXDeleteExpr(const CXXDeleteExpr*); + bool VisitCXXNewExpr(const CXXNewExpr* ); + bool VisitBinaryOperator(const BinaryOperator*); + bool VisitReturnStmt(const ReturnStmt*); + +private: + bool mbChecking; + std::set<SourceLocation> maVarUsesSet; + std::set<SourceLocation> maVarNewSet; + std::set<SourceLocation> maVarIgnoreSet; + std::map<SourceLocation,SourceRange> maVarDeclSourceRangeMap; + std::map<SourceLocation,SourceRange> maVarDeleteSourceRangeMap; + StringRef getFilename(SourceLocation loc); +}; + +StringRef MemoryVar::getFilename(SourceLocation loc) +{ + SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(loc); + StringRef name { getFilenameOfLocation(spellingLocation) }; + return name; +} + +bool MemoryVar::TraverseFunctionDecl(FunctionDecl * decl) +{ + if (ignoreLocation(decl)) { + return true; + } + if (!decl->hasBody() || !decl->isThisDeclarationADefinition()) { + return true; + } + + maVarUsesSet.clear(); + maVarNewSet.clear(); + maVarIgnoreSet.clear(); + maVarDeclSourceRangeMap.clear(); + maVarDeleteSourceRangeMap.clear(); + + assert(!mbChecking); + mbChecking = true; + TraverseStmt(decl->getBody()); + mbChecking = false; + + for (const auto& varLoc : maVarUsesSet) + { + // checking the location of the var instead of the function because for some reason + // I'm not getting accurate results from clang right now + StringRef aFileName = getFilename(varLoc); + // TODO these files are doing some weird stuff I don't know how to ignore yet + if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/vcl/source/filter/")) { + return true; + } + if (loplugin::isSamePathname(aFileName, SRCDIR "/sw/source/core/layout/frmtool.cxx")) { + return true; + } + + + if (maVarNewSet.find(varLoc) == maVarNewSet.end()) + continue; + if (maVarIgnoreSet.find(varLoc) != maVarIgnoreSet.end()) + continue; + + report(DiagnosticsEngine::Warning, + "calling new and delete on a local var, rather use std::unique_ptr", + varLoc) + << maVarDeclSourceRangeMap[varLoc]; + report(DiagnosticsEngine::Note, + "delete called here", + maVarDeleteSourceRangeMap[varLoc].getBegin()) + << maVarDeleteSourceRangeMap[varLoc]; + } + return true; +} + +bool MemoryVar::VisitCXXDeleteExpr(const CXXDeleteExpr *deleteExpr) +{ + if (!mbChecking) + return true; + if (ignoreLocation(deleteExpr)) { + return true; + } + const Expr* argumentExpr = deleteExpr->getArgument(); + if (isa<CastExpr>(argumentExpr)) { + argumentExpr = dyn_cast<CastExpr>(argumentExpr)->getSubExpr(); + } + const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(argumentExpr); + if (!declRefExpr) + return true; + const Decl* decl = declRefExpr->getDecl(); + if (!isa<VarDecl>(decl) || isa<ParmVarDecl>(decl)) { + return true; + } + const VarDecl * varDecl = dyn_cast<VarDecl>(decl)->getCanonicalDecl(); + if (varDecl->hasGlobalStorage()) { + return true; + } + + SourceLocation loc = varDecl->getLocation(); + + if (maVarUsesSet.insert(loc).second) { + maVarDeclSourceRangeMap[loc] = varDecl->getSourceRange(); + maVarDeleteSourceRangeMap[loc] = declRefExpr->getSourceRange(); + } + return true; +} + +bool MemoryVar::VisitCXXNewExpr(const CXXNewExpr *newExpr) +{ + if (!mbChecking) + return true; + if (ignoreLocation(newExpr)) { + return true; + } + const Stmt* stmt = getParentStmt(newExpr); + + const DeclStmt* declStmt = dyn_cast<DeclStmt>(stmt); + if (declStmt) { + const VarDecl* varDecl = dyn_cast<VarDecl>(declStmt->getSingleDecl()); + if (varDecl) { + varDecl = varDecl->getCanonicalDecl(); + SourceLocation loc = varDecl->getLocation(); + maVarNewSet.insert(loc); + } + return true; + } + + const BinaryOperator* binaryOp = dyn_cast<BinaryOperator>(stmt); + if (binaryOp && binaryOp->getOpcode() == BO_Assign) { + const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(binaryOp->getLHS()); + if (declRefExpr) { + const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()); + if (varDecl) { + varDecl = varDecl->getCanonicalDecl(); + SourceLocation loc = varDecl->getLocation(); + maVarNewSet.insert(loc); + } + } + } + return true; +} + +// Ignore cases where the variable in question is assigned to another variable +bool MemoryVar::VisitBinaryOperator(const BinaryOperator *binaryOp) +{ + if (!mbChecking) + return true; + if (ignoreLocation(binaryOp)) { + return true; + } + if (binaryOp->getOpcode() != BO_Assign) { + return true; + } + const Expr* expr = binaryOp->getRHS(); + // unwrap casts + while (isa<CastExpr>(expr)) { + expr = dyn_cast<CastExpr>(expr)->getSubExpr(); + } + const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(expr); + if (!declRefExpr) { + return true; + } + const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()); + if (!varDecl) { + return true; + } + varDecl = varDecl->getCanonicalDecl(); + maVarIgnoreSet.insert(varDecl->getLocation()); + return true; +} + +// Ignore cases where the variable in question is returned from a function +bool MemoryVar::VisitReturnStmt(const ReturnStmt *returnStmt) +{ + if (!mbChecking) + return true; + if (ignoreLocation(returnStmt)) { + return true; + } + const Expr* expr = returnStmt->getRetValue(); + if (!expr) { + return true; + } + // unwrap casts + while (isa<CastExpr>(expr)) { + expr = dyn_cast<CastExpr>(expr)->getSubExpr(); + } + const DeclRefExpr* declRefExpr = dyn_cast<DeclRefExpr>(expr); + if (!declRefExpr) { + return true; + } + const VarDecl* varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()); + if (!varDecl) { + return true; + } + varDecl = varDecl->getCanonicalDecl(); + maVarIgnoreSet.insert(varDecl->getLocation()); + return true; +} + +loplugin::Plugin::Registration< MemoryVar > X("memoryvar", false); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |