office-gobmx/compilerplugins/clang/memoryvar.cxx
Stephan Bergmann d1a2b80b9d Bump compiler plugins Clang baseline to 12.0.1
...as discussed in the mail thread starting at
<https://lists.freedesktop.org/archives/libreoffice/2020-November/086234.html>
"Bump --enable-compiler-plugins Clang baseline?" (and now picked up again at
<https://lists.freedesktop.org/archives/libreoffice/2022-February/088459.html>
"Re: Bump --enable-compiler-plugins Clang baseline?"), and clean up
compilerplugins/clang/ accordingly

Change-Id: I5e81c6fdcc363aeefd6227606225b526fdf7ac16
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129989
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2022-02-17 21:45:06 +01:00

238 lines
7.1 KiB
C++

/* -*- 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 "config_clang.h"
#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: */