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

source code of qtscxml/tools/qscxmlc/moc.cpp