[Libreoffice-commits] core.git: 2 commits - compilerplugins/clang unotools/source

Bjoern Michaelsen bjoern.michaelsen at canonical.com
Wed Nov 4 15:28:59 PST 2015


 compilerplugins/clang/getimplementationname.cxx |  306 ++++++++++++++++++++++++
 unotools/source/misc/ServiceDocumenter.cxx      |    2 
 2 files changed, 307 insertions(+), 1 deletion(-)

New commits:
commit 2e5be58e7c81436139785074ee2cc6991b6f9e24
Author: Bjoern Michaelsen <bjoern.michaelsen at canonical.com>
Date:   Thu Nov 5 00:00:07 2015 +0100

    complete initial compiler plugin for the ServiceDocumenter
    
    - this creates a set of static html-meta-redirect-pages linking the UNO
      implementation name to its C++ class, while doing a clang compile from
      scratch
    - the output is written to $(WORKDIR)/ServiceImplementations
    - still quite some corner cases missing, e.g:
      - anonymous namespaces
      - non-trivial getImplementationName() functions
    
    Change-Id: I19a0e8a3901277dc3811eb428cac9d00737f14a9

diff --git a/compilerplugins/clang/getimplementationname.cxx b/compilerplugins/clang/getimplementationname.cxx
index 3b999c5..ad2b93a 100644
--- a/compilerplugins/clang/getimplementationname.cxx
+++ b/compilerplugins/clang/getimplementationname.cxx
@@ -7,9 +7,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+// only compile this on unixy system
+// as we dont 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
+#if (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 7))
 #include <cassert>
-
+#include <stdlib.h>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <regex>
 #include "plugin.hxx"
+#include "clang/Frontend/CompilerInstance.h"
 
 namespace {
 
@@ -58,9 +69,12 @@ class GetImplementationName:
     public loplugin::Plugin
 {
 public:
-    explicit GetImplementationName(InstantiationData const & data): Plugin(data)
+    explicit GetImplementationName(InstantiationData const & data)
+        : Plugin(data)
+        , m_Outdir(initOutdir())
+        , m_OutdirCreated(false)
+        , m_Srcdir(initSrcdir())
     {}
-
     void run() override;
 
     bool VisitCXXMethodDecl(clang::CXXMethodDecl const * decl);
@@ -70,6 +84,28 @@ private:
 
     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() {
@@ -92,19 +128,15 @@ bool GetImplementationName::VisitCXXMethodDecl(
     {
         return true;
     }
-    clang::StringRef str;
-    if (!returnsStringConstant(decl, &str)) {
+    clang::StringRef unoimpl;
+    if (!returnsStringConstant(decl, &unoimpl)) {
         report(
             clang::DiagnosticsEngine::Warning,
             "cannot determine returned string", decl->getLocation())
             << decl->getSourceRange();
         return true;
     }
-    report(
-        clang::DiagnosticsEngine::Warning, "\"%0\" implemented by %1",
-        decl->getLocation())
-        << str << decl->getParent()->getQualifiedNameAsString()
-        << decl->getSourceRange();
+    generateOutput(decl, unoimpl.str(), decl->getParent()->getQualifiedNameAsString());
     return true;
 }
 
@@ -221,8 +253,54 @@ bool GetImplementationName::returnsStringConstant(
     }
 }
 
+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::regex doublecolonregex("::");
+    const std::string cppclassweb(std::regex_replace(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, dont 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, dont know where the source base is.");
+    }
+    return std::string(pSrcdir);
+    }
+}
 loplugin::Plugin::Registration<GetImplementationName> X(
     "getimplementationname");
 }
+#endif
+#endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/ServiceDocumenter.cxx b/unotools/source/misc/ServiceDocumenter.cxx
index 6750a69..0cc5af5 100644
--- a/unotools/source/misc/ServiceDocumenter.cxx
+++ b/unotools/source/misc/ServiceDocumenter.cxx
@@ -21,7 +21,7 @@ void unotools::misc::ServiceDocumenter::showCoreDocs(const Reference<XServiceInf
         return;
     auto xMSF(m_xContext->getServiceManager());
     Reference<system::XSystemShellExecute> xShell(xMSF->createInstanceWithContext("com.sun.star.system.SystemShellExecute", m_xContext), uno::UNO_QUERY);
-    xShell->execute(m_sCoreBaseUrl + xService->getImplementationName(), "", 0);
+    xShell->execute(m_sCoreBaseUrl + xService->getImplementationName() + ".html", "", 0);
 }
 
 void unotools::misc::ServiceDocumenter::showInterfaceDocs(const Reference<XTypeProvider>& xTypeProvider)
commit 0305078a6a5d4d2b0ef64996c056d86b5eda42ce
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Wed Nov 4 00:20:46 2015 +0100

    skeleton implementation for ServiceDocumenter compiler plugin
    
    Change-Id: I6a9c957c0c4dac16365d269e57c30210619d23c9

diff --git a/compilerplugins/clang/getimplementationname.cxx b/compilerplugins/clang/getimplementationname.cxx
new file mode 100644
index 0000000..3b999c5
--- /dev/null
+++ b/compilerplugins/clang/getimplementationname.cxx
@@ -0,0 +1,228 @@
+/* -*- 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 "plugin.hxx"
+
+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 isPlainChar(clang::QualType type) {
+    return type->isSpecificBuiltinType(clang::BuiltinType::Char_S)
+        || type->isSpecificBuiltinType(clang::BuiltinType::Char_U);
+}
+
+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;
+}
+
+class GetImplementationName:
+    public clang::RecursiveASTVisitor<GetImplementationName>,
+    public loplugin::Plugin
+{
+public:
+    explicit GetImplementationName(InstantiationData const & data): Plugin(data)
+    {}
+
+    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 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 str;
+    if (!returnsStringConstant(decl, &str)) {
+        report(
+            clang::DiagnosticsEngine::Warning,
+            "cannot determine returned string", decl->getLocation())
+            << decl->getSourceRange();
+        return true;
+    }
+    report(
+        clang::DiagnosticsEngine::Warning, "\"%0\" implemented by %1",
+        decl->getLocation())
+        << str << decl->getParent()->getQualifiedNameAsString()
+        << decl->getSourceRange();
+    return true;
+}
+
+bool GetImplementationName::isStringConstant(
+    Expr const * expr, clang::StringRef * string)
+{
+    QualType t = expr->getType();
+    if (!(t->isConstantArrayType() && t.isConstQualified()
+          && isPlainChar(t->getAsArrayTypeUnsafe()->getElementType())))
+    {
+        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);
+            }
+        }
+    }
+    StringLiteral const * lit = dyn_cast<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:
+        assert(false); //TODO???
+        return "BAD11";
+    }
+}
+
+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);
+    }
+}
+
+loplugin::Plugin::Registration<GetImplementationName> X(
+    "getimplementationname");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list