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

source code of qtbase/src/tools/moc/moc.cpp