bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / getimplementationname.cxx
blobcf234079750edb2a0963ef7b90293cc539a3485f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 // only compile this on unixy system
11 // as we don't want to bother with x-platform system()/mkdir()
12 #if defined(__unix__)
13 // only compile this on clang 3.7 or higher, which is known to work
14 // there were problems on clang 3.5 at least
15 #include "config_clang.h"
16 #include <cassert>
17 #include <stdlib.h>
18 #include <string>
19 #include <iostream>
20 #include <fstream>
21 #include <regex>
22 #include "check.hxx"
23 #include "plugin.hxx"
24 #include "clang/Frontend/CompilerInstance.h"
26 namespace {
28 clang::Expr const * ignoreParenImplicitComma(clang::Expr const * expr) {
29 for (;;) {
30 auto const e1 = expr->IgnoreParens()->IgnoreImplicit();
31 if (e1 != expr) {
32 expr = e1;
33 continue;
35 // auto const e1 = dyn_cast<clang::ExprWithCleanups>(expr);
36 // if (e1 != nullptr) {
37 // expr = e1->getSubExpr();
38 // continue;
39 // }
40 auto const e2 = dyn_cast<clang::BinaryOperator>(expr);
41 if (e2 != nullptr && e2->getOpcode() == clang::BO_Comma) {
42 expr = e2->getRHS();
43 continue;
45 return expr;
49 bool overridesXServiceInfo(clang::CXXMethodDecl const * decl) {
50 for (auto i = decl->begin_overridden_methods();
51 i != decl->end_overridden_methods(); ++i)
53 if (((*i)->getParent()->getQualifiedNameAsString()
54 == "com::sun::star::lang::XServiceInfo")
55 || overridesXServiceInfo(*i))
57 return true;
60 return false;
63 std::string replace_all(std::string subject, const std::string& search, const std::string& replace)
65 size_t pos = 0;
67 while ((pos = subject.find(search, pos)) != std::string::npos)
69 subject.replace(pos, search.length(), replace);
70 pos += replace.length();
73 return subject;
76 class GetImplementationName:
77 public loplugin::FilteringPlugin<GetImplementationName>
79 public:
80 explicit GetImplementationName(loplugin::InstantiationData const & data)
81 : FilteringPlugin(data)
82 , m_Outdir(initOutdir())
83 , m_OutdirCreated(false)
84 , m_Srcdir(initSrcdir())
86 void run() override;
88 bool VisitCXXMethodDecl(clang::CXXMethodDecl const * decl);
90 private:
91 bool isStringConstant(Expr const * expr, clang::StringRef * string);
93 bool returnsStringConstant(
94 FunctionDecl const * decl, clang::StringRef * string);
96 void ensureOutdirCreated()
98 if(m_OutdirCreated)
99 return;
100 std::string cmd("mkdir -p ");
101 cmd += "\"" + m_Outdir + "\"";
102 if(system(cmd.c_str()) != 0) {
103 report(
104 clang::DiagnosticsEngine::Error,
105 "Error creating ServiceImplementations output dir \"%0\".")
106 << m_Outdir;
108 m_OutdirCreated = true;
111 void generateOutput(FunctionDecl const * decl, const std::string unoimpl, const std::string cppclass);
112 std::string initOutdir();
113 std::string initSrcdir();
114 const std::string m_Outdir;
115 bool m_OutdirCreated;
116 const std::string m_Srcdir;
119 void GetImplementationName::run() {
120 if (compiler.getLangOpts().CPlusPlus) {
121 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
125 bool GetImplementationName::VisitCXXMethodDecl(
126 clang::CXXMethodDecl const * decl)
128 if (ignoreLocation(decl) || !decl->doesThisDeclarationHaveABody()
129 || !decl->isVirtual())
131 return true;
133 auto const id = decl->getIdentifier();
134 if (id == nullptr || id->getName() != "getImplementationName"
135 || !overridesXServiceInfo(decl))
137 return true;
139 clang::StringRef unoimpl;
140 if (!returnsStringConstant(decl, &unoimpl)) {
141 report(
142 clang::DiagnosticsEngine::Warning,
143 "cannot determine returned string", decl->getLocation())
144 << decl->getSourceRange();
145 return true;
147 generateOutput(decl, unoimpl.str(), decl->getParent()->getQualifiedNameAsString());
148 return true;
151 bool GetImplementationName::isStringConstant(
152 Expr const * expr, clang::StringRef * string)
154 QualType t = expr->getType();
155 if (!(t->isConstantArrayType() && t.isConstQualified()
156 && (loplugin::TypeCheck(t->getAsArrayTypeUnsafe()->getElementType())
157 .Char())))
159 return false;
161 DeclRefExpr const * dre = dyn_cast<DeclRefExpr>(expr);
162 if (dre != nullptr) {
163 VarDecl const * var = dyn_cast<VarDecl>(dre->getDecl());
164 if (var != nullptr) {
165 Expr const * init = var->getAnyInitializer();
166 if (init != nullptr) {
167 expr = ignoreParenImplicitComma(init);
171 clang::StringLiteral const * lit = dyn_cast<clang::StringLiteral>(expr);
172 if (lit != nullptr) {
173 if (!lit->isAscii()) {
174 return false;
176 *string = lit->getString();
177 return true;
179 APValue v;
180 if (!expr->isCXX11ConstantExpr(compiler.getASTContext(), &v)) {
181 return false;
183 switch (v.getKind()) {
184 case APValue::LValue:
185 return false; //TODO
186 case APValue::Array:
188 if (v.hasArrayFiller()) { //TODO: handle final NUL filler?
189 return false;
191 unsigned n = v.getArraySize();
192 assert(n != 0);
193 for (unsigned i = 0; i != n; ++i) {
194 APValue e(v.getArrayInitializedElt(i));
195 if (!e.isInt()) { //TODO: assert?
196 return false;
198 APSInt iv = e.getInt();
199 if (iv == 0) {
200 if (i == n -1) {
201 continue;
203 return false;
204 } else if (iv.uge(0x80)) {
205 return false;
207 //TODO
209 return false;//TODO
211 default:
212 assert(false); //TODO???
213 return "BAD11";
217 bool GetImplementationName::returnsStringConstant(
218 FunctionDecl const * decl, clang::StringRef * string)
220 auto s1 = decl->getBody();
221 if (s1 == nullptr) {
222 return false;
224 for (;;) {
225 auto s2 = dyn_cast<clang::CompoundStmt>(s1);
226 if (s2 == nullptr) {
227 auto const s3 = dyn_cast<clang::CXXTryStmt>(s1);
228 if (s3 == nullptr) {
229 break;
231 s2 = s3->getTryBlock();
233 if (s2->size() != 1) {
234 break;
236 s1 = s2->body_front();
238 auto const s4 = dyn_cast<clang::ReturnStmt>(s1);
239 if (s4 == nullptr) {
240 return false;
242 for (auto e1 = ignoreParenImplicitComma(s4->getRetValue());;) {
243 auto const e2 = dyn_cast<clang::CallExpr>(e1);
244 if (e2 != nullptr) {
245 auto const d = e2->getDirectCallee();
246 return d != nullptr && returnsStringConstant(d, string);
248 auto const e3 = dyn_cast<clang::CXXFunctionalCastExpr>(e1);
249 if (e3 != nullptr) {
250 e1 = ignoreParenImplicitComma(e3->getSubExpr());
251 continue;
253 auto const e4 = dyn_cast<clang::CXXConstructExpr>(e1);
254 if (e4 != nullptr) {
255 if (e4->getNumArgs() < 1) {
256 return false;
258 e1 = ignoreParenImplicitComma(e4->getArg(0));
259 continue;
261 return isStringConstant(e1, string);
265 void GetImplementationName::generateOutput(FunctionDecl const * decl, const std::string unoimpl, const std::string cppclass) {
266 ensureOutdirCreated();
267 clang::SourceManager& sm(compiler.getSourceManager());
268 const std::string absfilename(sm.getFilename(decl->getSourceRange().getBegin()).str());
269 if(absfilename.length() <= m_Srcdir.length())
270 return;
271 const std::string filename(absfilename.substr(m_Srcdir.length()+1));
272 const std::regex moduleregex("^\\w+");
273 std::smatch modulematch;
274 std::regex_search(filename, modulematch, moduleregex);
275 if(modulematch.empty())
276 return;
277 const std::string module(modulematch[0]);
278 const std::string doublecolonregex("::");
279 const std::string cppclassweb(replace_all(cppclass, doublecolonregex, "_1_1"));
280 std::ofstream redirectfile(m_Outdir + "/" + unoimpl + ".html");
281 redirectfile << "<meta http-equiv=\"refresh\" content=\"0; URL=http://docs.libreoffice.org/" << module << "/html/class" << cppclassweb << "\">\n";
282 redirectfile.close();
285 std::string GetImplementationName::initOutdir() {
287 char* pWorkdir = getenv("WORKDIR");
288 if(pWorkdir) {
289 std::string result(pWorkdir);
290 result += "/ServiceImplementations";
291 return result;
293 report(
294 clang::DiagnosticsEngine::Error, "WORKDIR unset, don't know where to write service implementation info.");
295 return std::string();
299 std::string GetImplementationName::initSrcdir() {
301 char* pSrcdir = getenv("SRCDIR");
302 if(!pSrcdir) {
303 report(
304 clang::DiagnosticsEngine::Error, "SRCDIR unset, don't know where the source base is.");
306 return std::string(pSrcdir);
309 loplugin::Plugin::Registration<GetImplementationName> X(
310 "getimplementationname", false);
312 #endif
314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */