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