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 "mocng.h" |
21 | #include "propertyparser.h" |
22 | #include "qbjs.h" |
23 | |
24 | #include <clang/Basic/Version.h> |
25 | #include <clang/Lex/Preprocessor.h> |
26 | #include <clang/Lex/LiteralSupport.h> |
27 | #include <clang/Lex/LexDiagnostic.h> |
28 | |
29 | #include <clang/AST/DeclCXX.h> |
30 | #include <clang/AST/DeclTemplate.h> |
31 | #include <clang/AST/ASTContext.h> |
32 | #include <clang/AST/Type.h> |
33 | #include <clang/Sema/Sema.h> |
34 | #include <clang/Sema/Lookup.h> |
35 | #include <llvm/ADT/SmallVector.h> |
36 | #include <llvm/Support/YAMLParser.h> |
37 | #include <llvm/Support/SourceMgr.h> |
38 | |
39 | #include <iostream> |
40 | |
41 | static clang::SourceLocation GetFromLiteral(clang::Token Tok, clang::StringLiteral *Lit, clang::Preprocessor &PP) { |
42 | return Lit->getLocationOfByte(PP.getSourceManager().getFileOffset(Tok.getLocation()), |
43 | PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo()); |
44 | } |
45 | |
46 | |
47 | //FIXME. make it less stupid |
48 | static void parseInterfaces(ClassDef &Def, clang::Expr *Content, clang::Sema &Sema) { |
49 | clang::Preprocessor &PP = Sema.getPreprocessor(); |
50 | clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(Content); |
51 | if (!Val) { |
52 | PP.getDiagnostics().Report(Content->getExprLoc(), |
53 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
54 | "Invalid Q_INTERFACES annotation" )); |
55 | return; |
56 | } |
57 | |
58 | llvm::MemoryBuffer* Buf = maybe_unique(llvm::MemoryBuffer::getMemBufferCopy(Val->getString(), "Q_INTERFACES" )); |
59 | clang::Lexer Lex(CreateFileIDForMemBuffer(PP, Buf, Content->getExprLoc()), |
60 | Buf, PP.getSourceManager(), PP.getLangOpts()); |
61 | |
62 | clang::Token Tok; |
63 | bool Append = false; |
64 | bool Error = false; |
65 | while (true) { |
66 | Lex.LexFromRawLexer(Tok); |
67 | |
68 | if (Tok.is(clang::tok::eof)) |
69 | break; |
70 | |
71 | if (Tok.is(clang::tok::raw_identifier)) |
72 | PP.LookUpIdentifierInfo(Tok); |
73 | |
74 | if (Tok.is(clang::tok::identifier)) { |
75 | if (Append) |
76 | Def.Interfaces.back() += PP.getSpelling(Tok); |
77 | else |
78 | Def.Interfaces.push_back(PP.getSpelling(Tok)); |
79 | Append = false; |
80 | continue; |
81 | } |
82 | |
83 | if (Append) { |
84 | Error = true; |
85 | break; |
86 | } |
87 | |
88 | if (Tok.is(clang::tok::coloncolon)) { |
89 | Def.Interfaces.back() += PP.getSpelling(Tok); |
90 | Append = true; |
91 | continue; |
92 | } |
93 | |
94 | if (!Tok.is(clang::tok::colon)) { |
95 | Error = true; |
96 | break; |
97 | } |
98 | } |
99 | |
100 | if (Error || Append || !Tok.is(clang::tok::eof)) { |
101 | PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), |
102 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
103 | "parse error in Q_INTERFACES" )); |
104 | } |
105 | |
106 | // TODO: check interface validity |
107 | } |
108 | |
109 | |
110 | static void parsePluginMetaData(ClassDef &Def, clang::Expr *Content, clang::Sema &Sema) { |
111 | clang::Preprocessor &PP = Sema.getPreprocessor(); |
112 | clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(Content); |
113 | if (!Val) { |
114 | PP.getDiagnostics().Report(Content->getExprLoc(), |
115 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
116 | "Invalid Q_PLUGIN_METADATA annotation" )); |
117 | return; |
118 | } |
119 | |
120 | llvm::MemoryBuffer* Buf = maybe_unique(llvm::MemoryBuffer::getMemBufferCopy(Val->getString(), "Q_PLUGIN_METADATA" )); |
121 | clang::Lexer Lex(CreateFileIDForMemBuffer(PP, Buf, Content->getExprLoc()), |
122 | Buf, PP.getSourceManager(), PP.getLangOpts()); |
123 | |
124 | clang::Token Tok; |
125 | Lex.LexFromRawLexer(Tok); |
126 | while (Tok.is(clang::tok::raw_identifier)) { |
127 | clang::IdentifierInfo *II = PP.LookUpIdentifierInfo(Tok); |
128 | if (II->getName() != "IID" && II->getName() != "FILE" ) { |
129 | Lex.LexFromRawLexer(Tok); |
130 | continue; |
131 | } |
132 | |
133 | Lex.LexFromRawLexer(Tok); |
134 | if (!Tok.is(clang::tok::string_literal)) { |
135 | PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), |
136 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
137 | "Expected string literal" )); |
138 | return; |
139 | } |
140 | |
141 | llvm::SmallVector<clang::Token, 4> StrToks; |
142 | do { |
143 | StrToks.push_back(Tok); |
144 | Lex.LexFromRawLexer(Tok); |
145 | } while (Tok.is(clang::tok::string_literal)); |
146 | #if CLANG_VERSION_MAJOR!=3 || CLANG_VERSION_MINOR>4 |
147 | clang::StringLiteralParser Literal(StrToks, PP); |
148 | #else |
149 | clang::StringLiteralParser Literal(&StrToks[0], StrToks.size(), PP); |
150 | #endif |
151 | if (Literal.hadError) |
152 | return; |
153 | |
154 | if (II->getName() == "IID" ) |
155 | Def.Plugin.IID = Literal.GetString(); |
156 | else { |
157 | llvm::StringRef Filename = Literal.GetString(); |
158 | const clang::DirectoryLookup *CurDir; |
159 | const clang::FileEntry *File = PP.LookupFile( |
160 | Val->getSourceRange().getBegin(), |
161 | Filename, false, nullptr, |
162 | #if CLANG_VERSION_MAJOR!=3 || CLANG_VERSION_MINOR>5 |
163 | nullptr, |
164 | #endif |
165 | CurDir, nullptr, nullptr, nullptr |
166 | #if CLANG_VERSION_MAJOR >= 5 |
167 | , nullptr |
168 | #endif |
169 | ); |
170 | |
171 | if (!File) { |
172 | PP.getDiagnostics().Report(GetFromLiteral(StrToks.front(), Val, PP), clang::diag::err_pp_file_not_found) |
173 | << Filename; |
174 | return; |
175 | } |
176 | const llvm::MemoryBuffer* JSonBuf = PP.getSourceManager().getMemoryBufferForFile(File); |
177 | llvm::SourceMgr SM; |
178 | llvm::yaml::Stream YAMLStream(JSonBuf->getBuffer(), SM); |
179 | llvm::yaml::document_iterator I = YAMLStream.begin(); |
180 | if (I == YAMLStream.end() || !I->getRoot() || !QBJS::Parse(I->getRoot(), Def.Plugin.MetaData)) { |
181 | // FIXME |
182 | PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), |
183 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
184 | "Error pwhile parsing JSON" )); |
185 | return; |
186 | } |
187 | } |
188 | } |
189 | |
190 | if (!Tok.is(clang::tok::eof)) { |
191 | PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), |
192 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
193 | "Parse error: Expected 'IID' or 'FILE'" )); |
194 | return; |
195 | } |
196 | |
197 | } |
198 | |
199 | |
200 | |
201 | static void parseEnums(BaseDef &Def, clang::DeclContext *Context, bool isFlag, clang::Expr *Content, clang::Sema &Sema) { |
202 | clang::Preprocessor &PP = Sema.getPreprocessor(); |
203 | clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(Content); |
204 | if (!Val) { |
205 | PP.getDiagnostics().Report(Content->getExprLoc(), |
206 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
207 | "Invalid Q_ENUMS annotation" )); |
208 | return; |
209 | } |
210 | |
211 | llvm::MemoryBuffer* Buf = maybe_unique(llvm::MemoryBuffer::getMemBufferCopy(Val->getString(), "Q_ENUMS" )); |
212 | clang::Lexer Lex(CreateFileIDForMemBuffer(PP, Buf, Content->getExprLoc()), |
213 | Buf, PP.getSourceManager(), PP.getLangOpts()); |
214 | |
215 | clang::CXXScopeSpec SS; |
216 | clang::Token Tok, Next; |
217 | Lex.LexFromRawLexer(Tok); |
218 | for (; !Tok.is(clang::tok::eof); Tok = Next) { |
219 | Lex.LexFromRawLexer(Next); |
220 | clang::IdentifierInfo* II = nullptr; |
221 | if (Tok.is(clang::tok::raw_identifier)) |
222 | II = PP.LookUpIdentifierInfo(Tok); |
223 | |
224 | |
225 | if (Tok.is(clang::tok::identifier)) { |
226 | |
227 | if (Next.is(clang::tok::coloncolon)) { |
228 | auto TokLoc = GetFromLiteral(Tok, Val, PP); |
229 | auto NextLoc = GetFromLiteral(Next, Val, PP); |
230 | #if CLANG_VERSION_MAJOR >= 4 |
231 | clang::Sema::NestedNameSpecInfo NameInfo(II, TokLoc, NextLoc); |
232 | if (Sema.ActOnCXXNestedNameSpecifier(Sema.getScopeForContext(Context), |
233 | NameInfo, false, SS)) |
234 | #else |
235 | if (Sema.ActOnCXXNestedNameSpecifier(Sema.getScopeForContext(Context), *II, |
236 | TokLoc, NextLoc, {}, false, SS)) |
237 | #endif |
238 | { |
239 | SS.SetInvalid({TokLoc, NextLoc}); |
240 | } |
241 | Lex.LexFromRawLexer(Next); |
242 | continue; |
243 | } |
244 | |
245 | clang::LookupResult Found(Sema, II, GetFromLiteral(Tok, Val, PP), clang::Sema::LookupNestedNameSpecifierName); |
246 | if (SS.isEmpty()) |
247 | Sema.LookupQualifiedName(Found, Context); |
248 | else { |
249 | clang::DeclContext* DC = Sema.computeDeclContext(SS); |
250 | Sema.LookupQualifiedName(Found, DC ? DC : Context); |
251 | } |
252 | |
253 | llvm::StringRef Alias; |
254 | clang::EnumDecl* R = Found.getAsSingle<clang::EnumDecl>(); |
255 | |
256 | if (!R) { |
257 | if (clang::TypedefDecl *TD = Found.getAsSingle<clang::TypedefDecl>()) { |
258 | const clang::EnumType* ET = TD->getUnderlyingType()->getAs<clang::EnumType>(); |
259 | const clang::TemplateSpecializationType* TDR = TD->getUnderlyingType()->getAs<clang::TemplateSpecializationType>(); |
260 | if(TDR && TDR->getNumArgs() == 1 && TDR->getTemplateName().getAsTemplateDecl()->getName() == "QFlags" ) |
261 | ET = TDR->getArg(0).getAsType()->getAs<clang::EnumType>(); |
262 | if (ET) { |
263 | R = ET->getDecl(); |
264 | if (TD->getIdentifier()) |
265 | Alias = TD->getName(); |
266 | } |
267 | } |
268 | } |
269 | |
270 | if (Found.empty() || !R) { |
271 | // TODO: typo correction |
272 | |
273 | // This should be an error, but the official moc do not understand that as an error. |
274 | PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), |
275 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, |
276 | "no enum names %0" )) << Found.getLookupName(); |
277 | break; |
278 | } |
279 | if (R->getDeclContext() == Context) { |
280 | if (Alias.empty() && R->getIdentifier()) |
281 | Alias = R->getName(); |
282 | Def.addEnum(R, Alias.empty() ? R->getNameAsString() : std::string(Alias), isFlag); |
283 | } else if (R->getDeclContext()->isRecord() && llvm::isa<clang::CXXRecordDecl>(R->getDeclContext())) { |
284 | // TODO: check it is a QObject |
285 | Def.addExtra(llvm::cast<clang::CXXRecordDecl>(R->getDeclContext())); |
286 | } |
287 | SS.clear(); |
288 | continue; |
289 | } else if (Tok.is(clang::tok::coloncolon)) { |
290 | if (SS.isEmpty()) { |
291 | SS.MakeGlobal(Sema.getASTContext(), GetFromLiteral(Tok, Val, PP)); |
292 | continue; |
293 | } |
294 | } |
295 | |
296 | PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), |
297 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
298 | "Invalid token in Q_ENUMS" )); |
299 | break; |
300 | } |
301 | |
302 | |
303 | } |
304 | |
305 | template<int N> |
306 | static std::pair<clang::StringLiteral*, clang::StringLiteral *> (clang::Expr *E, |
307 | const clang::Preprocessor &PP, |
308 | const char *Keyword, |
309 | const char (&Error)[N]) { |
310 | clang::BinaryOperator* BO = llvm::dyn_cast<clang::BinaryOperator>(E); |
311 | clang::StringLiteral *Val1 = nullptr, *Val2 = nullptr; |
312 | if (!BO) { |
313 | PP.getDiagnostics().Report(E->getExprLoc(), |
314 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
315 | "Invalid %0 annotation" )) << Keyword; |
316 | } else { |
317 | if (!(Val1 = llvm::dyn_cast<clang::StringLiteral>(BO->getLHS()))) |
318 | PP.getDiagnostics().Report(BO->getLHS()->getExprLoc(), |
319 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, Error)); |
320 | if (!(Val2 = llvm::dyn_cast<clang::StringLiteral>(BO->getRHS()))) |
321 | PP.getDiagnostics().Report(BO->getRHS()->getExprLoc(), |
322 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, Error)); |
323 | } |
324 | return {Val1, Val2}; |
325 | } |
326 | |
327 | static void parseClassInfo(BaseDef &Def, clang::Expr *SubExp, clang::Preprocessor &PP) |
328 | { |
329 | clang::StringLiteral *Val1 = nullptr, *Val2 = nullptr; |
330 | std::tie(Val1, Val2) = ExtractLiterals(SubExp, PP, "Q_CLASSINFO" , |
331 | "Expected string literal in Q_CLASSINFO" ); |
332 | |
333 | if (Val1 && Val2) { |
334 | Def.ClassInfo.emplace_back(Val1->getString(), Val2->getString()); |
335 | } |
336 | } |
337 | |
338 | static bool IsAnnotationStaticAssert(clang::Decl *Decl, llvm::StringRef *Key, clang::Expr **SubExp) { |
339 | if (clang::StaticAssertDecl *S = llvm::dyn_cast<clang::StaticAssertDecl>(Decl)) { |
340 | if (auto *E = llvm::dyn_cast<clang::UnaryExprOrTypeTraitExpr>(S->getAssertExpr())) |
341 | if (clang::ParenExpr *PE = llvm::dyn_cast<clang::ParenExpr>(E->getArgumentExpr())) |
342 | { |
343 | *Key = S->getMessage()->getString(); |
344 | *SubExp = PE->getSubExpr(); |
345 | return true; |
346 | } |
347 | } |
348 | return false; |
349 | } |
350 | |
351 | ClassDef MocNg::parseClass(clang::CXXRecordDecl* RD, clang::Sema& Sema) |
352 | { |
353 | clang::Preprocessor &PP = Sema.getPreprocessor(); |
354 | ClassDef Def; |
355 | Def.Record = RD; |
356 | |
357 | for (auto it = RD->decls_begin(); it != RD->decls_end(); ++it) { |
358 | llvm::StringRef key; |
359 | clang::Expr *SubExp; |
360 | if (IsAnnotationStaticAssert(*it, &key, &SubExp)) { |
361 | if (key == "qt_property" ) { |
362 | clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(SubExp); |
363 | if (Val) { |
364 | PropertyParser Parser(Val->getString(), |
365 | // Val->getStrTokenLoc(0), |
366 | Val->getLocationOfByte(0, PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo()), |
367 | Sema, Def.Record); |
368 | Def.Properties.push_back(Parser.parseProperty()); |
369 | Def.addExtra(Parser.Extra); |
370 | } else { |
371 | PP.getDiagnostics().Report((*it)->getLocation(), |
372 | PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, |
373 | "Invalid Q_PROPERTY annotation" )); |
374 | } |
375 | } else if (key == "qt_private_property" ) { |
376 | clang::StringLiteral *Val1 = nullptr, *Val2 = nullptr; |
377 | std::tie(Val1, Val2) = ExtractLiterals(SubExp, PP, "Q_PRIVATE_PROPERTY" , |
378 | "Invalid Q_PRIVATE_PROPERTY annotation" ); |
379 | |
380 | if (Val1 && Val2) { |
381 | PropertyParser Parser(Val2->getString(), |
382 | Val2->getLocationOfByte(0, PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo()), |
383 | Sema, Def.Record); |
384 | PropertyDef P = Parser.parseProperty(true); |
385 | P.inPrivateClass = Val1->getString(); |
386 | Def.Properties.push_back(std::move(P)); |
387 | Def.addExtra(Parser.Extra); |
388 | } |
389 | } else if (key == "qt_private_slot" ) { |
390 | clang::StringLiteral *Val1 = nullptr, *Val2 = nullptr; |
391 | std::tie(Val1, Val2) = ExtractLiterals(SubExp, PP, "Q_PRIVATE_SLOT" , |
392 | "Invalid Q_PRIVATE_SLOT annotation" ); |
393 | if (Val1 && Val2) { |
394 | PropertyParser Parser(Val2->getString(), |
395 | Val2->getLocationOfByte(0, PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo()), |
396 | Sema, Def.Record); |
397 | PrivateSlotDef P = Parser.parsePrivateSlot(); |
398 | P.InPrivateClass = Val1->getString(); |
399 | if (!P.Name.empty()) { |
400 | Def.PrivateSlotCount += P.NumDefault + 1; |
401 | Def.PrivateSlots.push_back(std::move(P)); |
402 | } |
403 | } |
404 | } else if (key == "qt_enums" ) { |
405 | parseEnums(Def, Def.Record, false, SubExp, Sema); |
406 | } else if (key == "qt_flags" ) { |
407 | parseEnums(Def, Def.Record, true, SubExp, Sema); |
408 | } else if (key == "qt_qobject" ) { |
409 | Def.HasQObject = true; |
410 | } else if (key == "qt_fake" ) { |
411 | Def.HasQGadget = false; |
412 | Def.HasQObject = false; |
413 | } else if (key == "qt_qgadget" ) { |
414 | Def.HasQGadget = true; |
415 | } else if (key == "qt_classinfo" ) { |
416 | parseClassInfo(Def, SubExp, PP); |
417 | } else if (key == "qt_interfaces" ) { |
418 | parseInterfaces(Def, SubExp, Sema); |
419 | } else if (key == "qt_plugin_metadata" ) { |
420 | parsePluginMetaData(Def, SubExp, Sema); |
421 | HasPlugin = true; |
422 | } |
423 | } else if (clang::CXXMethodDecl *M = llvm::dyn_cast<clang::CXXMethodDecl>(*it)) { |
424 | for (auto attr_it = M->specific_attr_begin<clang::AnnotateAttr>(); |
425 | attr_it != M->specific_attr_end<clang::AnnotateAttr>(); |
426 | ++attr_it) { |
427 | |
428 | const clang::AnnotateAttr *A = *attr_it; |
429 | if (A->getAnnotation() == "qt_signal" ) { |
430 | Def.Signals.push_back(M); |
431 | } else if (A->getAnnotation() == "qt_slot" ) { |
432 | Def.Slots.push_back(M); |
433 | } else if (A->getAnnotation() == "qt_invokable" || A->getAnnotation() == "qt_scriptable" ) { |
434 | if (auto *C = llvm::dyn_cast<clang::CXXConstructorDecl>(M)) { |
435 | Def.Constructors.push_back(C); |
436 | } else { |
437 | Def.Methods.push_back(M); |
438 | } |
439 | } else if (A->getAnnotation().startswith("qt_revision:" )) { |
440 | Def.RevisionMethodCount++; |
441 | } |
442 | } |
443 | } |
444 | } |
445 | |
446 | //Check notify Signals |
447 | for (PropertyDef &P: Def.Properties) { |
448 | if (!P.notify.Str.empty()) { |
449 | int Idx = 0; |
450 | auto errorLevel = clang::DiagnosticsEngine::Error; |
451 | for (clang::CXXMethodDecl *MD : Def.Signals) { |
452 | if (MD->getName() == P.notify.Str) { |
453 | P.notify.notifyId = Idx; |
454 | P.notify.MD = MD; |
455 | break; |
456 | } |
457 | Idx += 1 + MD->getNumParams() - MD->getMinRequiredArguments(); |
458 | } |
459 | if (P.notify.notifyId < 0 ) { |
460 | // Search in base classes |
461 | clang::CXXRecordDecl *Base = Def.Record; |
462 | do { |
463 | if (!Base->getNumBases()) |
464 | break; |
465 | Base = Base->bases_begin()->getType()->getAsCXXRecordDecl(); |
466 | if (!Base) |
467 | break; |
468 | for (auto it = Base->decls_begin(); it != Base->decls_end(); ++it) { |
469 | if (auto *MD = llvm::dyn_cast<clang::CXXMethodDecl>(*it)) { |
470 | |
471 | if (MD->getIdentifier() && MD->getName() == P.notify.Str) { |
472 | // We found a possible match. Check if it is indeed a signal |
473 | if (std::any_of(MD->specific_attr_begin<clang::AnnotateAttr>(), |
474 | MD->specific_attr_end<clang::AnnotateAttr>(), |
475 | [&](const clang::AnnotateAttr *a) { |
476 | return a->getAnnotation() == "qt_signal" ; |
477 | })) { |
478 | P.notify.MD = MD; |
479 | break; |
480 | } |
481 | // Since the official moc let this compile and the runtime will show |
482 | // a warning, we just change the level to Warning. |
483 | // (required for tst_qmetaobject which tests that) |
484 | errorLevel = clang::DiagnosticsEngine::Warning; |
485 | } |
486 | } |
487 | } |
488 | } while(!P.notify.MD); |
489 | } |
490 | if (!P.notify.MD) { |
491 | PP.getDiagnostics().Report(P.notify.Loc, |
492 | PP.getDiagnostics().getCustomDiagID(errorLevel, |
493 | "NOTIFY signal '%0' of property '%1' does not exist in class %2" )) |
494 | << P.notify.Str << P.name << Def.Record; |
495 | } |
496 | Def.NotifyCount++; |
497 | } |
498 | |
499 | if (P.revision > 0) |
500 | Def.RevisionPropertyCount++; |
501 | } |
502 | return Def; |
503 | } |
504 | |
505 | NamespaceDef MocNg::parseNamespace(clang::NamespaceDecl* ND, clang::Sema& Sema) |
506 | { |
507 | NamespaceDef Def; |
508 | Def.Namespace = ND; |
509 | for (auto it = ND->decls_begin(); it != ND->decls_end(); ++it) { |
510 | llvm::StringRef key; |
511 | clang::Expr *SubExp; |
512 | if (IsAnnotationStaticAssert(*it, &key, &SubExp)) { |
513 | if (key == "qt_qnamespace" ) { |
514 | Def.hasQNamespace = true; |
515 | } else if (key == "qt_enums" ) { |
516 | parseEnums(Def, ND, false, SubExp, Sema); |
517 | } else if (key == "qt_flags" ) { |
518 | parseEnums(Def, ND, true, SubExp, Sema); |
519 | } else if (key == "qt_classinfo" ) { |
520 | parseClassInfo(Def, SubExp, Sema.getPreprocessor()); |
521 | } |
522 | } |
523 | } |
524 | return Def; |
525 | } |
526 | |
527 | std::string MocNg::GetTag(clang::SourceLocation DeclLoc, const clang::SourceManager &SM) |
528 | { |
529 | clang::SourceLocation FileLoc = SM.getFileLoc(DeclLoc); |
530 | clang::FileID FID = SM.getFileID(FileLoc); |
531 | const llvm::MemoryBuffer *Buffer = SM.getBuffer(FID); |
532 | const char *B = Buffer->getBufferStart(); |
533 | int Off = SM.getFileOffset(FileLoc); |
534 | int Orig = Off; |
535 | while (Off > 0 && B[Off] != ';' && B[Off]!=',' && B[Off] != '}' && B[Off] != ':' /*&& B[Off] != '\n'*/ ) { |
536 | Off--; |
537 | } |
538 | |
539 | auto it_before = Tags.lower_bound(FileLoc.getLocWithOffset(Off - Orig)); |
540 | auto it_after = Tags.upper_bound(FileLoc); |
541 | if (it_before != Tags.end() && it_after != Tags.begin() && it_before == (--it_after)) { |
542 | return it_before->second; |
543 | } |
544 | return {}; |
545 | } |
546 | |
547 | bool MocNg::ShouldRegisterMetaType(clang::QualType T) |
548 | { |
549 | if (T->isVoidType() || (T->isReferenceType() && !T.getNonReferenceType().isConstQualified())) |
550 | return false; |
551 | |
552 | if (registered_meta_type.count(T->getCanonicalTypeUnqualified().getTypePtr())) |
553 | return true; |
554 | |
555 | T = T.getNonReferenceType(); |
556 | |
557 | if (T->isPointerType()) { |
558 | // registering pointer to forward declared type fails. |
559 | const clang::CXXRecordDecl* Pointee = T->getPointeeCXXRecordDecl(); |
560 | if (Pointee && !Pointee->hasDefinition()) |
561 | return false; |
562 | return true; |
563 | } |
564 | |
565 | if (auto TD = llvm::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(T->getAsCXXRecordDecl())) { |
566 | if (!TD->hasDefinition()) { |
567 | if (auto CTD = TD->getSpecializedTemplate()) { |
568 | if (CTD->getTemplatedDecl() && !CTD->getTemplatedDecl()->hasDefinition()) |
569 | return false; |
570 | } |
571 | } |
572 | for (uint I = 0; I < TD->getTemplateArgs().size(); ++I) { |
573 | const auto &Arg = TD->getTemplateArgs().get(I); |
574 | if (Arg.getKind() == clang::TemplateArgument::Type) { |
575 | if (!ShouldRegisterMetaType(Arg.getAsType())) |
576 | return false; |
577 | } |
578 | } |
579 | } |
580 | return true; |
581 | } |
582 | |