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"
24 #include "clang/Frontend/CompilerInstance.h"
28 clang::Expr
const * ignoreParenImplicitComma(clang::Expr
const * expr
) {
30 auto const e1
= expr
->IgnoreParens()->IgnoreImplicit();
35 // auto const e1 = dyn_cast<clang::ExprWithCleanups>(expr);
36 // if (e1 != nullptr) {
37 // expr = e1->getSubExpr();
40 auto const e2
= dyn_cast
<clang::BinaryOperator
>(expr
);
41 if (e2
!= nullptr && e2
->getOpcode() == clang::BO_Comma
) {
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
))
63 std::string
replace_all(std::string subject
, const std::string
& search
, const std::string
& replace
)
67 while ((pos
= subject
.find(search
, pos
)) != std::string::npos
)
69 subject
.replace(pos
, search
.length(), replace
);
70 pos
+= replace
.length();
76 class GetImplementationName
:
77 public loplugin::FilteringPlugin
<GetImplementationName
>
80 explicit GetImplementationName(loplugin::InstantiationData
const & data
)
81 : FilteringPlugin(data
)
82 , m_Outdir(initOutdir())
83 , m_OutdirCreated(false)
84 , m_Srcdir(initSrcdir())
88 bool VisitCXXMethodDecl(clang::CXXMethodDecl
const * decl
);
91 bool isStringConstant(Expr
const * expr
, clang::StringRef
* string
);
93 bool returnsStringConstant(
94 FunctionDecl
const * decl
, clang::StringRef
* string
);
96 void ensureOutdirCreated()
100 std::string
cmd("mkdir -p ");
101 cmd
+= "\"" + m_Outdir
+ "\"";
102 if(system(cmd
.c_str()) != 0) {
104 clang::DiagnosticsEngine::Error
,
105 "Error creating ServiceImplementations output dir \"%0\".")
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())
133 auto const id
= decl
->getIdentifier();
134 if (id
== nullptr || id
->getName() != "getImplementationName"
135 || !overridesXServiceInfo(decl
))
139 clang::StringRef unoimpl
;
140 if (!returnsStringConstant(decl
, &unoimpl
)) {
142 clang::DiagnosticsEngine::Warning
,
143 "cannot determine returned string", decl
->getLocation())
144 << decl
->getSourceRange();
147 generateOutput(decl
, unoimpl
.str(), decl
->getParent()->getQualifiedNameAsString());
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())
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()) {
176 *string
= lit
->getString();
180 if (!expr
->isCXX11ConstantExpr(compiler
.getASTContext(), &v
)) {
183 switch (v
.getKind()) {
184 case APValue::LValue
:
188 if (v
.hasArrayFiller()) { //TODO: handle final NUL filler?
191 unsigned n
= v
.getArraySize();
193 for (unsigned i
= 0; i
!= n
; ++i
) {
194 APValue
e(v
.getArrayInitializedElt(i
));
195 if (!e
.isInt()) { //TODO: assert?
198 APSInt iv
= e
.getInt();
204 } else if (iv
.uge(0x80)) {
216 bool GetImplementationName::returnsStringConstant(
217 FunctionDecl
const * decl
, clang::StringRef
* string
)
219 auto s1
= decl
->getBody();
224 auto s2
= dyn_cast
<clang::CompoundStmt
>(s1
);
226 auto const s3
= dyn_cast
<clang::CXXTryStmt
>(s1
);
230 s2
= s3
->getTryBlock();
232 if (s2
->size() != 1) {
235 s1
= s2
->body_front();
237 auto const s4
= dyn_cast
<clang::ReturnStmt
>(s1
);
241 for (auto e1
= ignoreParenImplicitComma(s4
->getRetValue());;) {
242 auto const e2
= dyn_cast
<clang::CallExpr
>(e1
);
244 auto const d
= e2
->getDirectCallee();
245 return d
!= nullptr && returnsStringConstant(d
, string
);
247 auto const e3
= dyn_cast
<clang::CXXFunctionalCastExpr
>(e1
);
249 e1
= ignoreParenImplicitComma(e3
->getSubExpr());
252 auto const e4
= dyn_cast
<clang::CXXConstructExpr
>(e1
);
254 if (e4
->getNumArgs() < 1) {
257 e1
= ignoreParenImplicitComma(e4
->getArg(0));
260 return isStringConstant(e1
, string
);
264 void GetImplementationName::generateOutput(FunctionDecl
const * decl
, const std::string unoimpl
, const std::string cppclass
) {
265 ensureOutdirCreated();
266 clang::SourceManager
& sm(compiler
.getSourceManager());
267 const std::string
absfilename(sm
.getFilename(decl
->getSourceRange().getBegin()).str());
268 if(absfilename
.length() <= m_Srcdir
.length())
270 const std::string
filename(absfilename
.substr(m_Srcdir
.length()+1));
271 const std::regex
moduleregex("^\\w+");
272 std::smatch modulematch
;
273 std::regex_search(filename
, modulematch
, moduleregex
);
274 if(modulematch
.empty())
276 const std::string
module(modulematch
[0]);
277 const std::string
doublecolonregex("::");
278 const std::string
cppclassweb(replace_all(cppclass
, doublecolonregex
, "_1_1"));
279 std::ofstream
redirectfile(m_Outdir
+ "/" + unoimpl
+ ".html");
280 redirectfile
<< "<meta http-equiv=\"refresh\" content=\"0; URL=http://docs.libreoffice.org/" << module
<< "/html/class" << cppclassweb
<< "\">\n";
281 redirectfile
.close();
284 std::string
GetImplementationName::initOutdir() {
286 char* pWorkdir
= getenv("WORKDIR");
288 std::string
result(pWorkdir
);
289 result
+= "/ServiceImplementations";
293 clang::DiagnosticsEngine::Error
, "WORKDIR unset, don't know where to write service implementation info.");
294 return std::string();
298 std::string
GetImplementationName::initSrcdir() {
300 char* pSrcdir
= getenv("SRCDIR");
303 clang::DiagnosticsEngine::Error
, "SRCDIR unset, don't know where the source base is.");
305 return std::string(pSrcdir
);
308 loplugin::Plugin::Registration
<GetImplementationName
> X(
309 "getimplementationname", false);
313 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */