bef96f7a7b
The function is not just about a spelling location. Change-Id: I96e9e9ef7e27a9763397b4b86473c1c30d0e3eeb Reviewed-on: https://gerrit.libreoffice.org/80381 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
218 lines
6.9 KiB
C++
218 lines
6.9 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 <cassert>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <set>
|
|
#include "plugin.hxx"
|
|
#include <fstream>
|
|
|
|
/**
|
|
Look for virtual methods where we never call the defining virtual method, and only call the overriding virtual
|
|
methods, which indicates a places where the virtual-ness is unwarranted, normally a result of premature abstraction.
|
|
|
|
The process goes something like this:
|
|
$ make check
|
|
$ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='VirtualDown' check
|
|
$ ./compilerplugins/clang/VirtualDown.py
|
|
|
|
@TODO for some reason, we get false+ for operator== methods
|
|
@TODO some templates confuse it and we get false+
|
|
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
struct MyFuncInfo
|
|
{
|
|
std::string name;
|
|
std::string sourceLocation;
|
|
};
|
|
bool operator<(const MyFuncInfo& lhs, const MyFuncInfo& rhs) { return lhs.name < rhs.name; }
|
|
|
|
// try to limit the voluminous output a little
|
|
static std::set<MyFuncInfo> definitionSet;
|
|
static std::set<std::string> callSet;
|
|
|
|
class VirtualDown : public loplugin::FilteringPlugin<VirtualDown>
|
|
{
|
|
public:
|
|
explicit VirtualDown(loplugin::InstantiationData const& data)
|
|
: FilteringPlugin(data)
|
|
{
|
|
}
|
|
|
|
virtual void run() override
|
|
{
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
|
|
// dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
|
|
// writing to the same logfile
|
|
std::string output;
|
|
for (const MyFuncInfo& s : definitionSet)
|
|
output += "definition:\t" + s.name + "\t" + s.sourceLocation + "\n";
|
|
for (const std::string& s : callSet)
|
|
output += "call:\t" + s + "\n";
|
|
std::ofstream myfile;
|
|
myfile.open(WORKDIR "/loplugin.virtualdown.log", std::ios::app | std::ios::out);
|
|
myfile << output;
|
|
myfile.close();
|
|
}
|
|
bool shouldVisitTemplateInstantiations() const { return true; }
|
|
bool shouldVisitImplicitCode() const { return true; }
|
|
|
|
bool VisitCXXMethodDecl(CXXMethodDecl const*);
|
|
bool VisitCXXMemberCallExpr(CXXMemberCallExpr const*);
|
|
bool TraverseFunctionDecl(FunctionDecl*);
|
|
bool TraverseCXXMethodDecl(CXXMethodDecl*);
|
|
bool TraverseCXXConversionDecl(CXXConversionDecl*);
|
|
bool TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl*);
|
|
|
|
private:
|
|
std::string toString(SourceLocation loc);
|
|
std::string niceName(const CXXMethodDecl* functionDecl);
|
|
FunctionDecl const* currentFunctionDecl = nullptr;
|
|
};
|
|
|
|
bool VirtualDown::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
|
|
{
|
|
if (ignoreLocation(methodDecl))
|
|
{
|
|
return true;
|
|
}
|
|
if (!methodDecl->isThisDeclarationADefinition() || !methodDecl->isVirtual()
|
|
|| methodDecl->isDeleted())
|
|
{
|
|
return true;
|
|
}
|
|
methodDecl = methodDecl->getCanonicalDecl();
|
|
// ignore stuff that forms part of the stable URE interface
|
|
if (isInUnoIncludeFile(methodDecl))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
std::string aNiceName = niceName(methodDecl);
|
|
|
|
if (isa<CXXDestructorDecl>(methodDecl))
|
|
return true;
|
|
if (isa<CXXConstructorDecl>(methodDecl))
|
|
return true;
|
|
|
|
if (methodDecl->size_overridden_methods() == 0)
|
|
definitionSet.insert({ aNiceName, toString(methodDecl->getLocation()) });
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VirtualDown::VisitCXXMemberCallExpr(CXXMemberCallExpr const* expr)
|
|
{
|
|
// Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
|
|
// from template instantiation deep inside the STL and other external code
|
|
|
|
FunctionDecl const* calleeFunctionDecl = expr->getDirectCallee();
|
|
if (calleeFunctionDecl == nullptr)
|
|
{
|
|
Expr const* callee = expr->getCallee()->IgnoreParenImpCasts();
|
|
DeclRefExpr const* dr = dyn_cast<DeclRefExpr>(callee);
|
|
if (dr)
|
|
{
|
|
calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
|
|
if (calleeFunctionDecl)
|
|
goto gotfunc;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
gotfunc:
|
|
|
|
// ignore recursive calls
|
|
if (currentFunctionDecl == calleeFunctionDecl)
|
|
return true;
|
|
|
|
auto cxxMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
|
|
if (!cxxMethodDecl)
|
|
return true;
|
|
|
|
while (cxxMethodDecl->getTemplateInstantiationPattern())
|
|
cxxMethodDecl = dyn_cast<CXXMethodDecl>(cxxMethodDecl->getTemplateInstantiationPattern());
|
|
while (cxxMethodDecl->getInstantiatedFromMemberFunction())
|
|
cxxMethodDecl = dyn_cast<CXXMethodDecl>(cxxMethodDecl->getInstantiatedFromMemberFunction());
|
|
if (cxxMethodDecl->getLocation().isValid() && !ignoreLocation(cxxMethodDecl))
|
|
callSet.insert(niceName(cxxMethodDecl));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VirtualDown::TraverseFunctionDecl(FunctionDecl* f)
|
|
{
|
|
auto copy = currentFunctionDecl;
|
|
currentFunctionDecl = f;
|
|
bool ret = RecursiveASTVisitor::TraverseFunctionDecl(f);
|
|
currentFunctionDecl = copy;
|
|
return ret;
|
|
}
|
|
bool VirtualDown::TraverseCXXMethodDecl(CXXMethodDecl* f)
|
|
{
|
|
auto copy = currentFunctionDecl;
|
|
currentFunctionDecl = f;
|
|
bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(f);
|
|
currentFunctionDecl = copy;
|
|
return ret;
|
|
}
|
|
bool VirtualDown::TraverseCXXConversionDecl(CXXConversionDecl* f)
|
|
{
|
|
auto copy = currentFunctionDecl;
|
|
currentFunctionDecl = f;
|
|
bool ret = RecursiveASTVisitor::TraverseCXXConversionDecl(f);
|
|
currentFunctionDecl = copy;
|
|
return ret;
|
|
}
|
|
bool VirtualDown::TraverseCXXDeductionGuideDecl(CXXDeductionGuideDecl* f)
|
|
{
|
|
auto copy = currentFunctionDecl;
|
|
currentFunctionDecl = f;
|
|
bool ret = RecursiveASTVisitor::TraverseCXXDeductionGuideDecl(f);
|
|
currentFunctionDecl = copy;
|
|
return ret;
|
|
}
|
|
|
|
std::string VirtualDown::niceName(const CXXMethodDecl* cxxMethodDecl)
|
|
{
|
|
std::string s = cxxMethodDecl->getReturnType().getCanonicalType().getAsString() + " "
|
|
+ cxxMethodDecl->getQualifiedNameAsString() + "(";
|
|
for (const ParmVarDecl* pParmVarDecl : cxxMethodDecl->parameters())
|
|
{
|
|
s += pParmVarDecl->getType().getCanonicalType().getAsString();
|
|
s += ",";
|
|
}
|
|
s += ")";
|
|
if (cxxMethodDecl->isConst())
|
|
{
|
|
s += "const";
|
|
}
|
|
return s;
|
|
}
|
|
|
|
std::string VirtualDown::toString(SourceLocation loc)
|
|
{
|
|
SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc(loc);
|
|
StringRef name = getFilenameOfLocation(expansionLoc);
|
|
std::string sourceLocation
|
|
= std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
|
|
+ std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
|
|
loplugin::normalizeDotDotInFilePath(sourceLocation);
|
|
return sourceLocation;
|
|
}
|
|
|
|
loplugin::Plugin::Registration<VirtualDown> X("virtualdown", false);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|