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