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 if (!test(RPAREN)) {
600 if (!test(RPAREN))
601 return false;
602 }
603
604 def->isConst = test(CONST);
605
606 while (test(IDENTIFIER))
607 ;
608
609 if (test(THROW)) {
610 next(LPAREN);
611 until(RPAREN);
612 }
613
614 if (def->type.name == "auto" && test(ARROW))
615 def->type = parseType(); // Parse trailing return-type
616
617 if (scopedFunctionName
618 && (def->isSignal || def->isSlot || def->isInvokable)) {
619 const QByteArray msg = "parsemaybe: Function declaration " + def->name
620 + " contains extra qualification. Ignoring as signal or slot.";
621 warning(msg.constData());
622 return false;
623 }
624
625 if (def->isSlot || def->isSignal || def->isInvokable) {
626 QList<QByteArray> typeNameParts = normalizeType(def->type.name).split(' ');
627 if (typeNameParts.contains("auto")) {
628 // We expected a trailing return type but we haven't seen one
629 error("Function declared with auto as return type but missing trailing return type. "
630 "Return type deduction is not supported.");
631 }
632 }
633 // we don't support references as return types, it's too dangerous
634 if (def->type.referenceType == Type::Reference) {
635 QByteArray rawName = def->type.rawName;
636 def->type = Type("void");
637 def->type.rawName = rawName;
638 }
639
640 def->normalizedType = normalizeType(def->type.name);
641 return true;
642}
643
644inline void handleDefaultArguments(QList<FunctionDef> *functionList, FunctionDef &function)
645{
646 // support a function with a default argument by pretending there is an
647 // overload without the argument (the original function is the overload with
648 // all arguments present)
649 while (function.arguments.size() > 0 && function.arguments.constLast().isDefault) {
650 function.wasCloned = true;
651 function.arguments.removeLast();
652 *functionList += function;
653 }
654}
655
656void Moc::prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const
657{
658 auto it = namespaceList.crbegin();
659 const auto rend = namespaceList.crend();
660 for (; it != rend; ++it) {
661 if (inNamespace(&*it))
662 def.qualified.prepend(it->classname + "::");
663 }
664}
665
666void Moc::checkListSizes(const ClassDef &def)
667{
668 if (Q_UNLIKELY(def.nonClassSignalList.size() > std::numeric_limits<int>::max()))
669 error("number of signals defined in parent class(es) exceeds "
670 "std::numeric_limits<int>::max().");
671
672 if (Q_UNLIKELY(def.propertyList.size() > std::numeric_limits<int>::max()))
673 error("number of bindable properties exceeds std::numeric_limits<int>::max().");
674
675 if (Q_UNLIKELY(def.classInfoList.size() > std::numeric_limits<int>::max()))
676 error("number of times Q_CLASSINFO macro is used exceeds "
677 "std::numeric_limits<int>::max().");
678
679 if (Q_UNLIKELY(def.enumList.size() > std::numeric_limits<int>::max()))
680 error("number of enumerations exceeds std::numeric_limits<int>::max().");
681
682 if (Q_UNLIKELY(def.superclassList.size() > std::numeric_limits<int>::max()))
683 error("number of super classes exceeds std::numeric_limits<int>::max().");
684
685 if (Q_UNLIKELY(def.constructorList.size() > std::numeric_limits<int>::max()))
686 error("number of constructor parameters exceeds std::numeric_limits<int>::max().");
687
688 if (Q_UNLIKELY(def.signalList.size() > std::numeric_limits<int>::max()))
689 error("number of signals exceeds std::numeric_limits<int>::max().");
690
691 if (Q_UNLIKELY(def.slotList.size() > std::numeric_limits<int>::max()))
692 error("number of declared slots exceeds std::numeric_limits<int>::max().");
693
694 if (Q_UNLIKELY(def.methodList.size() > std::numeric_limits<int>::max()))
695 error("number of methods exceeds std::numeric_limits<int>::max().");
696
697 if (Q_UNLIKELY(def.publicList.size() > std::numeric_limits<int>::max()))
698 error("number of public functions declared in this class exceeds "
699 "std::numeric_limits<int>::max().");
700}
701
702void Moc::parseModuleDeclaration(qsizetype rewind, bool exported)
703{
704 // If we already found a module declaration, this can't be one.
706 return;
707
708 if (test(SEMIC)) {
710 return;
711 }
712
713 if (!test(IDENTIFIER)) {
714 index = rewind;
715 return;
716 }
717
718 // Collect full module name, including dots.
719 QByteArray name = lexem();
720 while (test(DOT)) {
721 if (!test(IDENTIFIER)) {
722 index = rewind;
723 return;
724 }
725 name += '.';
726 name += lexem();
727 }
728
729 QByteArray partition;
730 if (test(COLON)) {
731 if (!test(IDENTIFIER)) {
732 index = rewind;
733 return;
734 }
735 partition = lexem();
736 }
737
738 if (!test(SEMIC)) {
739 index = rewind;
740 return;
741 }
742
743 moduleName = name;
744 modulePartitionName = partition;
745 if (exported) {
748 } else {
751 }
752}
753
754void Moc::parsePrivateModuleFragment(qsizetype rewind)
755{
756 // A private module fragment can only occur once, and only in the primary module interface.
758 index = rewind;
759 return;
760 }
761
762 if (!test(PRIVATE)) {
763 index = rewind;
764 return;
765 }
766
767 if (!test(SEMIC)) {
768 index = rewind;
769 return;
770 }
771
773}
774
775void Moc::parse()
776{
777 QList<NamespaceDef> namespaceList;
778 bool templateClass = false;
779 while (hasNext()) {
780 Token t = next();
781 switch (t) {
782 case NAMESPACE: {
783 qsizetype rewind = index;
784 if (test(IDENTIFIER)) {
785 QByteArray nsName = lexem();
786 QByteArrayList nested;
787 while (test(SCOPE)) {
788 /* treat (C++20's) namespace A::inline B {} as A::B
789 this is mostly to not break compilation when encountering such
790 a construct in a header; the interaction of Qt's meta-macros with
791 inline namespaces is still rather poor.
792 */
793 test(INLINE);
794 next(IDENTIFIER);
795 nested.append(nsName);
796 nsName = lexem();
797 }
798 if (test(EQ)) {
799 // namespace Foo = Bar::Baz;
800 until(SEMIC);
801 } else if (test(LPAREN)) {
802 // Ignore invalid code such as: 'namespace __identifier("x")' (QTBUG-56634)
803 until(RPAREN);
804 } else if (!test(SEMIC)) {
805 NamespaceDef def;
806 def.classname = nsName;
807 def.lineNumber = symbol().lineNum;
808 def.doGenerate = currentFilenames.size() <= 1;
809
810 next(LBRACE);
811 def.begin = index - 1;
812 until(RBRACE);
813 def.end = index;
814 index = def.begin + 1;
815
816 prependNamespaces(def, namespaceList);
817
818 for (const QByteArray &ns : nested) {
819 NamespaceDef parentNs;
820 parentNs.classname = ns;
821 parentNs.qualified = def.qualified;
822 def.qualified += ns + "::";
823 parentNs.begin = def.begin;
824 parentNs.end = def.end;
825 namespaceList += parentNs;
826 }
827
828 while (inNamespace(&def) && hasNext()) {
829 switch (next()) {
830 case NAMESPACE:
831 if (test(IDENTIFIER)) {
832 while (test(SCOPE)) {
833 test(INLINE); // ignore inline namespaces
834 next(IDENTIFIER);
835 }
836 if (test(EQ)) {
837 // namespace Foo = Bar::Baz;
838 until(SEMIC);
839 } else if (!test(SEMIC)) {
840 until(RBRACE);
841 }
842 }
843 break;
844 case Q_NAMESPACE_EXPORT_TOKEN:
845 next(LPAREN);
846 while (test(IDENTIFIER))
847 {}
848 next(RPAREN);
849 Q_FALLTHROUGH();
850 case Q_NAMESPACE_TOKEN:
851 def.hasQNamespace = true;
852 if (moduleUnitKind == ModuleUnitKind::ImplementationUnit)
853 error("Q_NAMESPACE is not supported in a module implementation unit");
854 if (inPrivateModuleFragment)
855 error("Q_NAMESPACE is not supported in a private module fragment");
856 break;
857 case Q_ENUMS_TOKEN:
858 case Q_ENUM_NS_TOKEN:
859 parseEnumOrFlag(&def, {});
860 break;
861 case Q_ENUM_TOKEN:
862 error("Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead");
863 break;
864 case Q_FLAGS_TOKEN:
865 case Q_FLAG_NS_TOKEN:
866 parseEnumOrFlag(&def, EnumIsFlag);
867 break;
868 case Q_FLAG_TOKEN:
869 error("Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead");
870 break;
871 case Q_DECLARE_FLAGS_TOKEN:
872 parseFlag(&def);
873 break;
874 case Q_CLASSINFO_TOKEN:
875 parseClassInfo(&def);
876 break;
877 case Q_MOC_INCLUDE_TOKEN:
878 // skip it, the namespace is parsed twice
879 next(LPAREN);
880 lexemUntil(RPAREN);
881 break;
882 case ENUM: {
883 EnumDef enumDef;
884 if (parseEnum(&enumDef, nullptr))
885 def.enumList += enumDef;
886 } break;
887 case CLASS:
888 case STRUCT: {
889 ClassDef classdef;
890 if (!parseClassHead(&classdef))
891 continue;
892 while (inClass(&classdef) && hasNext())
893 next(); // consume all Q_XXXX macros from this class
894 } break;
895 default: break;
896 }
897 }
898 namespaceList += def;
899 index = rewind;
900 if (!def.hasQNamespace && (!def.classInfoList.isEmpty() || !def.enumDeclarations.isEmpty()))
901 error("Namespace declaration lacks Q_NAMESPACE macro.");
902 }
903 }
904 break;
905 }
906 case SEMIC:
907 case RBRACE:
908 templateClass = false;
909 break;
910 case TEMPLATE:
911 templateClass = true;
912 break;
913 case MOC_INCLUDE_BEGIN:
914 // Record (top-level) includes in the global module fragment for later
915 // insertion into the generated file.
916 // As it is fiendishly difficult to find out if and how the contents of
917 // a header file are being used, we have to assume that the compiler will need
918 // all of them when building our generated file.
919 if (hasGlobalModuleFragment && moduleUnitKind == ModuleUnitKind::NotAModule
920 && currentFilenames.size() == 1) {
921 moduleFragmentIncludes += symbol().lexem();
922 }
923
924 currentFilenames.push(symbol().unquotedLexem());
925 break;
926 case MOC_INCLUDE_END:
927 currentFilenames.pop();
928 break;
929 case Q_DECLARE_INTERFACE_TOKEN:
931 break;
932 case Q_DECLARE_METATYPE_TOKEN:
934 break;
935 case Q_MOC_INCLUDE_TOKEN:
937 break;
938 case USING:
939 if (test(NAMESPACE)) {
940 while (test(SCOPE) || test(IDENTIFIER))
941 ;
942 // Ignore invalid code such as: 'using namespace __identifier("x")' (QTBUG-63772)
943 if (test(LPAREN))
944 until(RPAREN);
945 next(SEMIC);
946 }
947 break;
948 case CLASS:
949 case STRUCT: {
950 if (currentFilenames.size() <= 1)
951 break;
952
953 ClassDef def;
954 if (!parseClassHead(&def))
955 continue;
956
957 while (inClass(&def) && hasNext()) {
958 switch (next()) {
959 case Q_OBJECT_TOKEN:
960 def.hasQObject = true;
961 break;
962 case Q_GADGET_EXPORT_TOKEN:
963 next(LPAREN);
964 while (test(IDENTIFIER))
965 {}
966 next(RPAREN);
967 Q_FALLTHROUGH();
968 case Q_GADGET_TOKEN:
969 def.hasQGadget = true;
970 break;
971 default: break;
972 }
973 }
974
975 if (!def.hasQObject && !def.hasQGadget)
976 continue;
977
978 prependNamespaces(def, namespaceList);
979
980 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
981 classHash.insert(def.classname, def.qualified);
982 classHash.insert(def.qualified, def.qualified);
983
984 continue; }
985 case EXPORT: {
986 const qsizetype afterExport = index;
987 if (currentFilenames.size() == 1 && test(IDENTIFIER) && lexem() == "module")
988 parseModuleDeclaration(index, /*exported=*/true);
989 else
990 index = afterExport;
991 break;
992 }
993 case IDENTIFIER:
994 if (currentFilenames.size() == 1 && lexem() == "module") {
995 const qsizetype afterModule = index;
996 if (test(COLON)) {
997 // A private module fragment can follow any ordinary declaration
998 // in the primary interface.
999 parsePrivateModuleFragment(afterModule);
1000 } else {
1001 // Since "module" is a legal identifier in most contexts, we
1002 // must be careful about false positives.
1003 const qsizetype afterModuleInSource =
1004 afterModule - firstSourceSymbolIndex;
1005 const bool precededByBareModuleFragment =
1006 afterModuleInSource == 3
1007 && symbols.at(afterModule - 2).token == SEMIC
1008 && symbols.at(afterModule - 3).token == IDENTIFIER
1009 && symbols.at(afterModule - 3).lexem() == "module";
1010 const bool isModuleCandidate = afterModuleInSource < 2
1011 || precededByBareModuleFragment
1012 || (hasGlobalModuleFragment
1013 && symbols.at(afterModule - 2).token == MOC_INCLUDE_END);
1014 if (isModuleCandidate)
1015 parseModuleDeclaration(afterModule, /*exported=*/false);
1016 }
1017 }
1018 break;
1019 default: break;
1020 }
1021 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
1022 continue;
1023 ClassDef def;
1024 if (parseClassHead(&def)) {
1025 Symbol qmlRegistrationMacroSymbol = {};
1026 prependNamespaces(def, namespaceList);
1027
1029 while (inClass(&def) && hasNext()) {
1030 switch ((t = next())) {
1031 case PRIVATE:
1032 access = FunctionDef::Private;
1033 if (test(Q_SIGNALS_TOKEN))
1034 error("Signals cannot have access specifier");
1035 break;
1036 case PROTECTED:
1037 access = FunctionDef::Protected;
1038 if (test(Q_SIGNALS_TOKEN))
1039 error("Signals cannot have access specifier");
1040 break;
1041 case PUBLIC:
1042 access = FunctionDef::Public;
1043 if (test(Q_SIGNALS_TOKEN))
1044 error("Signals cannot have access specifier");
1045 break;
1046 case STRUCT:
1047 case CLASS: {
1048 ClassDef nestedDef;
1049 if (parseClassHead(&nestedDef)) {
1050 while (inClass(&nestedDef) && inClass(&def)) {
1051 t = next();
1052 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
1053 error("Meta object features not supported for nested classes");
1054 }
1055 }
1056 } break;
1057 case Q_SIGNALS_TOKEN:
1058 parseSignals(&def);
1059 break;
1060 case Q_SLOTS_TOKEN:
1061 switch (lookup(-1)) {
1062 case PUBLIC:
1063 case PROTECTED:
1064 case PRIVATE:
1065 parseSlots(&def, access);
1066 break;
1067 default:
1068 error("Missing access specifier for slots");
1069 }
1070 break;
1071 case Q_OBJECT_TOKEN:
1072 def.hasQObject = true;
1073 if (templateClass)
1074 error("Template classes not supported by Q_OBJECT");
1075 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
1076 error("Class contains Q_OBJECT macro but does not inherit from QObject");
1077 if (moduleUnitKind == ModuleUnitKind::ImplementationUnit)
1078 error("Q_OBJECT is not supported in a module implementation unit");
1079 if (inPrivateModuleFragment)
1080 error("Q_OBJECT is not supported in a private module fragment");
1081 break;
1082 case Q_GADGET_EXPORT_TOKEN:
1083 next(LPAREN);
1084 while (test(IDENTIFIER))
1085 {}
1086 next(RPAREN);
1087 Q_FALLTHROUGH();
1088 case Q_GADGET_TOKEN:
1089 def.hasQGadget = true;
1090 if (templateClass)
1091 error("Template classes not supported by Q_GADGET");
1092 if (moduleUnitKind == ModuleUnitKind::ImplementationUnit)
1093 error("Q_GADGET is not supported in a module implementation unit");
1094 if (inPrivateModuleFragment)
1095 error("Q_GADGET is not supported in a private module fragment");
1096 break;
1097 case Q_PROPERTY_TOKEN:
1099 break;
1100 case QT_ANONYMOUS_PROPERTY_TOKEN:
1102 break;
1103 case Q_PLUGIN_METADATA_TOKEN:
1105 break;
1106 case Q_ENUMS_TOKEN:
1107 case Q_ENUM_TOKEN:
1108 parseEnumOrFlag(&def, {});
1109 break;
1110 case Q_ENUM_NS_TOKEN:
1111 error("Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead");
1112 break;
1113 case Q_FLAGS_TOKEN:
1114 case Q_FLAG_TOKEN:
1115 parseEnumOrFlag(&def, EnumIsFlag);
1116 break;
1117 case Q_FLAG_NS_TOKEN:
1118 error("Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead");
1119 break;
1120 case Q_DECLARE_FLAGS_TOKEN:
1121 parseFlag(&def);
1122 break;
1123 case Q_CLASSINFO_TOKEN:
1124 parseClassInfo(&def);
1125 break;
1126 case Q_MOC_INCLUDE_TOKEN:
1128 break;
1129 case Q_INTERFACES_TOKEN:
1131 break;
1132 case Q_PRIVATE_SLOT_TOKEN:
1133 parseSlotInPrivate(&def, access);
1134 break;
1135 case Q_PRIVATE_PROPERTY_TOKEN:
1137 break;
1138 case QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN:
1140 break;
1141 case ENUM: {
1142 EnumDef enumDef;
1143 if (parseEnum(&enumDef, &def))
1144 def.enumList += enumDef;
1145 } break;
1146 case SEMIC:
1147 case COLON:
1148 break;
1149 case IDENTIFIER:
1150 {
1151 const QByteArrayView lex = lexemView();
1152 if (lex.startsWith("QML_")) {
1153 if ( lex == "QML_ELEMENT" || lex == "QML_NAMED_ELEMENT"
1154 || lex == "QML_ANONYMOUS" || lex == "QML_VALUE_TYPE") {
1155 qmlRegistrationMacroSymbol = symbol();
1156 }
1157 }
1158 }
1159 Q_FALLTHROUGH();
1160 default:
1161 FunctionDef funcDef;
1162 funcDef.access = access;
1163 qsizetype rewind = index--;
1164 if (parseMaybeFunction(&def, &funcDef)) {
1165 if (funcDef.isConstructor) {
1166 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
1167 def.constructorList += funcDef;
1168 handleDefaultArguments(&def.constructorList, funcDef);
1169 }
1170 } else if (funcDef.isDestructor) {
1171 // don't care about destructors
1172 } else {
1173 if (access == FunctionDef::Public)
1174 def.publicList += funcDef;
1175 if (funcDef.isSlot) {
1176 def.slotList += funcDef;
1177 handleDefaultArguments(&def.slotList, funcDef);
1178 if (funcDef.revision > 0)
1179 ++def.revisionedMethods;
1180 } else if (funcDef.isSignal) {
1181 def.signalList += funcDef;
1182 handleDefaultArguments(&def.signalList, funcDef);
1183 if (funcDef.revision > 0)
1184 ++def.revisionedMethods;
1185 } else if (funcDef.isInvokable) {
1186 def.methodList += funcDef;
1187 handleDefaultArguments(&def.methodList, funcDef);
1188 if (funcDef.revision > 0)
1189 ++def.revisionedMethods;
1190 }
1191 }
1192 } else {
1193 index = rewind;
1194 }
1195 }
1196 }
1197
1198 next(RBRACE);
1199
1200 /* if the header is available, moc will see a Q_CLASSINFO entry; the
1201 token is only visible if the header is missing
1202 To avoid false positives, we only warn when encountering the token in a QObject or gadget
1203 */
1204 if ((def.hasQObject || def.hasQGadget) && qmlRegistrationMacroSymbol.token != NOTOKEN) {
1205 QByteArray msg("Potential QML registration macro was found, but no header containing it was included.\n"
1206 "This might cause runtime errors in QML applications\n"
1207 "Include <QtQmlIntegration/qqmlintegration.h> or <QtQml/qqmlregistration.h> to fix this.");
1208 if (qmlMacroWarningIsFatal)
1209 error(qmlRegistrationMacroSymbol, msg.constData());
1210 else
1211 warning(qmlRegistrationMacroSymbol, msg.constData());
1212 }
1213
1214 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
1215 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
1216 continue; // no meta object code required
1217
1218
1219 if (!def.hasQObject && !def.hasQGadget)
1220 error("Class declaration lacks Q_OBJECT macro.");
1221
1222 // Add meta tags to the plugin meta data:
1223 if (!def.pluginData.iid.isEmpty())
1224 def.pluginData.metaArgs = metaArgs;
1225
1226 if (def.hasQObject && !def.superclassList.isEmpty())
1228
1230
1232
1233 classList += def;
1234 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
1235 classHash.insert(def.classname, def.qualified);
1236 classHash.insert(def.qualified, def.qualified);
1237 }
1238 }
1239 for (const auto &n : std::as_const(namespaceList)) {
1240 if (!n.hasQNamespace)
1241 continue;
1242 ClassDef def;
1243 static_cast<BaseDef &>(def) = static_cast<BaseDef>(n);
1244 def.qualified += def.classname;
1245 def.hasQNamespace = true;
1246 auto it = std::find_if(classList.begin(), classList.end(), [&def](const ClassDef &val) {
1247 return def.classname == val.classname && def.qualified == val.qualified;
1248 });
1249
1250 if (it != classList.end()) {
1251 it->classInfoList += def.classInfoList;
1252 Q_ASSERT(it->classInfoList.size() <= std::numeric_limits<int>::max());
1253 it->enumDeclarations.insert(def.enumDeclarations);
1254 it->enumList += def.enumList;
1255 Q_ASSERT(it->enumList.size() <= std::numeric_limits<int>::max());
1256 it->flagAliases.insert(def.flagAliases);
1257 } else {
1258 knownGadgets.insert(def.classname, def.qualified);
1259 knownGadgets.insert(def.qualified, def.qualified);
1260 if (n.doGenerate)
1261 classList += def;
1262 }
1263 }
1264}
1265
1267{
1268 QByteArrayView fn = QByteArrayView(filename);
1269
1270 auto isSlash = [](char ch) { return ch == '/' || ch == '\\'; };
1271 auto rit = std::find_if(fn.crbegin(), fn.crend(), isSlash);
1272 if (rit != fn.crend())
1273 fn = fn.last(rit - fn.crbegin());
1274
1275 return fn;
1276}
1277
1278static bool any_type_contains(const QList<PropertyDef> &properties, const QByteArray &pattern)
1279{
1280 for (const auto &p : properties) {
1281 if (p.type.contains(pattern))
1282 return true;
1283 }
1284 return false;
1285}
1286
1287static bool any_arg_contains(const QList<FunctionDef> &functions, const QByteArray &pattern)
1288{
1289 for (const auto &f : functions) {
1290 for (const auto &arg : f.arguments) {
1291 if (arg.normalizedType.contains(pattern))
1292 return true;
1293 }
1294 }
1295 return false;
1296}
1297
1299{
1300 QByteArrayList result;
1301 result
1302#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER
1303 QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER)
1304#undef STREAM_SMART_POINTER
1305#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME
1306 QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE)
1307#undef STREAM_1ARG_TEMPLATE
1308 ;
1309 return result;
1310}
1311
1313{
1314 static const QByteArrayList candidates = make_candidates();
1315
1316 QByteArrayList required;
1317 required.reserve(candidates.size());
1318
1319 bool needsQProperty = false;
1320
1321 for (const auto &candidate : candidates) {
1322 const QByteArray pattern = candidate + '<';
1323
1324 for (const auto &c : classes) {
1325 for (const auto &p : c.propertyList)
1326 needsQProperty |= !p.bind.isEmpty();
1327 if (any_type_contains(c.propertyList, pattern) ||
1328 any_arg_contains(c.slotList, pattern) ||
1329 any_arg_contains(c.signalList, pattern) ||
1330 any_arg_contains(c.methodList, pattern)) {
1331 required.push_back(candidate);
1332 break;
1333 }
1334 }
1335 }
1336
1337 if (needsQProperty)
1338 required.push_back("QProperty");
1339
1340 return required;
1341}
1342
1343void Moc::generate(FILE *out, FILE *jsonOutput)
1344{
1345 QByteArrayView fn = strippedFileName();
1346
1347 fprintf(out, "/****************************************************************************\n"
1348 "** Meta object code from reading C++ file '%s'\n**\n" , fn.constData());
1349 fprintf(out, "** Created by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , mocOutputRevision, QT_VERSION_STR);
1350 fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
1351 "*****************************************************************************/\n\n");
1352
1353 // If the source file is part of a module, the generated code becomes an implementation unit
1354 // of the same module, so that it gains access to the module's interface and can import
1355 // internal partitions, if needed.
1356 // Includes from the source file's own global module fragment need to be copied over.
1358 fprintf(out, "module;\n\n");
1359 for (const QByteArray &inc : std::as_const(moduleFragmentIncludes))
1360 fprintf(out, "#include \"%s\"\n", inc.constData());
1361 } else if (!noInclude) {
1362 // include header(s) of user class definitions at _first_ to allow
1363 // for preprocessor definitions possibly affecting standard headers.
1364 // see https://codereview.qt-project.org/c/qt/qtbase/+/445937
1365 if (includePath.size() && !includePath.endsWith('/'))
1366 includePath += '/';
1367 for (QByteArray inc : std::as_const(includeFiles)) {
1368 if (!inc.isEmpty() && inc.at(0) != '<' && inc.at(0) != '"') {
1369 if (includePath.size() && includePath != "./")
1370 inc.prepend(includePath);
1371 inc = '\"' + inc + '\"';
1372 }
1373 fprintf(out, "#include %s\n", inc.constData());
1374 }
1375 }
1376 if (classList.size() && classList.constFirst().classname == "Qt")
1377 fprintf(out, "#include <QtCore/qobject.h>\n");
1378
1379 fprintf(out, "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type
1381 fprintf(out, "#include <QtCore/qplugin.h>\n");
1382
1383 const auto qtContainers = requiredQtContainers(classList);
1384 for (const QByteArray &qtContainer : qtContainers)
1385 fprintf(out, "#include <QtCore/%s>\n", qtContainer.constData());
1386
1387 fprintf(out, "\n#include <QtCore/qtmochelpers.h>\n");
1388
1389 fprintf(out, "\n#include <memory>\n\n"); // For std::addressof
1390 fprintf(out, "\n#include <QtCore/qxptype_traits.h>\n"); // is_detected
1391
1393 fprintf(out, "\nmodule %s;\n", moduleName.constData());
1394
1395 // Internal partitions need to be imported to get access to them.
1396 // Interface partitions should normally be available implicitly
1397 // via the re-export from the primary interface, but we import them
1398 // anyway to be on the safe side.
1401 fprintf(out, "import :%s;\n", modulePartitionName.constData());
1402 }
1403
1404 fprintf(out, "\n");
1405 }
1406
1407 fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
1408 "#error \"The header file '%s' doesn't include <QObject>.\"\n", fn.constData());
1409 fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
1410 fprintf(out, "#error \"This file was generated using the moc from %s."
1411 " It\"\n#error \"cannot be used with the include files from"
1412 " this version of Qt.\"\n#error \"(The moc has changed too"
1413 " much.)\"\n", QT_VERSION_STR);
1414 fprintf(out, "#endif\n\n");
1415
1416#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
1417 fprintf(out, "#ifndef Q_CONSTINIT\n"
1418 "#define Q_CONSTINIT\n"
1419 "#endif\n\n");
1420#endif
1421
1422 // filter out undeclared enumerators and sets
1423 for (ClassDef &cdef : classList) {
1424 QList<EnumDef> enumList;
1425 for (EnumDef def : std::as_const(cdef.enumList)) {
1426 if (cdef.enumDeclarations.contains(def.name)) {
1427 enumList += def;
1428 }
1429 def.enumName = def.name;
1430 QByteArray alias = cdef.flagAliases.value(def.name);
1431 if (cdef.enumDeclarations.contains(alias)) {
1432 def.name = alias;
1433 def.flags |= cdef.enumDeclarations[alias];
1434 enumList += def;
1435 }
1436 }
1437 cdef.enumList = enumList;
1438 }
1439
1440 fprintf(out, "QT_WARNING_PUSH\n");
1441 fprintf(out, "QT_WARNING_DISABLE_DEPRECATED\n");
1442 fprintf(out, "QT_WARNING_DISABLE_GCC(\"-Wuseless-cast\")\n");
1443
1444 fputs("", out);
1445 for (const ClassDef &def : std::as_const(classList)) {
1446 Generator generator(this, &def, metaTypes, knownQObjectClasses, knownGadgets, out,
1447 requireCompleteTypes);
1448 generator.generateCode();
1449
1450 // generator.generateCode() should have already registered all strings
1451 if (Q_UNLIKELY(generator.registeredStringsCount() >= std::numeric_limits<int>::max())) {
1452 error("internal limit exceeded: number of parsed strings is too big.");
1453 exit(EXIT_FAILURE);
1454 }
1455 }
1456 fputs("", out);
1457
1458 fprintf(out, "QT_WARNING_POP\n");
1459
1460 if (jsonOutput) {
1461 QJsonObject mocData;
1462 mocData["outputRevision"_L1] = mocOutputRevision;
1463 mocData["inputFile"_L1] = QLatin1StringView(fn.constData());
1464
1466 QByteArray moduleId = moduleName;
1467 if (!modulePartitionName.isEmpty())
1468 moduleId += ':' + modulePartitionName;
1469 mocData["module"_L1] = QLatin1StringView(moduleId.constData(), moduleId.size());
1470 }
1471
1472 QJsonArray classesJsonFormatted;
1473
1474 for (const ClassDef &cdef: std::as_const(classList))
1475 classesJsonFormatted.append(cdef.toJson());
1476
1477 if (!classesJsonFormatted.isEmpty())
1478 mocData["classes"_L1] = classesJsonFormatted;
1479
1480 QJsonDocument jsonDoc(mocData);
1481 fputs(jsonDoc.toJson().constData(), jsonOutput);
1482 }
1483}
1484
1486{
1487 QTypeRevision defaultRevision;
1488 if (test(Q_REVISION_TOKEN))
1489 defaultRevision = parseRevision();
1490
1491 next(COLON);
1492 while (inClass(def) && hasNext()) {
1493 switch (next()) {
1494 case PUBLIC:
1495 case PROTECTED:
1496 case PRIVATE:
1497 case Q_SIGNALS_TOKEN:
1498 case Q_SLOTS_TOKEN:
1499 prev();
1500 return;
1501 case SEMIC:
1502 continue;
1503 case FRIEND:
1504 until(SEMIC);
1505 continue;
1506 case USING:
1507 error("'using' directive not supported in 'slots' section");
1508 default:
1509 prev();
1510 }
1511
1512 FunctionDef funcDef;
1513 funcDef.access = access;
1514 if (!parseFunction(&funcDef))
1515 continue;
1516 if (funcDef.revision > 0) {
1518 } else if (defaultRevision.isValid()) {
1519 funcDef.revision = defaultRevision.toEncodedVersion<int>();
1521 }
1522 def->slotList += funcDef;
1523 handleDefaultArguments(&def->slotList, funcDef);
1524 }
1525}
1526
1528{
1529 QTypeRevision defaultRevision;
1530 if (test(Q_REVISION_TOKEN))
1531 defaultRevision = parseRevision();
1532
1533 next(COLON);
1534 while (inClass(def) && hasNext()) {
1535 switch (next()) {
1536 case PUBLIC:
1537 case PROTECTED:
1538 case PRIVATE:
1539 case Q_SIGNALS_TOKEN:
1540 case Q_SLOTS_TOKEN:
1541 prev();
1542 return;
1543 case SEMIC:
1544 continue;
1545 case FRIEND:
1546 until(SEMIC);
1547 continue;
1548 case USING:
1549 error("'using' directive not supported in 'signals' section");
1550 default:
1551 prev();
1552 }
1553 FunctionDef funcDef;
1555 parseFunction(&funcDef);
1556 if (funcDef.isVirtual)
1557 warning("Signals cannot be declared virtual");
1558 if (funcDef.inlineCode)
1559 error("Not a signal declaration");
1560 if (funcDef.revision > 0) {
1562 } else if (defaultRevision.isValid()) {
1563 funcDef.revision = defaultRevision.toEncodedVersion<int>();
1565 }
1566 def->signalList += funcDef;
1567 handleDefaultArguments(&def->signalList, funcDef);
1568 }
1569}
1570
1571void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex, Moc::PropertyMode mode)
1572{
1573 propDef.location = index;
1574 propDef.relativeIndex = propertyIndex;
1575 propDef.lineNumber = symbol().lineNum;
1576
1577 Type t = parseType();
1578 QByteArray type = t.name;
1579 if (type.isEmpty())
1580 error();
1581 propDef.typeTag = t.typeTag;
1582 propDef.designable = propDef.scriptable = propDef.stored = "true";
1583 propDef.user = "false";
1584 /*
1585 The Q_PROPERTY construct cannot contain any commas, since
1586 commas separate macro arguments. We therefore expect users
1587 to type "QMap" instead of "QMap<QString, QVariant>". For
1588 coherence, we also expect the same for
1589 QValueList<QVariant>, the other template class supported by
1590 QVariant.
1591 */
1592 type = normalizeType(type);
1593 if (type == "QMap")
1594 type = "QMap<QString,QVariant>";
1595 else if (type == "LongLong")
1596 type = "qlonglong";
1597 else if (type == "ULongLong")
1598 type = "qulonglong";
1599
1600 propDef.type = type;
1601
1602 if (mode == Moc::Named) {
1603 next();
1604 propDef.name = lexem();
1605 }
1606
1608}
1609
1611{
1612 auto checkIsFunction = [&](const QByteArray &def, const char *name) {
1613 if (def.endsWith(')')) {
1614 QByteArray msg = "Providing a function for ";
1615 msg += name;
1616 msg += " in a property declaration is not be supported in Qt 6.";
1617 error(msg.constData());
1618 }
1619 };
1620
1621 while (test(IDENTIFIER)) {
1622 const Symbol &lsym = symbol();
1623 const QByteArrayView l = lsym.lexemView();
1624 if (l[0] == 'C' && l == "CONSTANT") {
1625 propDef.constant = true;
1626 continue;
1627 } else if (l[0] == 'F' && l == "FINAL") {
1628 propDef.final = true;
1629 continue;
1630 } else if (l[0] == 'N' && l == "NAME") {
1631 next(IDENTIFIER);
1632 propDef.name = lexem();
1633 continue;
1634 } else if (l[0] == 'O' && l == "OVERRIDE") {
1635 propDef.override = true;
1636 continue;
1637 } else if (l[0] == 'R' && l == "REQUIRED") {
1638 propDef.required = true;
1639 continue;
1640 } else if (l[0] == 'R' && l == "REVISION" && test(LPAREN)) {
1641 prev();
1642 propDef.revision = parseRevision().toEncodedVersion<int>();
1643 continue;
1644 } else if (l[0] == 'V' && l == "VIRTUAL") {
1645 propDef.virtual_ = true;
1646 continue;
1647 }
1648
1649 QByteArray v, v2;
1650 if (test(LPAREN)) {
1651 v = lexemUntil(RPAREN);
1652 v = v.mid(1, v.size() - 2); // removes the '(' and ')'
1653 } else if (test(INTEGER_LITERAL)) {
1654 v = lexem();
1655 if (l != "REVISION")
1656 error(lsym);
1657 } else if (test(DEFAULT)) {
1658 v = lexem();
1659 if (l != "READ" && l != "WRITE")
1660 error(lsym);
1661 } else {
1662 next(IDENTIFIER);
1663 v = lexem();
1664 if (test(LPAREN))
1665 v2 = lexemUntil(RPAREN);
1666 else if (v != "true" && v != "false")
1667 v2 = "()";
1668 }
1669 switch (l[0]) {
1670 case 'M':
1671 if (l == "MEMBER")
1672 propDef.member = v;
1673 else
1674 error(lsym);
1675 break;
1676 case 'R':
1677 if (l == "READ")
1678 propDef.read = v;
1679 else if (l == "RESET")
1680 propDef.reset = v;
1681 else if (l == "REVISION") {
1682 bool ok = false;
1683 const int minor = v.toInt(&ok);
1684 if (!ok || !QTypeRevision::isValidSegment(minor))
1685 error(lsym);
1686 propDef.revision = QTypeRevision::fromMinorVersion(minor).toEncodedVersion<int>();
1687 } else
1688 error(lsym);
1689 break;
1690 case 'S':
1691 if (l == "SCRIPTABLE") {
1692 propDef.scriptable = v + v2;
1693 checkIsFunction(propDef.scriptable, "SCRIPTABLE");
1694 } else if (l == "STORED") {
1695 propDef.stored = v + v2;
1696 checkIsFunction(propDef.stored, "STORED");
1697 } else
1698 error(lsym);
1699 break;
1700 case 'W': if (l != "WRITE") error(lsym);
1701 propDef.write = v;
1702 break;
1703 case 'B': if (l != "BINDABLE") error(lsym);
1704 propDef.bind = v;
1705 break;
1706 case 'D': if (l != "DESIGNABLE") error(lsym);
1707 propDef.designable = v + v2;
1708 checkIsFunction(propDef.designable, "DESIGNABLE");
1709 break;
1710 case 'N': if (l != "NOTIFY") error(lsym);
1711 propDef.notify = v;
1712 break;
1713 case 'U': if (l != "USER") error(lsym);
1714 propDef.user = v + v2;
1715 checkIsFunction(propDef.user, "USER");
1716 break;
1717 default:
1718 error(lsym);
1719 }
1720 }
1721 if (propDef.constant && !propDef.write.isNull()) {
1722 const QByteArray msg = "Property declaration " + propDef.name
1723 + " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
1724 propDef.constant = false;
1725 warning(msg.constData());
1726 }
1727 if (propDef.constant && !propDef.notify.isNull()) {
1728 const QByteArray msg = "Property declaration " + propDef.name
1729 + " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
1730 propDef.constant = false;
1731 warning(msg.constData());
1732 }
1733 if (propDef.constant && !propDef.bind.isNull()) {
1734 const QByteArray msg = "Property declaration " + propDef.name
1735 + " is both BINDable and CONSTANT. CONSTANT will be ignored.";
1736 propDef.constant = false;
1737 warning(msg.constData());
1738 }
1739 if (propDef.read == "default" && propDef.bind.isNull()) {
1740 const QByteArray msg = "Property declaration " + propDef.name
1741 + " is not BINDable but default-READable. READ will be ignored.";
1742 propDef.read = "";
1743 warning(msg.constData());
1744 }
1745 if (propDef.write == "default" && propDef.bind.isNull()) {
1746 const QByteArray msg = "Property declaration " + propDef.name
1747 + " is not BINDable but default-WRITEable. WRITE will be ignored.";
1748 propDef.write = "";
1749 warning(msg.constData());
1750 }
1751 if (propDef.override && propDef.virtual_) {
1752 const QByteArray msg = "Issue with property declaration " + propDef.name
1753 + ": VIRTUAL is redundant when overriding a property. The OVERRIDE "
1754 "must only be used when actually overriding an existing property; using it on a "
1755 "new property is an error.";
1756 error(msg.constData());
1757 }
1758 if (propDef.override && propDef.final) {
1759 const QByteArray msg = "Issue with property declaration " + propDef.name
1760 + ": OVERRIDE is redundant when property is marked FINAL";
1761 error(msg.constData());
1762 }
1763 if (propDef.virtual_ && propDef.final) {
1764 const QByteArray msg = "Issue with property declaration " + propDef.name
1765 + ": The VIRTUAL cannot be combined with FINAL, as these attributes are mutually "
1766 "exclusive";
1767 error(msg.constData());
1768 }
1769}
1770
1772{
1773 next(LPAREN);
1774 PropertyDef propDef;
1775 createPropertyDef(propDef, int(def->propertyList.size()), mode);
1776 next(RPAREN);
1777
1778 def->propertyList += propDef;
1779}
1780
1782{
1783 next(LPAREN);
1784 QByteArray metaData;
1785 while (test(IDENTIFIER)) {
1786 QByteArray l = lexem();
1787 if (l == "IID") {
1788 next(STRING_LITERAL);
1789 def->pluginData.iid = unquotedLexem();
1790 } else if (l == "URI") {
1791 next(STRING_LITERAL);
1792 def->pluginData.uri = unquotedLexem();
1793 } else if (l == "FILE") {
1794 next(STRING_LITERAL);
1795 QByteArrayView metaDataFile = unquotedLexemView();
1796 QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top())).dir(),
1797 QString::fromLocal8Bit(metaDataFile));
1798 for (const IncludePath &p : std::as_const(includes)) {
1799 if (fi.exists())
1800 break;
1801 if (p.isFrameworkPath)
1802 continue;
1803
1804 fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(metaDataFile));
1805 // try again, maybe there's a file later in the include paths with the same name
1806 if (fi.isDir()) {
1807 fi = QFileInfo();
1808 continue;
1809 }
1810 }
1811 if (!fi.exists()) {
1812 const QByteArray msg = "Plugin Metadata file " + lexemView()
1813 + " does not exist. Declaration will be ignored";
1814 error(msg.constData());
1815 return;
1816 }
1817 QFile file(fi.canonicalFilePath());
1818 if (!file.open(QFile::ReadOnly)) {
1819 QByteArray msg = "Plugin Metadata file " + lexemView() + " could not be opened: "
1820 + file.errorString().toUtf8();
1821 error(msg.constData());
1822 return;
1823 }
1824 parsedPluginMetadataFiles.append(fi.canonicalFilePath());
1825 metaData = file.readAll();
1826 }
1827 }
1828
1829 if (!metaData.isEmpty()) {
1830 def->pluginData.metaData = QJsonDocument::fromJson(metaData);
1831 if (!def->pluginData.metaData.isObject()) {
1832 const QByteArray msg = "Plugin Metadata file " + lexemView()
1833 + " does not contain a valid JSON object. Declaration will be ignored";
1834 warning(msg.constData());
1835 def->pluginData.iid = QByteArray();
1836 def->pluginData.uri = QByteArray();
1837 return;
1838 }
1839 }
1840
1841 mustIncludeQPluginH = true;
1842 next(RPAREN);
1843}
1844
1846{
1847 int nesting = 0;
1848 QByteArray accessor;
1849 while (1) {
1850 Token t = peek();
1851 if (!nesting && (t == RPAREN || t == COMMA))
1852 break;
1853 t = next();
1854 if (t == LPAREN)
1855 ++nesting;
1856 if (t == RPAREN)
1857 --nesting;
1858 accessor += lexemView();
1859 }
1860 return accessor;
1861}
1862
1864{
1865 next(LPAREN);
1866 PropertyDef propDef;
1867 propDef.inPrivateClass = parsePropertyAccessor();
1868
1869 next(COMMA);
1870
1871 createPropertyDef(propDef, int(def->propertyList.size()), mode);
1872
1873 def->propertyList += propDef;
1874}
1875
1876void Moc::parseEnumOrFlag(BaseDef *def, EnumFlags flags)
1877{
1878 next(LPAREN);
1879 QByteArray identifier;
1880 while (test(IDENTIFIER)) {
1881 identifier = lexem();
1882 while (test(SCOPE) && test(IDENTIFIER)) {
1883 identifier += "::";
1884 identifier += lexemView();
1885 }
1886 def->enumDeclarations[identifier] = flags;
1887 }
1888 next(RPAREN);
1889}
1890
1892{
1893 next(LPAREN);
1894 QByteArray flagName, enumName;
1895 while (test(IDENTIFIER)) {
1896 flagName = lexem();
1897 while (test(SCOPE) && test(IDENTIFIER)) {
1898 flagName += "::";
1899 flagName += lexemView();
1900 }
1901 }
1902 next(COMMA);
1903 while (test(IDENTIFIER)) {
1904 enumName = lexem();
1905 while (test(SCOPE) && test(IDENTIFIER)) {
1906 enumName += "::";
1907 enumName += lexemView();
1908 }
1909 }
1910
1911 def->flagAliases.insert(enumName, flagName);
1912 next(RPAREN);
1913}
1914
1916{
1917 bool encounteredQmlMacro = false;
1918 next(LPAREN);
1919 ClassInfoDef infoDef;
1920 next(STRING_LITERAL);
1921 infoDef.name = symbol().unquotedLexem();
1922 if (infoDef.name.startsWith("QML."))
1923 encounteredQmlMacro = true;
1924 next(COMMA);
1925 if (test(STRING_LITERAL)) {
1926 infoDef.value = symbol().unquotedLexem();
1927 } else if (test(Q_REVISION_TOKEN)) {
1928 infoDef.value = QByteArray::number(parseRevision().toEncodedVersion<quint16>());
1929 } else {
1930 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1931 next(IDENTIFIER);
1932 next(LPAREN);
1933 next(STRING_LITERAL);
1934 infoDef.value = symbol().unquotedLexem();
1935 next(RPAREN);
1936 }
1937 next(RPAREN);
1938 def->classInfoList += infoDef;
1939 return encounteredQmlMacro ? EncounteredQmlMacro::Yes : EncounteredQmlMacro::No;
1940}
1941
1943{
1944 if (parseClassInfo(static_cast<BaseDef *>(def)) == EncounteredQmlMacro::Yes)
1946}
1947
1949{
1950 next(LPAREN);
1951 while (test(IDENTIFIER)) {
1952 QList<ClassDef::Interface> iface;
1953 iface += ClassDef::Interface(lexem());
1954 while (test(SCOPE)) {
1955 iface.last().className += lexemView();
1956 next(IDENTIFIER);
1957 iface.last().className += lexemView();
1958 }
1959 while (test(COLON)) {
1960 next(IDENTIFIER);
1961 iface += ClassDef::Interface(lexem());
1962 while (test(SCOPE)) {
1963 iface.last().className += lexemView();
1964 next(IDENTIFIER);
1965 iface.last().className += lexemView();
1966 }
1967 }
1968 // resolve from classnames to interface ids
1969 for (qsizetype i = 0; i < iface.size(); ++i) {
1970 const QByteArray iid = interface2IdMap.value(iface.at(i).className);
1971 if (iid.isEmpty())
1972 error("Undefined interface");
1973
1974 iface[i].interfaceId = iid;
1975 }
1976 def->interfaceList += iface;
1977 }
1978 next(RPAREN);
1979}
1980
1982{
1983 next(LPAREN);
1984 QByteArray interface;
1985 next(IDENTIFIER);
1986 interface += lexemView();
1987 while (test(SCOPE)) {
1988 interface += lexemView();
1989 next(IDENTIFIER);
1990 interface += lexemView();
1991 }
1992 next(COMMA);
1993 QByteArray iid;
1994 if (test(STRING_LITERAL)) {
1995 iid = lexem();
1996 } else {
1997 next(IDENTIFIER);
1998 iid = lexem();
1999 }
2000 interface2IdMap.insert(interface, iid);
2001 next(RPAREN);
2002}
2003
2005{
2006 next(LPAREN);
2007 QByteArray typeName = lexemUntil(RPAREN);
2008 typeName.remove(0, 1);
2009 typeName.chop(1);
2010 metaTypes.append(typeName);
2011}
2012
2014{
2015 next(LPAREN);
2016 QByteArray include = lexemUntil(RPAREN);
2017 // remove parentheses
2018 include.remove(0, 1);
2019 include.chop(1);
2020 includeFiles.append(include);
2021}
2022
2024{
2025 next(LPAREN);
2026 FunctionDef funcDef;
2027 next(IDENTIFIER);
2028 funcDef.inPrivateClass = lexem();
2029 // also allow void functions
2030 if (test(LPAREN)) {
2031 next(RPAREN);
2032 funcDef.inPrivateClass += "()";
2033 }
2034 next(COMMA);
2035 funcDef.access = access;
2036 parseFunction(&funcDef, true);
2037 def->slotList += funcDef;
2038 handleDefaultArguments(&def->slotList, funcDef);
2039 if (funcDef.revision > 0)
2041
2042}
2043
2045{
2046 qsizetype from = index;
2047 until(target);
2048 QByteArray s;
2049 while (from <= index) {
2050 QByteArray n = symbols.at(from++-1).lexem();
2051 if (s.size() && n.size()) {
2052 char prev = s.at(s.size()-1);
2053 char next = n.at(0);
2054 if ((is_ident_char(prev) && is_ident_char(next))
2055 || (prev == '<' && next == ':')
2056 || (prev == '>' && next == '>'))
2057 s += ' ';
2058 }
2059 s += n;
2060 }
2061 return s;
2062}
2063
2064bool Moc::until(Token target) {
2065 int braceCount = 0;
2066 int brackCount = 0;
2067 int parenCount = 0;
2068 int angleCount = 0;
2069 if (index) {
2070 switch(symbols.at(index-1).token) {
2071 case LBRACE: ++braceCount; break;
2072 case LBRACK: ++brackCount; break;
2073 case LPAREN: ++parenCount; break;
2074 case LANGLE: ++angleCount; break;
2075 default: break;
2076 }
2077 }
2078
2079 //when searching commas within the default argument, we should take care of template depth (anglecount)
2080 // unfortunately, we do not have enough semantic information to know if '<' is the operator< or
2081 // the beginning of a template type. so we just use heuristics.
2082 qsizetype possible = -1;
2083
2084 while (index < symbols.size()) {
2085 Token t = symbols.at(index++).token;
2086 switch (t) {
2087 case LBRACE: ++braceCount; break;
2088 case RBRACE: --braceCount; break;
2089 case LBRACK: ++brackCount; break;
2090 case RBRACK: --brackCount; break;
2091 case LPAREN: ++parenCount; break;
2092 case RPAREN: --parenCount; break;
2093 case LANGLE:
2094 if (parenCount == 0 && braceCount == 0)
2095 ++angleCount;
2096 break;
2097 case RANGLE:
2098 if (parenCount == 0 && braceCount == 0)
2099 --angleCount;
2100 break;
2101 case GTGT:
2102 if (parenCount == 0 && braceCount == 0) {
2103 angleCount -= 2;
2104 t = RANGLE;
2105 }
2106 break;
2107 default: break;
2108 }
2109 if (t == target
2110 && braceCount <= 0
2111 && brackCount <= 0
2112 && parenCount <= 0
2113 && (target != RANGLE || angleCount <= 0)) {
2114 if (target != COMMA || angleCount <= 0)
2115 return true;
2116 possible = index;
2117 }
2118
2119 if (target == COMMA && t == EQ && possible != -1) {
2120 index = possible;
2121 return true;
2122 }
2123
2124 if (braceCount < 0 || brackCount < 0 || parenCount < 0
2125 || (target == RANGLE && angleCount < 0)) {
2126 --index;
2127 break;
2128 }
2129
2130 if (braceCount <= 0 && t == SEMIC) {
2131 // Abort on semicolon. Allow recovering bad template parsing (QTBUG-31218)
2132 break;
2133 }
2134 }
2135
2136 if (target == COMMA && angleCount != 0 && possible != -1) {
2137 index = possible;
2138 return true;
2139 }
2140
2141 return false;
2142}
2143
2145{
2146 Q_ASSERT(!def->superclassList.isEmpty());
2147 const QByteArray &firstSuperclass = def->superclassList.at(0).classname;
2148
2149 if (!knownQObjectClasses.contains(firstSuperclass)) {
2150 // enable once we /require/ include paths
2151#if 0
2152 const QByteArray msg
2153 = "Class "
2154 + def->className
2155 + " contains the Q_OBJECT macro and inherits from "
2156 + def->superclassList.value(0)
2157 + " but that is not a known QObject subclass. You may get compilation errors.";
2158 warning(msg.constData());
2159#endif
2160 return;
2161 }
2162
2163 auto isRegisteredInterface = [&def](QByteArrayView super) {
2164 auto matchesSuperClass = [&super](const auto &ifaces) {
2165 return !ifaces.isEmpty() && ifaces.first().className == super;
2166 };
2167 return std::any_of(def->interfaceList.cbegin(), def->interfaceList.cend(), matchesSuperClass);
2168 };
2169
2170 const auto end = def->superclassList.cend();
2171 auto it = def->superclassList.cbegin() + 1;
2172 for (; it != end; ++it) {
2173 const QByteArray &superClass = it->classname;
2174 if (knownQObjectClasses.contains(superClass)) {
2175 const QByteArray msg
2176 = "Class "
2177 + def->classname
2178 + " inherits from two QObject subclasses "
2179 + firstSuperclass
2180 + " and "
2181 + superClass
2182 + ". This is not supported!";
2183 warning(msg.constData());
2184 }
2185
2186 if (interface2IdMap.contains(superClass)) {
2187 if (!isRegisteredInterface(superClass)) {
2188 const QByteArray msg
2189 = "Class "
2190 + def->classname
2191 + " implements the interface "
2192 + superClass
2193 + " but does not list it in Q_INTERFACES. qobject_cast to "
2194 + superClass
2195 + " will not work!";
2196 warning(msg.constData());
2197 }
2198 }
2199 }
2200}
2201
2203{
2204 //
2205 // specify get function, for compatibility we accept functions
2206 // returning pointers, or const char * for QByteArray.
2207 //
2208 QDuplicateTracker<QByteArray> definedProperties(cdef->propertyList.size());
2209 auto hasNoAttributes = [&](const PropertyDef &p) {
2210 if (definedProperties.hasSeen(p.name)) {
2211 QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + ".";
2212 warning(msg.constData());
2213 }
2214
2215 if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) {
2216 QByteArray msg = "Property declaration " + p.name + " has neither an associated QProperty<> member"
2217 ", nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.";
2218 const auto &sym = p.location >= 0 ? symbolAt(p.location) : Symbol();
2219 warning(sym, msg.constData());
2220 if (p.write.isEmpty())
2221 return true;
2222 }
2223 return false;
2224 };
2225 cdef->propertyList.removeIf(hasNoAttributes);
2226
2227 for (PropertyDef &p : cdef->propertyList) {
2228 for (const FunctionDef &f : std::as_const(cdef->publicList)) {
2229 if (f.name != p.read)
2230 continue;
2231 if (!f.isConst) // get functions must be const
2232 continue;
2233 if (f.arguments.size()) // and must not take any arguments
2234 continue;
2235 PropertyDef::Specification spec = PropertyDef::ValueSpec;
2236 QByteArray tmp = f.normalizedType;
2237 if (p.type == "QByteArray" && tmp == "const char *")
2238 tmp = "QByteArray";
2239 if (tmp.left(6) == "const ")
2240 tmp = tmp.mid(6);
2241 if (p.type != tmp && tmp.endsWith('*')) {
2242 tmp.chop(1);
2243 spec = PropertyDef::PointerSpec;
2244 } else if (f.type.name.endsWith('&')) { // raw type, not normalized type
2245 spec = PropertyDef::ReferenceSpec;
2246 }
2247 if (p.type != tmp)
2248 continue;
2249 p.gspec = spec;
2250 break;
2251 }
2252 if (!p.notify.isEmpty()) {
2253 int notifyId = -1;
2254 for (int j = 0; j < int(cdef->signalList.size()); ++j) {
2255 const FunctionDef &f = cdef->signalList.at(j);
2256 if (f.name != p.notify) {
2257 continue;
2258 } else {
2259 notifyId = j /* Signal indexes start from 0 */;
2260 break;
2261 }
2262 }
2263 p.notifyId = notifyId;
2264 if (notifyId == -1) {
2265 const int index = int(cdef->nonClassSignalList.indexOf(p.notify));
2266 if (index == -1) {
2267 cdef->nonClassSignalList << p.notify;
2268 p.notifyId = int(-1 - cdef->nonClassSignalList.size());
2269 } else {
2270 p.notifyId = int(-2 - index);
2271 }
2272 }
2273 }
2274 }
2275}
2276
2277QJsonObject ClassDef::toJson() const
2278{
2279 QJsonObject cls;
2280 cls["className"_L1] = QString::fromUtf8(classname.constData());
2281 cls["qualifiedClassName"_L1] = QString::fromUtf8(qualified.constData());
2282 cls["lineNumber"_L1] = lineNumber;
2283 if (isFinal)
2284 cls["final"_L1] = true;
2285
2286 QJsonArray classInfos;
2287 for (const auto &info: std::as_const(classInfoList)) {
2288 QJsonObject infoJson;
2289 infoJson["name"_L1] = QString::fromUtf8(info.name);
2290 infoJson["value"_L1] = QString::fromUtf8(info.value);
2291 classInfos.append(infoJson);
2292 }
2293
2294 if (classInfos.size())
2295 cls["classInfos"_L1] = classInfos;
2296
2297 int methodIndex = 0;
2298 const auto appendFunctions
2299 = [&cls, &methodIndex](const QString &type, const QList<FunctionDef> &funcs) {
2300 QJsonArray jsonFuncs;
2301
2302 for (const FunctionDef &fdef: funcs)
2303 jsonFuncs.append(fdef.toJson(methodIndex++));
2304
2305 if (!jsonFuncs.isEmpty())
2306 cls[type] = jsonFuncs;
2307 };
2308
2309 // signals, slots, and methods, in this order, follow the same index
2310 appendFunctions("signals"_L1, signalList);
2311 appendFunctions("slots"_L1, slotList);
2312 appendFunctions("methods"_L1, methodList);
2313
2314 // constructors are indexed separately.
2315 methodIndex = 0;
2316 appendFunctions("constructors"_L1, constructorList);
2317
2318 QJsonArray props;
2319
2320 for (const PropertyDef &propDef: std::as_const(propertyList))
2321 props.append(propDef.toJson());
2322
2323 if (!props.isEmpty())
2324 cls["properties"_L1] = props;
2325
2326 if (hasQObject)
2327 cls["object"_L1] = true;
2328 if (hasQGadget)
2329 cls["gadget"_L1] = true;
2330 if (hasQNamespace)
2331 cls["namespace"_L1] = true;
2332
2333 QJsonArray superClasses;
2334
2335 for (const auto &super: std::as_const(superclassList)) {
2336 QJsonObject superCls;
2337 superCls["name"_L1] = QString::fromUtf8(super.classname);
2338 if (super.classname != super.qualified)
2339 superCls["fullyQualifiedName"_L1] = QString::fromUtf8(super.qualified);
2340 FunctionDef::accessToJson(&superCls, super.access);
2341 superClasses.append(superCls);
2342 }
2343
2344 if (!superClasses.isEmpty())
2345 cls["superClasses"_L1] = superClasses;
2346
2347 QJsonArray enums;
2348 for (const EnumDef &enumDef: std::as_const(enumList))
2349 enums.append(enumDef.toJson(*this));
2350 if (!enums.isEmpty())
2351 cls["enums"_L1] = enums;
2352
2353 QJsonArray ifaces;
2354 for (const QList<Interface> &ifaceList : interfaceList) {
2355 QJsonArray jsonList;
2356 for (const Interface &iface: ifaceList) {
2357 QJsonObject ifaceJson;
2358 ifaceJson["id"_L1] = QString::fromUtf8(iface.interfaceId);
2359 ifaceJson["className"_L1] = QString::fromUtf8(iface.className);
2360 jsonList.append(ifaceJson);
2361 }
2362 ifaces.append(jsonList);
2363 }
2364 if (!ifaces.isEmpty())
2365 cls["interfaces"_L1] = ifaces;
2366
2367 return cls;
2368}
2369
2370QJsonObject FunctionDef::toJson(int index) const
2371{
2372 QJsonObject fdef;
2373 fdef["name"_L1] = QString::fromUtf8(name);
2374 fdef["index"_L1] = index;
2375 if (!tag.isEmpty())
2376 fdef["tag"_L1] = QString::fromUtf8(tag);
2377 fdef["returnType"_L1] = QString::fromUtf8(normalizedType);
2378 if (isConst)
2379 fdef["isConst"_L1] = true;
2380
2381 QJsonArray args;
2382 for (const ArgumentDef &arg: arguments)
2383 args.append(arg.toJson());
2384
2385 if (!args.isEmpty())
2386 fdef["arguments"_L1] = args;
2387
2389
2390 if (revision > 0)
2391 fdef["revision"_L1] = revision;
2392 fdef["lineNumber"_L1] = lineNumber;
2393
2394 if (wasCloned)
2395 fdef["isCloned"_L1] = true;
2396
2397 return fdef;
2398}
2399
2400void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs)
2401{
2402 switch (acs) {
2403 case Private: (*obj)["access"_L1] = "private"_L1; break;
2404 case Public: (*obj)["access"_L1] = "public"_L1; break;
2405 case Protected: (*obj)["access"_L1] = "protected"_L1; break;
2406 }
2407}
2408
2409QJsonObject ArgumentDef::toJson() const
2410{
2411 QJsonObject arg;
2412 arg["type"_L1] = QString::fromUtf8(normalizedType);
2413 if (!name.isEmpty())
2414 arg["name"_L1] = QString::fromUtf8(name);
2415 return arg;
2416}
2417
2418QJsonObject PropertyDef::toJson() const
2419{
2420 QJsonObject prop;
2421 prop["name"_L1] = QString::fromUtf8(name);
2422 prop["type"_L1] = QString::fromUtf8(type);
2423
2424 const auto jsonify = [&prop](const char *str, const QByteArray &member) {
2425 if (!member.isEmpty())
2426 prop[QLatin1StringView(str)] = QString::fromUtf8(member);
2427 };
2428
2429 jsonify("member", member);
2430 jsonify("read", read);
2431 jsonify("write", write);
2432 jsonify("bindable", bind);
2433 jsonify("reset", reset);
2434 jsonify("notify", notify);
2435 jsonify("privateClass", inPrivateClass);
2436
2437 const auto jsonifyBoolOrString = [&prop](const char *str, const QByteArray &boolOrString) {
2438 QJsonValue value;
2439 if (boolOrString == "true")
2440 value = true;
2441 else if (boolOrString == "false")
2442 value = false;
2443 else
2444 value = QString::fromUtf8(boolOrString); // function name to query at run-time
2445 prop[QLatin1StringView(str)] = value;
2446 };
2447
2448 jsonifyBoolOrString("designable", designable);
2449 jsonifyBoolOrString("scriptable", scriptable);
2450 jsonifyBoolOrString("stored", stored);
2451 jsonifyBoolOrString("user", user);
2452
2453 prop["constant"_L1] = constant;
2454 prop["final"_L1] = final;
2455 prop["virtual"_L1] = virtual_;
2456 prop["override"_L1] = override;
2457 prop["required"_L1] = required;
2458 prop["index"_L1] = relativeIndex;
2459 prop["lineNumber"_L1] = lineNumber;
2460 if (revision > 0)
2461 prop["revision"_L1] = revision;
2462
2463 return prop;
2464}
2465
2466QJsonObject EnumDef::toJson(const ClassDef &cdef) const
2467{
2468 QJsonObject def;
2469 uint flags = this->flags | cdef.enumDeclarations.value(name);
2470 def["name"_L1] = QString::fromUtf8(name);
2471 def["lineNumber"_L1] = lineNumber;
2472 if (!enumName.isEmpty())
2473 def["alias"_L1] = QString::fromUtf8(enumName);
2474 if (!type.isEmpty())
2475 def["type"_L1] = QString::fromUtf8(type);
2476 def["isFlag"_L1] = (flags & EnumIsFlag) != 0;
2477 def["isClass"_L1] = (flags & EnumIsScoped) != 0;
2478
2479 QJsonArray valueArr;
2480 for (const QByteArray &value: values)
2481 valueArr.append(QString::fromUtf8(value));
2482 if (!valueArr.isEmpty())
2483 def["values"_L1] = valueArr;
2484
2485 return def;
2486}
2487
2489{
2490 if (name == cdef->classname) {
2491 // The name of the enclosing namespace is the same as the enum class name
2492 if (cdef->qualified.contains("::")) {
2493 // QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum
2494 // class name and enclosing namespace, e.g.:
2495 // namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } }
2496 return cdef->qualified % "::" % name;
2497 } else {
2498 // Just "B"; otherwise the compiler complains about the type "B::B" inside
2499 // "B::staticMetaObject" in the generated code; e.g.:
2500 // namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) }
2501 return name;
2502 }
2503 }
2504 return cdef->classname % "::" % name;
2505}
2506
2507QT_END_NAMESPACE
Definition moc.h:234
bool testFunctionAttribute(Token tok, FunctionDef *def)
Definition moc.cpp:359
void parseEnumOrFlag(BaseDef *def, QtMocConstants::EnumFlags flags)
Definition moc.cpp:1876
bool hasGlobalModuleFragment
Definition moc.h:262
void checkSuperClasses(ClassDef *def)
Definition moc.cpp:2144
void parse()
Definition moc.cpp:775
bool inPrivateModuleFragment
Definition moc.h:261
bool parseEnum(EnumDef *def, ClassDef *containingClass)
Definition moc.cpp:250
void parseProperty(ClassDef *def, PropertyMode mode)
Definition moc.cpp:1771
QByteArray lexemUntil(Token)
Definition moc.cpp:2044
EncounteredQmlMacro parseClassInfo(BaseDef *def)
Definition moc.cpp:1915
bool until(Token)
Definition moc.cpp:2064
void createPropertyDef(PropertyDef &def, int propertyIndex, PropertyMode mode)
Definition moc.cpp:1571
void parseClassInfo(ClassDef *def)
Definition moc.cpp:1942
QByteArrayView strippedFileName() const
Definition moc.cpp:1266
bool parseClassHead(ClassDef *def)
Definition moc.cpp:39
void parsePrivateProperty(ClassDef *def, PropertyMode mode)
Definition moc.cpp:1863
void parseFlag(BaseDef *def)
Definition moc.cpp:1891
void parsePrivateModuleFragment(qsizetype rewind)
Definition moc.cpp:754
void parseSignals(ClassDef *def)
Definition moc.cpp:1527
void parseDeclareMetatype()
Definition moc.cpp:2004
void parsePropertyAttributes(PropertyDef &propDef)
Definition moc.cpp:1610
void parseInterfaces(ClassDef *def)
Definition moc.cpp:1948
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:656
bool parseFunction(FunctionDef *def, bool inMacro=false)
Definition moc.cpp:434
QByteArray parsePropertyAccessor()
Definition moc.cpp:1845
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:1485
void parseDeclareInterface()
Definition moc.cpp:1981
void checkListSizes(const ClassDef &def)
Definition moc.cpp:666
QTypeRevision parseRevision()
Definition moc.cpp:391
void parseMocInclude()
Definition moc.cpp:2013
bool skipCxxAttributes()
Definition moc.cpp:382
PropertyMode
Definition moc.h:236
@ Anonymous
Definition moc.h:236
@ Named
Definition moc.h:236
ModuleUnitKind moduleUnitKind
Definition moc.h:258
bool mustIncludeQPluginH
Definition moc.h:245
bool parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
Definition moc.cpp:546
void checkProperties(ClassDef *cdef)
Definition moc.cpp:2202
Type parseType()
Definition moc.cpp:130
void parseModuleDeclaration(qsizetype rewind, bool exported)
Definition moc.cpp:702
void parseFunctionArguments(FunctionDef *def)
Definition moc.cpp:310
void parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
Definition moc.cpp:2023
bool inClass(const ClassDef *def) const
Definition moc.h:283
void parsePluginData(ClassDef *def)
Definition moc.cpp:1781
void generate(FILE *out, FILE *jsonOutput)
Definition moc.cpp:1343
bool noInclude
Definition moc.h:244
EncounteredQmlMacro
Definition moc.h:312
bool inNamespace(const NamespaceDef *def) const
Definition moc.h:287
\inmodule QtCore\reentrant
Definition qlist.h:82
\inmodule QtCore
constexpr QTypeRevision()=default
Produces an invalid revision.
static bool any_arg_contains(const QList< FunctionDef > &functions, const QByteArray &pattern)
Definition moc.cpp:1287
static QByteArrayList requiredQtContainers(const QList< ClassDef > &classes)
Definition moc.cpp:1312
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:1298
static bool any_type_contains(const QList< PropertyDef > &properties, const QByteArray &pattern)
Definition moc.cpp:1278
void handleDefaultArguments(QList< FunctionDef > *functionList, FunctionDef &function)
Definition moc.cpp:644
ModuleUnitKind
Definition moc.h:225
@ InterfacePartition
Definition moc.h:228
@ InternalPartition
Definition moc.h:229
@ PrimaryInterface
Definition moc.h:227
@ ImplementationUnit
Definition moc.h:230
TypeTag
Definition moc.h:23
@ HasEnum
Definition moc.h:27
@ HasClass
Definition moc.h:26
@ HasStruct
Definition moc.h:25
bool is_ident_char(char s)
Definition utils.h:30
QJsonObject toJson() const
Definition moc.cpp:2409
bool isDefault
Definition moc.h:70
Definition moc.h:162
bool hasQObject
Definition moc.h:208
bool hasQGadget
Definition moc.h:209
bool isFinal
Definition moc.h:212
bool requireCompleteMethodTypes
Definition moc.h:211
int revisionedMethods
Definition moc.h:206
QJsonObject toJson() const
Definition moc.cpp:2277
Definition moc.h:53
QByteArray qualifiedType(const ClassDef *cdef) const
Definition moc.cpp:2488
QJsonObject toJson(const ClassDef &cdef) const
Definition moc.cpp:2466
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:2370
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:2400
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:220
int lineNumber
Definition moc.h:137
bool final
Definition moc.h:132
QJsonObject toJson() const
Definition moc.cpp:2418
bool constant
Definition moc.h:131
bool override
Definition moc.h:134
bool virtual_
Definition moc.h:133
int revision
Definition moc.h:129
int relativeIndex
Definition moc.h:136
bool required
Definition moc.h:135
Token token
Definition symbols.h:58
Symbol()=default