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

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