office-gobmx/compilerplugins/clang/getimplementationname.cxx
Stephan Bergmann 9e2dbeea9f Adapt to LLVM 15 trunk clang::StringLiteral::isAscii rename
<a9a60f20e6>
"[Clang] Rename StringLiteral::isAscii() => isOrdinary() [NFC]"

Change-Id: Iac293c19bd135a94dcc3a3ef9f252ca6175c959a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136744
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2022-07-01 22:12:41 +02:00

314 lines
9.5 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/.
*/
// only compile this on unixy system
// as we don't want to bother with x-platform system()/mkdir()
#if defined(__unix__)
// only compile this on clang 3.7 or higher, which is known to work
// there were problems on clang 3.5 at least
#include "config_clang.h"
#include <cassert>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <fstream>
#include <regex>
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
#include "clang/Frontend/CompilerInstance.h"
namespace {
clang::Expr const * ignoreParenImplicitComma(clang::Expr const * expr) {
for (;;) {
auto const e1 = expr->IgnoreParens()->IgnoreImplicit();
if (e1 != expr) {
expr = e1;
continue;
}
// auto const e1 = dyn_cast<clang::ExprWithCleanups>(expr);
// if (e1 != nullptr) {
// expr = e1->getSubExpr();
// continue;
// }
auto const e2 = dyn_cast<clang::BinaryOperator>(expr);
if (e2 != nullptr && e2->getOpcode() == clang::BO_Comma) {
expr = e2->getRHS();
continue;
}
return expr;
}
}
bool overridesXServiceInfo(clang::CXXMethodDecl const * decl) {
for (auto i = decl->begin_overridden_methods();
i != decl->end_overridden_methods(); ++i)
{
if (((*i)->getParent()->getQualifiedNameAsString()
== "com::sun::star::lang::XServiceInfo")
|| overridesXServiceInfo(*i))
{
return true;
}
}
return false;
}
std::string replace_all(std::string subject, const std::string& search, const std::string& replace)
{
size_t pos = 0;
while ((pos = subject.find(search, pos)) != std::string::npos)
{
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
return subject;
}
class GetImplementationName:
public loplugin::FilteringPlugin<GetImplementationName>
{
public:
explicit GetImplementationName(loplugin::InstantiationData const & data)
: FilteringPlugin(data)
, m_Outdir(initOutdir())
, m_OutdirCreated(false)
, m_Srcdir(initSrcdir())
{}
void run() override;
bool VisitCXXMethodDecl(clang::CXXMethodDecl const * decl);
private:
bool isStringConstant(Expr const * expr, clang::StringRef * string);
bool returnsStringConstant(
FunctionDecl const * decl, clang::StringRef * string);
void ensureOutdirCreated()
{
if(m_OutdirCreated)
return;
std::string cmd("mkdir -p ");
cmd += "\"" + m_Outdir + "\"";
if(system(cmd.c_str()) != 0) {
report(
clang::DiagnosticsEngine::Error,
"Error creating ServiceImplementations output dir \"%0\".")
<< m_Outdir;
}
m_OutdirCreated = true;
}
void generateOutput(FunctionDecl const * decl, const std::string unoimpl, const std::string cppclass);
std::string initOutdir();
std::string initSrcdir();
const std::string m_Outdir;
bool m_OutdirCreated;
const std::string m_Srcdir;
};
void GetImplementationName::run() {
if (compiler.getLangOpts().CPlusPlus) {
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
}
bool GetImplementationName::VisitCXXMethodDecl(
clang::CXXMethodDecl const * decl)
{
if (ignoreLocation(decl) || !decl->doesThisDeclarationHaveABody()
|| !decl->isVirtual())
{
return true;
}
auto const id = decl->getIdentifier();
if (id == nullptr || id->getName() != "getImplementationName"
|| !overridesXServiceInfo(decl))
{
return true;
}
clang::StringRef unoimpl;
if (!returnsStringConstant(decl, &unoimpl)) {
report(
clang::DiagnosticsEngine::Warning,
"cannot determine returned string", decl->getLocation())
<< decl->getSourceRange();
return true;
}
generateOutput(decl, unoimpl.str(), decl->getParent()->getQualifiedNameAsString());
return true;
}
bool GetImplementationName::isStringConstant(
Expr const * expr, clang::StringRef * string)
{
QualType t = expr->getType();
if (!(t->isConstantArrayType() && t.isConstQualified()
&& (loplugin::TypeCheck(t->getAsArrayTypeUnsafe()->getElementType())
.Char())))
{
return false;
}
DeclRefExpr const * dre = dyn_cast<DeclRefExpr>(expr);
if (dre != nullptr) {
VarDecl const * var = dyn_cast<VarDecl>(dre->getDecl());
if (var != nullptr) {
Expr const * init = var->getAnyInitializer();
if (init != nullptr) {
expr = ignoreParenImplicitComma(init);
}
}
}
clang::StringLiteral const * lit = dyn_cast<clang::StringLiteral>(expr);
if (lit != nullptr) {
if (!compat::isOrdinary(lit)) {
return false;
}
*string = lit->getString();
return true;
}
APValue v;
if (!expr->isCXX11ConstantExpr(compiler.getASTContext(), &v)) {
return false;
}
switch (v.getKind()) {
case APValue::LValue:
return false; //TODO
case APValue::Array:
{
if (v.hasArrayFiller()) { //TODO: handle final NUL filler?
return false;
}
unsigned n = v.getArraySize();
assert(n != 0);
for (unsigned i = 0; i != n; ++i) {
APValue e(v.getArrayInitializedElt(i));
if (!e.isInt()) { //TODO: assert?
return false;
}
APSInt iv = e.getInt();
if (iv == 0) {
if (i == n -1) {
continue;
}
return false;
} else if (iv.uge(0x80)) {
return false;
}
//TODO
}
return false;//TODO
}
default:
abort(); //TODO???
}
}
bool GetImplementationName::returnsStringConstant(
FunctionDecl const * decl, clang::StringRef * string)
{
auto s1 = decl->getBody();
if (s1 == nullptr) {
return false;
}
for (;;) {
auto s2 = dyn_cast<clang::CompoundStmt>(s1);
if (s2 == nullptr) {
auto const s3 = dyn_cast<clang::CXXTryStmt>(s1);
if (s3 == nullptr) {
break;
}
s2 = s3->getTryBlock();
}
if (s2->size() != 1) {
break;
}
s1 = s2->body_front();
}
auto const s4 = dyn_cast<clang::ReturnStmt>(s1);
if (s4 == nullptr) {
return false;
}
for (auto e1 = ignoreParenImplicitComma(s4->getRetValue());;) {
auto const e2 = dyn_cast<clang::CallExpr>(e1);
if (e2 != nullptr) {
auto const d = e2->getDirectCallee();
return d != nullptr && returnsStringConstant(d, string);
}
auto const e3 = dyn_cast<clang::CXXFunctionalCastExpr>(e1);
if (e3 != nullptr) {
e1 = ignoreParenImplicitComma(e3->getSubExpr());
continue;
}
auto const e4 = dyn_cast<clang::CXXConstructExpr>(e1);
if (e4 != nullptr) {
if (e4->getNumArgs() < 1) {
return false;
}
e1 = ignoreParenImplicitComma(e4->getArg(0));
continue;
}
return isStringConstant(e1, string);
}
}
void GetImplementationName::generateOutput(FunctionDecl const * decl, const std::string unoimpl, const std::string cppclass) {
ensureOutdirCreated();
clang::SourceManager& sm(compiler.getSourceManager());
const std::string absfilename(sm.getFilename(decl->getSourceRange().getBegin()).str());
if(absfilename.length() <= m_Srcdir.length())
return;
const std::string filename(absfilename.substr(m_Srcdir.length()+1));
const std::regex moduleregex("^\\w+");
std::smatch modulematch;
std::regex_search(filename, modulematch, moduleregex);
if(modulematch.empty())
return;
const std::string module(modulematch[0]);
const std::string doublecolonregex("::");
const std::string cppclassweb(replace_all(cppclass, doublecolonregex, "_1_1"));
std::ofstream redirectfile(m_Outdir + "/" + unoimpl + ".html");
redirectfile << "<meta http-equiv=\"refresh\" content=\"0; URL=http://docs.libreoffice.org/" << module << "/html/class" << cppclassweb << "\">\n";
redirectfile.close();
}
std::string GetImplementationName::initOutdir() {
{
char* pWorkdir = getenv("WORKDIR");
if(pWorkdir) {
std::string result(pWorkdir);
result += "/ServiceImplementations";
return result;
}
report(
clang::DiagnosticsEngine::Error, "WORKDIR unset, don't know where to write service implementation info.");
return std::string();
}
}
std::string GetImplementationName::initSrcdir() {
{
char* pSrcdir = getenv("SRCDIR");
if(!pSrcdir) {
report(
clang::DiagnosticsEngine::Error, "SRCDIR unset, don't know where the source base is.");
}
return std::string(pSrcdir);
}
}
loplugin::Plugin::Registration<GetImplementationName> X(
"getimplementationname", false);
}
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */