summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/getimplementationname.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compilerplugins/clang/getimplementationname.cxx313
1 files changed, 313 insertions, 0 deletions
diff --git a/compilerplugins/clang/getimplementationname.cxx b/compilerplugins/clang/getimplementationname.cxx
new file mode 100644
index 000000000..acc45acb7
--- /dev/null
+++ b/compilerplugins/clang/getimplementationname.cxx
@@ -0,0 +1,313 @@
+/* -*- 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 "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 (!lit->isAscii()) {
+ 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: */