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