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