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