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.michael...@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 <sberg...@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: */ _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits