1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4
5#include "moc.h"
6#include "generator.h"
7#include "qdatetime.h"
8#include "utils.h"
9#include "outputrevision.h"
10#include <QtCore/qfile.h>
11#include <QtCore/qfileinfo.h>
12#include <QtCore/qdir.h>
13#include <QtCore/qjsondocument.h>
14
15// for normalizeTypeInternal
16#include <private/qmetaobject_moc_p.h>
17#include <private/qduplicatetracker_p.h>
18
19QT_BEGIN_NAMESPACE
20
21using namespace Qt::StringLiterals;
22
23// only moc needs this function
24static QByteArray normalizeType(const QByteArray &ba)
25{
26 return ba.size() ? normalizeTypeInternal(t: ba.constBegin(), e: ba.constEnd()) : ba;
27}
28
29const QByteArray &Moc::toFullyQualified(const QByteArray &name) const noexcept
30{
31 if (auto it = knownQObjectClasses.find(key: name); it != knownQObjectClasses.end())
32 return it.value();
33 if (auto it = knownGadgets.find(key: name); it != knownGadgets.end())
34 return it.value();
35 return name;
36}
37
38bool Moc::parseClassHead(ClassDef *def)
39{
40 // figure out whether this is a class declaration, or only a
41 // forward or variable declaration.
42 int i = 0;
43 Token token;
44 do {
45 token = lookup(k: i++);
46 if (token == COLON || token == LBRACE)
47 break;
48 if (token == SEMIC || token == RANGLE)
49 return false;
50 } while (token);
51
52 // support attributes like "class [[deprecated]]] name"
53 skipCxxAttributes();
54
55 if (!test(token: IDENTIFIER)) // typedef struct { ... }
56 return false;
57 QByteArray name = lexem();
58
59 // support "class IDENT name" and "class IDENT(IDENT) name"
60 // also support "class IDENT name (final|sealed|Q_DECL_FINAL)"
61 if (test(token: LPAREN)) {
62 until(RPAREN);
63 if (!test(token: IDENTIFIER))
64 return false;
65 name = lexem();
66 } else if (test(token: IDENTIFIER)) {
67 const QByteArray lex = lexem();
68 if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
69 name = lex;
70 }
71
72 def->qualified += name;
73 while (test(token: SCOPE)) {
74 def->qualified += lexem();
75 if (test(token: IDENTIFIER)) {
76 name = lexem();
77 def->qualified += name;
78 }
79 }
80 def->classname = name;
81 def->lineNumber = symbol().lineNum;
82
83 if (test(token: IDENTIFIER)) {
84 const QByteArray lex = lexem();
85 if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
86 return false;
87 }
88
89 if (test(token: COLON)) {
90 do {
91 test(token: VIRTUAL);
92 FunctionDef::Access access = FunctionDef::Public;
93 if (test(token: PRIVATE))
94 access = FunctionDef::Private;
95 else if (test(token: PROTECTED))
96 access = FunctionDef::Protected;
97 else
98 test(token: PUBLIC);
99 test(token: VIRTUAL);
100 const Type type = parseType();
101 // ignore the 'class Foo : BAR(Baz)' case
102 if (test(token: LPAREN)) {
103 until(RPAREN);
104 } else {
105 def->superclassList.push_back(t: {.classname: type.name, .qualified: toFullyQualified(name: type.name), .access: access});
106 }
107 } while (test(token: COMMA));
108
109 if (!def->superclassList.isEmpty()
110 && knownGadgets.contains(key: def->superclassList.constFirst().classname)) {
111 // Q_GADGET subclasses are treated as Q_GADGETs
112 knownGadgets.insert(key: def->classname, value: def->qualified);
113 knownGadgets.insert(key: def->qualified, value: def->qualified);
114 }
115 }
116 if (!test(token: LBRACE))
117 return false;
118 def->begin = index - 1;
119 bool foundRBrace = until(RBRACE);
120 def->end = index;
121 index = def->begin + 1;
122 return foundRBrace;
123}
124
125Type Moc::parseType()
126{
127 Type type;
128 bool hasSignedOrUnsigned = false;
129 bool isVoid = false;
130 type.firstToken = lookup();
131 for (;;) {
132 skipCxxAttributes();
133 switch (next()) {
134 case SIGNED:
135 case UNSIGNED:
136 hasSignedOrUnsigned = true;
137 Q_FALLTHROUGH();
138 case CONST:
139 case VOLATILE:
140 type.name += lexem();
141 type.name += ' ';
142 if (lookup(k: 0) == VOLATILE)
143 type.isVolatile = true;
144 continue;
145 case Q_MOC_COMPAT_TOKEN:
146 case Q_INVOKABLE_TOKEN:
147 case Q_SCRIPTABLE_TOKEN:
148 case Q_SIGNALS_TOKEN:
149 case Q_SLOTS_TOKEN:
150 case Q_SIGNAL_TOKEN:
151 case Q_SLOT_TOKEN:
152 type.name += lexem();
153 return type;
154 case NOTOKEN:
155 return type;
156 default:
157 prev();
158 break;
159 }
160 break;
161 }
162
163 skipCxxAttributes();
164 test(token: ENUM) || test(token: CLASS) || test(token: STRUCT);
165 for(;;) {
166 skipCxxAttributes();
167 switch (next()) {
168 case IDENTIFIER:
169 // void mySlot(unsigned myArg)
170 if (hasSignedOrUnsigned) {
171 prev();
172 break;
173 }
174 Q_FALLTHROUGH();
175 case CHAR:
176 case SHORT:
177 case INT:
178 case LONG:
179 type.name += lexem();
180 // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
181 if (test(token: LONG) || test(token: INT) || test(token: DOUBLE)) {
182 type.name += ' ';
183 prev();
184 continue;
185 }
186 break;
187 case FLOAT:
188 case DOUBLE:
189 case VOID:
190 case BOOL:
191 case AUTO:
192 type.name += lexem();
193 isVoid |= (lookup(k: 0) == VOID);
194 break;
195 case NOTOKEN:
196 return type;
197 default:
198 prev();
199 ;
200 }
201 if (test(token: LANGLE)) {
202 if (type.name.isEmpty()) {
203 // '<' cannot start a type
204 return type;
205 }
206 type.name += lexemUntil(RANGLE);
207 }
208 if (test(token: SCOPE)) {
209 type.name += lexem();
210 type.isScoped = true;
211 } else {
212 break;
213 }
214 }
215 while (test(token: CONST) || test(token: VOLATILE) || test(token: SIGNED) || test(token: UNSIGNED)
216 || test(token: STAR) || test(token: AND) || test(token: ANDAND)) {
217 type.name += ' ';
218 type.name += lexem();
219 if (lookup(k: 0) == AND)
220 type.referenceType = Type::Reference;
221 else if (lookup(k: 0) == ANDAND)
222 type.referenceType = Type::RValueReference;
223 else if (lookup(k: 0) == STAR)
224 type.referenceType = Type::Pointer;
225 }
226 type.rawName = type.name;
227 // transform stupid things like 'const void' or 'void const' into 'void'
228 if (isVoid && type.referenceType == Type::NoReference) {
229 type.name = "void";
230 }
231 return type;
232}
233
234enum class IncludeState {
235 IncludeBegin,
236 IncludeEnd,
237 NoInclude,
238};
239
240bool Moc::parseEnum(EnumDef *def)
241{
242 bool isTypdefEnum = false; // typedef enum { ... } Foo;
243
244 if (test(token: CLASS) || test(token: STRUCT))
245 def->isEnumClass = true;
246
247 if (test(token: IDENTIFIER)) {
248 def->name = lexem();
249 } else {
250 if (lookup(k: -1) != TYPEDEF)
251 return false; // anonymous enum
252 isTypdefEnum = true;
253 }
254 if (test(token: COLON)) { // C++11 strongly typed enum
255 // enum Foo : unsigned long { ... };
256 def->type = normalizeType(ba: parseType().name);
257 }
258 if (!test(token: LBRACE))
259 return false;
260 auto handleInclude = [this]() -> IncludeState {
261 bool hadIncludeBegin = false;
262 if (test(token: MOC_INCLUDE_BEGIN)) {
263 currentFilenames.push(x: symbol().unquotedLexem());
264 // we do not return early to handle empty headers in one go
265 hadIncludeBegin = true;
266 }
267 if (test(token: NOTOKEN)) {
268 next(token: MOC_INCLUDE_END);
269 currentFilenames.pop();
270 return IncludeState::IncludeEnd;
271 }
272 if (hadIncludeBegin)
273 return IncludeState::IncludeBegin;
274 else
275 return IncludeState::NoInclude;
276 };
277 do {
278 handleInclude();
279 if (lookup() == RBRACE) // accept trailing comma
280 break;
281 next(token: IDENTIFIER);
282 def->values += lexem();
283 handleInclude();
284 skipCxxAttributes();
285 } while (test(token: EQ) ? until(COMMA) : test(token: COMMA));
286 next(token: RBRACE);
287 if (isTypdefEnum) {
288 if (!test(token: IDENTIFIER))
289 return false;
290 def->name = lexem();
291 }
292 return true;
293}
294
295void Moc::parseFunctionArguments(FunctionDef *def)
296{
297 Q_UNUSED(def);
298 while (hasNext()) {
299 ArgumentDef arg;
300 arg.type = parseType();
301 if (arg.type.name == "void")
302 break;
303 if (test(token: IDENTIFIER))
304 arg.name = lexem();
305 while (test(token: LBRACK)) {
306 arg.rightType += lexemUntil(RBRACK);
307 }
308 if (test(token: CONST) || test(token: VOLATILE)) {
309 arg.rightType += ' ';
310 arg.rightType += lexem();
311 }
312 arg.normalizedType = normalizeType(ba: QByteArray(arg.type.name + ' ' + arg.rightType));
313 arg.typeNameForCast = QByteArray("std::add_pointer_t<"+arg.normalizedType+">");
314 if (test(token: EQ))
315 arg.isDefault = true;
316 def->arguments += arg;
317 if (!until(COMMA))
318 break;
319 }
320
321 if (!def->arguments.isEmpty()
322 && def->arguments.constLast().normalizedType == "QPrivateSignal") {
323 def->arguments.removeLast();
324 def->isPrivateSignal = true;
325 }
326 if (def->arguments.size() == 1
327 && def->arguments.constLast().normalizedType == "QMethodRawArguments") {
328 def->arguments.removeLast();
329 def->isRawSlot = true;
330 }
331
332 if (Q_UNLIKELY(def->arguments.size() >= std::numeric_limits<int>::max()))
333 error(msg: "number of function arguments exceeds std::numeric_limits<int>::max()");
334}
335
336bool Moc::testFunctionAttribute(FunctionDef *def)
337{
338 if (index < symbols.size() && testFunctionAttribute(tok: symbols.at(i: index).token, def)) {
339 ++index;
340 return true;
341 }
342 return false;
343}
344
345bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
346{
347 switch (tok) {
348 case Q_MOC_COMPAT_TOKEN:
349 def->isCompat = true;
350 return true;
351 case Q_INVOKABLE_TOKEN:
352 def->isInvokable = true;
353 return true;
354 case Q_SIGNAL_TOKEN:
355 def->isSignal = true;
356 return true;
357 case Q_SLOT_TOKEN:
358 def->isSlot = true;
359 return true;
360 case Q_SCRIPTABLE_TOKEN:
361 def->isInvokable = def->isScriptable = true;
362 return true;
363 default: break;
364 }
365 return false;
366}
367
368bool Moc::skipCxxAttributes()
369{
370 auto rewind = index;
371 if (test(token: LBRACK) && test(token: LBRACK) && until(RBRACK) && test(token: RBRACK))
372 return true;
373 index = rewind;
374 return false;
375}
376
377QTypeRevision Moc::parseRevision()
378{
379 next(token: LPAREN);
380 QByteArray revisionString = lexemUntil(RPAREN);
381 revisionString.remove(index: 0, len: 1);
382 revisionString.chop(n: 1);
383 const QList<QByteArray> majorMinor = revisionString.split(sep: ',');
384 switch (majorMinor.size()) {
385 case 1: {
386 bool ok = false;
387 const int revision = revisionString.toInt(ok: &ok);
388 if (!ok || !QTypeRevision::isValidSegment(segment: revision))
389 error(msg: "Invalid revision");
390 return QTypeRevision::fromMinorVersion(minorVersion: revision);
391 }
392 case 2: { // major.minor
393 bool ok = false;
394 const int major = majorMinor[0].toInt(ok: &ok);
395 if (!ok || !QTypeRevision::isValidSegment(segment: major))
396 error(msg: "Invalid major version");
397 const int minor = majorMinor[1].toInt(ok: &ok);
398 if (!ok || !QTypeRevision::isValidSegment(segment: minor))
399 error(msg: "Invalid minor version");
400 return QTypeRevision::fromVersion(majorVersion: major, minorVersion: minor);
401 }
402 default:
403 error(msg: "Invalid revision");
404 return QTypeRevision();
405 }
406}
407
408bool Moc::testFunctionRevision(FunctionDef *def)
409{
410
411 if (test(token: Q_REVISION_TOKEN)) {
412 def->revision = parseRevision().toEncodedVersion<int>();
413 return true;
414 }
415
416 return false;
417}
418
419// returns false if the function should be ignored
420bool Moc::parseFunction(FunctionDef *def, bool inMacro)
421{
422 def->isVirtual = false;
423 def->isStatic = false;
424 //skip modifiers and attributes
425 while (testForFunctionModifiers(def)
426 || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
427 bool templateFunction = (lookup() == TEMPLATE);
428 def->type = parseType();
429 if (def->type.name.isEmpty()) {
430 if (templateFunction)
431 error(msg: "Template function as signal or slot");
432 else
433 error();
434 }
435 bool scopedFunctionName = false;
436 // we might have modifiers and attributes after a tag
437 // note that testFunctionAttribute is handled further below,
438 // and revisions and attributes must come first
439 while (testForFunctionModifiers(def)) {}
440 Type tempType = parseType();
441 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
442 if (testFunctionAttribute(tok: def->type.firstToken, def))
443 ; // fine
444 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
445 error();
446 else if (def->type.firstToken == Q_SLOTS_TOKEN)
447 error();
448 else {
449 if (!def->tag.isEmpty())
450 def->tag += ' ';
451 def->tag += def->type.name;
452 }
453 def->type = tempType;
454 tempType = parseType();
455 }
456 next(token: LPAREN, msg: "Not a signal or slot declaration");
457 def->name = tempType.name;
458 scopedFunctionName = tempType.isScoped;
459
460 if (!test(token: RPAREN)) {
461 parseFunctionArguments(def);
462 next(token: RPAREN);
463 }
464
465 // support optional macros with compiler specific options
466 while (test(token: IDENTIFIER))
467 ;
468
469 def->isConst = test(token: CONST);
470
471 while (test(token: IDENTIFIER))
472 ;
473
474 if (inMacro) {
475 next(token: RPAREN);
476 prev();
477 } else {
478 if (test(token: THROW)) {
479 next(token: LPAREN);
480 until(RPAREN);
481 }
482
483 if (def->type.name == "auto" && test(token: ARROW))
484 def->type = parseType(); // Parse trailing return-type
485
486 if (test(token: SEMIC))
487 ;
488 else if ((def->inlineCode = test(token: LBRACE)))
489 until(RBRACE);
490 else if ((def->isAbstract = test(token: EQ)))
491 until(SEMIC);
492 else if (skipCxxAttributes())
493 until(SEMIC);
494 else
495 error();
496 }
497 if (scopedFunctionName) {
498 const QByteArray msg = "Function declaration " + def->name
499 + " contains extra qualification. Ignoring as signal or slot.";
500 warning(msg.constData());
501 return false;
502 }
503
504 QList<QByteArray> typeNameParts = normalizeType(ba: def->type.name).split(sep: ' ');
505 if (typeNameParts.contains(t: "auto")) {
506 // We expected a trailing return type but we haven't seen one
507 error(msg: "Function declared with auto as return type but missing trailing return type. "
508 "Return type deduction is not supported.");
509 }
510
511 // we don't support references as return types, it's too dangerous
512 if (def->type.referenceType == Type::Reference) {
513 QByteArray rawName = def->type.rawName;
514 def->type = Type("void");
515 def->type.rawName = rawName;
516 }
517
518 def->normalizedType = normalizeType(ba: def->type.name);
519 return true;
520}
521
522bool Moc::testForFunctionModifiers(FunctionDef *def)
523{
524 return test(token: EXPLICIT) || test(token: INLINE) ||
525 (test(token: STATIC) && (def->isStatic = true)) ||
526 (test(token: VIRTUAL) && (def->isVirtual = true));
527}
528
529// like parseFunction, but never aborts with an error
530bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
531{
532 def->isVirtual = false;
533 def->isStatic = false;
534 //skip modifiers and attributes
535 while (testForFunctionModifiers(def)
536 || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
537 bool tilde = test(token: TILDE);
538 def->type = parseType();
539 if (def->type.name.isEmpty())
540 return false;
541 bool scopedFunctionName = false;
542 if (test(token: LPAREN)) {
543 def->name = def->type.name;
544 scopedFunctionName = def->type.isScoped;
545 if (def->name == cdef->classname) {
546 def->isDestructor = tilde;
547 def->isConstructor = !tilde;
548 def->type = Type();
549 } else {
550 // missing type name? => Skip
551 return false;
552 }
553 } else {
554 // ### TODO: The condition before testForFunctionModifiers shoulnd't be necessary,
555 // but otherwise we end up with misparses
556 if (def->isSlot || def->isSignal || def->isInvokable)
557 while (testForFunctionModifiers(def)) {}
558 Type tempType = parseType();
559 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
560 if (testFunctionAttribute(tok: def->type.firstToken, def))
561 ; // fine
562 else if (def->type.name == "Q_SIGNAL")
563 def->isSignal = true;
564 else if (def->type.name == "Q_SLOT")
565 def->isSlot = true;
566 else {
567 if (!def->tag.isEmpty())
568 def->tag += ' ';
569 def->tag += def->type.name;
570 }
571 def->type = tempType;
572 tempType = parseType();
573 }
574 if (!test(token: LPAREN))
575 return false;
576 def->name = tempType.name;
577 scopedFunctionName = tempType.isScoped;
578 }
579
580 // we don't support references as return types, it's too dangerous
581 if (def->type.referenceType == Type::Reference) {
582 QByteArray rawName = def->type.rawName;
583 def->type = Type("void");
584 def->type.rawName = rawName;
585 }
586
587 def->normalizedType = normalizeType(ba: def->type.name);
588
589 if (!test(token: RPAREN)) {
590 parseFunctionArguments(def);
591 if (!test(token: RPAREN))
592 return false;
593 }
594 def->isConst = test(token: CONST);
595 if (scopedFunctionName
596 && (def->isSignal || def->isSlot || def->isInvokable)) {
597 const QByteArray msg = "parsemaybe: Function declaration " + def->name
598 + " contains extra qualification. Ignoring as signal or slot.";
599 warning(msg.constData());
600 return false;
601 }
602 return true;
603}
604
605inline void handleDefaultArguments(QList<FunctionDef> *functionList, FunctionDef &function)
606{
607 // support a function with a default argument by pretending there is an
608 // overload without the argument (the original function is the overload with
609 // all arguments present)
610 while (function.arguments.size() > 0 && function.arguments.constLast().isDefault) {
611 function.wasCloned = true;
612 function.arguments.removeLast();
613 *functionList += function;
614 }
615}
616
617void Moc::prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const
618{
619 auto it = namespaceList.crbegin();
620 const auto rend = namespaceList.crend();
621 for (; it != rend; ++it) {
622 if (inNamespace(def: &*it))
623 def.qualified.prepend(a: it->classname + "::");
624 }
625}
626
627void Moc::checkListSizes(const ClassDef &def)
628{
629 if (Q_UNLIKELY(def.nonClassSignalList.size() > std::numeric_limits<int>::max()))
630 error(msg: "number of signals defined in parent class(es) exceeds "
631 "std::numeric_limits<int>::max().");
632
633 if (Q_UNLIKELY(def.propertyList.size() > std::numeric_limits<int>::max()))
634 error(msg: "number of bindable properties exceeds std::numeric_limits<int>::max().");
635
636 if (Q_UNLIKELY(def.classInfoList.size() > std::numeric_limits<int>::max()))
637 error(msg: "number of times Q_CLASSINFO macro is used exceeds "
638 "std::numeric_limits<int>::max().");
639
640 if (Q_UNLIKELY(def.enumList.size() > std::numeric_limits<int>::max()))
641 error(msg: "number of enumerations exceeds std::numeric_limits<int>::max().");
642
643 if (Q_UNLIKELY(def.superclassList.size() > std::numeric_limits<int>::max()))
644 error(msg: "number of super classes exceeds std::numeric_limits<int>::max().");
645
646 if (Q_UNLIKELY(def.constructorList.size() > std::numeric_limits<int>::max()))
647 error(msg: "number of constructor parameters exceeds std::numeric_limits<int>::max().");
648
649 if (Q_UNLIKELY(def.signalList.size() > std::numeric_limits<int>::max()))
650 error(msg: "number of signals exceeds std::numeric_limits<int>::max().");
651
652 if (Q_UNLIKELY(def.slotList.size() > std::numeric_limits<int>::max()))
653 error(msg: "number of declared slots exceeds std::numeric_limits<int>::max().");
654
655 if (Q_UNLIKELY(def.methodList.size() > std::numeric_limits<int>::max()))
656 error(msg: "number of methods exceeds std::numeric_limits<int>::max().");
657
658 if (Q_UNLIKELY(def.publicList.size() > std::numeric_limits<int>::max()))
659 error(msg: "number of public functions declared in this class exceeds "
660 "std::numeric_limits<int>::max().");
661}
662
663void Moc::parse()
664{
665 QList<NamespaceDef> namespaceList;
666 bool templateClass = false;
667 while (hasNext()) {
668 Token t = next();
669 switch (t) {
670 case NAMESPACE: {
671 qsizetype rewind = index;
672 if (test(token: IDENTIFIER)) {
673 QByteArray nsName = lexem();
674 QByteArrayList nested;
675 while (test(token: SCOPE)) {
676 /* treat (C++20's) namespace A::inline B {} as A::B
677 this is mostly to not break compilation when encountering such
678 a construct in a header; the interaction of Qt's meta-macros with
679 inline namespaces is still rather poor.
680 */
681 test(token: INLINE);
682 next(token: IDENTIFIER);
683 nested.append(t: nsName);
684 nsName = lexem();
685 }
686 if (test(token: EQ)) {
687 // namespace Foo = Bar::Baz;
688 until(SEMIC);
689 } else if (test(token: LPAREN)) {
690 // Ignore invalid code such as: 'namespace __identifier("x")' (QTBUG-56634)
691 until(RPAREN);
692 } else if (!test(token: SEMIC)) {
693 NamespaceDef def;
694 def.classname = nsName;
695 def.lineNumber = symbol().lineNum;
696 def.doGenerate = currentFilenames.size() <= 1;
697
698 next(token: LBRACE);
699 def.begin = index - 1;
700 until(RBRACE);
701 def.end = index;
702 index = def.begin + 1;
703
704 prependNamespaces(def, namespaceList);
705
706 for (const QByteArray &ns : nested) {
707 NamespaceDef parentNs;
708 parentNs.classname = ns;
709 parentNs.qualified = def.qualified;
710 def.qualified += ns + "::";
711 parentNs.begin = def.begin;
712 parentNs.end = def.end;
713 namespaceList += parentNs;
714 }
715
716 while (inNamespace(def: &def) && hasNext()) {
717 switch (next()) {
718 case NAMESPACE:
719 if (test(token: IDENTIFIER)) {
720 while (test(token: SCOPE)) {
721 test(token: INLINE); // ignore inline namespaces
722 next(token: IDENTIFIER);
723 }
724 if (test(token: EQ)) {
725 // namespace Foo = Bar::Baz;
726 until(SEMIC);
727 } else if (!test(token: SEMIC)) {
728 until(RBRACE);
729 }
730 }
731 break;
732 case Q_NAMESPACE_TOKEN:
733 def.hasQNamespace = true;
734 break;
735 case Q_NAMESPACE_EXPORT_TOKEN:
736 next(token: LPAREN);
737 while (test(token: IDENTIFIER))
738 {}
739 next(token: RPAREN);
740 def.hasQNamespace = true;
741 break;
742 case Q_ENUMS_TOKEN:
743 case Q_ENUM_NS_TOKEN:
744 parseEnumOrFlag(def: &def, isFlag: false);
745 break;
746 case Q_ENUM_TOKEN:
747 error(msg: "Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead");
748 break;
749 case Q_FLAGS_TOKEN:
750 case Q_FLAG_NS_TOKEN:
751 parseEnumOrFlag(def: &def, isFlag: true);
752 break;
753 case Q_FLAG_TOKEN:
754 error(msg: "Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead");
755 break;
756 case Q_DECLARE_FLAGS_TOKEN:
757 parseFlag(def: &def);
758 break;
759 case Q_CLASSINFO_TOKEN:
760 parseClassInfo(def: &def);
761 break;
762 case Q_MOC_INCLUDE_TOKEN:
763 // skip it, the namespace is parsed twice
764 next(token: LPAREN);
765 lexemUntil(RPAREN);
766 break;
767 case ENUM: {
768 EnumDef enumDef;
769 if (parseEnum(def: &enumDef))
770 def.enumList += enumDef;
771 } break;
772 case CLASS:
773 case STRUCT: {
774 ClassDef classdef;
775 if (!parseClassHead(def: &classdef))
776 continue;
777 while (inClass(def: &classdef) && hasNext())
778 next(); // consume all Q_XXXX macros from this class
779 } break;
780 default: break;
781 }
782 }
783 namespaceList += def;
784 index = rewind;
785 if (!def.hasQNamespace && (!def.classInfoList.isEmpty() || !def.enumDeclarations.isEmpty()))
786 error(msg: "Namespace declaration lacks Q_NAMESPACE macro.");
787 }
788 }
789 break;
790 }
791 case SEMIC:
792 case RBRACE:
793 templateClass = false;
794 break;
795 case TEMPLATE:
796 templateClass = true;
797 break;
798 case MOC_INCLUDE_BEGIN:
799 currentFilenames.push(x: symbol().unquotedLexem());
800 break;
801 case MOC_INCLUDE_END:
802 currentFilenames.pop();
803 break;
804 case Q_DECLARE_INTERFACE_TOKEN:
805 parseDeclareInterface();
806 break;
807 case Q_DECLARE_METATYPE_TOKEN:
808 parseDeclareMetatype();
809 break;
810 case Q_MOC_INCLUDE_TOKEN:
811 parseMocInclude();
812 break;
813 case USING:
814 if (test(token: NAMESPACE)) {
815 while (test(token: SCOPE) || test(token: IDENTIFIER))
816 ;
817 // Ignore invalid code such as: 'using namespace __identifier("x")' (QTBUG-63772)
818 if (test(token: LPAREN))
819 until(RPAREN);
820 next(token: SEMIC);
821 }
822 break;
823 case CLASS:
824 case STRUCT: {
825 if (currentFilenames.size() <= 1)
826 break;
827
828 ClassDef def;
829 if (!parseClassHead(def: &def))
830 continue;
831
832 while (inClass(def: &def) && hasNext()) {
833 switch (next()) {
834 case Q_OBJECT_TOKEN:
835 def.hasQObject = true;
836 break;
837 case Q_GADGET_EXPORT_TOKEN:
838 next(token: LPAREN);
839 while (test(token: IDENTIFIER))
840 {}
841 next(token: RPAREN);
842 Q_FALLTHROUGH();
843 case Q_GADGET_TOKEN:
844 def.hasQGadget = true;
845 break;
846 default: break;
847 }
848 }
849
850 if (!def.hasQObject && !def.hasQGadget)
851 continue;
852
853 prependNamespaces(def, namespaceList);
854
855 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
856 classHash.insert(key: def.classname, value: def.qualified);
857 classHash.insert(key: def.qualified, value: def.qualified);
858
859 continue; }
860 default: break;
861 }
862 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
863 continue;
864 ClassDef def;
865 if (parseClassHead(def: &def)) {
866 prependNamespaces(def, namespaceList);
867
868 FunctionDef::Access access = FunctionDef::Private;
869 while (inClass(def: &def) && hasNext()) {
870 switch ((t = next())) {
871 case PRIVATE:
872 access = FunctionDef::Private;
873 if (test(token: Q_SIGNALS_TOKEN))
874 error(msg: "Signals cannot have access specifier");
875 break;
876 case PROTECTED:
877 access = FunctionDef::Protected;
878 if (test(token: Q_SIGNALS_TOKEN))
879 error(msg: "Signals cannot have access specifier");
880 break;
881 case PUBLIC:
882 access = FunctionDef::Public;
883 if (test(token: Q_SIGNALS_TOKEN))
884 error(msg: "Signals cannot have access specifier");
885 break;
886 case STRUCT:
887 case CLASS: {
888 ClassDef nestedDef;
889 if (parseClassHead(def: &nestedDef)) {
890 while (inClass(def: &nestedDef) && inClass(def: &def)) {
891 t = next();
892 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
893 error(msg: "Meta object features not supported for nested classes");
894 }
895 }
896 } break;
897 case Q_SIGNALS_TOKEN:
898 parseSignals(def: &def);
899 break;
900 case Q_SLOTS_TOKEN:
901 switch (lookup(k: -1)) {
902 case PUBLIC:
903 case PROTECTED:
904 case PRIVATE:
905 parseSlots(def: &def, access);
906 break;
907 default:
908 error(msg: "Missing access specifier for slots");
909 }
910 break;
911 case Q_OBJECT_TOKEN:
912 def.hasQObject = true;
913 if (templateClass)
914 error(msg: "Template classes not supported by Q_OBJECT");
915 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
916 error(msg: "Class contains Q_OBJECT macro but does not inherit from QObject");
917 break;
918 case Q_GADGET_EXPORT_TOKEN:
919 next(token: LPAREN);
920 while (test(token: IDENTIFIER))
921 {}
922 next(token: RPAREN);
923 Q_FALLTHROUGH();
924 case Q_GADGET_TOKEN:
925 def.hasQGadget = true;
926 if (templateClass)
927 error(msg: "Template classes not supported by Q_GADGET");
928 break;
929 case Q_PROPERTY_TOKEN:
930 parseProperty(def: &def, mode: Named);
931 break;
932 case QT_ANONYMOUS_PROPERTY_TOKEN:
933 parseProperty(def: &def, mode: Anonymous);
934 break;
935 case Q_PLUGIN_METADATA_TOKEN:
936 parsePluginData(def: &def);
937 break;
938 case Q_ENUMS_TOKEN:
939 case Q_ENUM_TOKEN:
940 parseEnumOrFlag(def: &def, isFlag: false);
941 break;
942 case Q_ENUM_NS_TOKEN:
943 error(msg: "Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead");
944 break;
945 case Q_FLAGS_TOKEN:
946 case Q_FLAG_TOKEN:
947 parseEnumOrFlag(def: &def, isFlag: true);
948 break;
949 case Q_FLAG_NS_TOKEN:
950 error(msg: "Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead");
951 break;
952 case Q_DECLARE_FLAGS_TOKEN:
953 parseFlag(def: &def);
954 break;
955 case Q_CLASSINFO_TOKEN:
956 parseClassInfo(def: &def);
957 break;
958 case Q_MOC_INCLUDE_TOKEN:
959 parseMocInclude();
960 break;
961 case Q_INTERFACES_TOKEN:
962 parseInterfaces(def: &def);
963 break;
964 case Q_PRIVATE_SLOT_TOKEN:
965 parseSlotInPrivate(def: &def, access);
966 break;
967 case Q_PRIVATE_PROPERTY_TOKEN:
968 parsePrivateProperty(def: &def, mode: Named);
969 break;
970 case QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN:
971 parsePrivateProperty(def: &def, mode: Anonymous);
972 break;
973 case ENUM: {
974 EnumDef enumDef;
975 if (parseEnum(def: &enumDef))
976 def.enumList += enumDef;
977 } break;
978 case SEMIC:
979 case COLON:
980 break;
981 default:
982 FunctionDef funcDef;
983 funcDef.access = access;
984 qsizetype rewind = index--;
985 if (parseMaybeFunction(cdef: &def, def: &funcDef)) {
986 if (funcDef.isConstructor) {
987 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
988 def.constructorList += funcDef;
989 handleDefaultArguments(functionList: &def.constructorList, function&: funcDef);
990 }
991 } else if (funcDef.isDestructor) {
992 // don't care about destructors
993 } else {
994 if (access == FunctionDef::Public)
995 def.publicList += funcDef;
996 if (funcDef.isSlot) {
997 def.slotList += funcDef;
998 handleDefaultArguments(functionList: &def.slotList, function&: funcDef);
999 if (funcDef.revision > 0)
1000 ++def.revisionedMethods;
1001 } else if (funcDef.isSignal) {
1002 def.signalList += funcDef;
1003 handleDefaultArguments(functionList: &def.signalList, function&: funcDef);
1004 if (funcDef.revision > 0)
1005 ++def.revisionedMethods;
1006 } else if (funcDef.isInvokable) {
1007 def.methodList += funcDef;
1008 handleDefaultArguments(functionList: &def.methodList, function&: funcDef);
1009 if (funcDef.revision > 0)
1010 ++def.revisionedMethods;
1011 }
1012 }
1013 } else {
1014 index = rewind;
1015 }
1016 }
1017 }
1018
1019 next(token: RBRACE);
1020
1021 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
1022 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
1023 continue; // no meta object code required
1024
1025
1026 if (!def.hasQObject && !def.hasQGadget)
1027 error(msg: "Class declaration lacks Q_OBJECT macro.");
1028
1029 // Add meta tags to the plugin meta data:
1030 if (!def.pluginData.iid.isEmpty())
1031 def.pluginData.metaArgs = metaArgs;
1032
1033 if (def.hasQObject && !def.superclassList.isEmpty())
1034 checkSuperClasses(def: &def);
1035
1036 checkProperties(cdef: &def);
1037
1038 checkListSizes(def);
1039
1040 classList += def;
1041 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
1042 classHash.insert(key: def.classname, value: def.qualified);
1043 classHash.insert(key: def.qualified, value: def.qualified);
1044 }
1045 }
1046 for (const auto &n : std::as_const(t&: namespaceList)) {
1047 if (!n.hasQNamespace)
1048 continue;
1049 ClassDef def;
1050 static_cast<BaseDef &>(def) = static_cast<BaseDef>(n);
1051 def.qualified += def.classname;
1052 def.hasQNamespace = true;
1053 auto it = std::find_if(first: classList.begin(), last: classList.end(), pred: [&def](const ClassDef &val) {
1054 return def.classname == val.classname && def.qualified == val.qualified;
1055 });
1056
1057 if (it != classList.end()) {
1058 it->classInfoList += def.classInfoList;
1059 Q_ASSERT(it->classInfoList.size() <= std::numeric_limits<int>::max());
1060 it->enumDeclarations.insert(map: def.enumDeclarations);
1061 it->enumList += def.enumList;
1062 Q_ASSERT(it->enumList.size() <= std::numeric_limits<int>::max());
1063 it->flagAliases.insert(map: def.flagAliases);
1064 } else {
1065 knownGadgets.insert(key: def.classname, value: def.qualified);
1066 knownGadgets.insert(key: def.qualified, value: def.qualified);
1067 if (n.doGenerate)
1068 classList += def;
1069 }
1070 }
1071}
1072
1073static bool any_type_contains(const QList<PropertyDef> &properties, const QByteArray &pattern)
1074{
1075 for (const auto &p : properties) {
1076 if (p.type.contains(bv: pattern))
1077 return true;
1078 }
1079 return false;
1080}
1081
1082static bool any_arg_contains(const QList<FunctionDef> &functions, const QByteArray &pattern)
1083{
1084 for (const auto &f : functions) {
1085 for (const auto &arg : f.arguments) {
1086 if (arg.normalizedType.contains(bv: pattern))
1087 return true;
1088 }
1089 }
1090 return false;
1091}
1092
1093static QByteArrayList make_candidates()
1094{
1095 QByteArrayList result;
1096 result
1097#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER
1098 QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER)
1099#undef STREAM_SMART_POINTER
1100#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME
1101 QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE)
1102#undef STREAM_1ARG_TEMPLATE
1103 ;
1104 return result;
1105}
1106
1107static QByteArrayList requiredQtContainers(const QList<ClassDef> &classes)
1108{
1109 static const QByteArrayList candidates = make_candidates();
1110
1111 QByteArrayList required;
1112 required.reserve(asize: candidates.size());
1113
1114 bool needsQProperty = false;
1115
1116 for (const auto &candidate : candidates) {
1117 const QByteArray pattern = candidate + '<';
1118
1119 for (const auto &c : classes) {
1120 for (const auto &p : c.propertyList)
1121 needsQProperty |= !p.bind.isEmpty();
1122 if (any_type_contains(properties: c.propertyList, pattern) ||
1123 any_arg_contains(functions: c.slotList, pattern) ||
1124 any_arg_contains(functions: c.signalList, pattern) ||
1125 any_arg_contains(functions: c.methodList, pattern)) {
1126 required.push_back(t: candidate);
1127 break;
1128 }
1129 }
1130 }
1131
1132 if (needsQProperty)
1133 required.push_back(t: "QProperty");
1134
1135 return required;
1136}
1137
1138void Moc::generate(FILE *out, FILE *jsonOutput)
1139{
1140 QByteArrayView fn = QByteArrayView(filename);
1141
1142 auto isSlash = [](char ch) { return ch == '/' || ch == '\\'; };
1143 auto rit = std::find_if(first: fn.crbegin(), last: fn.crend(), pred: isSlash);
1144 if (rit != fn.crend())
1145 fn = fn.last(n: rit - fn.crbegin());
1146
1147 fprintf(stream: out, format: "/****************************************************************************\n"
1148 "** Meta object code from reading C++ file '%s'\n**\n" , fn.constData());
1149 fprintf(stream: out, format: "** Created by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , mocOutputRevision, QT_VERSION_STR);
1150 fprintf(stream: out, format: "** WARNING! All changes made in this file will be lost!\n"
1151 "*****************************************************************************/\n\n");
1152
1153 // include header(s) of user class definitions at _first_ to allow
1154 // for preprocessor definitions possibly affecting standard headers.
1155 // see https://codereview.qt-project.org/c/qt/qtbase/+/445937
1156 if (!noInclude) {
1157 if (includePath.size() && !includePath.endsWith(c: '/'))
1158 includePath += '/';
1159 for (QByteArray inc : std::as_const(t&: includeFiles)) {
1160 if (!inc.isEmpty() && inc.at(i: 0) != '<' && inc.at(i: 0) != '"') {
1161 if (includePath.size() && includePath != "./")
1162 inc.prepend(a: includePath);
1163 inc = '\"' + inc + '\"';
1164 }
1165 fprintf(stream: out, format: "#include %s\n", inc.constData());
1166 }
1167 }
1168 if (classList.size() && classList.constFirst().classname == "Qt")
1169 fprintf(stream: out, format: "#include <QtCore/qobject.h>\n");
1170
1171 fprintf(stream: out, format: "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type
1172 if (mustIncludeQPluginH)
1173 fprintf(stream: out, format: "#include <QtCore/qplugin.h>\n");
1174
1175 const auto qtContainers = requiredQtContainers(classes: classList);
1176 for (const QByteArray &qtContainer : qtContainers)
1177 fprintf(stream: out, format: "#include <QtCore/%s>\n", qtContainer.constData());
1178
1179 fprintf(stream: out, format: "\n#include <QtCore/qtmochelpers.h>\n");
1180
1181 fprintf(stream: out, format: "\n#include <memory>\n\n"); // For std::addressof
1182 fprintf(stream: out, format: "\n#include <QtCore/qxptype_traits.h>\n"); // is_detected
1183
1184 fprintf(stream: out, format: "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
1185 "#error \"The header file '%s' doesn't include <QObject>.\"\n", fn.constData());
1186 fprintf(stream: out, format: "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
1187 fprintf(stream: out, format: "#error \"This file was generated using the moc from %s."
1188 " It\"\n#error \"cannot be used with the include files from"
1189 " this version of Qt.\"\n#error \"(The moc has changed too"
1190 " much.)\"\n", QT_VERSION_STR);
1191 fprintf(stream: out, format: "#endif\n\n");
1192
1193#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
1194 fprintf(stream: out, format: "#ifndef Q_CONSTINIT\n"
1195 "#define Q_CONSTINIT\n"
1196 "#endif\n\n");
1197#endif
1198
1199 fprintf(stream: out, format: "QT_WARNING_PUSH\n");
1200 fprintf(stream: out, format: "QT_WARNING_DISABLE_DEPRECATED\n");
1201 fprintf(stream: out, format: "QT_WARNING_DISABLE_GCC(\"-Wuseless-cast\")\n");
1202
1203 fputs(s: "", stream: out);
1204 for (ClassDef &def : classList) {
1205 Generator generator(this, &def, metaTypes, knownQObjectClasses, knownGadgets, out,
1206 requireCompleteTypes);
1207 generator.generateCode();
1208
1209 // generator.generateCode() should have already registered all strings
1210 if (Q_UNLIKELY(generator.registeredStringsCount() >= std::numeric_limits<int>::max())) {
1211 error(msg: "internal limit exceeded: number of parsed strings is too big.");
1212 exit(EXIT_FAILURE);
1213 }
1214 }
1215 fputs(s: "", stream: out);
1216
1217 fprintf(stream: out, format: "QT_WARNING_POP\n");
1218
1219 if (jsonOutput) {
1220 QJsonObject mocData;
1221 mocData["outputRevision"_L1] = mocOutputRevision;
1222 mocData["inputFile"_L1] = QLatin1StringView(fn.constData());
1223
1224 QJsonArray classesJsonFormatted;
1225
1226 for (const ClassDef &cdef: std::as_const(t&: classList))
1227 classesJsonFormatted.append(value: cdef.toJson());
1228
1229 if (!classesJsonFormatted.isEmpty())
1230 mocData["classes"_L1] = classesJsonFormatted;
1231
1232 QJsonDocument jsonDoc(mocData);
1233 fputs(s: jsonDoc.toJson().constData(), stream: jsonOutput);
1234 }
1235}
1236
1237void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
1238{
1239 QTypeRevision defaultRevision;
1240 if (test(token: Q_REVISION_TOKEN))
1241 defaultRevision = parseRevision();
1242
1243 next(token: COLON);
1244 while (inClass(def) && hasNext()) {
1245 switch (next()) {
1246 case PUBLIC:
1247 case PROTECTED:
1248 case PRIVATE:
1249 case Q_SIGNALS_TOKEN:
1250 case Q_SLOTS_TOKEN:
1251 prev();
1252 return;
1253 case SEMIC:
1254 continue;
1255 case FRIEND:
1256 until(SEMIC);
1257 continue;
1258 case USING:
1259 error(msg: "'using' directive not supported in 'slots' section");
1260 default:
1261 prev();
1262 }
1263
1264 FunctionDef funcDef;
1265 funcDef.access = access;
1266 if (!parseFunction(def: &funcDef))
1267 continue;
1268 if (funcDef.revision > 0) {
1269 ++def->revisionedMethods;
1270 } else if (defaultRevision.isValid()) {
1271 funcDef.revision = defaultRevision.toEncodedVersion<int>();
1272 ++def->revisionedMethods;
1273 }
1274 def->slotList += funcDef;
1275 handleDefaultArguments(functionList: &def->slotList, function&: funcDef);
1276 }
1277}
1278
1279void Moc::parseSignals(ClassDef *def)
1280{
1281 QTypeRevision defaultRevision;
1282 if (test(token: Q_REVISION_TOKEN))
1283 defaultRevision = parseRevision();
1284
1285 next(token: COLON);
1286 while (inClass(def) && hasNext()) {
1287 switch (next()) {
1288 case PUBLIC:
1289 case PROTECTED:
1290 case PRIVATE:
1291 case Q_SIGNALS_TOKEN:
1292 case Q_SLOTS_TOKEN:
1293 prev();
1294 return;
1295 case SEMIC:
1296 continue;
1297 case FRIEND:
1298 until(SEMIC);
1299 continue;
1300 case USING:
1301 error(msg: "'using' directive not supported in 'signals' section");
1302 default:
1303 prev();
1304 }
1305 FunctionDef funcDef;
1306 funcDef.access = FunctionDef::Public;
1307 parseFunction(def: &funcDef);
1308 if (funcDef.isVirtual)
1309 warning("Signals cannot be declared virtual");
1310 if (funcDef.inlineCode)
1311 error(msg: "Not a signal declaration");
1312 if (funcDef.revision > 0) {
1313 ++def->revisionedMethods;
1314 } else if (defaultRevision.isValid()) {
1315 funcDef.revision = defaultRevision.toEncodedVersion<int>();
1316 ++def->revisionedMethods;
1317 }
1318 def->signalList += funcDef;
1319 handleDefaultArguments(functionList: &def->signalList, function&: funcDef);
1320 }
1321}
1322
1323void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex, Moc::PropertyMode mode)
1324{
1325 propDef.location = index;
1326 propDef.relativeIndex = propertyIndex;
1327
1328 QByteArray type = parseType().name;
1329 if (type.isEmpty())
1330 error();
1331 propDef.designable = propDef.scriptable = propDef.stored = "true";
1332 propDef.user = "false";
1333 /*
1334 The Q_PROPERTY construct cannot contain any commas, since
1335 commas separate macro arguments. We therefore expect users
1336 to type "QMap" instead of "QMap<QString, QVariant>". For
1337 coherence, we also expect the same for
1338 QValueList<QVariant>, the other template class supported by
1339 QVariant.
1340 */
1341 type = normalizeType(ba: type);
1342 if (type == "QMap")
1343 type = "QMap<QString,QVariant>";
1344 else if (type == "QValueList")
1345 type = "QValueList<QVariant>";
1346 else if (type == "LongLong")
1347 type = "qlonglong";
1348 else if (type == "ULongLong")
1349 type = "qulonglong";
1350
1351 propDef.type = type;
1352
1353 if (mode == Moc::Named) {
1354 next();
1355 propDef.name = lexem();
1356 }
1357
1358 parsePropertyAttributes(propDef);
1359}
1360
1361void Moc::parsePropertyAttributes(PropertyDef &propDef)
1362{
1363 auto checkIsFunction = [&](const QByteArray &def, const char *name) {
1364 if (def.endsWith(c: ')')) {
1365 QByteArray msg = "Providing a function for ";
1366 msg += name;
1367 msg += " in a property declaration is not be supported in Qt 6.";
1368 error(msg: msg.constData());
1369 }
1370 };
1371
1372 while (test(token: IDENTIFIER)) {
1373 const Symbol &lsym = symbol();
1374 const QByteArray l = lsym.lexem();
1375 if (l[0] == 'C' && l == "CONSTANT") {
1376 propDef.constant = true;
1377 continue;
1378 } else if (l[0] == 'F' && l == "FINAL") {
1379 propDef.final = true;
1380 continue;
1381 } else if (l[0] == 'N' && l == "NAME") {
1382 next(token: IDENTIFIER);
1383 propDef.name = lexem();
1384 continue;
1385 } else if (l[0] == 'R' && l == "REQUIRED") {
1386 propDef.required = true;
1387 continue;
1388 } else if (l[0] == 'R' && l == "REVISION" && test(token: LPAREN)) {
1389 prev();
1390 propDef.revision = parseRevision().toEncodedVersion<int>();
1391 continue;
1392 }
1393
1394 QByteArray v, v2;
1395 if (test(token: LPAREN)) {
1396 v = lexemUntil(RPAREN);
1397 v = v.mid(index: 1, len: v.size() - 2); // removes the '(' and ')'
1398 } else if (test(token: INTEGER_LITERAL)) {
1399 v = lexem();
1400 if (l != "REVISION")
1401 error(symbol: lsym);
1402 } else if (test(token: DEFAULT)) {
1403 v = lexem();
1404 if (l != "READ" && l != "WRITE")
1405 error(symbol: lsym);
1406 } else {
1407 next(token: IDENTIFIER);
1408 v = lexem();
1409 if (test(token: LPAREN))
1410 v2 = lexemUntil(RPAREN);
1411 else if (v != "true" && v != "false")
1412 v2 = "()";
1413 }
1414 switch (l[0]) {
1415 case 'M':
1416 if (l == "MEMBER")
1417 propDef.member = v;
1418 else
1419 error(symbol: lsym);
1420 break;
1421 case 'R':
1422 if (l == "READ")
1423 propDef.read = v;
1424 else if (l == "RESET")
1425 propDef.reset = v;
1426 else if (l == "REVISION") {
1427 bool ok = false;
1428 const int minor = v.toInt(ok: &ok);
1429 if (!ok || !QTypeRevision::isValidSegment(segment: minor))
1430 error(symbol: lsym);
1431 propDef.revision = QTypeRevision::fromMinorVersion(minorVersion: minor).toEncodedVersion<int>();
1432 } else
1433 error(symbol: lsym);
1434 break;
1435 case 'S':
1436 if (l == "SCRIPTABLE") {
1437 propDef.scriptable = v + v2;
1438 checkIsFunction(propDef.scriptable, "SCRIPTABLE");
1439 } else if (l == "STORED") {
1440 propDef.stored = v + v2;
1441 checkIsFunction(propDef.stored, "STORED");
1442 } else
1443 error(symbol: lsym);
1444 break;
1445 case 'W': if (l != "WRITE") error(symbol: lsym);
1446 propDef.write = v;
1447 break;
1448 case 'B': if (l != "BINDABLE") error(symbol: lsym);
1449 propDef.bind = v;
1450 break;
1451 case 'D': if (l != "DESIGNABLE") error(symbol: lsym);
1452 propDef.designable = v + v2;
1453 checkIsFunction(propDef.designable, "DESIGNABLE");
1454 break;
1455 case 'N': if (l != "NOTIFY") error(symbol: lsym);
1456 propDef.notify = v;
1457 break;
1458 case 'U': if (l != "USER") error(symbol: lsym);
1459 propDef.user = v + v2;
1460 checkIsFunction(propDef.user, "USER");
1461 break;
1462 default:
1463 error(symbol: lsym);
1464 }
1465 }
1466 if (propDef.constant && !propDef.write.isNull()) {
1467 const QByteArray msg = "Property declaration " + propDef.name
1468 + " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
1469 propDef.constant = false;
1470 warning(msg.constData());
1471 }
1472 if (propDef.constant && !propDef.notify.isNull()) {
1473 const QByteArray msg = "Property declaration " + propDef.name
1474 + " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
1475 propDef.constant = false;
1476 warning(msg.constData());
1477 }
1478 if (propDef.constant && !propDef.bind.isNull()) {
1479 const QByteArray msg = "Property declaration " + propDef.name
1480 + " is both BINDable and CONSTANT. CONSTANT will be ignored.";
1481 propDef.constant = false;
1482 warning(msg.constData());
1483 }
1484 if (propDef.read == "default" && propDef.bind.isNull()) {
1485 const QByteArray msg = "Property declaration " + propDef.name
1486 + " is not BINDable but default-READable. READ will be ignored.";
1487 propDef.read = "";
1488 warning(msg.constData());
1489 }
1490 if (propDef.write == "default" && propDef.bind.isNull()) {
1491 const QByteArray msg = "Property declaration " + propDef.name
1492 + " is not BINDable but default-WRITEable. WRITE will be ignored.";
1493 propDef.write = "";
1494 warning(msg.constData());
1495 }
1496}
1497
1498void Moc::parseProperty(ClassDef *def, Moc::PropertyMode mode)
1499{
1500 next(token: LPAREN);
1501 PropertyDef propDef;
1502 createPropertyDef(propDef, propertyIndex: int(def->propertyList.size()), mode);
1503 next(token: RPAREN);
1504
1505 def->propertyList += propDef;
1506}
1507
1508void Moc::parsePluginData(ClassDef *def)
1509{
1510 next(token: LPAREN);
1511 QByteArray metaData;
1512 while (test(token: IDENTIFIER)) {
1513 QByteArray l = lexem();
1514 if (l == "IID") {
1515 next(token: STRING_LITERAL);
1516 def->pluginData.iid = unquotedLexem();
1517 } else if (l == "URI") {
1518 next(token: STRING_LITERAL);
1519 def->pluginData.uri = unquotedLexem();
1520 } else if (l == "FILE") {
1521 next(token: STRING_LITERAL);
1522 QByteArray metaDataFile = unquotedLexem();
1523 QFileInfo fi(QFileInfo(QString::fromLocal8Bit(ba: currentFilenames.top())).dir(),
1524 QString::fromLocal8Bit(ba: metaDataFile));
1525 for (const IncludePath &p : std::as_const(t&: includes)) {
1526 if (fi.exists())
1527 break;
1528 if (p.isFrameworkPath)
1529 continue;
1530
1531 fi.setFile(dir: QString::fromLocal8Bit(ba: p.path.constData()), file: QString::fromLocal8Bit(ba: metaDataFile.constData()));
1532 // try again, maybe there's a file later in the include paths with the same name
1533 if (fi.isDir()) {
1534 fi = QFileInfo();
1535 continue;
1536 }
1537 }
1538 if (!fi.exists()) {
1539 const QByteArray msg = "Plugin Metadata file " + lexem()
1540 + " does not exist. Declaration will be ignored";
1541 error(msg: msg.constData());
1542 return;
1543 }
1544 QFile file(fi.canonicalFilePath());
1545 if (!file.open(flags: QFile::ReadOnly)) {
1546 QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: "
1547 + file.errorString().toUtf8();
1548 error(msg: msg.constData());
1549 return;
1550 }
1551 parsedPluginMetadataFiles.append(t: fi.canonicalFilePath());
1552 metaData = file.readAll();
1553 }
1554 }
1555
1556 if (!metaData.isEmpty()) {
1557 def->pluginData.metaData = QJsonDocument::fromJson(json: metaData);
1558 if (!def->pluginData.metaData.isObject()) {
1559 const QByteArray msg = "Plugin Metadata file " + lexem()
1560 + " does not contain a valid JSON object. Declaration will be ignored";
1561 warning(msg.constData());
1562 def->pluginData.iid = QByteArray();
1563 def->pluginData.uri = QByteArray();
1564 return;
1565 }
1566 }
1567
1568 mustIncludeQPluginH = true;
1569 next(token: RPAREN);
1570}
1571
1572QByteArray Moc::parsePropertyAccessor()
1573{
1574 int nesting = 0;
1575 QByteArray accessor;
1576 while (1) {
1577 Token t = peek();
1578 if (!nesting && (t == RPAREN || t == COMMA))
1579 break;
1580 t = next();
1581 if (t == LPAREN)
1582 ++nesting;
1583 if (t == RPAREN)
1584 --nesting;
1585 accessor += lexem();
1586 }
1587 return accessor;
1588}
1589
1590void Moc::parsePrivateProperty(ClassDef *def, Moc::PropertyMode mode)
1591{
1592 next(token: LPAREN);
1593 PropertyDef propDef;
1594 propDef.inPrivateClass = parsePropertyAccessor();
1595
1596 next(token: COMMA);
1597
1598 createPropertyDef(propDef, propertyIndex: int(def->propertyList.size()), mode);
1599
1600 def->propertyList += propDef;
1601}
1602
1603void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag)
1604{
1605 next(token: LPAREN);
1606 QByteArray identifier;
1607 while (test(token: IDENTIFIER)) {
1608 identifier = lexem();
1609 while (test(token: SCOPE) && test(token: IDENTIFIER)) {
1610 identifier += "::";
1611 identifier += lexem();
1612 }
1613 def->enumDeclarations[identifier] = isFlag;
1614 }
1615 next(token: RPAREN);
1616}
1617
1618void Moc::parseFlag(BaseDef *def)
1619{
1620 next(token: LPAREN);
1621 QByteArray flagName, enumName;
1622 while (test(token: IDENTIFIER)) {
1623 flagName = lexem();
1624 while (test(token: SCOPE) && test(token: IDENTIFIER)) {
1625 flagName += "::";
1626 flagName += lexem();
1627 }
1628 }
1629 next(token: COMMA);
1630 while (test(token: IDENTIFIER)) {
1631 enumName = lexem();
1632 while (test(token: SCOPE) && test(token: IDENTIFIER)) {
1633 enumName += "::";
1634 enumName += lexem();
1635 }
1636 }
1637
1638 def->flagAliases.insert(key: enumName, value: flagName);
1639 next(token: RPAREN);
1640}
1641
1642Moc::EncounteredQmlMacro Moc::parseClassInfo(BaseDef *def)
1643{
1644 bool encounteredQmlMacro = false;
1645 next(token: LPAREN);
1646 ClassInfoDef infoDef;
1647 next(token: STRING_LITERAL);
1648 infoDef.name = symbol().unquotedLexem();
1649 if (infoDef.name.startsWith(bv: "QML."))
1650 encounteredQmlMacro = true;
1651 next(token: COMMA);
1652 if (test(token: STRING_LITERAL)) {
1653 infoDef.value = symbol().unquotedLexem();
1654 } else if (test(token: Q_REVISION_TOKEN)) {
1655 infoDef.value = QByteArray::number(parseRevision().toEncodedVersion<quint16>());
1656 } else {
1657 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1658 next(token: IDENTIFIER);
1659 next(token: LPAREN);
1660 next(token: STRING_LITERAL);
1661 infoDef.value = symbol().unquotedLexem();
1662 next(token: RPAREN);
1663 }
1664 next(token: RPAREN);
1665 def->classInfoList += infoDef;
1666 return encounteredQmlMacro ? EncounteredQmlMacro::Yes : EncounteredQmlMacro::No;
1667}
1668
1669void Moc::parseClassInfo(ClassDef *def)
1670{
1671 if (parseClassInfo(def: static_cast<BaseDef *>(def)) == EncounteredQmlMacro::Yes)
1672 def->requireCompleteMethodTypes = true;
1673}
1674
1675void Moc::parseInterfaces(ClassDef *def)
1676{
1677 next(token: LPAREN);
1678 while (test(token: IDENTIFIER)) {
1679 QList<ClassDef::Interface> iface;
1680 iface += ClassDef::Interface(lexem());
1681 while (test(token: SCOPE)) {
1682 iface.last().className += lexem();
1683 next(token: IDENTIFIER);
1684 iface.last().className += lexem();
1685 }
1686 while (test(token: COLON)) {
1687 next(token: IDENTIFIER);
1688 iface += ClassDef::Interface(lexem());
1689 while (test(token: SCOPE)) {
1690 iface.last().className += lexem();
1691 next(token: IDENTIFIER);
1692 iface.last().className += lexem();
1693 }
1694 }
1695 // resolve from classnames to interface ids
1696 for (qsizetype i = 0; i < iface.size(); ++i) {
1697 const QByteArray iid = interface2IdMap.value(key: iface.at(i).className);
1698 if (iid.isEmpty())
1699 error(msg: "Undefined interface");
1700
1701 iface[i].interfaceId = iid;
1702 }
1703 def->interfaceList += iface;
1704 }
1705 next(token: RPAREN);
1706}
1707
1708void Moc::parseDeclareInterface()
1709{
1710 next(token: LPAREN);
1711 QByteArray interface;
1712 next(token: IDENTIFIER);
1713 interface += lexem();
1714 while (test(token: SCOPE)) {
1715 interface += lexem();
1716 next(token: IDENTIFIER);
1717 interface += lexem();
1718 }
1719 next(token: COMMA);
1720 QByteArray iid;
1721 if (test(token: STRING_LITERAL)) {
1722 iid = lexem();
1723 } else {
1724 next(token: IDENTIFIER);
1725 iid = lexem();
1726 }
1727 interface2IdMap.insert(key: interface, value: iid);
1728 next(token: RPAREN);
1729}
1730
1731void Moc::parseDeclareMetatype()
1732{
1733 next(token: LPAREN);
1734 QByteArray typeName = lexemUntil(RPAREN);
1735 typeName.remove(index: 0, len: 1);
1736 typeName.chop(n: 1);
1737 metaTypes.append(t: typeName);
1738}
1739
1740void Moc::parseMocInclude()
1741{
1742 next(token: LPAREN);
1743 QByteArray include = lexemUntil(RPAREN);
1744 // remove parentheses
1745 include.remove(index: 0, len: 1);
1746 include.chop(n: 1);
1747 includeFiles.append(t: include);
1748}
1749
1750void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1751{
1752 next(token: LPAREN);
1753 FunctionDef funcDef;
1754 next(token: IDENTIFIER);
1755 funcDef.inPrivateClass = lexem();
1756 // also allow void functions
1757 if (test(token: LPAREN)) {
1758 next(token: RPAREN);
1759 funcDef.inPrivateClass += "()";
1760 }
1761 next(token: COMMA);
1762 funcDef.access = access;
1763 parseFunction(def: &funcDef, inMacro: true);
1764 def->slotList += funcDef;
1765 handleDefaultArguments(functionList: &def->slotList, function&: funcDef);
1766 if (funcDef.revision > 0)
1767 ++def->revisionedMethods;
1768
1769}
1770
1771QByteArray Moc::lexemUntil(Token target)
1772{
1773 qsizetype from = index;
1774 until(target);
1775 QByteArray s;
1776 while (from <= index) {
1777 QByteArray n = symbols.at(i: from++-1).lexem();
1778 if (s.size() && n.size()) {
1779 char prev = s.at(i: s.size()-1);
1780 char next = n.at(i: 0);
1781 if ((is_ident_char(s: prev) && is_ident_char(s: next))
1782 || (prev == '<' && next == ':')
1783 || (prev == '>' && next == '>'))
1784 s += ' ';
1785 }
1786 s += n;
1787 }
1788 return s;
1789}
1790
1791bool Moc::until(Token target) {
1792 int braceCount = 0;
1793 int brackCount = 0;
1794 int parenCount = 0;
1795 int angleCount = 0;
1796 if (index) {
1797 switch(symbols.at(i: index-1).token) {
1798 case LBRACE: ++braceCount; break;
1799 case LBRACK: ++brackCount; break;
1800 case LPAREN: ++parenCount; break;
1801 case LANGLE: ++angleCount; break;
1802 default: break;
1803 }
1804 }
1805
1806 //when searching commas within the default argument, we should take care of template depth (anglecount)
1807 // unfortunately, we do not have enough semantic information to know if '<' is the operator< or
1808 // the beginning of a template type. so we just use heuristics.
1809 qsizetype possible = -1;
1810
1811 while (index < symbols.size()) {
1812 Token t = symbols.at(i: index++).token;
1813 switch (t) {
1814 case LBRACE: ++braceCount; break;
1815 case RBRACE: --braceCount; break;
1816 case LBRACK: ++brackCount; break;
1817 case RBRACK: --brackCount; break;
1818 case LPAREN: ++parenCount; break;
1819 case RPAREN: --parenCount; break;
1820 case LANGLE:
1821 if (parenCount == 0 && braceCount == 0)
1822 ++angleCount;
1823 break;
1824 case RANGLE:
1825 if (parenCount == 0 && braceCount == 0)
1826 --angleCount;
1827 break;
1828 case GTGT:
1829 if (parenCount == 0 && braceCount == 0) {
1830 angleCount -= 2;
1831 t = RANGLE;
1832 }
1833 break;
1834 default: break;
1835 }
1836 if (t == target
1837 && braceCount <= 0
1838 && brackCount <= 0
1839 && parenCount <= 0
1840 && (target != RANGLE || angleCount <= 0)) {
1841 if (target != COMMA || angleCount <= 0)
1842 return true;
1843 possible = index;
1844 }
1845
1846 if (target == COMMA && t == EQ && possible != -1) {
1847 index = possible;
1848 return true;
1849 }
1850
1851 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1852 || (target == RANGLE && angleCount < 0)) {
1853 --index;
1854 break;
1855 }
1856
1857 if (braceCount <= 0 && t == SEMIC) {
1858 // Abort on semicolon. Allow recovering bad template parsing (QTBUG-31218)
1859 break;
1860 }
1861 }
1862
1863 if (target == COMMA && angleCount != 0 && possible != -1) {
1864 index = possible;
1865 return true;
1866 }
1867
1868 return false;
1869}
1870
1871void Moc::checkSuperClasses(ClassDef *def)
1872{
1873 Q_ASSERT(!def->superclassList.isEmpty());
1874 const QByteArray &firstSuperclass = def->superclassList.at(i: 0).classname;
1875
1876 if (!knownQObjectClasses.contains(key: firstSuperclass)) {
1877 // enable once we /require/ include paths
1878#if 0
1879 const QByteArray msg
1880 = "Class "
1881 + def->className
1882 + " contains the Q_OBJECT macro and inherits from "
1883 + def->superclassList.value(0)
1884 + " but that is not a known QObject subclass. You may get compilation errors.";
1885 warning(msg.constData());
1886#endif
1887 return;
1888 }
1889
1890 auto isRegisteredInterface = [&def](QByteArrayView super) {
1891 auto matchesSuperClass = [&super](const auto &ifaces) {
1892 return !ifaces.isEmpty() && ifaces.first().className == super;
1893 };
1894 return std::any_of(first: def->interfaceList.cbegin(), last: def->interfaceList.cend(), pred: matchesSuperClass);
1895 };
1896
1897 const auto end = def->superclassList.cend();
1898 auto it = def->superclassList.cbegin() + 1;
1899 for (; it != end; ++it) {
1900 const QByteArray &superClass = it->classname;
1901 if (knownQObjectClasses.contains(key: superClass)) {
1902 const QByteArray msg
1903 = "Class "
1904 + def->classname
1905 + " inherits from two QObject subclasses "
1906 + firstSuperclass
1907 + " and "
1908 + superClass
1909 + ". This is not supported!";
1910 warning(msg.constData());
1911 }
1912
1913 if (interface2IdMap.contains(key: superClass)) {
1914 if (!isRegisteredInterface(superClass)) {
1915 const QByteArray msg
1916 = "Class "
1917 + def->classname
1918 + " implements the interface "
1919 + superClass
1920 + " but does not list it in Q_INTERFACES. qobject_cast to "
1921 + superClass
1922 + " will not work!";
1923 warning(msg.constData());
1924 }
1925 }
1926 }
1927}
1928
1929void Moc::checkProperties(ClassDef *cdef)
1930{
1931 //
1932 // specify get function, for compatibility we accept functions
1933 // returning pointers, or const char * for QByteArray.
1934 //
1935 QDuplicateTracker<QByteArray> definedProperties(cdef->propertyList.size());
1936 auto hasNoAttributes = [&](const PropertyDef &p) {
1937 if (definedProperties.hasSeen(s: p.name)) {
1938 QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + ".";
1939 warning(msg.constData());
1940 }
1941
1942 if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) {
1943 QByteArray msg = "Property declaration " + p.name + " has neither an associated QProperty<> member"
1944 ", nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.";
1945 const auto &sym = p.location >= 0 ? symbolAt(idx: p.location) : Symbol();
1946 warning(sym, msg: msg.constData());
1947 if (p.write.isEmpty())
1948 return true;
1949 }
1950 return false;
1951 };
1952 cdef->propertyList.removeIf(pred: hasNoAttributes);
1953
1954 for (PropertyDef &p : cdef->propertyList) {
1955 for (const FunctionDef &f : std::as_const(t&: cdef->publicList)) {
1956 if (f.name != p.read)
1957 continue;
1958 if (!f.isConst) // get functions must be const
1959 continue;
1960 if (f.arguments.size()) // and must not take any arguments
1961 continue;
1962 PropertyDef::Specification spec = PropertyDef::ValueSpec;
1963 QByteArray tmp = f.normalizedType;
1964 if (p.type == "QByteArray" && tmp == "const char *")
1965 tmp = "QByteArray";
1966 if (tmp.left(n: 6) == "const ")
1967 tmp = tmp.mid(index: 6);
1968 if (p.type != tmp && tmp.endsWith(c: '*')) {
1969 tmp.chop(n: 1);
1970 spec = PropertyDef::PointerSpec;
1971 } else if (f.type.name.endsWith(c: '&')) { // raw type, not normalized type
1972 spec = PropertyDef::ReferenceSpec;
1973 }
1974 if (p.type != tmp)
1975 continue;
1976 p.gspec = spec;
1977 break;
1978 }
1979 if (!p.notify.isEmpty()) {
1980 int notifyId = -1;
1981 for (int j = 0; j < int(cdef->signalList.size()); ++j) {
1982 const FunctionDef &f = cdef->signalList.at(i: j);
1983 if (f.name != p.notify) {
1984 continue;
1985 } else {
1986 notifyId = j /* Signal indexes start from 0 */;
1987 break;
1988 }
1989 }
1990 p.notifyId = notifyId;
1991 if (notifyId == -1) {
1992 const int index = int(cdef->nonClassSignalList.indexOf(t: p.notify));
1993 if (index == -1) {
1994 cdef->nonClassSignalList << p.notify;
1995 p.notifyId = int(-1 - cdef->nonClassSignalList.size());
1996 } else {
1997 p.notifyId = int(-2 - index);
1998 }
1999 }
2000 }
2001 }
2002}
2003
2004QJsonObject ClassDef::toJson() const
2005{
2006 QJsonObject cls;
2007 cls["className"_L1] = QString::fromUtf8(utf8: classname.constData());
2008 cls["qualifiedClassName"_L1] = QString::fromUtf8(utf8: qualified.constData());
2009 cls["lineNumber"_L1] = lineNumber;
2010
2011 QJsonArray classInfos;
2012 for (const auto &info: std::as_const(t: classInfoList)) {
2013 QJsonObject infoJson;
2014 infoJson["name"_L1] = QString::fromUtf8(ba: info.name);
2015 infoJson["value"_L1] = QString::fromUtf8(ba: info.value);
2016 classInfos.append(value: infoJson);
2017 }
2018
2019 if (classInfos.size())
2020 cls["classInfos"_L1] = classInfos;
2021
2022 int methodIndex = 0;
2023 const auto appendFunctions
2024 = [&cls, &methodIndex](const QString &type, const QList<FunctionDef> &funcs) {
2025 QJsonArray jsonFuncs;
2026
2027 for (const FunctionDef &fdef: funcs)
2028 jsonFuncs.append(value: fdef.toJson(index: methodIndex++));
2029
2030 if (!jsonFuncs.isEmpty())
2031 cls[type] = jsonFuncs;
2032 };
2033
2034 // signals, slots, and methods, in this order, follow the same index
2035 appendFunctions("signals"_L1, signalList);
2036 appendFunctions("slots"_L1, slotList);
2037 appendFunctions("methods"_L1, methodList);
2038
2039 // constructors are indexed separately.
2040 methodIndex = 0;
2041 appendFunctions("constructors"_L1, constructorList);
2042
2043 QJsonArray props;
2044
2045 for (const PropertyDef &propDef: std::as_const(t: propertyList))
2046 props.append(value: propDef.toJson());
2047
2048 if (!props.isEmpty())
2049 cls["properties"_L1] = props;
2050
2051 if (hasQObject)
2052 cls["object"_L1] = true;
2053 if (hasQGadget)
2054 cls["gadget"_L1] = true;
2055 if (hasQNamespace)
2056 cls["namespace"_L1] = true;
2057
2058 QJsonArray superClasses;
2059
2060 for (const auto &super: std::as_const(t: superclassList)) {
2061 QJsonObject superCls;
2062 superCls["name"_L1] = QString::fromUtf8(ba: super.classname);
2063 if (super.classname != super.qualified)
2064 superCls["fullyQualifiedName"_L1] = QString::fromUtf8(ba: super.qualified);
2065 FunctionDef::accessToJson(obj: &superCls, acs: super.access);
2066 superClasses.append(value: superCls);
2067 }
2068
2069 if (!superClasses.isEmpty())
2070 cls["superClasses"_L1] = superClasses;
2071
2072 QJsonArray enums;
2073 for (const EnumDef &enumDef: std::as_const(t: enumList))
2074 enums.append(value: enumDef.toJson(cdef: *this));
2075 if (!enums.isEmpty())
2076 cls["enums"_L1] = enums;
2077
2078 QJsonArray ifaces;
2079 for (const QList<Interface> &ifaceList : interfaceList) {
2080 QJsonArray jsonList;
2081 for (const Interface &iface: ifaceList) {
2082 QJsonObject ifaceJson;
2083 ifaceJson["id"_L1] = QString::fromUtf8(ba: iface.interfaceId);
2084 ifaceJson["className"_L1] = QString::fromUtf8(ba: iface.className);
2085 jsonList.append(value: ifaceJson);
2086 }
2087 ifaces.append(value: jsonList);
2088 }
2089 if (!ifaces.isEmpty())
2090 cls["interfaces"_L1] = ifaces;
2091
2092 return cls;
2093}
2094
2095QJsonObject FunctionDef::toJson(int index) const
2096{
2097 QJsonObject fdef;
2098 fdef["name"_L1] = QString::fromUtf8(ba: name);
2099 fdef["index"_L1] = index;
2100 if (!tag.isEmpty())
2101 fdef["tag"_L1] = QString::fromUtf8(ba: tag);
2102 fdef["returnType"_L1] = QString::fromUtf8(ba: normalizedType);
2103
2104 QJsonArray args;
2105 for (const ArgumentDef &arg: arguments)
2106 args.append(value: arg.toJson());
2107
2108 if (!args.isEmpty())
2109 fdef["arguments"_L1] = args;
2110
2111 accessToJson(obj: &fdef, acs: access);
2112
2113 if (revision > 0)
2114 fdef["revision"_L1] = revision;
2115
2116 if (wasCloned)
2117 fdef["isCloned"_L1] = true;
2118
2119 return fdef;
2120}
2121
2122void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs)
2123{
2124 switch (acs) {
2125 case Private: (*obj)["access"_L1] = "private"_L1; break;
2126 case Public: (*obj)["access"_L1] = "public"_L1; break;
2127 case Protected: (*obj)["access"_L1] = "protected"_L1; break;
2128 }
2129}
2130
2131QJsonObject ArgumentDef::toJson() const
2132{
2133 QJsonObject arg;
2134 arg["type"_L1] = QString::fromUtf8(ba: normalizedType);
2135 if (!name.isEmpty())
2136 arg["name"_L1] = QString::fromUtf8(ba: name);
2137 return arg;
2138}
2139
2140QJsonObject PropertyDef::toJson() const
2141{
2142 QJsonObject prop;
2143 prop["name"_L1] = QString::fromUtf8(ba: name);
2144 prop["type"_L1] = QString::fromUtf8(ba: type);
2145
2146 const auto jsonify = [&prop](const char *str, const QByteArray &member) {
2147 if (!member.isEmpty())
2148 prop[QLatin1StringView(str)] = QString::fromUtf8(ba: member);
2149 };
2150
2151 jsonify("member", member);
2152 jsonify("read", read);
2153 jsonify("write", write);
2154 jsonify("bindable", bind);
2155 jsonify("reset", reset);
2156 jsonify("notify", notify);
2157 jsonify("privateClass", inPrivateClass);
2158
2159 const auto jsonifyBoolOrString = [&prop](const char *str, const QByteArray &boolOrString) {
2160 QJsonValue value;
2161 if (boolOrString == "true")
2162 value = true;
2163 else if (boolOrString == "false")
2164 value = false;
2165 else
2166 value = QString::fromUtf8(ba: boolOrString); // function name to query at run-time
2167 prop[QLatin1StringView(str)] = value;
2168 };
2169
2170 jsonifyBoolOrString("designable", designable);
2171 jsonifyBoolOrString("scriptable", scriptable);
2172 jsonifyBoolOrString("stored", stored);
2173 jsonifyBoolOrString("user", user);
2174
2175 prop["constant"_L1] = constant;
2176 prop["final"_L1] = final;
2177 prop["required"_L1] = required;
2178 prop["index"_L1] = relativeIndex;
2179 if (revision > 0)
2180 prop["revision"_L1] = revision;
2181
2182 return prop;
2183}
2184
2185QJsonObject EnumDef::toJson(const ClassDef &cdef) const
2186{
2187 QJsonObject def;
2188 def["name"_L1] = QString::fromUtf8(ba: name);
2189 if (!enumName.isEmpty())
2190 def["alias"_L1] = QString::fromUtf8(ba: enumName);
2191 if (!type.isEmpty())
2192 def["type"_L1] = QString::fromUtf8(ba: type);
2193 def["isFlag"_L1] = cdef.enumDeclarations.value(key: name);
2194 def["isClass"_L1] = isEnumClass;
2195
2196 QJsonArray valueArr;
2197 for (const QByteArray &value: values)
2198 valueArr.append(value: QString::fromUtf8(ba: value));
2199 if (!valueArr.isEmpty())
2200 def["values"_L1] = valueArr;
2201
2202 return def;
2203}
2204
2205QByteArray EnumDef::qualifiedType(const ClassDef *cdef) const
2206{
2207 if (name == cdef->classname) {
2208 // The name of the enclosing namespace is the same as the enum class name
2209 if (cdef->qualified.contains(bv: "::")) {
2210 // QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum
2211 // class name and enclosing namespace, e.g.:
2212 // namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } }
2213 return cdef->qualified % "::" % name;
2214 } else {
2215 // Just "B"; otherwise the compiler complains about the type "B::B" inside
2216 // "B::staticMetaObject" in the generated code; e.g.:
2217 // namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) }
2218 return name;
2219 }
2220 }
2221 return cdef->classname % "::" % name;
2222}
2223
2224QT_END_NAMESPACE
2225

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/tools/moc/moc.cpp