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