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