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