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 "qdatetime.h"
32#include "utils.h"
33#include "outputrevision.h"
34#include <QtCore/qfile.h>
35#include <QtCore/qfileinfo.h>
36#include <QtCore/qdir.h>
37
38// for normalizeTypeInternal
39#include <private/qmetaobject_moc_p.h>
40
41QT_BEGIN_NAMESPACE
42
43// only moc needs this function
44static QByteArray normalizeType(const QByteArray &ba, bool fixScope = false)
45{
46 const char *s = ba.constData();
47 int len = ba.size();
48 char stackbuf[64];
49 char *buf = (len >= 64 ? new char[len + 1] : stackbuf);
50 char *d = buf;
51 char last = 0;
52 while(*s && is_space(s: *s))
53 s++;
54 while (*s) {
55 while (*s && !is_space(s: *s))
56 last = *d++ = *s++;
57 while (*s && is_space(s: *s))
58 s++;
59 if (*s && ((is_ident_char(s: *s) && is_ident_char(s: last))
60 || ((*s == ':') && (last == '<')))) {
61 last = *d++ = ' ';
62 }
63 }
64 *d = '\0';
65 QByteArray result = normalizeTypeInternal(t: buf, e: d, fixScope);
66 if (buf != stackbuf)
67 delete [] buf;
68 return result;
69}
70
71bool Moc::parseClassHead(ClassDef *def)
72{
73 // figure out whether this is a class declaration, or only a
74 // forward or variable declaration.
75 int i = 0;
76 Token token;
77 do {
78 token = lookup(k: i++);
79 if (token == COLON || token == LBRACE)
80 break;
81 if (token == SEMIC || token == RANGLE)
82 return false;
83 } while (token);
84
85 if (!test(token: IDENTIFIER)) // typedef struct { ... }
86 return false;
87 QByteArray name = lexem();
88
89 // support "class IDENT name" and "class IDENT(IDENT) name"
90 // also support "class IDENT name (final|sealed|Q_DECL_FINAL)"
91 if (test(token: LPAREN)) {
92 until(RPAREN);
93 if (!test(token: IDENTIFIER))
94 return false;
95 name = lexem();
96 } else if (test(token: IDENTIFIER)) {
97 const QByteArray lex = lexem();
98 if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
99 name = lex;
100 }
101
102 def->qualified += name;
103 while (test(token: SCOPE)) {
104 def->qualified += lexem();
105 if (test(token: IDENTIFIER)) {
106 name = lexem();
107 def->qualified += name;
108 }
109 }
110 def->classname = name;
111
112 if (test(token: IDENTIFIER)) {
113 const QByteArray lex = lexem();
114 if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
115 return false;
116 }
117
118 if (test(token: COLON)) {
119 do {
120 test(token: VIRTUAL);
121 FunctionDef::Access access = FunctionDef::Public;
122 if (test(token: PRIVATE))
123 access = FunctionDef::Private;
124 else if (test(token: PROTECTED))
125 access = FunctionDef::Protected;
126 else
127 test(token: PUBLIC);
128 test(token: VIRTUAL);
129 const QByteArray type = parseType().name;
130 // ignore the 'class Foo : BAR(Baz)' case
131 if (test(token: LPAREN)) {
132 until(RPAREN);
133 } else {
134 def->superclassList += qMakePair(x: type, y: access);
135 }
136 } while (test(token: COMMA));
137
138 if (!def->superclassList.isEmpty()
139 && knownGadgets.contains(akey: def->superclassList.constFirst().first)) {
140 // Q_GADGET subclasses are treated as Q_GADGETs
141 knownGadgets.insert(akey: def->classname, avalue: def->qualified);
142 knownGadgets.insert(akey: def->qualified, avalue: def->qualified);
143 }
144 }
145 if (!test(token: LBRACE))
146 return false;
147 def->begin = index - 1;
148 bool foundRBrace = until(RBRACE);
149 def->end = index;
150 index = def->begin + 1;
151 return foundRBrace;
152}
153
154Type Moc::parseType()
155{
156 Type type;
157 bool hasSignedOrUnsigned = false;
158 bool isVoid = false;
159 type.firstToken = lookup();
160 for (;;) {
161 switch (next()) {
162 case SIGNED:
163 case UNSIGNED:
164 hasSignedOrUnsigned = true;
165 Q_FALLTHROUGH();
166 case CONST:
167 case VOLATILE:
168 type.name += lexem();
169 type.name += ' ';
170 if (lookup(k: 0) == VOLATILE)
171 type.isVolatile = true;
172 continue;
173 case Q_MOC_COMPAT_TOKEN:
174 case Q_INVOKABLE_TOKEN:
175 case Q_SCRIPTABLE_TOKEN:
176 case Q_SIGNALS_TOKEN:
177 case Q_SLOTS_TOKEN:
178 case Q_SIGNAL_TOKEN:
179 case Q_SLOT_TOKEN:
180 type.name += lexem();
181 return type;
182 case NOTOKEN:
183 return type;
184 default:
185 prev();
186 break;
187 }
188 break;
189 }
190 test(token: ENUM) || test(token: CLASS) || test(token: STRUCT);
191 for(;;) {
192 switch (next()) {
193 case IDENTIFIER:
194 // void mySlot(unsigned myArg)
195 if (hasSignedOrUnsigned) {
196 prev();
197 break;
198 }
199 Q_FALLTHROUGH();
200 case CHAR:
201 case SHORT:
202 case INT:
203 case LONG:
204 type.name += lexem();
205 // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
206 if (test(token: LONG) || test(token: INT) || test(token: DOUBLE)) {
207 type.name += ' ';
208 prev();
209 continue;
210 }
211 break;
212 case FLOAT:
213 case DOUBLE:
214 case VOID:
215 case BOOL:
216 type.name += lexem();
217 isVoid |= (lookup(k: 0) == VOID);
218 break;
219 case NOTOKEN:
220 return type;
221 default:
222 prev();
223 ;
224 }
225 if (test(token: LANGLE)) {
226 if (type.name.isEmpty()) {
227 // '<' cannot start a type
228 return type;
229 }
230 type.name += lexemUntil(RANGLE);
231 }
232 if (test(token: SCOPE)) {
233 type.name += lexem();
234 type.isScoped = true;
235 } else {
236 break;
237 }
238 }
239 while (test(token: CONST) || test(token: VOLATILE) || test(token: SIGNED) || test(token: UNSIGNED)
240 || test(token: STAR) || test(token: AND) || test(token: ANDAND)) {
241 type.name += ' ';
242 type.name += lexem();
243 if (lookup(k: 0) == AND)
244 type.referenceType = Type::Reference;
245 else if (lookup(k: 0) == ANDAND)
246 type.referenceType = Type::RValueReference;
247 else if (lookup(k: 0) == STAR)
248 type.referenceType = Type::Pointer;
249 }
250 type.rawName = type.name;
251 // transform stupid things like 'const void' or 'void const' into 'void'
252 if (isVoid && type.referenceType == Type::NoReference) {
253 type.name = "void";
254 }
255 return type;
256}
257
258bool Moc::parseEnum(EnumDef *def)
259{
260 bool isTypdefEnum = false; // typedef enum { ... } Foo;
261
262 if (test(token: CLASS))
263 def->isEnumClass = true;
264
265 if (test(token: IDENTIFIER)) {
266 def->name = lexem();
267 } else {
268 if (lookup(k: -1) != TYPEDEF)
269 return false; // anonymous enum
270 isTypdefEnum = true;
271 }
272 if (test(token: COLON)) { // C++11 strongly typed enum
273 // enum Foo : unsigned long { ... };
274 parseType(); //ignore the result
275 }
276 if (!test(token: LBRACE))
277 return false;
278 do {
279 if (lookup() == RBRACE) // accept trailing comma
280 break;
281 next(token: IDENTIFIER);
282 def->values += lexem();
283 } while (test(token: EQ) ? until(COMMA) : test(token: COMMA));
284 next(token: RBRACE);
285 if (isTypdefEnum) {
286 if (!test(token: IDENTIFIER))
287 return false;
288 def->name = lexem();
289 }
290 return true;
291}
292
293void Moc::parseFunctionArguments(FunctionDef *def)
294{
295 Q_UNUSED(def);
296 while (hasNext()) {
297 ArgumentDef arg;
298 arg.type = parseType();
299 if (arg.type.name == "void")
300 break;
301 if (test(token: IDENTIFIER))
302 arg.name = lexem();
303 while (test(token: LBRACK)) {
304 arg.rightType += lexemUntil(RBRACK);
305 }
306 if (test(token: CONST) || test(token: VOLATILE)) {
307 arg.rightType += ' ';
308 arg.rightType += lexem();
309 }
310 arg.normalizedType = normalizeType(ba: QByteArray(arg.type.name + ' ' + arg.rightType));
311 arg.typeNameForCast = normalizeType(ba: QByteArray(noRef(type: arg.type.name) + "(*)" + arg.rightType));
312 if (test(token: EQ))
313 arg.isDefault = true;
314 def->arguments += arg;
315 if (!until(COMMA))
316 break;
317 }
318
319 if (!def->arguments.isEmpty()
320 && def->arguments.constLast().normalizedType == "QPrivateSignal") {
321 def->arguments.removeLast();
322 def->isPrivateSignal = true;
323 }
324}
325
326bool Moc::testFunctionAttribute(FunctionDef *def)
327{
328 if (index < symbols.size() && testFunctionAttribute(tok: symbols.at(i: index).token, def)) {
329 ++index;
330 return true;
331 }
332 return false;
333}
334
335bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
336{
337 switch (tok) {
338 case Q_MOC_COMPAT_TOKEN:
339 def->isCompat = true;
340 return true;
341 case Q_INVOKABLE_TOKEN:
342 def->isInvokable = true;
343 return true;
344 case Q_SIGNAL_TOKEN:
345 def->isSignal = true;
346 return true;
347 case Q_SLOT_TOKEN:
348 def->isSlot = true;
349 return true;
350 case Q_SCRIPTABLE_TOKEN:
351 def->isInvokable = def->isScriptable = true;
352 return true;
353 default: break;
354 }
355 return false;
356}
357
358bool Moc::testFunctionRevision(FunctionDef *def)
359{
360 if (test(token: Q_REVISION_TOKEN)) {
361 next(token: LPAREN);
362 QByteArray revision = lexemUntil(RPAREN);
363 revision.remove(index: 0, len: 1);
364 revision.chop(n: 1);
365 bool ok = false;
366 def->revision = revision.toInt(ok: &ok);
367 if (!ok || def->revision < 0)
368 error(msg: "Invalid revision");
369 return true;
370 }
371
372 return false;
373}
374
375// returns false if the function should be ignored
376bool Moc::parseFunction(FunctionDef *def, bool inMacro)
377{
378 def->isVirtual = false;
379 def->isStatic = false;
380 //skip modifiers and attributes
381 while (test(token: INLINE) || (test(token: STATIC) && (def->isStatic = true) == true) ||
382 (test(token: VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual
383 || testFunctionAttribute(def) || testFunctionRevision(def)) {}
384 bool templateFunction = (lookup() == TEMPLATE);
385 def->type = parseType();
386 if (def->type.name.isEmpty()) {
387 if (templateFunction)
388 error(msg: "Template function as signal or slot");
389 else
390 error();
391 }
392 bool scopedFunctionName = false;
393 if (test(token: LPAREN)) {
394 def->name = def->type.name;
395 scopedFunctionName = def->type.isScoped;
396 def->type = Type("int");
397 } else {
398 Type tempType = parseType();;
399 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
400 if (testFunctionAttribute(tok: def->type.firstToken, def))
401 ; // fine
402 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
403 error();
404 else if (def->type.firstToken == Q_SLOTS_TOKEN)
405 error();
406 else {
407 if (!def->tag.isEmpty())
408 def->tag += ' ';
409 def->tag += def->type.name;
410 }
411 def->type = tempType;
412 tempType = parseType();
413 }
414 next(token: LPAREN, msg: "Not a signal or slot declaration");
415 def->name = tempType.name;
416 scopedFunctionName = tempType.isScoped;
417 }
418
419 // we don't support references as return types, it's too dangerous
420 if (def->type.referenceType == Type::Reference) {
421 QByteArray rawName = def->type.rawName;
422 def->type = Type("void");
423 def->type.rawName = rawName;
424 }
425
426 def->normalizedType = normalizeType(ba: def->type.name);
427
428 if (!test(token: RPAREN)) {
429 parseFunctionArguments(def);
430 next(token: RPAREN);
431 }
432
433 // support optional macros with compiler specific options
434 while (test(token: IDENTIFIER))
435 ;
436
437 def->isConst = test(token: CONST);
438
439 while (test(token: IDENTIFIER))
440 ;
441
442 if (inMacro) {
443 next(token: RPAREN);
444 prev();
445 } else {
446 if (test(token: THROW)) {
447 next(token: LPAREN);
448 until(RPAREN);
449 }
450 if (test(token: SEMIC))
451 ;
452 else if ((def->inlineCode = test(token: LBRACE)))
453 until(RBRACE);
454 else if ((def->isAbstract = test(token: EQ)))
455 until(SEMIC);
456 else
457 error();
458 }
459
460 if (scopedFunctionName) {
461 const QByteArray msg = "Function declaration " + def->name
462 + " contains extra qualification. Ignoring as signal or slot.";
463 warning(msg.constData());
464 return false;
465 }
466 return true;
467}
468
469// like parseFunction, but never aborts with an error
470bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
471{
472 def->isVirtual = false;
473 def->isStatic = false;
474 //skip modifiers and attributes
475 while (test(token: EXPLICIT) || test(token: INLINE) || (test(token: STATIC) && (def->isStatic = true) == true) ||
476 (test(token: VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual
477 || testFunctionAttribute(def) || testFunctionRevision(def)) {}
478 bool tilde = test(token: TILDE);
479 def->type = parseType();
480 if (def->type.name.isEmpty())
481 return false;
482 bool scopedFunctionName = false;
483 if (test(token: LPAREN)) {
484 def->name = def->type.name;
485 scopedFunctionName = def->type.isScoped;
486 if (def->name == cdef->classname) {
487 def->isDestructor = tilde;
488 def->isConstructor = !tilde;
489 def->type = Type();
490 } else {
491 def->type = Type("int");
492 }
493 } else {
494 Type tempType = parseType();;
495 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
496 if (testFunctionAttribute(tok: def->type.firstToken, def))
497 ; // fine
498 else if (def->type.name == "Q_SIGNAL")
499 def->isSignal = true;
500 else if (def->type.name == "Q_SLOT")
501 def->isSlot = true;
502 else {
503 if (!def->tag.isEmpty())
504 def->tag += ' ';
505 def->tag += def->type.name;
506 }
507 def->type = tempType;
508 tempType = parseType();
509 }
510 if (!test(token: LPAREN))
511 return false;
512 def->name = tempType.name;
513 scopedFunctionName = tempType.isScoped;
514 }
515
516 // we don't support references as return types, it's too dangerous
517 if (def->type.referenceType == Type::Reference) {
518 QByteArray rawName = def->type.rawName;
519 def->type = Type("void");
520 def->type.rawName = rawName;
521 }
522
523 def->normalizedType = normalizeType(ba: def->type.name);
524
525 if (!test(token: RPAREN)) {
526 parseFunctionArguments(def);
527 if (!test(token: RPAREN))
528 return false;
529 }
530 def->isConst = test(token: CONST);
531 if (scopedFunctionName
532 && (def->isSignal || def->isSlot || def->isInvokable)) {
533 const QByteArray msg = "parsemaybe: Function declaration " + def->name
534 + " contains extra qualification. Ignoring as signal or slot.";
535 warning(msg.constData());
536 return false;
537 }
538 return true;
539}
540
541void Moc::parse()
542{
543 QVector<NamespaceDef> namespaceList;
544 bool templateClass = false;
545 while (hasNext()) {
546 Token t = next();
547 switch (t) {
548 case NAMESPACE: {
549 int rewind = index;
550 if (test(token: IDENTIFIER)) {
551 QByteArray nsName = lexem();
552 QByteArrayList nested;
553 while (test(token: SCOPE)) {
554 next(token: IDENTIFIER);
555 nested.append(t: nsName);
556 nsName = lexem();
557 }
558 if (test(token: EQ)) {
559 // namespace Foo = Bar::Baz;
560 until(SEMIC);
561 } else if (!test(token: SEMIC)) {
562 NamespaceDef def;
563 def.classname = nsName;
564
565 next(token: LBRACE);
566 def.begin = index - 1;
567 until(RBRACE);
568 def.end = index;
569 index = def.begin + 1;
570
571 const bool parseNamespace = currentFilenames.size() <= 1;
572 if (parseNamespace) {
573 for (int i = namespaceList.size() - 1; i >= 0; --i) {
574 if (inNamespace(def: &namespaceList.at(i))) {
575 def.qualified.prepend(a: namespaceList.at(i).classname + "::");
576 }
577 }
578 for (const QByteArray &ns : nested) {
579 NamespaceDef parentNs;
580 parentNs.classname = ns;
581 parentNs.qualified = def.qualified;
582 def.qualified += ns + "::";
583 parentNs.begin = def.begin;
584 parentNs.end = def.end;
585 namespaceList += parentNs;
586 }
587 }
588
589 while (parseNamespace && inNamespace(def: &def) && hasNext()) {
590 switch (next()) {
591 case NAMESPACE:
592 if (test(token: IDENTIFIER)) {
593 while (test(token: SCOPE))
594 next(token: IDENTIFIER);
595 if (test(token: EQ)) {
596 // namespace Foo = Bar::Baz;
597 until(SEMIC);
598 } else if (!test(token: SEMIC)) {
599 until(RBRACE);
600 }
601 }
602 break;
603 case Q_NAMESPACE_TOKEN:
604 def.hasQNamespace = true;
605 break;
606 case Q_ENUMS_TOKEN:
607 case Q_ENUM_NS_TOKEN:
608 parseEnumOrFlag(def: &def, isFlag: false);
609 break;
610 case Q_ENUM_TOKEN:
611 error(msg: "Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead");
612 break;
613 case Q_FLAGS_TOKEN:
614 case Q_FLAG_NS_TOKEN:
615 parseEnumOrFlag(def: &def, isFlag: true);
616 break;
617 case Q_FLAG_TOKEN:
618 error(msg: "Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead");
619 break;
620 case Q_DECLARE_FLAGS_TOKEN:
621 parseFlag(def: &def);
622 break;
623 case Q_CLASSINFO_TOKEN:
624 parseClassInfo(def: &def);
625 break;
626 case ENUM: {
627 EnumDef enumDef;
628 if (parseEnum(def: &enumDef))
629 def.enumList += enumDef;
630 } break;
631 case CLASS:
632 case STRUCT: {
633 ClassDef classdef;
634 if (!parseClassHead(def: &classdef))
635 continue;
636 while (inClass(def: &classdef) && hasNext())
637 next(); // consume all Q_XXXX macros from this class
638 } break;
639 default: break;
640 }
641 }
642 namespaceList += def;
643 index = rewind;
644 if (!def.hasQNamespace && (!def.classInfoList.isEmpty() || !def.enumDeclarations.isEmpty()))
645 error(msg: "Namespace declaration lacks Q_NAMESPACE macro.");
646 }
647 }
648 break;
649 }
650 case SEMIC:
651 case RBRACE:
652 templateClass = false;
653 break;
654 case TEMPLATE:
655 templateClass = true;
656 break;
657 case MOC_INCLUDE_BEGIN:
658 currentFilenames.push(x: symbol().unquotedLexem());
659 break;
660 case MOC_INCLUDE_END:
661 currentFilenames.pop();
662 break;
663 case Q_DECLARE_INTERFACE_TOKEN:
664 parseDeclareInterface();
665 break;
666 case Q_DECLARE_METATYPE_TOKEN:
667 parseDeclareMetatype();
668 break;
669 case USING:
670 if (test(token: NAMESPACE)) {
671 while (test(token: SCOPE) || test(token: IDENTIFIER))
672 ;
673 next(token: SEMIC);
674 }
675 break;
676 case CLASS:
677 case STRUCT: {
678 if (currentFilenames.size() <= 1)
679 break;
680
681 ClassDef def;
682 if (!parseClassHead(def: &def))
683 continue;
684
685 while (inClass(def: &def) && hasNext()) {
686 switch (next()) {
687 case Q_OBJECT_TOKEN:
688 def.hasQObject = true;
689 break;
690 case Q_GADGET_TOKEN:
691 def.hasQGadget = true;
692 break;
693 default: break;
694 }
695 }
696
697 if (!def.hasQObject && !def.hasQGadget)
698 continue;
699
700 for (int i = namespaceList.size() - 1; i >= 0; --i)
701 if (inNamespace(def: &namespaceList.at(i)))
702 def.qualified.prepend(a: namespaceList.at(i).classname + "::");
703
704 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
705 classHash.insert(akey: def.classname, avalue: def.qualified);
706 classHash.insert(akey: def.qualified, avalue: def.qualified);
707
708 continue; }
709 default: break;
710 }
711 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
712 continue;
713 ClassDef def;
714 if (parseClassHead(def: &def)) {
715 FunctionDef::Access access = FunctionDef::Private;
716 for (int i = namespaceList.size() - 1; i >= 0; --i)
717 if (inNamespace(def: &namespaceList.at(i)))
718 def.qualified.prepend(a: namespaceList.at(i).classname + "::");
719 while (inClass(def: &def) && hasNext()) {
720 switch ((t = next())) {
721 case PRIVATE:
722 access = FunctionDef::Private;
723 if (test(token: Q_SIGNALS_TOKEN))
724 error(msg: "Signals cannot have access specifier");
725 break;
726 case PROTECTED:
727 access = FunctionDef::Protected;
728 if (test(token: Q_SIGNALS_TOKEN))
729 error(msg: "Signals cannot have access specifier");
730 break;
731 case PUBLIC:
732 access = FunctionDef::Public;
733 if (test(token: Q_SIGNALS_TOKEN))
734 error(msg: "Signals cannot have access specifier");
735 break;
736 case CLASS: {
737 ClassDef nestedDef;
738 if (parseClassHead(def: &nestedDef)) {
739 while (inClass(def: &nestedDef) && inClass(def: &def)) {
740 t = next();
741 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
742 error(msg: "Meta object features not supported for nested classes");
743 }
744 }
745 } break;
746 case Q_SIGNALS_TOKEN:
747 parseSignals(def: &def);
748 break;
749 case Q_SLOTS_TOKEN:
750 switch (lookup(k: -1)) {
751 case PUBLIC:
752 case PROTECTED:
753 case PRIVATE:
754 parseSlots(def: &def, access);
755 break;
756 default:
757 error(msg: "Missing access specifier for slots");
758 }
759 break;
760 case Q_OBJECT_TOKEN:
761 def.hasQObject = true;
762 if (templateClass)
763 error(msg: "Template classes not supported by Q_OBJECT");
764 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
765 error(msg: "Class contains Q_OBJECT macro but does not inherit from QObject");
766 break;
767 case Q_GADGET_TOKEN:
768 def.hasQGadget = true;
769 if (templateClass)
770 error(msg: "Template classes not supported by Q_GADGET");
771 break;
772 case Q_PROPERTY_TOKEN:
773 parseProperty(def: &def);
774 break;
775 case Q_PLUGIN_METADATA_TOKEN:
776 parsePluginData(def: &def);
777 break;
778 case Q_ENUMS_TOKEN:
779 case Q_ENUM_TOKEN:
780 parseEnumOrFlag(def: &def, isFlag: false);
781 break;
782 case Q_ENUM_NS_TOKEN:
783 error(msg: "Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead");
784 break;
785 case Q_FLAGS_TOKEN:
786 case Q_FLAG_TOKEN:
787 parseEnumOrFlag(def: &def, isFlag: true);
788 break;
789 case Q_FLAG_NS_TOKEN:
790 error(msg: "Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead");
791 break;
792 case Q_DECLARE_FLAGS_TOKEN:
793 parseFlag(def: &def);
794 break;
795 case Q_CLASSINFO_TOKEN:
796 parseClassInfo(def: &def);
797 break;
798 case Q_INTERFACES_TOKEN:
799 parseInterfaces(def: &def);
800 break;
801 case Q_PRIVATE_SLOT_TOKEN:
802 parseSlotInPrivate(def: &def, access);
803 break;
804 case Q_PRIVATE_PROPERTY_TOKEN:
805 parsePrivateProperty(def: &def);
806 break;
807 case ENUM: {
808 EnumDef enumDef;
809 if (parseEnum(def: &enumDef))
810 def.enumList += enumDef;
811 } break;
812 case SEMIC:
813 case COLON:
814 break;
815 default:
816 FunctionDef funcDef;
817 funcDef.access = access;
818 int rewind = index--;
819 if (parseMaybeFunction(cdef: &def, def: &funcDef)) {
820 if (funcDef.isConstructor) {
821 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
822 def.constructorList += funcDef;
823 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
824 funcDef.wasCloned = true;
825 funcDef.arguments.removeLast();
826 def.constructorList += funcDef;
827 }
828 }
829 } else if (funcDef.isDestructor) {
830 // don't care about destructors
831 } else {
832 if (access == FunctionDef::Public)
833 def.publicList += funcDef;
834 if (funcDef.isSlot) {
835 def.slotList += funcDef;
836 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
837 funcDef.wasCloned = true;
838 funcDef.arguments.removeLast();
839 def.slotList += funcDef;
840 }
841 if (funcDef.revision > 0)
842 ++def.revisionedMethods;
843 } else if (funcDef.isSignal) {
844 def.signalList += funcDef;
845 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
846 funcDef.wasCloned = true;
847 funcDef.arguments.removeLast();
848 def.signalList += funcDef;
849 }
850 if (funcDef.revision > 0)
851 ++def.revisionedMethods;
852 } else if (funcDef.isInvokable) {
853 def.methodList += funcDef;
854 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
855 funcDef.wasCloned = true;
856 funcDef.arguments.removeLast();
857 def.methodList += funcDef;
858 }
859 if (funcDef.revision > 0)
860 ++def.revisionedMethods;
861 }
862 }
863 } else {
864 index = rewind;
865 }
866 }
867 }
868
869 next(token: RBRACE);
870
871 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
872 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
873 continue; // no meta object code required
874
875
876 if (!def.hasQObject && !def.hasQGadget)
877 error(msg: "Class declaration lacks Q_OBJECT macro.");
878
879 // Add meta tags to the plugin meta data:
880 if (!def.pluginData.iid.isEmpty())
881 def.pluginData.metaArgs = metaArgs;
882
883 checkSuperClasses(def: &def);
884 checkProperties(cdef: &def);
885
886 classList += def;
887 QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
888 classHash.insert(akey: def.classname, avalue: def.qualified);
889 classHash.insert(akey: def.qualified, avalue: def.qualified);
890 }
891 }
892 for (const auto &n : qAsConst(t&: namespaceList)) {
893 if (!n.hasQNamespace)
894 continue;
895 ClassDef def;
896 static_cast<BaseDef &>(def) = static_cast<BaseDef>(n);
897 def.qualified += def.classname;
898 def.hasQGadget = true;
899 auto it = std::find_if(first: classList.begin(), last: classList.end(), pred: [&def](const ClassDef &val) {
900 return def.classname == val.classname && def.qualified == val.qualified;
901 });
902
903 if (it != classList.end()) {
904 it->classInfoList += def.classInfoList;
905 it->enumDeclarations.insert(map: def.enumDeclarations);
906 it->enumList += def.enumList;
907 it->flagAliases.insert(map: def.flagAliases);
908 } else {
909 knownGadgets.insert(akey: def.classname, avalue: def.qualified);
910 knownGadgets.insert(akey: def.qualified, avalue: def.qualified);
911 classList += def;
912 }
913 }
914}
915
916void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
917{
918 int defaultRevision = -1;
919 if (test(token: Q_REVISION_TOKEN)) {
920 next(token: LPAREN);
921 QByteArray revision = lexemUntil(RPAREN);
922 revision.remove(index: 0, len: 1);
923 revision.chop(n: 1);
924 bool ok = false;
925 defaultRevision = revision.toInt(ok: &ok);
926 if (!ok || defaultRevision < 0)
927 error(msg: "Invalid revision");
928 }
929
930 next(token: COLON);
931 while (inClass(def) && hasNext()) {
932 switch (next()) {
933 case PUBLIC:
934 case PROTECTED:
935 case PRIVATE:
936 case Q_SIGNALS_TOKEN:
937 case Q_SLOTS_TOKEN:
938 prev();
939 return;
940 case SEMIC:
941 continue;
942 case FRIEND:
943 until(SEMIC);
944 continue;
945 case USING:
946 error(msg: "'using' directive not supported in 'slots' section");
947 default:
948 prev();
949 }
950
951 FunctionDef funcDef;
952 funcDef.access = access;
953 if (!parseFunction(def: &funcDef))
954 continue;
955 if (funcDef.revision > 0) {
956 ++def->revisionedMethods;
957 } else if (defaultRevision != -1) {
958 funcDef.revision = defaultRevision;
959 ++def->revisionedMethods;
960 }
961 def->slotList += funcDef;
962 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
963 funcDef.wasCloned = true;
964 funcDef.arguments.removeLast();
965 def->slotList += funcDef;
966 }
967 }
968}
969
970void Moc::parseSignals(ClassDef *def)
971{
972 int defaultRevision = -1;
973 if (test(token: Q_REVISION_TOKEN)) {
974 next(token: LPAREN);
975 QByteArray revision = lexemUntil(RPAREN);
976 revision.remove(index: 0, len: 1);
977 revision.chop(n: 1);
978 bool ok = false;
979 defaultRevision = revision.toInt(ok: &ok);
980 if (!ok || defaultRevision < 0)
981 error(msg: "Invalid revision");
982 }
983
984 next(token: COLON);
985 while (inClass(def) && hasNext()) {
986 switch (next()) {
987 case PUBLIC:
988 case PROTECTED:
989 case PRIVATE:
990 case Q_SIGNALS_TOKEN:
991 case Q_SLOTS_TOKEN:
992 prev();
993 return;
994 case SEMIC:
995 continue;
996 case FRIEND:
997 until(SEMIC);
998 continue;
999 case USING:
1000 error(msg: "'using' directive not supported in 'signals' section");
1001 default:
1002 prev();
1003 }
1004 FunctionDef funcDef;
1005 funcDef.access = FunctionDef::Public;
1006 parseFunction(def: &funcDef);
1007 if (funcDef.isVirtual)
1008 warning("Signals cannot be declared virtual");
1009 if (funcDef.inlineCode)
1010 error(msg: "Not a signal declaration");
1011 if (funcDef.revision > 0) {
1012 ++def->revisionedMethods;
1013 } else if (defaultRevision != -1) {
1014 funcDef.revision = defaultRevision;
1015 ++def->revisionedMethods;
1016 }
1017 def->signalList += funcDef;
1018 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
1019 funcDef.wasCloned = true;
1020 funcDef.arguments.removeLast();
1021 def->signalList += funcDef;
1022 }
1023 }
1024}
1025
1026void Moc::createPropertyDef(PropertyDef &propDef)
1027{
1028 QByteArray type = parseType().name;
1029 if (type.isEmpty())
1030 error();
1031 propDef.designable = propDef.scriptable = propDef.stored = "true";
1032 propDef.user = "false";
1033 /*
1034 The Q_PROPERTY construct cannot contain any commas, since
1035 commas separate macro arguments. We therefore expect users
1036 to type "QMap" instead of "QMap<QString, QVariant>". For
1037 coherence, we also expect the same for
1038 QValueList<QVariant>, the other template class supported by
1039 QVariant.
1040 */
1041 type = normalizeType(ba: type);
1042 if (type == "QMap")
1043 type = "QMap<QString,QVariant>";
1044 else if (type == "QValueList")
1045 type = "QValueList<QVariant>";
1046 else if (type == "LongLong")
1047 type = "qlonglong";
1048 else if (type == "ULongLong")
1049 type = "qulonglong";
1050
1051 propDef.type = type;
1052
1053 next();
1054 propDef.name = lexem();
1055 while (test(token: IDENTIFIER)) {
1056 const QByteArray l = lexem();
1057 if (l[0] == 'C' && l == "CONSTANT") {
1058 propDef.constant = true;
1059 continue;
1060 } else if(l[0] == 'F' && l == "FINAL") {
1061 propDef.final = true;
1062 continue;
1063 }
1064
1065 QByteArray v, v2;
1066 if (test(token: LPAREN)) {
1067 v = lexemUntil(RPAREN);
1068 v = v.mid(index: 1, len: v.length() - 2); // removes the '(' and ')'
1069 } else if (test(token: INTEGER_LITERAL)) {
1070 v = lexem();
1071 if (l != "REVISION")
1072 error(rollback: 1);
1073 } else {
1074 next(token: IDENTIFIER);
1075 v = lexem();
1076 if (test(token: LPAREN))
1077 v2 = lexemUntil(RPAREN);
1078 else if (v != "true" && v != "false")
1079 v2 = "()";
1080 }
1081 switch (l[0]) {
1082 case 'M':
1083 if (l == "MEMBER")
1084 propDef.member = v;
1085 else
1086 error(rollback: 2);
1087 break;
1088 case 'R':
1089 if (l == "READ")
1090 propDef.read = v;
1091 else if (l == "RESET")
1092 propDef.reset = v + v2;
1093 else if (l == "REVISION") {
1094 bool ok = false;
1095 propDef.revision = v.toInt(ok: &ok);
1096 if (!ok || propDef.revision < 0)
1097 error(rollback: 1);
1098 } else
1099 error(rollback: 2);
1100 break;
1101 case 'S':
1102 if (l == "SCRIPTABLE")
1103 propDef.scriptable = v + v2;
1104 else if (l == "STORED")
1105 propDef.stored = v + v2;
1106 else
1107 error(rollback: 2);
1108 break;
1109 case 'W': if (l != "WRITE") error(rollback: 2);
1110 propDef.write = v;
1111 break;
1112 case 'D': if (l != "DESIGNABLE") error(rollback: 2);
1113 propDef.designable = v + v2;
1114 break;
1115 case 'E': if (l != "EDITABLE") error(rollback: 2);
1116 propDef.editable = v + v2;
1117 break;
1118 case 'N': if (l != "NOTIFY") error(rollback: 2);
1119 propDef.notify = v;
1120 break;
1121 case 'U': if (l != "USER") error(rollback: 2);
1122 propDef.user = v + v2;
1123 break;
1124 default:
1125 error(rollback: 2);
1126 }
1127 }
1128 if (propDef.read.isNull() && propDef.member.isNull()) {
1129 const QByteArray msg = "Property declaration " + propDef.name
1130 + " has no READ accessor function or associated MEMBER variable. The property will be invalid.";
1131 warning(msg.constData());
1132 }
1133 if (propDef.constant && !propDef.write.isNull()) {
1134 const QByteArray msg = "Property declaration " + propDef.name
1135 + " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
1136 propDef.constant = false;
1137 warning(msg.constData());
1138 }
1139 if (propDef.constant && !propDef.notify.isNull()) {
1140 const QByteArray msg = "Property declaration " + propDef.name
1141 + " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
1142 propDef.constant = false;
1143 warning(msg.constData());
1144 }
1145}
1146
1147void Moc::parseProperty(ClassDef *def)
1148{
1149 next(token: LPAREN);
1150 PropertyDef propDef;
1151 createPropertyDef(propDef);
1152 next(token: RPAREN);
1153
1154 if(!propDef.notify.isEmpty())
1155 def->notifyableProperties++;
1156 if (propDef.revision > 0)
1157 ++def->revisionedProperties;
1158 def->propertyList += propDef;
1159}
1160
1161void Moc::parsePluginData(ClassDef *def)
1162{
1163 next(token: LPAREN);
1164 QByteArray metaData;
1165 while (test(token: IDENTIFIER)) {
1166 QByteArray l = lexem();
1167 if (l == "IID") {
1168 next(token: STRING_LITERAL);
1169 def->pluginData.iid = unquotedLexem();
1170 } else if (l == "FILE") {
1171 next(token: STRING_LITERAL);
1172 QByteArray metaDataFile = unquotedLexem();
1173 QFileInfo fi(QFileInfo(QString::fromLocal8Bit(str: currentFilenames.top().constData())).dir(), QString::fromLocal8Bit(str: metaDataFile.constData()));
1174 for (int j = 0; j < includes.size() && !fi.exists(); ++j) {
1175 const IncludePath &p = includes.at(i: j);
1176 if (p.isFrameworkPath)
1177 continue;
1178
1179 fi.setFile(dir: QString::fromLocal8Bit(str: p.path.constData()), file: QString::fromLocal8Bit(str: metaDataFile.constData()));
1180 // try again, maybe there's a file later in the include paths with the same name
1181 if (fi.isDir()) {
1182 fi = QFileInfo();
1183 continue;
1184 }
1185 }
1186 if (!fi.exists()) {
1187 const QByteArray msg = "Plugin Metadata file " + lexem()
1188 + " does not exist. Declaration will be ignored";
1189 error(msg: msg.constData());
1190 return;
1191 }
1192 QFile file(fi.canonicalFilePath());
1193 if (!file.open(flags: QFile::ReadOnly)) {
1194 QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: "
1195 + file.errorString().toUtf8();
1196 error(msg: msg.constData());
1197 return;
1198 }
1199 metaData = file.readAll();
1200 }
1201 }
1202
1203 if (!metaData.isEmpty()) {
1204 def->pluginData.metaData = QJsonDocument::fromJson(json: metaData);
1205 if (!def->pluginData.metaData.isObject()) {
1206 const QByteArray msg = "Plugin Metadata file " + lexem()
1207 + " does not contain a valid JSON object. Declaration will be ignored";
1208 warning(msg.constData());
1209 def->pluginData.iid = QByteArray();
1210 return;
1211 }
1212 }
1213
1214 mustIncludeQPluginH = true;
1215 next(token: RPAREN);
1216}
1217
1218void Moc::parsePrivateProperty(ClassDef *def)
1219{
1220 next(token: LPAREN);
1221 PropertyDef propDef;
1222 next(token: IDENTIFIER);
1223 propDef.inPrivateClass = lexem();
1224 while (test(token: SCOPE)) {
1225 propDef.inPrivateClass += lexem();
1226 next(token: IDENTIFIER);
1227 propDef.inPrivateClass += lexem();
1228 }
1229 // also allow void functions
1230 if (test(token: LPAREN)) {
1231 next(token: RPAREN);
1232 propDef.inPrivateClass += "()";
1233 }
1234
1235 next(token: COMMA);
1236
1237 createPropertyDef(propDef);
1238
1239 if(!propDef.notify.isEmpty())
1240 def->notifyableProperties++;
1241 if (propDef.revision > 0)
1242 ++def->revisionedProperties;
1243
1244 def->propertyList += propDef;
1245}
1246
1247void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag)
1248{
1249 next(token: LPAREN);
1250 QByteArray identifier;
1251 while (test(token: IDENTIFIER)) {
1252 identifier = lexem();
1253 while (test(token: SCOPE) && test(token: IDENTIFIER)) {
1254 identifier += "::";
1255 identifier += lexem();
1256 }
1257 def->enumDeclarations[identifier] = isFlag;
1258 }
1259 next(token: RPAREN);
1260}
1261
1262void Moc::parseFlag(BaseDef *def)
1263{
1264 next(token: LPAREN);
1265 QByteArray flagName, enumName;
1266 while (test(token: IDENTIFIER)) {
1267 flagName = lexem();
1268 while (test(token: SCOPE) && test(token: IDENTIFIER)) {
1269 flagName += "::";
1270 flagName += lexem();
1271 }
1272 }
1273 next(token: COMMA);
1274 while (test(token: IDENTIFIER)) {
1275 enumName = lexem();
1276 while (test(token: SCOPE) && test(token: IDENTIFIER)) {
1277 enumName += "::";
1278 enumName += lexem();
1279 }
1280 }
1281
1282 def->flagAliases.insert(akey: enumName, avalue: flagName);
1283 next(token: RPAREN);
1284}
1285
1286void Moc::parseClassInfo(BaseDef *def)
1287{
1288 next(token: LPAREN);
1289 ClassInfoDef infoDef;
1290 next(token: STRING_LITERAL);
1291 infoDef.name = symbol().unquotedLexem();
1292 next(token: COMMA);
1293 if (test(token: STRING_LITERAL)) {
1294 infoDef.value = symbol().unquotedLexem();
1295 } else {
1296 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1297 next(token: IDENTIFIER);
1298 next(token: LPAREN);
1299 next(token: STRING_LITERAL);
1300 infoDef.value = symbol().unquotedLexem();
1301 next(token: RPAREN);
1302 }
1303 next(token: RPAREN);
1304 def->classInfoList += infoDef;
1305}
1306
1307void Moc::parseInterfaces(ClassDef *def)
1308{
1309 next(token: LPAREN);
1310 while (test(token: IDENTIFIER)) {
1311 QVector<ClassDef::Interface> iface;
1312 iface += ClassDef::Interface(lexem());
1313 while (test(token: SCOPE)) {
1314 iface.last().className += lexem();
1315 next(token: IDENTIFIER);
1316 iface.last().className += lexem();
1317 }
1318 while (test(token: COLON)) {
1319 next(token: IDENTIFIER);
1320 iface += ClassDef::Interface(lexem());
1321 while (test(token: SCOPE)) {
1322 iface.last().className += lexem();
1323 next(token: IDENTIFIER);
1324 iface.last().className += lexem();
1325 }
1326 }
1327 // resolve from classnames to interface ids
1328 for (int i = 0; i < iface.count(); ++i) {
1329 const QByteArray iid = interface2IdMap.value(akey: iface.at(i).className);
1330 if (iid.isEmpty())
1331 error(msg: "Undefined interface");
1332
1333 iface[i].interfaceId = iid;
1334 }
1335 def->interfaceList += iface;
1336 }
1337 next(token: RPAREN);
1338}
1339
1340void Moc::parseDeclareInterface()
1341{
1342 next(token: LPAREN);
1343 QByteArray interface;
1344 next(token: IDENTIFIER);
1345 interface += lexem();
1346 while (test(token: SCOPE)) {
1347 interface += lexem();
1348 next(token: IDENTIFIER);
1349 interface += lexem();
1350 }
1351 next(token: COMMA);
1352 QByteArray iid;
1353 if (test(token: STRING_LITERAL)) {
1354 iid = lexem();
1355 } else {
1356 next(token: IDENTIFIER);
1357 iid = lexem();
1358 }
1359 interface2IdMap.insert(akey: interface, avalue: iid);
1360 next(token: RPAREN);
1361}
1362
1363void Moc::parseDeclareMetatype()
1364{
1365 next(token: LPAREN);
1366 QByteArray typeName = lexemUntil(RPAREN);
1367 typeName.remove(index: 0, len: 1);
1368 typeName.chop(n: 1);
1369 metaTypes.append(t: typeName);
1370}
1371
1372void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1373{
1374 next(token: LPAREN);
1375 FunctionDef funcDef;
1376 next(token: IDENTIFIER);
1377 funcDef.inPrivateClass = lexem();
1378 // also allow void functions
1379 if (test(token: LPAREN)) {
1380 next(token: RPAREN);
1381 funcDef.inPrivateClass += "()";
1382 }
1383 next(token: COMMA);
1384 funcDef.access = access;
1385 parseFunction(def: &funcDef, inMacro: true);
1386 def->slotList += funcDef;
1387 while (funcDef.arguments.size() > 0 && funcDef.arguments.constLast().isDefault) {
1388 funcDef.wasCloned = true;
1389 funcDef.arguments.removeLast();
1390 def->slotList += funcDef;
1391 }
1392 if (funcDef.revision > 0)
1393 ++def->revisionedMethods;
1394
1395}
1396
1397QByteArray Moc::lexemUntil(Token target)
1398{
1399 int from = index;
1400 until(target);
1401 QByteArray s;
1402 while (from <= index) {
1403 QByteArray n = symbols.at(i: from++-1).lexem();
1404 if (s.size() && n.size()) {
1405 char prev = s.at(i: s.size()-1);
1406 char next = n.at(i: 0);
1407 if ((is_ident_char(s: prev) && is_ident_char(s: next))
1408 || (prev == '<' && next == ':')
1409 || (prev == '>' && next == '>'))
1410 s += ' ';
1411 }
1412 s += n;
1413 }
1414 return s;
1415}
1416
1417bool Moc::until(Token target) {
1418 int braceCount = 0;
1419 int brackCount = 0;
1420 int parenCount = 0;
1421 int angleCount = 0;
1422 if (index) {
1423 switch(symbols.at(i: index-1).token) {
1424 case LBRACE: ++braceCount; break;
1425 case LBRACK: ++brackCount; break;
1426 case LPAREN: ++parenCount; break;
1427 case LANGLE: ++angleCount; break;
1428 default: break;
1429 }
1430 }
1431
1432 //when searching commas within the default argument, we should take care of template depth (anglecount)
1433 // unfortunatelly, we do not have enough semantic information to know if '<' is the operator< or
1434 // the beginning of a template type. so we just use heuristics.
1435 int possible = -1;
1436
1437 while (index < symbols.size()) {
1438 Token t = symbols.at(i: index++).token;
1439 switch (t) {
1440 case LBRACE: ++braceCount; break;
1441 case RBRACE: --braceCount; break;
1442 case LBRACK: ++brackCount; break;
1443 case RBRACK: --brackCount; break;
1444 case LPAREN: ++parenCount; break;
1445 case RPAREN: --parenCount; break;
1446 case LANGLE:
1447 if (parenCount == 0 && braceCount == 0)
1448 ++angleCount;
1449 break;
1450 case RANGLE:
1451 if (parenCount == 0 && braceCount == 0)
1452 --angleCount;
1453 break;
1454 case GTGT:
1455 if (parenCount == 0 && braceCount == 0) {
1456 angleCount -= 2;
1457 t = RANGLE;
1458 }
1459 break;
1460 default: break;
1461 }
1462 if (t == target
1463 && braceCount <= 0
1464 && brackCount <= 0
1465 && parenCount <= 0
1466 && (target != RANGLE || angleCount <= 0)) {
1467 if (target != COMMA || angleCount <= 0)
1468 return true;
1469 possible = index;
1470 }
1471
1472 if (target == COMMA && t == EQ && possible != -1) {
1473 index = possible;
1474 return true;
1475 }
1476
1477 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1478 || (target == RANGLE && angleCount < 0)) {
1479 --index;
1480 break;
1481 }
1482
1483 if (braceCount <= 0 && t == SEMIC) {
1484 // Abort on semicolon. Allow recovering bad template parsing (QTBUG-31218)
1485 break;
1486 }
1487 }
1488
1489 if(target == COMMA && angleCount != 0 && possible != -1) {
1490 index = possible;
1491 return true;
1492 }
1493
1494 return false;
1495}
1496
1497void Moc::checkSuperClasses(ClassDef *def)
1498{
1499 const QByteArray firstSuperclass = def->superclassList.value(i: 0).first;
1500
1501 if (!knownQObjectClasses.contains(akey: firstSuperclass)) {
1502 // enable once we /require/ include paths
1503#if 0
1504 const QByteArray msg
1505 = "Class "
1506 + def->className
1507 + " contains the Q_OBJECT macro and inherits from "
1508 + def->superclassList.value(0)
1509 + " but that is not a known QObject subclass. You may get compilation errors.";
1510 warning(msg.constData());
1511#endif
1512 return;
1513 }
1514 for (int i = 1; i < def->superclassList.count(); ++i) {
1515 const QByteArray superClass = def->superclassList.at(i).first;
1516 if (knownQObjectClasses.contains(akey: superClass)) {
1517 const QByteArray msg
1518 = "Class "
1519 + def->classname
1520 + " inherits from two QObject subclasses "
1521 + firstSuperclass
1522 + " and "
1523 + superClass
1524 + ". This is not supported!";
1525 warning(msg.constData());
1526 }
1527
1528 if (interface2IdMap.contains(akey: superClass)) {
1529 bool registeredInterface = false;
1530 for (int i = 0; i < def->interfaceList.count(); ++i)
1531 if (def->interfaceList.at(i).constFirst().className == superClass) {
1532 registeredInterface = true;
1533 break;
1534 }
1535
1536 if (!registeredInterface) {
1537 const QByteArray msg
1538 = "Class "
1539 + def->classname
1540 + " implements the interface "
1541 + superClass
1542 + " but does not list it in Q_INTERFACES. qobject_cast to "
1543 + superClass
1544 + " will not work!";
1545 warning(msg.constData());
1546 }
1547 }
1548 }
1549}
1550
1551void Moc::checkProperties(ClassDef *cdef)
1552{
1553 //
1554 // specify get function, for compatibiliy we accept functions
1555 // returning pointers, or const char * for QByteArray.
1556 //
1557 QSet<QByteArray> definedProperties;
1558 for (int i = 0; i < cdef->propertyList.count(); ++i) {
1559 PropertyDef &p = cdef->propertyList[i];
1560 if (p.read.isEmpty() && p.member.isEmpty())
1561 continue;
1562 if (definedProperties.contains(value: p.name)) {
1563 QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + ".";
1564 warning(msg.constData());
1565 }
1566 definedProperties.insert(value: p.name);
1567
1568 for (int j = 0; j < cdef->publicList.count(); ++j) {
1569 const FunctionDef &f = cdef->publicList.at(i: j);
1570 if (f.name != p.read)
1571 continue;
1572 if (!f.isConst) // get functions must be const
1573 continue;
1574 if (f.arguments.size()) // and must not take any arguments
1575 continue;
1576 PropertyDef::Specification spec = PropertyDef::ValueSpec;
1577 QByteArray tmp = f.normalizedType;
1578 if (p.type == "QByteArray" && tmp == "const char *")
1579 tmp = "QByteArray";
1580 if (tmp.left(len: 6) == "const ")
1581 tmp = tmp.mid(index: 6);
1582 if (p.type != tmp && tmp.endsWith(c: '*')) {
1583 tmp.chop(n: 1);
1584 spec = PropertyDef::PointerSpec;
1585 } else if (f.type.name.endsWith(c: '&')) { // raw type, not normalized type
1586 spec = PropertyDef::ReferenceSpec;
1587 }
1588 if (p.type != tmp)
1589 continue;
1590 p.gspec = spec;
1591 break;
1592 }
1593 if(!p.notify.isEmpty()) {
1594 int notifyId = -1;
1595 for (int j = 0; j < cdef->signalList.count(); ++j) {
1596 const FunctionDef &f = cdef->signalList.at(i: j);
1597 if(f.name != p.notify) {
1598 continue;
1599 } else {
1600 notifyId = j /* Signal indexes start from 0 */;
1601 break;
1602 }
1603 }
1604 p.notifyId = notifyId;
1605 if (notifyId == -1) {
1606 QByteArray msg = "NOTIFY signal '" + p.notify + "' of property '" + p.name
1607 + "' does not exist in class " + cdef->classname + ".";
1608 error(msg: msg.constData());
1609 }
1610 }
1611 }
1612}
1613
1614
1615
1616QT_END_NAMESPACE
1617

source code of qtremoteobjects/tools/repc/moc_copy/moc.cpp