1/****************************************************************************
2 * Copyright (C) 2013-2016 Woboq GmbH
3 * Olivier Goffart <contact at woboq.com>
4 * https://woboq.com/
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <clang/Frontend/FrontendPluginRegistry.h>
21#include <clang/AST/DeclCXX.h>
22#include <clang/AST/ASTContext.h>
23#include <clang/AST/Attr.h>
24
25#include "mocastconsumer.h"
26#include "mocppcallbacks.h"
27#include "generator.h"
28
29static bool IsQtInternal(const clang::CXXMethodDecl *MD) {
30 if (!MD->getIdentifier())
31 return false;
32 auto Name = MD->getName();
33 // qt_metacall, qt_metacast, qt_static_metacall, qt_check_for_QGADGET_macro
34 return (Name.startswith("qt_") || Name == "metaObject");
35}
36
37class MocPluginASTConsumer : public MocASTConsumer {
38 bool done = false;
39
40 bool HandleTopLevelDecl(clang::DeclGroupRef D) override {
41 MocASTConsumer::HandleTopLevelDecl(D);
42 auto &PP = ci.getPreprocessor();
43
44 if (done) {
45 PP.enableIncrementalProcessing(false);
46 return true;
47 }
48
49 #if 0
50 //TODO: make use of this to check that interfaces are registered.
51 if (D.isSingleDecl()) {
52 clang::FunctionDecl* FD = llvm::dyn_cast<clang::FunctionDecl>(D.getSingleDecl());
53 if (FD && FD->getIdentifier() && FD->getName() == "qobject_interface_iid" ) {
54
55 do {
56 const clang::TemplateArgumentList* Args = FD->getTemplateSpecializationArgs();
57 if (!Args || Args->size() != 1 || Args->get(0).getKind() != clang::TemplateArgument::Type)
58 break;
59 const clang::CXXRecordDecl *RC = Args->get(0).getAsType()->getPointeeCXXRecordDecl();
60 if (!RC) break;
61 auto *Body = llvm::dyn_cast_or_null<clang::CompoundStmt>(FD->getBody());
62 if (!Body) break;
63 auto *Ret = llvm::dyn_cast_or_null<clang::ReturnStmt>(Body->body_back());
64 if (!Ret) break;
65 auto *Cast = llvm::dyn_cast_or_null<clang::CastExpr>(Ret->getRetValue());
66 if (!Cast) break;
67 clang::StringLiteral *Lit = llvm::dyn_cast_or_null<clang::StringLiteral>(Cast->getSubExpr());
68 if (!Lit) break;
69
70 Moc.interfaces.insert( {Lit->getString(), RC});
71 } while (false);
72
73 }
74 }
75 #endif
76
77
78 if (!objects.size() && !namespaces.size())
79 return true;
80
81 if (!PPCallbacks->IsInMainFile)
82 return true;
83
84 PP.EnableBacktrackAtThisPos();
85 clang::Token Tok;
86 PP.Lex(Tok);
87
88 while(Tok.is(clang::tok::semi))
89 PP.Lex(Tok);
90
91 if (Tok.is(clang::tok::eof)) {
92 done = true;
93 PP.CommitBacktrackedTokens();
94 std::string code = generate();
95 if (!code.empty()) {
96 objects.clear();
97 namespaces.clear();
98 auto Buf = maybe_unique(llvm::MemoryBuffer::getMemBufferCopy(code, "qt_moc"));
99 PP.EnterSourceFile(CreateFileIDForMemBuffer(PP, Buf, {}), nullptr, {});
100 } else {
101 ci.getPreprocessor().enableIncrementalProcessing(false);
102 PP.Backtrack();
103 }
104 } else {
105 PP.Backtrack();
106 }
107 return true;
108 }
109
110 std::string generate()
111 {
112 std::string Code;
113 llvm::raw_string_ostream OS(Code);
114
115 for (const ClassDef &Def : objects ) {
116 auto RD = Def.Record;
117
118 // find a key function: first non inline virtual method
119#if CLANG_VERSION_MAJOR != 3 || CLANG_VERSION_MINOR > 2
120 const clang::CXXMethodDecl *Key = ctx->getCurrentKeyFunction(RD);
121#else
122 const clang::CXXMethodDecl *Key = ctx->getKeyFunction(RD);
123#endif
124 if (Key && IsQtInternal(Key))
125 Key = nullptr;
126
127 if (!Key) {
128 for (auto it = RD->method_begin(); it != RD->method_end(); ++it ) {
129
130 if (Key && !it->isVirtual())
131 continue;
132
133 if (it->isPure() || it->isImplicit() || it->hasInlineBody()
134 || it->isInlineSpecified() || !it->isUserProvided() )
135 continue;
136
137 const clang::FunctionDecl *Def;
138 if (it->hasBody(Def) && Def->isInlineSpecified())
139 continue;
140
141 if (IsQtInternal(*it))
142 continue;
143
144 /* if (Key->isFunctionTemplateSpecialization())
145 continue; */
146
147 if (std::any_of(it->specific_attr_begin<clang::AnnotateAttr>(),
148 it->specific_attr_end<clang::AnnotateAttr>(),
149 [](clang::AnnotateAttr *A) {
150 return A->getAnnotation() == "qt_signal";
151 }))
152 continue;
153
154 Key = *it;
155 if (Key->isVirtual())
156 break;
157 }
158 }
159 if (Key && !Key->hasBody())
160 continue;
161
162 Generator G(&Def, OS, *ctx, &Moc);
163 G.GenerateCode();
164 }
165 for (const NamespaceDef &Def : namespaces) {
166 const clang::FunctionDecl *Key = nullptr;
167 for (auto it = Def.Namespace->decls_begin(); it != Def.Namespace->decls_end(); ++it) {
168 Key = llvm::dyn_cast<const clang::FunctionDecl>(*it);
169 if (Key)
170 break;
171 if (auto RD = llvm::dyn_cast<const clang::CXXRecordDecl>(*it)) {
172 Key = ctx->getCurrentKeyFunction(RD);
173 if (Key)
174 break;
175 }
176 }
177 if (Key && !Key->hasBody())
178 continue;
179 Generator G(&Def, OS, *ctx, &Moc);
180 G.GenerateCode();
181 }
182 return Code;
183 }
184
185public:
186 MocPluginASTConsumer(clang::CompilerInstance& ci) : MocASTConsumer(ci) {}
187};
188
189class MocPluginAction : public clang::PluginASTAction {
190protected:
191 #if CLANG_VERSION_MAJOR == 3 && CLANG_VERSION_MINOR <= 5
192 clang::ASTConsumer *
193 #else
194 std::unique_ptr<clang::ASTConsumer>
195 #endif
196 CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef f) override {
197 return maybe_unique(new MocPluginASTConsumer(CI));
198 }
199 bool ParseArgs(const clang::CompilerInstance& CI, const std::vector< std::string >& arg) override {
200 return true;
201 }
202};
203
204
205static clang::FrontendPluginRegistry::Add<MocPluginAction>
206X("moc", "Qt MetaObjectCompiler");
207