1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 // only compile this on unixy system
11 // as we don't want to bother with x-platform system()/mkdir()
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"
25 #include "clang/Frontend/CompilerInstance.h"
29 clang::Expr
const * ignoreParenImplicitComma(clang::Expr
const * expr
) {
31 auto const e1
= expr
->IgnoreParens()->IgnoreImplicit();
36 // auto const e1 = dyn_cast<clang::ExprWithCleanups>(expr);
37 // if (e1 != nullptr) {
38 // expr = e1->getSubExpr();
41 auto const e2
= dyn_cast
<clang::BinaryOperator
>(expr
);
42 if (e2
!= nullptr && e2
->getOpcode() == clang::BO_Comma
) {
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
))
64 std::string
replace_all(std::string subject
, const std::string
& search
, const std::string
& replace
)
68 while ((pos
= subject
.find(search
, pos
)) != std::string::npos
)
70 subject
.replace(pos
, search
.length(), replace
);
71 pos
+= replace
.length();
77 class GetImplementationName
:
78 public loplugin::FilteringPlugin
<GetImplementationName
>
81 explicit GetImplementationName(loplugin::InstantiationData
const & data
)
82 : FilteringPlugin(data
)
83 , m_Outdir(initOutdir())
84 , m_OutdirCreated(false)
85 , m_Srcdir(initSrcdir())
89 bool VisitCXXMethodDecl(clang::CXXMethodDecl
const * decl
);
92 bool isStringConstant(Expr
const * expr
, clang::StringRef
* string
);
94 bool returnsStringConstant(
95 FunctionDecl
const * decl
, clang::StringRef
* string
);
97 void ensureOutdirCreated()
101 std::string
cmd("mkdir -p ");
102 cmd
+= "\"" + m_Outdir
+ "\"";
103 if(system(cmd
.c_str()) != 0) {
105 clang::DiagnosticsEngine::Error
,
106 "Error creating ServiceImplementations output dir \"%0\".")
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())
134 auto const id
= decl
->getIdentifier();
135 if (id
== nullptr || id
->getName() != "getImplementationName"
136 || !overridesXServiceInfo(decl
))
140 clang::StringRef unoimpl
;
141 if (!returnsStringConstant(decl
, &unoimpl
)) {
143 clang::DiagnosticsEngine::Warning
,
144 "cannot determine returned string", decl
->getLocation())
145 << decl
->getSourceRange();
148 generateOutput(decl
, unoimpl
.str(), decl
->getParent()->getQualifiedNameAsString());
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())
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
)) {
177 *string
= lit
->getString();
181 if (!expr
->isCXX11ConstantExpr(compiler
.getASTContext(), &v
)) {
184 switch (v
.getKind()) {
185 case APValue::LValue
:
189 if (v
.hasArrayFiller()) { //TODO: handle final NUL filler?
192 unsigned n
= v
.getArraySize();
194 for (unsigned i
= 0; i
!= n
; ++i
) {
195 APValue
e(v
.getArrayInitializedElt(i
));
196 if (!e
.isInt()) { //TODO: assert?
199 APSInt iv
= e
.getInt();
205 } else if (iv
.uge(0x80)) {
217 bool GetImplementationName::returnsStringConstant(
218 FunctionDecl
const * decl
, clang::StringRef
* string
)
220 auto s1
= decl
->getBody();
225 auto s2
= dyn_cast
<clang::CompoundStmt
>(s1
);
227 auto const s3
= dyn_cast
<clang::CXXTryStmt
>(s1
);
231 s2
= s3
->getTryBlock();
233 if (s2
->size() != 1) {
236 s1
= s2
->body_front();
238 auto const s4
= dyn_cast
<clang::ReturnStmt
>(s1
);
242 for (auto e1
= ignoreParenImplicitComma(s4
->getRetValue());;) {
243 auto const e2
= dyn_cast
<clang::CallExpr
>(e1
);
245 auto const d
= e2
->getDirectCallee();
246 return d
!= nullptr && returnsStringConstant(d
, string
);
248 auto const e3
= dyn_cast
<clang::CXXFunctionalCastExpr
>(e1
);
250 e1
= ignoreParenImplicitComma(e3
->getSubExpr());
253 auto const e4
= dyn_cast
<clang::CXXConstructExpr
>(e1
);
255 if (e4
->getNumArgs() < 1) {
258 e1
= ignoreParenImplicitComma(e4
->getArg(0));
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())
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())
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");
289 std::string
result(pWorkdir
);
290 result
+= "/ServiceImplementations";
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");
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);
314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */