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 clang::RecursiveASTVisitor
<GetImplementationName
>,
78 public loplugin::Plugin
81 explicit GetImplementationName(loplugin::InstantiationData
const & 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 (!lit
->isAscii()) {
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)) {
213 assert(false); //TODO???
218 bool GetImplementationName::returnsStringConstant(
219 FunctionDecl
const * decl
, clang::StringRef
* string
)
221 auto s1
= decl
->getBody();
226 auto s2
= dyn_cast
<clang::CompoundStmt
>(s1
);
228 auto const s3
= dyn_cast
<clang::CXXTryStmt
>(s1
);
232 s2
= s3
->getTryBlock();
234 if (s2
->size() != 1) {
237 s1
= s2
->body_front();
239 auto const s4
= dyn_cast
<clang::ReturnStmt
>(s1
);
243 for (auto e1
= ignoreParenImplicitComma(s4
->getRetValue());;) {
244 auto const e2
= dyn_cast
<clang::CallExpr
>(e1
);
246 auto const d
= e2
->getDirectCallee();
247 return d
!= nullptr && returnsStringConstant(d
, string
);
249 auto const e3
= dyn_cast
<clang::CXXFunctionalCastExpr
>(e1
);
251 e1
= ignoreParenImplicitComma(e3
->getSubExpr());
254 auto const e4
= dyn_cast
<clang::CXXConstructExpr
>(e1
);
256 if (e4
->getNumArgs() < 1) {
259 e1
= ignoreParenImplicitComma(e4
->getArg(0));
262 return isStringConstant(e1
, string
);
266 void GetImplementationName::generateOutput(FunctionDecl
const * decl
, const std::string unoimpl
, const std::string cppclass
) {
267 ensureOutdirCreated();
268 clang::SourceManager
& sm(compiler
.getSourceManager());
269 const std::string
absfilename(sm
.getFilename(decl
->getSourceRange().getBegin()).str());
270 if(absfilename
.length() <= m_Srcdir
.length())
272 const std::string
filename(absfilename
.substr(m_Srcdir
.length()+1));
273 const std::regex
moduleregex("^\\w+");
274 std::smatch modulematch
;
275 std::regex_search(filename
, modulematch
, moduleregex
);
276 if(modulematch
.empty())
278 const std::string
module(modulematch
[0]);
279 const std::string
doublecolonregex("::");
280 const std::string
cppclassweb(replace_all(cppclass
, doublecolonregex
, "_1_1"));
281 std::ofstream
redirectfile(m_Outdir
+ "/" + unoimpl
+ ".html");
282 redirectfile
<< "<meta http-equiv=\"refresh\" content=\"0; URL=http://docs.libreoffice.org/" << module
<< "/html/class" << cppclassweb
<< "\">\n";
283 redirectfile
.close();
286 std::string
GetImplementationName::initOutdir() {
288 char* pWorkdir
= getenv("WORKDIR");
290 std::string
result(pWorkdir
);
291 result
+= "/ServiceImplementations";
295 clang::DiagnosticsEngine::Error
, "WORKDIR unset, don't know where to write service implementation info.");
296 return std::string();
300 std::string
GetImplementationName::initSrcdir() {
302 char* pSrcdir
= getenv("SRCDIR");
305 clang::DiagnosticsEngine::Error
, "SRCDIR unset, don't know where the source base is.");
307 return std::string(pSrcdir
);
310 loplugin::Plugin::Registration
<GetImplementationName
> X(
311 "getimplementationname", false);
315 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */