1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the tools applications of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include "generator.h"
44 #include "qdatetime.h"
46 #include "outputrevision.h"
48 // for normalizeTypeInternal
49 #include <private/qmetaobject_p.h>
53 // only moc needs this function
54 static QByteArray normalizeType(const char *s, bool fixScope = false)
58 char *buf = (len >= 64 ? new char[len + 1] : stackbuf);
61 while(*s && is_space(*s))
64 while (*s && !is_space(*s))
66 while (*s && is_space(*s))
68 if (*s && ((is_ident_char(*s) && is_ident_char(last))
69 || ((*s == ':') && (last == '<')))) {
75 if (strncmp("void", buf, d - buf) != 0)
76 result = normalizeTypeInternal(buf, d, fixScope);
82 bool Moc::parseClassHead(ClassDef *def)
84 // figure out whether this is a class declaration, or only a
85 // forward or variable declaration.
90 if (token == COLON || token == LBRACE)
92 if (token == SEMIC || token == RANGLE)
96 if (!test(IDENTIFIER)) // typedef struct { ... }
98 QByteArray name = lexem();
100 // support "class IDENT name" and "class IDENT(IDENT) name"
103 if (!test(IDENTIFIER))
106 } else if (test(IDENTIFIER)) {
110 def->qualified += name;
111 while (test(SCOPE)) {
112 def->qualified += lexem();
113 if (test(IDENTIFIER)) {
115 def->qualified += name;
118 def->classname = name;
122 FunctionDef::Access access = FunctionDef::Public;
124 access = FunctionDef::Private;
125 else if (test(PROTECTED))
126 access = FunctionDef::Protected;
130 const QByteArray type = parseType().name;
131 // ignore the 'class Foo : BAR(Baz)' case
135 def->superclassList += qMakePair(type, access);
137 } while (test(COMMA));
141 def->begin = index - 1;
142 bool foundRBrace = until(RBRACE);
144 index = def->begin + 1;
148 Type Moc::parseType()
151 bool hasSignedOrUnsigned = false;
153 type.firstToken = lookup();
158 hasSignedOrUnsigned = true;
162 type.name += lexem();
164 if (lookup(0) == VOLATILE)
165 type.isVolatile = true;
167 case Q_MOC_COMPAT_TOKEN:
168 case Q_QT3_SUPPORT_TOKEN:
169 case Q_INVOKABLE_TOKEN:
170 case Q_SCRIPTABLE_TOKEN:
171 case Q_SIGNALS_TOKEN:
175 type.name += lexem();
183 test(ENUM) || test(CLASS) || test(STRUCT);
187 // void mySlot(unsigned myArg)
188 if (hasSignedOrUnsigned) {
196 type.name += lexem();
197 // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
198 if (test(LONG) || test(INT) || test(DOUBLE)) {
208 type.name += lexem();
209 isVoid |= (lookup(0) == VOID);
216 QByteArray templ = lexemUntil(RANGLE);
217 for (int i = 0; i < templ.size(); ++i) {
218 type.name += templ.at(i);
219 if ((templ.at(i) == '<' && i+1 < templ.size() && templ.at(i+1) == ':')
220 || (templ.at(i) == '>' && i+1 < templ.size() && templ.at(i+1) == '>')) {
226 type.name += lexem();
227 type.isScoped = true;
232 while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED)
233 || test(STAR) || test(AND) || test(ANDAND)) {
235 type.name += lexem();
236 if (lookup(0) == AND)
237 type.referenceType = Type::Reference;
238 else if (lookup(0) == ANDAND)
239 type.referenceType = Type::RValueReference;
240 else if (lookup(0) == STAR)
241 type.referenceType = Type::Pointer;
243 // transform stupid things like 'const void' or 'void const' into 'void'
244 if (isVoid && type.referenceType == Type::NoReference) {
250 bool Moc::parseEnum(EnumDef *def)
252 bool isTypdefEnum = false; // typedef enum { ... } Foo;
254 if (test(IDENTIFIER)) {
257 if (lookup(-1) != TYPEDEF)
258 return false; // anonymous enum
264 if (lookup() == RBRACE) // accept trailing comma
267 def->values += lexem();
268 } while (test(EQ) ? until(COMMA) : test(COMMA));
271 if (!test(IDENTIFIER))
278 void Moc::parseFunctionArguments(FunctionDef *def)
283 arg.type = parseType();
284 if (arg.type.name == "void")
286 if (test(IDENTIFIER))
288 while (test(LBRACK)) {
289 arg.rightType += lexemUntil(RBRACK);
291 if (test(CONST) || test(VOLATILE)) {
292 arg.rightType += ' ';
293 arg.rightType += lexem();
295 arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType));
296 arg.typeNameForCast = normalizeType(QByteArray(noRef(arg.type.name) + "(*)" + arg.rightType));
298 arg.isDefault = true;
299 def->arguments += arg;
305 bool Moc::testFunctionAttribute(FunctionDef *def)
307 if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) {
314 bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
317 case Q_MOC_COMPAT_TOKEN:
318 case Q_QT3_SUPPORT_TOKEN:
319 def->isCompat = true;
321 case Q_INVOKABLE_TOKEN:
322 def->isInvokable = true;
325 def->isSignal = true;
330 case Q_SCRIPTABLE_TOKEN:
331 def->isInvokable = def->isScriptable = true;
338 bool Moc::testFunctionRevision(FunctionDef *def)
340 if (test(Q_REVISION_TOKEN)) {
342 QByteArray revision = lexemUntil(RPAREN);
343 revision.remove(0, 1);
346 def->revision = revision.toInt(&ok);
347 if (!ok || def->revision < 0)
348 error("Invalid revision");
355 // returns false if the function should be ignored
356 bool Moc::parseFunction(FunctionDef *def, bool inMacro)
358 def->isVirtual = false;
359 def->isStatic = false;
360 //skip modifiers and attributes
361 while (test(INLINE) || (test(STATIC) && (def->isStatic = true)) ||
362 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
363 || testFunctionAttribute(def) || testFunctionRevision(def)) {}
364 bool templateFunction = (lookup() == TEMPLATE);
365 def->type = parseType();
366 if (def->type.name.isEmpty()) {
367 if (templateFunction)
368 error("Template function as signal or slot");
372 bool scopedFunctionName = false;
374 def->name = def->type.name;
375 scopedFunctionName = def->type.isScoped;
376 def->type = Type("int");
378 Type tempType = parseType();;
379 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
380 if (testFunctionAttribute(def->type.firstToken, def))
382 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
384 else if (def->type.firstToken == Q_SLOTS_TOKEN)
387 if (!def->tag.isEmpty())
389 def->tag += def->type.name;
391 def->type = tempType;
392 tempType = parseType();
394 next(LPAREN, "Not a signal or slot declaration");
395 def->name = tempType.name;
396 scopedFunctionName = tempType.isScoped;
399 // we don't support references as return types, it's too dangerous
400 if (def->type.referenceType == Type::Reference)
401 def->type = Type("void");
403 def->normalizedType = normalizeType(def->type.name);
406 parseFunctionArguments(def);
410 // support optional macros with compiler specific options
411 while (test(IDENTIFIER))
414 def->isConst = test(CONST);
416 while (test(IDENTIFIER))
429 else if ((def->inlineCode = test(LBRACE)))
431 else if ((def->isAbstract = test(EQ)))
437 if (scopedFunctionName) {
438 QByteArray msg("Function declaration ");
440 msg += " contains extra qualification. Ignoring as signal or slot.";
441 warning(msg.constData());
447 // like parseFunction, but never aborts with an error
448 bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
450 def->isVirtual = false;
451 def->isStatic = false;
452 //skip modifiers and attributes
453 while (test(EXPLICIT) || test(INLINE) || (test(STATIC) && (def->isStatic = true)) ||
454 (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual
455 || testFunctionAttribute(def) || testFunctionRevision(def)) {}
456 bool tilde = test(TILDE);
457 def->type = parseType();
458 if (def->type.name.isEmpty())
460 bool scopedFunctionName = false;
462 def->name = def->type.name;
463 scopedFunctionName = def->type.isScoped;
464 if (def->name == cdef->classname) {
465 def->isDestructor = tilde;
466 def->isConstructor = !tilde;
469 def->type = Type("int");
472 Type tempType = parseType();;
473 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
474 if (testFunctionAttribute(def->type.firstToken, def))
476 else if (def->type.name == "Q_SIGNAL")
477 def->isSignal = true;
478 else if (def->type.name == "Q_SLOT")
481 if (!def->tag.isEmpty())
483 def->tag += def->type.name;
485 def->type = tempType;
486 tempType = parseType();
490 def->name = tempType.name;
491 scopedFunctionName = tempType.isScoped;
494 // we don't support references as return types, it's too dangerous
495 if (def->type.referenceType == Type::Reference)
496 def->type = Type("void");
498 def->normalizedType = normalizeType(def->type.name);
501 parseFunctionArguments(def);
505 def->isConst = test(CONST);
506 if (scopedFunctionName
507 && (def->isSignal || def->isSlot || def->isInvokable)) {
508 QByteArray msg("parsemaybe: Function declaration ");
510 msg += " contains extra qualification. Ignoring as signal or slot.";
511 warning(msg.constData());
520 QList<NamespaceDef> namespaceList;
521 bool templateClass = false;
527 if (test(IDENTIFIER)) {
529 // namespace Foo = Bar::Baz;
531 } else if (!test(SEMIC)) {
535 def.begin = index - 1;
538 index = def.begin + 1;
539 namespaceList += def;
547 templateClass = false;
550 templateClass = true;
552 case MOC_INCLUDE_BEGIN:
553 currentFilenames.push(symbol().unquotedLexem());
555 case MOC_INCLUDE_END:
556 currentFilenames.pop();
558 case Q_DECLARE_INTERFACE_TOKEN:
559 parseDeclareInterface();
561 case Q_DECLARE_METATYPE_TOKEN:
562 parseDeclareMetatype();
565 if (test(NAMESPACE)) {
566 while (test(SCOPE) || test(IDENTIFIER))
573 if (currentFilenames.size() <= 1)
577 if (!parseClassHead(&def))
580 while (inClass(&def) && hasNext()) {
581 if (next() == Q_OBJECT_TOKEN) {
582 def.hasQObject = true;
590 for (int i = namespaceList.size() - 1; i >= 0; --i)
591 if (inNamespace(&namespaceList.at(i)))
592 def.qualified.prepend(namespaceList.at(i).name + "::");
594 knownQObjectClasses.insert(def.classname);
595 knownQObjectClasses.insert(def.qualified);
600 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
603 if (parseClassHead(&def)) {
604 FunctionDef::Access access = FunctionDef::Private;
605 for (int i = namespaceList.size() - 1; i >= 0; --i)
606 if (inNamespace(&namespaceList.at(i)))
607 def.qualified.prepend(namespaceList.at(i).name + "::");
608 while (inClass(&def) && hasNext()) {
609 switch ((t = next())) {
611 access = FunctionDef::Private;
612 if (test(Q_SIGNALS_TOKEN))
613 error("Signals cannot have access specifier");
616 access = FunctionDef::Protected;
617 if (test(Q_SIGNALS_TOKEN))
618 error("Signals cannot have access specifier");
621 access = FunctionDef::Public;
622 if (test(Q_SIGNALS_TOKEN))
623 error("Signals cannot have access specifier");
627 if (parseClassHead(&nestedDef)) {
628 while (inClass(&nestedDef) && inClass(&def)) {
630 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
631 error("Meta object features not supported for nested classes");
635 case Q_SIGNALS_TOKEN:
639 switch (lookup(-1)) {
643 parseSlots(&def, access);
646 error("Missing access specifier for slots");
650 def.hasQObject = true;
652 error("Template classes not supported by Q_OBJECT");
653 if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
654 error("Class contains Q_OBJECT macro but does not inherit from QObject");
657 def.hasQGadget = true;
659 error("Template classes not supported by Q_GADGET");
661 case Q_PROPERTY_TOKEN:
665 parseEnumOrFlag(&def, false);
668 parseEnumOrFlag(&def, true);
670 case Q_DECLARE_FLAGS_TOKEN:
673 case Q_CLASSINFO_TOKEN:
674 parseClassInfo(&def);
676 case Q_INTERFACES_TOKEN:
677 parseInterfaces(&def);
679 case Q_PRIVATE_SLOT_TOKEN:
680 parseSlotInPrivate(&def, access);
682 case Q_PRIVATE_PROPERTY_TOKEN:
683 parsePrivateProperty(&def);
687 if (parseEnum(&enumDef))
688 def.enumList += enumDef;
695 funcDef.access = access;
696 int rewind = index--;
697 if (parseMaybeFunction(&def, &funcDef)) {
698 if (funcDef.isConstructor) {
699 if ((access == FunctionDef::Public) && funcDef.isInvokable) {
700 def.constructorList += funcDef;
701 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
702 funcDef.wasCloned = true;
703 funcDef.arguments.removeLast();
704 def.constructorList += funcDef;
707 } else if (funcDef.isDestructor) {
708 // don't care about destructors
710 if (access == FunctionDef::Public)
711 def.publicList += funcDef;
712 if (funcDef.isSlot) {
713 def.slotList += funcDef;
714 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
715 funcDef.wasCloned = true;
716 funcDef.arguments.removeLast();
717 def.slotList += funcDef;
719 if (funcDef.revision > 0)
720 ++def.revisionedMethods;
721 } else if (funcDef.isSignal) {
722 def.signalList += funcDef;
723 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
724 funcDef.wasCloned = true;
725 funcDef.arguments.removeLast();
726 def.signalList += funcDef;
728 if (funcDef.revision > 0)
729 ++def.revisionedMethods;
730 } else if (funcDef.isInvokable) {
731 def.methodList += funcDef;
732 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
733 funcDef.wasCloned = true;
734 funcDef.arguments.removeLast();
735 def.methodList += funcDef;
737 if (funcDef.revision > 0)
738 ++def.revisionedMethods;
749 if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
750 && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
751 continue; // no meta object code required
754 if (!def.hasQObject && !def.hasQGadget)
755 error("Class declarations lacks Q_OBJECT macro.");
757 checkSuperClasses(&def);
758 checkProperties(&def);
761 knownQObjectClasses.insert(def.classname);
762 knownQObjectClasses.insert(def.qualified);
767 void Moc::generate(FILE *out)
770 QDateTime dt = QDateTime::currentDateTime();
771 QByteArray dstr = dt.toString().toLatin1();
772 QByteArray fn = filename;
773 int i = filename.length()-1;
774 while (i>0 && filename[i-1] != '/' && filename[i-1] != '\\')
777 fn = filename.mid(i);
778 fprintf(out, "/****************************************************************************\n"
779 "** Meta object code from reading C++ file '%s'\n**\n" , (const char*)fn);
780 fprintf(out, "** Created: %s\n"
781 "** by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , dstr.data(), mocOutputRevision, QT_VERSION_STR);
782 fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
783 "*****************************************************************************/\n\n");
787 if (includePath.size() && !includePath.endsWith('/'))
789 for (int i = 0; i < includeFiles.size(); ++i) {
790 QByteArray inc = includeFiles.at(i);
791 if (inc[0] != '<' && inc[0] != '"') {
792 if (includePath.size() && includePath != "./")
793 inc.prepend(includePath);
794 inc = '\"' + inc + '\"';
796 fprintf(out, "#include %s\n", inc.constData());
799 if (classList.size() && classList.first().classname == "Qt")
800 fprintf(out, "#include <QtCore/qobject.h>\n");
802 if (mustIncludeQMetaTypeH)
803 fprintf(out, "#include <QtCore/qmetatype.h>\n");
805 fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
806 "#error \"The header file '%s' doesn't include <QObject>.\"\n", (const char *)fn);
807 fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
808 fprintf(out, "#error \"This file was generated using the moc from %s."
809 " It\"\n#error \"cannot be used with the include files from"
810 " this version of Qt.\"\n#error \"(The moc has changed too"
811 " much.)\"\n", QT_VERSION_STR);
812 fprintf(out, "#endif\n\n");
814 fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
816 for (i = 0; i < classList.size(); ++i) {
817 Generator generator(&classList[i], metaTypes, out);
818 generator.generateCode();
821 fprintf(out, "QT_END_MOC_NAMESPACE\n");
825 QList<QMetaObject*> Moc::generate(bool ignoreProperties)
827 QList<QMetaObject*> result;
828 for (int i = 0; i < classList.size(); ++i) {
829 Generator generator(&classList[i], metaTypes);
830 result << generator.generateMetaObject(ignoreProperties);
835 void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
837 int defaultRevision = -1;
838 if (test(Q_REVISION_TOKEN)) {
840 QByteArray revision = lexemUntil(RPAREN);
841 revision.remove(0, 1);
844 defaultRevision = revision.toInt(&ok);
845 if (!ok || defaultRevision < 0)
846 error("Invalid revision");
850 while (inClass(def) && hasNext()) {
855 case Q_SIGNALS_TOKEN:
865 error("'using' directive not supported in 'slots' section");
871 funcDef.access = access;
872 if (!parseFunction(&funcDef))
874 if (funcDef.revision > 0) {
875 ++def->revisionedMethods;
876 } else if (defaultRevision != -1) {
877 funcDef.revision = defaultRevision;
878 ++def->revisionedMethods;
880 def->slotList += funcDef;
881 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
882 funcDef.wasCloned = true;
883 funcDef.arguments.removeLast();
884 def->slotList += funcDef;
889 void Moc::parseSignals(ClassDef *def)
891 int defaultRevision = -1;
892 if (test(Q_REVISION_TOKEN)) {
894 QByteArray revision = lexemUntil(RPAREN);
895 revision.remove(0, 1);
898 defaultRevision = revision.toInt(&ok);
899 if (!ok || defaultRevision < 0)
900 error("Invalid revision");
904 while (inClass(def) && hasNext()) {
909 case Q_SIGNALS_TOKEN:
919 error("'using' directive not supported in 'signals' section");
924 funcDef.access = FunctionDef::Protected;
925 parseFunction(&funcDef);
926 if (funcDef.isVirtual)
927 warning("Signals cannot be declared virtual");
928 if (funcDef.inlineCode)
929 error("Not a signal declaration");
930 if (funcDef.revision > 0) {
931 ++def->revisionedMethods;
932 } else if (defaultRevision != -1) {
933 funcDef.revision = defaultRevision;
934 ++def->revisionedMethods;
936 def->signalList += funcDef;
937 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
938 funcDef.wasCloned = true;
939 funcDef.arguments.removeLast();
940 def->signalList += funcDef;
945 void Moc::createPropertyDef(PropertyDef &propDef)
947 QByteArray type = parseType().name;
950 propDef.designable = propDef.scriptable = propDef.stored = "true";
951 propDef.user = "false";
953 The Q_PROPERTY construct cannot contain any commas, since
954 commas separate macro arguments. We therefore expect users
955 to type "QMap" instead of "QMap<QString, QVariant>". For
956 coherence, we also expect the same for
957 QValueList<QVariant>, the other template class supported by
960 type = normalizeType(type);
962 type = "QMap<QString,QVariant>";
963 else if (type == "QValueList")
964 type = "QValueList<QVariant>";
965 else if (type == "LongLong")
967 else if (type == "ULongLong")
969 else if (type == "qreal")
970 mustIncludeQMetaTypeH = true;
975 propDef.name = lexem();
976 while (test(IDENTIFIER)) {
977 QByteArray l = lexem();
978 if (l[0] == 'C' && l == "CONSTANT") {
979 propDef.constant = true;
981 } else if(l[0] == 'F' && l == "FINAL") {
982 propDef.final = true;
988 v = lexemUntil(RPAREN);
989 } else if (test(INTEGER_LITERAL)) {
997 v2 = lexemUntil(RPAREN);
998 else if (v != "true" && v != "false")
1005 else if (l == "RESET")
1006 propDef.reset = v + v2;
1007 else if (l == "REVISION") {
1009 propDef.revision = v.toInt(&ok);
1010 if (!ok || propDef.revision < 0)
1016 if (l == "SCRIPTABLE")
1017 propDef.scriptable = v + v2;
1018 else if (l == "STORED")
1019 propDef.stored = v + v2;
1023 case 'W': if (l != "WRITE") error(2);
1026 case 'D': if (l != "DESIGNABLE") error(2);
1027 propDef.designable = v + v2;
1029 case 'E': if (l != "EDITABLE") error(2);
1030 propDef.editable = v + v2;
1032 case 'N': if (l != "NOTIFY") error(2);
1035 case 'U': if (l != "USER") error(2);
1036 propDef.user = v + v2;
1042 if (propDef.read.isNull()) {
1044 msg += "Property declaration ";
1045 msg += propDef.name;
1046 msg += " has no READ accessor function. The property will be invalid.";
1047 warning(msg.constData());
1049 if (propDef.constant && !propDef.write.isNull()) {
1051 msg += "Property declaration ";
1052 msg += propDef.name;
1053 msg += " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
1054 propDef.constant = false;
1055 warning(msg.constData());
1057 if (propDef.constant && !propDef.notify.isNull()) {
1059 msg += "Property declaration ";
1060 msg += propDef.name;
1061 msg += " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
1062 propDef.constant = false;
1063 warning(msg.constData());
1067 void Moc::parseProperty(ClassDef *def)
1070 PropertyDef propDef;
1071 createPropertyDef(propDef);
1075 if(!propDef.notify.isEmpty())
1076 def->notifyableProperties++;
1077 if (propDef.revision > 0)
1078 ++def->revisionedProperties;
1079 def->propertyList += propDef;
1082 void Moc::parsePrivateProperty(ClassDef *def)
1085 PropertyDef propDef;
1087 propDef.inPrivateClass = lexem();
1088 while (test(SCOPE)) {
1089 propDef.inPrivateClass += lexem();
1091 propDef.inPrivateClass += lexem();
1093 // also allow void functions
1096 propDef.inPrivateClass += "()";
1101 createPropertyDef(propDef);
1103 if(!propDef.notify.isEmpty())
1104 def->notifyableProperties++;
1105 if (propDef.revision > 0)
1106 ++def->revisionedProperties;
1108 def->propertyList += propDef;
1111 void Moc::parseEnumOrFlag(ClassDef *def, bool isFlag)
1114 QByteArray identifier;
1115 while (test(IDENTIFIER)) {
1116 identifier = lexem();
1117 while (test(SCOPE) && test(IDENTIFIER)) {
1119 identifier += lexem();
1121 def->enumDeclarations[identifier] = isFlag;
1126 void Moc::parseFlag(ClassDef *def)
1129 QByteArray flagName, enumName;
1130 while (test(IDENTIFIER)) {
1132 while (test(SCOPE) && test(IDENTIFIER)) {
1134 flagName += lexem();
1138 while (test(IDENTIFIER)) {
1140 while (test(SCOPE) && test(IDENTIFIER)) {
1142 enumName += lexem();
1146 def->flagAliases.insert(enumName, flagName);
1150 void Moc::parseClassInfo(ClassDef *def)
1153 ClassInfoDef infoDef;
1154 next(STRING_LITERAL);
1155 infoDef.name = symbol().unquotedLexem();
1157 if (test(STRING_LITERAL)) {
1158 infoDef.value = symbol().unquotedLexem();
1160 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1163 next(STRING_LITERAL);
1164 infoDef.value = symbol().unquotedLexem();
1168 def->classInfoList += infoDef;
1171 void Moc::parseInterfaces(ClassDef *def)
1174 while (test(IDENTIFIER)) {
1175 QList<ClassDef::Interface> iface;
1176 iface += ClassDef::Interface(lexem());
1177 while (test(SCOPE)) {
1178 iface.last().className += lexem();
1180 iface.last().className += lexem();
1182 while (test(COLON)) {
1184 iface += ClassDef::Interface(lexem());
1185 while (test(SCOPE)) {
1186 iface.last().className += lexem();
1188 iface.last().className += lexem();
1191 // resolve from classnames to interface ids
1192 for (int i = 0; i < iface.count(); ++i) {
1193 const QByteArray iid = interface2IdMap.value(iface.at(i).className);
1195 error("Undefined interface");
1197 iface[i].interfaceId = iid;
1199 def->interfaceList += iface;
1204 void Moc::parseDeclareInterface()
1207 QByteArray interface;
1209 interface += lexem();
1210 while (test(SCOPE)) {
1211 interface += lexem();
1213 interface += lexem();
1217 if (test(STRING_LITERAL)) {
1223 interface2IdMap.insert(interface, iid);
1227 void Moc::parseDeclareMetatype()
1230 QByteArray typeName = lexemUntil(RPAREN);
1231 typeName.remove(0, 1);
1233 metaTypes.append(typeName);
1236 void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1239 FunctionDef funcDef;
1241 funcDef.inPrivateClass = lexem();
1242 // also allow void functions
1245 funcDef.inPrivateClass += "()";
1248 funcDef.access = access;
1249 parseFunction(&funcDef, true);
1250 def->slotList += funcDef;
1251 while (funcDef.arguments.size() > 0 && funcDef.arguments.last().isDefault) {
1252 funcDef.wasCloned = true;
1253 funcDef.arguments.removeLast();
1254 def->slotList += funcDef;
1256 if (funcDef.revision > 0)
1257 ++def->revisionedMethods;
1261 QByteArray Moc::lexemUntil(Token target)
1266 while (from <= index) {
1267 QByteArray n = symbols.at(from++-1).lexem();
1268 if (s.size() && n.size()
1269 && is_ident_char(s.at(s.size()-1))
1270 && is_ident_char(n.at(0)))
1277 bool Moc::until(Token target) {
1283 switch(symbols.at(index-1).token) {
1284 case LBRACE: ++braceCount; break;
1285 case LBRACK: ++brackCount; break;
1286 case LPAREN: ++parenCount; break;
1287 case LANGLE: ++angleCount; break;
1292 //when searching commas within the default argument, we should take care of template depth (anglecount)
1293 // unfortunatelly, we do not have enough semantic information to know if '<' is the operator< or
1294 // the beginning of a template type. so we just use heuristics.
1297 while (index < symbols.size()) {
1298 Token t = symbols.at(index++).token;
1300 case LBRACE: ++braceCount; break;
1301 case RBRACE: --braceCount; break;
1302 case LBRACK: ++brackCount; break;
1303 case RBRACK: --brackCount; break;
1304 case LPAREN: ++parenCount; break;
1305 case RPAREN: --parenCount; break;
1306 case LANGLE: ++angleCount; break;
1307 case RANGLE: --angleCount; break;
1308 case GTGT: angleCount -= 2; t = RANGLE; break;
1315 && (target != RANGLE || angleCount <= 0)) {
1316 if (target != COMMA || angleCount <= 0)
1321 if (target == COMMA && t == EQ && possible != -1) {
1326 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1327 || (target == RANGLE && angleCount < 0)) {
1333 if(target == COMMA && angleCount != 0 && possible != -1) {
1341 void Moc::checkSuperClasses(ClassDef *def)
1343 const QByteArray firstSuperclass = def->superclassList.value(0).first;
1345 if (!knownQObjectClasses.contains(firstSuperclass)) {
1346 // enable once we /require/ include paths
1350 msg += def->className;
1351 msg += " contains the Q_OBJECT macro and inherits from ";
1352 msg += def->superclassList.value(0);
1353 msg += " but that is not a known QObject subclass. You may get compilation errors.";
1354 warning(msg.constData());
1358 for (int i = 1; i < def->superclassList.count(); ++i) {
1359 const QByteArray superClass = def->superclassList.at(i).first;
1360 if (knownQObjectClasses.contains(superClass)) {
1363 msg += def->classname;
1364 msg += " inherits from two QObject subclasses ";
1365 msg += firstSuperclass;
1368 msg += ". This is not supported!";
1369 warning(msg.constData());
1372 if (interface2IdMap.contains(superClass)) {
1373 bool registeredInterface = false;
1374 for (int i = 0; i < def->interfaceList.count(); ++i)
1375 if (def->interfaceList.at(i).first().className == superClass) {
1376 registeredInterface = true;
1380 if (!registeredInterface) {
1383 msg += def->classname;
1384 msg += " implements the interface ";
1386 msg += " but does not list it in Q_INTERFACES. qobject_cast to ";
1388 msg += " will not work!";
1389 warning(msg.constData());
1395 void Moc::checkProperties(ClassDef *cdef)
1398 // specify get function, for compatibiliy we accept functions
1399 // returning pointers, or const char * for QByteArray.
1401 for (int i = 0; i < cdef->propertyList.count(); ++i) {
1402 PropertyDef &p = cdef->propertyList[i];
1403 if (p.read.isEmpty())
1405 for (int j = 0; j < cdef->publicList.count(); ++j) {
1406 const FunctionDef &f = cdef->publicList.at(j);
1407 if (f.name != p.read)
1409 if (!f.isConst) // get functions must be const
1411 if (f.arguments.size()) // and must not take any arguments
1413 PropertyDef::Specification spec = PropertyDef::ValueSpec;
1414 QByteArray tmp = f.normalizedType;
1415 if (p.type == "QByteArray" && tmp == "const char *")
1417 if (tmp.left(6) == "const ")
1419 if (p.type != tmp && tmp.endsWith('*')) {
1421 spec = PropertyDef::PointerSpec;
1422 } else if (f.type.name.endsWith('&')) { // raw type, not normalized type
1423 spec = PropertyDef::ReferenceSpec;
1430 if(!p.notify.isEmpty()) {
1432 for (int j = 0; j < cdef->signalList.count(); ++j) {
1433 const FunctionDef &f = cdef->signalList.at(j);
1434 if(f.name != p.notify) {
1437 notifyId = j /* Signal indexes start from 0 */;
1441 p.notifyId = notifyId;
1442 if (notifyId == -1) {
1443 QByteArray msg = "NOTIFY signal '" + p.notify + "' of property '" + p.name
1444 + "' does not exist in class " + cdef->classname + ".";
1445 error(msg.constData());