cid#1555694 COPY_INSTEAD_OF_MOVE
[LibreOffice.git] / compilerplugins / clang / getimplementationname.cxx
blob65cbabfdd4e996712903c596d09144598b3f20d3
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 "compat.hxx"
24 #include "plugin.hxx"
25 #include "clang/Frontend/CompilerInstance.h"
27 namespace {
29 clang::Expr const * ignoreParenImplicitComma(clang::Expr const * expr) {
30 for (;;) {
31 auto const e1 = expr->IgnoreParens()->IgnoreImplicit();
32 if (e1 != expr) {
33 expr = e1;
34 continue;
36 // auto const e1 = dyn_cast<clang::ExprWithCleanups>(expr);
37 // if (e1 != nullptr) {
38 // expr = e1->getSubExpr();
39 // continue;
40 // }
41 auto const e2 = dyn_cast<clang::BinaryOperator>(expr);
42 if (e2 != nullptr && e2->getOpcode() == clang::BO_Comma) {
43 expr = e2->getRHS();
44 continue;
46 return expr;
50 bool overridesXServiceInfo(clang::CXXMethodDecl const * decl) {
51 for (auto i = decl->begin_overridden_methods();
52 i != decl->end_overridden_methods(); ++i)
54 if (((*i)->getParent()->getQualifiedNameAsString()
55 == "com::sun::star::lang::XServiceInfo")
56 || overridesXServiceInfo(*i))
58 return true;
61 return false;
64 std::string replace_all(std::string subject, const std::string& search, const std::string& replace)
66 size_t pos = 0;
68 while ((pos = subject.find(search, pos)) != std::string::npos)
70 subject.replace(pos, search.length(), replace);
71 pos += replace.length();
74 return subject;
77 class GetImplementationName:
78 public loplugin::FilteringPlugin<GetImplementationName>
80 public:
81 explicit GetImplementationName(loplugin::InstantiationData const & data)
82 : FilteringPlugin(data)
83 , m_Outdir(initOutdir())
84 , m_OutdirCreated(false)
85 , m_Srcdir(initSrcdir())
87 void run() override;
89 bool VisitCXXMethodDecl(clang::CXXMethodDecl const * decl);
91 private:
92 bool isStringConstant(Expr const * expr, clang::StringRef * string);
94 bool returnsStringConstant(
95 FunctionDecl const * decl, clang::StringRef * string);
97 void ensureOutdirCreated()
99 if(m_OutdirCreated)
100 return;
101 std::string cmd("mkdir -p ");
102 cmd += "\"" + m_Outdir + "\"";
103 if(system(cmd.c_str()) != 0) {
104 report(
105 clang::DiagnosticsEngine::Error,
106 "Error creating ServiceImplementations output dir \"%0\".")
107 << m_Outdir;
109 m_OutdirCreated = true;
112 void generateOutput(FunctionDecl const * decl, const std::string unoimpl, const std::string cppclass);
113 std::string initOutdir();
114 std::string initSrcdir();
115 const std::string m_Outdir;
116 bool m_OutdirCreated;
117 const std::string m_Srcdir;
120 void GetImplementationName::run() {
121 if (compiler.getLangOpts().CPlusPlus) {
122 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
126 bool GetImplementationName::VisitCXXMethodDecl(
127 clang::CXXMethodDecl const * decl)
129 if (ignoreLocation(decl) || !decl->doesThisDeclarationHaveABody()
130 || !decl->isVirtual())
132 return true;
134 auto const id = decl->getIdentifier();
135 if (id == nullptr || id->getName() != "getImplementationName"
136 || !overridesXServiceInfo(decl))
138 return true;
140 clang::StringRef unoimpl;
141 if (!returnsStringConstant(decl, &unoimpl)) {
142 report(
143 clang::DiagnosticsEngine::Warning,
144 "cannot determine returned string", decl->getLocation())
145 << decl->getSourceRange();
146 return true;
148 generateOutput(decl, unoimpl.str(), decl->getParent()->getQualifiedNameAsString());
149 return true;
152 bool GetImplementationName::isStringConstant(
153 Expr const * expr, clang::StringRef * string)
155 QualType t = expr->getType();
156 if (!(t->isConstantArrayType() && t.isConstQualified()
157 && (loplugin::TypeCheck(t->getAsArrayTypeUnsafe()->getElementType())
158 .Char())))
160 return false;
162 DeclRefExpr const * dre = dyn_cast<DeclRefExpr>(expr);
163 if (dre != nullptr) {
164 VarDecl const * var = dyn_cast<VarDecl>(dre->getDecl());
165 if (var != nullptr) {
166 Expr const * init = var->getAnyInitializer();
167 if (init != nullptr) {
168 expr = ignoreParenImplicitComma(init);
172 clang::StringLiteral const * lit = dyn_cast<clang::StringLiteral>(expr);
173 if (lit != nullptr) {
174 if (!compat::isOrdinary(lit)) {
175 return false;
177 *string = lit->getString();
178 return true;
180 APValue v;
181 if (!expr->isCXX11ConstantExpr(compiler.getASTContext(), &v)) {
182 return false;
184 switch (v.getKind()) {
185 case APValue::LValue:
186 return false; //TODO
187 case APValue::Array:
189 if (v.hasArrayFiller()) { //TODO: handle final NUL filler?
190 return false;
192 unsigned n = v.getArraySize();
193 assert(n != 0);
194 for (unsigned i = 0; i != n; ++i) {
195 APValue e(v.getArrayInitializedElt(i));
196 if (!e.isInt()) { //TODO: assert?
197 return false;
199 APSInt iv = e.getInt();
200 if (iv == 0) {
201 if (i == n -1) {
202 continue;
204 return false;
205 } else if (iv.uge(0x80)) {
206 return false;
208 //TODO
210 return false;//TODO
212 default:
213 abort(); //TODO???
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: */