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

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