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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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