office-gobmx/compilerplugins/clang/finalmethods.cxx

154 lines
5.1 KiB
C++
Raw Normal View History

/* -*- 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 <unordered_map>
#include <unordered_set>
#include <string>
#include <iostream>
#include "config_clang.h"
#include "compat.hxx"
#include "plugin.hxx"
#include <fstream>
/**
Look for methods that are final i.e. nothing overrides them
Making the method final means the compiler can devirtualise
some method calls.
The process goes something like this:
$ make check
$ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='finalmethods' check
$ ./compilerplugins/clang/finalmethods.py
*/
namespace
{
// try to limit the voluminous output a little
static std::unordered_set<std::string> overriddenSet;
static std::unordered_map<std::string, std::string> definitionMap; // methodName -> location
class FinalMethods : public RecursiveASTVisitor<FinalMethods>, public loplugin::Plugin
{
public:
explicit FinalMethods(loplugin::InstantiationData const& data)
: Plugin(data)
{
}
virtual void run() override
{
handler.enableTreeWideAnalysisMode();
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 std::string& s : overriddenSet)
output += "overridden:\t" + s + "\n";
for (const auto& s : definitionMap)
output += "definition:\t" + s.first + "\t" + s.second + "\n";
std::ofstream myfile;
myfile.open(WORKDIR "/loplugin.finalmethods.log", std::ios::app | std::ios::out);
myfile << output;
myfile.close();
}
bool shouldVisitTemplateInstantiations() const { return true; }
bool shouldVisitImplicitCode() const { return true; }
bool VisitCXXMethodDecl(const CXXMethodDecl*);
private:
std::string toString(SourceLocation loc);
std::string niceName(const CXXMethodDecl*);
};
bool FinalMethods::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
{
if (ignoreLocation(methodDecl))
return true;
if (!methodDecl->isThisDeclarationADefinition())
return true;
// don't care about compiler-generated functions
if (methodDecl->isImplicit())
return true;
if (!methodDecl->isVirtual())
return true;
if (!methodDecl->getLocation().isValid())
return true;
if (isa<CXXDestructorDecl>(methodDecl) || isa<CXXConstructorDecl>(methodDecl))
return true;
methodDecl = methodDecl->getCanonicalDecl();
if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(methodDecl->getLocation())))
return true;
std::string methodNiceName = niceName(methodDecl);
// If the containing class/struct is final, then the method is effectively final too.
if (!methodDecl->hasAttr<FinalAttr>() && !methodDecl->getParent()->hasAttr<FinalAttr>())
definitionMap.insert({ methodNiceName, toString(methodDecl->getBeginLoc()) });
for (auto it = methodDecl->begin_overridden_methods();
it != methodDecl->end_overridden_methods(); ++it)
overriddenSet.insert(niceName(*it));
return true;
}
std::string FinalMethods::niceName(const CXXMethodDecl* methodDecl)
{
if (methodDecl->getInstantiatedFromMemberFunction())
methodDecl = dyn_cast<CXXMethodDecl>(methodDecl->getInstantiatedFromMemberFunction());
else if (methodDecl->getTemplateInstantiationPattern())
methodDecl = dyn_cast<CXXMethodDecl>(methodDecl->getTemplateInstantiationPattern());
std::string returnType = methodDecl->getReturnType().getCanonicalType().getAsString();
const CXXRecordDecl* recordDecl = methodDecl->getParent();
std::string nameAndParams
= recordDecl->getQualifiedNameAsString() + "::" + methodDecl->getNameAsString() + "(";
bool bFirst = true;
for (const ParmVarDecl* pParmVarDecl : methodDecl->parameters())
{
if (bFirst)
bFirst = false;
else
nameAndParams += ",";
nameAndParams += pParmVarDecl->getType().getCanonicalType().getAsString();
}
nameAndParams += ")";
if (methodDecl->isConst())
nameAndParams += " const";
return returnType + " " + nameAndParams + " " + returnType;
}
std::string FinalMethods::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<FinalMethods> X("finalmethods", false);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */