1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
43 #include "generator.h"
44 #include "qdatetime.h"
46 #include "outputrevision.h"
47 #include <QtCore/qfile.h>
48 #include <QtCore/qfileinfo.h>
49 #include <QtCore/qdir.h>
51 // for normalizeTypeInternal
52 #include <private/qmetaobject_moc_p.h>
56 // only moc needs this function
57 static QByteArray normalizeType(const QByteArray &ba, bool fixScope = false)
59 const char *s = ba.constData();
62 char *buf = (len >= 64 ? new char[len + 1] : stackbuf);
65 while(*s && is_space(*s))
68 while (*s && !is_space(*s))
70 while (*s && is_space(*s))
72 if (*s && ((is_ident_char(*s) && is_ident_char(last))
73 || ((*s == ':') && (last == '<')))) {
78 QByteArray result = normalizeTypeInternal(buf, d, fixScope);
84 bool Moc::parseClassHead(ClassDef *def)
86 // figure out whether this is a class declaration, or only a
87 // forward or variable declaration.
92 if (token == COLON || token == LBRACE)
94 if (token == SEMIC || token == RANGLE)
98 if (!test(IDENTIFIER)) // typedef struct { ... }
100 QByteArray name = lexem();
102 // support "class IDENT name" and "class IDENT(IDENT) name"
105 if (!test(IDENTIFIER))
108 } else if (test(IDENTIFIER)) {
112 def->qualified += name;
113 while (test(SCOPE)) {
114 def->qualified += lexem();
115 if (test(IDENTIFIER)) {
117 def->qualified += name;
120 def->classname = name;
124 FunctionDef::Access access = FunctionDef::Public;
126 access = FunctionDef::Private;
127 else if (test(PROTECTED))
128 access = FunctionDef::Protected;
132 const QByteArray type = parseType().name;
133 // ignore the 'class Foo : BAR(Baz)' case
137 def->superclassList += qMakePair(type, access);
139 } while (test(COMMA));
143 def->begin = index - 1;
144 bool foundRBrace = until(RBRACE);
146 index = def->begin + 1;
150 Type Moc::parseType()
153 bool hasSignedOrUnsigned = false;
155 type.firstToken = lookup();
160 hasSignedOrUnsigned = true;
164 type.name += lexem();
166 if (lookup(0) == VOLATILE)
167 type.isVolatile = true;
169 case Q_MOC_COMPAT_TOKEN:
170 case Q_INVOKABLE_TOKEN:
171 case Q_SCRIPTABLE_TOKEN:
172 case Q_SIGNALS_TOKEN:
176 type.name += lexem();
184 test(ENUM) || test(CLASS) || test(STRUCT);
188 // void mySlot(unsigned myArg)
189 if (hasSignedOrUnsigned) {
197 type.name += lexem();
198 // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
199 if (test(LONG) || test(INT) || test(DOUBLE)) {
209 type.name += lexem();
210 isVoid |= (lookup(0) == VOID);
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) == '>')) {
227 type.name += lexem();
228 type.isScoped = true;
233 while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED)
234 || test(STAR) || test(AND) || test(ANDAND)) {
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;
244 type.rawName = type.name;
245 // transform stupid things like 'const void' or 'void const' into 'void'
246 if (isVoid && type.referenceType == Type::NoReference) {
252 bool Moc::parseEnum(EnumDef *def)
254 bool isTypdefEnum = false; // typedef enum { ... } Foo;
257 def->isEnumClass = true;
259 if (test(IDENTIFIER)) {
262 if (lookup(-1) != TYPEDEF)
263 return false; // anonymous enum
266 if (test(COLON)) { // C++11 strongly typed enum
267 // enum Foo : unsigned long { ... };
268 parseType(); //ignore the result
273 if (lookup() == RBRACE) // accept trailing comma
276 def->values += lexem();
277 } while (test(EQ) ? until(COMMA) : test(COMMA));
280 if (!test(IDENTIFIER))
287 void Moc::parseFunctionArguments(FunctionDef *def)
292 arg.type = parseType();
293 if (arg.type.name == "void")
295 if (test(IDENTIFIER))
297 while (test(LBRACK)) {
298 arg.rightType += lexemUntil(RBRACK);
300 if (test(CONST) || test(VOLATILE)) {
301 arg.rightType += ' ';
302 arg.rightType += lexem();
304 arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType));
305 arg.typeNameForCast = normalizeType(QByteArray(noRef(arg.type.name) + "(*)" + arg.rightType));
307 arg.isDefault = true;
308 def->arguments += arg;
314 bool Moc::testFunctionAttribute(FunctionDef *def)
316 if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) {
323 bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
326 case Q_MOC_COMPAT_TOKEN:
327 def->isCompat = true;
329 case Q_INVOKABLE_TOKEN:
330 def->isInvokable = true;
333 def->isSignal = true;
338 case Q_SCRIPTABLE_TOKEN:
339 def->isInvokable = def->isScriptable = true;
346 bool Moc::testFunctionRevision(FunctionDef *def)
348 if (test(Q_REVISION_TOKEN)) {
350 QByteArray revision = lexemUntil(RPAREN);
351 revision.remove(0, 1);
354 def->revision = revision.toInt(&ok);
355 if (!ok || def->revision < 0)
356 error("Invalid revision");
363 // returns false if the function should be ignored
364 bool Moc::parseFunction(FunctionDef *def, bool inMacro)
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");
380 bool scopedFunctionName = false;
382 def->name = def->type.name;
383 scopedFunctionName = def->type.isScoped;
384 def->type = Type("int");
386 Type tempType = parseType();;
387 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
388 if (testFunctionAttribute(def->type.firstToken, def))
390 else if (def->type.firstToken == Q_SIGNALS_TOKEN)
392 else if (def->type.firstToken == Q_SLOTS_TOKEN)
395 if (!def->tag.isEmpty())
397 def->tag += def->type.name;
399 def->type = tempType;
400 tempType = parseType();
402 next(LPAREN, "Not a signal or slot declaration");
403 def->name = tempType.name;
404 scopedFunctionName = tempType.isScoped;
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;
414 def->normalizedType = normalizeType(def->type.name);
417 parseFunctionArguments(def);
421 // support optional macros with compiler specific options
422 while (test(IDENTIFIER))
425 def->isConst = test(CONST);
427 while (test(IDENTIFIER))
440 else if ((def->inlineCode = test(LBRACE)))
442 else if ((def->isAbstract = test(EQ)))
448 if (scopedFunctionName) {
449 QByteArray msg("Function declaration ");
451 msg += " contains extra qualification. Ignoring as signal or slot.";
452 warning(msg.constData());
458 // like parseFunction, but never aborts with an error
459 bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
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())
471 bool scopedFunctionName = false;
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;
480 def->type = Type("int");
483 Type tempType = parseType();;
484 while (!tempType.name.isEmpty() && lookup() != LPAREN) {
485 if (testFunctionAttribute(def->type.firstToken, def))
487 else if (def->type.name == "Q_SIGNAL")
488 def->isSignal = true;
489 else if (def->type.name == "Q_SLOT")
492 if (!def->tag.isEmpty())
494 def->tag += def->type.name;
496 def->type = tempType;
497 tempType = parseType();
501 def->name = tempType.name;
502 scopedFunctionName = tempType.isScoped;
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;
512 def->normalizedType = normalizeType(def->type.name);
515 parseFunctionArguments(def);
519 def->isConst = test(CONST);
520 if (scopedFunctionName
521 && (def->isSignal || def->isSlot || def->isInvokable)) {
522 QByteArray msg("parsemaybe: Function declaration ");
524 msg += " contains extra qualification. Ignoring as signal or slot.";
525 warning(msg.constData());
534 QList<NamespaceDef> namespaceList;
535 bool templateClass = false;
541 if (test(IDENTIFIER)) {
543 // namespace Foo = Bar::Baz;
545 } else if (!test(SEMIC)) {
549 def.begin = index - 1;
552 index = def.begin + 1;
553 namespaceList += def;
561 templateClass = false;
564 templateClass = true;
566 case MOC_INCLUDE_BEGIN:
567 currentFilenames.push(symbol().unquotedLexem());
569 case MOC_INCLUDE_END:
570 currentFilenames.pop();
572 case Q_DECLARE_INTERFACE_TOKEN:
573 parseDeclareInterface();
575 case Q_DECLARE_METATYPE_TOKEN:
576 parseDeclareMetatype();
579 if (test(NAMESPACE)) {
580 while (test(SCOPE) || test(IDENTIFIER))
587 if (currentFilenames.size() <= 1)
591 if (!parseClassHead(&def))
594 while (inClass(&def) && hasNext()) {
595 if (next() == Q_OBJECT_TOKEN) {
596 def.hasQObject = true;
604 for (int i = namespaceList.size() - 1; i >= 0; --i)
605 if (inNamespace(&namespaceList.at(i)))
606 def.qualified.prepend(namespaceList.at(i).name + "::");
608 knownQObjectClasses.insert(def.classname);
609 knownQObjectClasses.insert(def.qualified);
614 if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
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())) {
625 access = FunctionDef::Private;
626 if (test(Q_SIGNALS_TOKEN))
627 error("Signals cannot have access specifier");
630 access = FunctionDef::Protected;
631 if (test(Q_SIGNALS_TOKEN))
632 error("Signals cannot have access specifier");
635 access = FunctionDef::Public;
636 if (test(Q_SIGNALS_TOKEN))
637 error("Signals cannot have access specifier");
641 if (parseClassHead(&nestedDef)) {
642 while (inClass(&nestedDef) && inClass(&def)) {
644 if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
645 error("Meta object features not supported for nested classes");
649 case Q_SIGNALS_TOKEN:
653 switch (lookup(-1)) {
657 parseSlots(&def, access);
660 error("Missing access specifier for slots");
664 def.hasQObject = true;
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");
671 def.hasQGadget = true;
673 error("Template classes not supported by Q_GADGET");
675 case Q_PROPERTY_TOKEN:
678 case Q_PLUGIN_METADATA_TOKEN:
679 parsePluginData(&def);
682 parseEnumOrFlag(&def, false);
685 parseEnumOrFlag(&def, true);
687 case Q_DECLARE_FLAGS_TOKEN:
690 case Q_CLASSINFO_TOKEN:
691 parseClassInfo(&def);
693 case Q_INTERFACES_TOKEN:
694 parseInterfaces(&def);
696 case Q_PRIVATE_SLOT_TOKEN:
697 parseSlotInPrivate(&def, access);
699 case Q_PRIVATE_PROPERTY_TOKEN:
700 parsePrivateProperty(&def);
704 if (parseEnum(&enumDef))
705 def.enumList += enumDef;
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;
724 } else if (funcDef.isDestructor) {
725 // don't care about destructors
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;
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;
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;
754 if (funcDef.revision > 0)
755 ++def.revisionedMethods;
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
771 if (!def.hasQObject && !def.hasQGadget)
772 error("Class declarations lacks Q_OBJECT macro.");
774 checkSuperClasses(&def);
775 checkProperties(&def);
778 knownQObjectClasses.insert(def.classname);
779 knownQObjectClasses.insert(def.qualified);
784 void Moc::generate(FILE *out)
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] != '\\')
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");
804 if (includePath.size() && !includePath.endsWith('/'))
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 + '\"';
813 fprintf(out, "#include %s\n", inc.constData());
816 if (classList.size() && classList.first().classname == "Qt")
817 fprintf(out, "#include <QtCore/qobject.h>\n");
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");
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");
833 fprintf(out, "QT_BEGIN_MOC_NAMESPACE\n");
835 for (i = 0; i < classList.size(); ++i) {
836 Generator generator(&classList[i], metaTypes, out);
837 generator.generateCode();
840 fprintf(out, "QT_END_MOC_NAMESPACE\n");
843 void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
845 int defaultRevision = -1;
846 if (test(Q_REVISION_TOKEN)) {
848 QByteArray revision = lexemUntil(RPAREN);
849 revision.remove(0, 1);
852 defaultRevision = revision.toInt(&ok);
853 if (!ok || defaultRevision < 0)
854 error("Invalid revision");
858 while (inClass(def) && hasNext()) {
863 case Q_SIGNALS_TOKEN:
873 error("'using' directive not supported in 'slots' section");
879 funcDef.access = access;
880 if (!parseFunction(&funcDef))
882 if (funcDef.revision > 0) {
883 ++def->revisionedMethods;
884 } else if (defaultRevision != -1) {
885 funcDef.revision = defaultRevision;
886 ++def->revisionedMethods;
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;
897 void Moc::parseSignals(ClassDef *def)
899 int defaultRevision = -1;
900 if (test(Q_REVISION_TOKEN)) {
902 QByteArray revision = lexemUntil(RPAREN);
903 revision.remove(0, 1);
906 defaultRevision = revision.toInt(&ok);
907 if (!ok || defaultRevision < 0)
908 error("Invalid revision");
912 while (inClass(def) && hasNext()) {
917 case Q_SIGNALS_TOKEN:
927 error("'using' directive not supported in 'signals' section");
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;
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;
953 void Moc::createPropertyDef(PropertyDef &propDef)
955 QByteArray type = parseType().name;
958 propDef.designable = propDef.scriptable = propDef.stored = "true";
959 propDef.user = "false";
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
968 type = normalizeType(type);
970 type = "QMap<QString,QVariant>";
971 else if (type == "QValueList")
972 type = "QValueList<QVariant>";
973 else if (type == "LongLong")
975 else if (type == "ULongLong")
981 propDef.name = lexem();
982 while (test(IDENTIFIER)) {
983 QByteArray l = lexem();
984 if (l[0] == 'C' && l == "CONSTANT") {
985 propDef.constant = true;
987 } else if(l[0] == 'F' && l == "FINAL") {
988 propDef.final = true;
994 v = lexemUntil(RPAREN);
995 } else if (test(INTEGER_LITERAL)) {
1003 v2 = lexemUntil(RPAREN);
1004 else if (v != "true" && v != "false")
1011 else if (l == "RESET")
1012 propDef.reset = v + v2;
1013 else if (l == "REVISION") {
1015 propDef.revision = v.toInt(&ok);
1016 if (!ok || propDef.revision < 0)
1022 if (l == "SCRIPTABLE")
1023 propDef.scriptable = v + v2;
1024 else if (l == "STORED")
1025 propDef.stored = v + v2;
1029 case 'W': if (l != "WRITE") error(2);
1032 case 'D': if (l != "DESIGNABLE") error(2);
1033 propDef.designable = v + v2;
1035 case 'E': if (l != "EDITABLE") error(2);
1036 propDef.editable = v + v2;
1038 case 'N': if (l != "NOTIFY") error(2);
1041 case 'U': if (l != "USER") error(2);
1042 propDef.user = v + v2;
1048 if (propDef.read.isNull()) {
1050 msg += "Property declaration ";
1051 msg += propDef.name;
1052 msg += " has no READ accessor function. The property will be invalid.";
1053 warning(msg.constData());
1055 if (propDef.constant && !propDef.write.isNull()) {
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());
1063 if (propDef.constant && !propDef.notify.isNull()) {
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());
1073 void Moc::parseProperty(ClassDef *def)
1076 PropertyDef propDef;
1077 createPropertyDef(propDef);
1081 if(!propDef.notify.isEmpty())
1082 def->notifyableProperties++;
1083 if (propDef.revision > 0)
1084 ++def->revisionedProperties;
1085 def->propertyList += propDef;
1088 void Moc::parsePluginData(ClassDef *def)
1091 QByteArray metaData;
1092 while (test(IDENTIFIER)) {
1093 QByteArray l = lexem();
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()));
1103 msg += "Plugin Metadata file ";
1105 msg += " does not exist. Declaration will be ignored";
1106 warning(msg.constData());
1109 QFile file(fi.canonicalFilePath());
1110 file.open(QFile::ReadOnly);
1111 metaData = file.readAll();
1115 if (!metaData.isEmpty()) {
1116 def->pluginData.metaData = QJsonDocument::fromJson(metaData);
1117 if (!def->pluginData.metaData.isObject()) {
1119 msg += "Plugin Metadata file ";
1121 msg += " does not contain a valid JSON object. Declaration will be ignored";
1122 warning(msg.constData());
1123 def->pluginData.iid = QByteArray();
1128 mustIncludeQPluginH = true;
1132 void Moc::parsePrivateProperty(ClassDef *def)
1135 PropertyDef propDef;
1137 propDef.inPrivateClass = lexem();
1138 while (test(SCOPE)) {
1139 propDef.inPrivateClass += lexem();
1141 propDef.inPrivateClass += lexem();
1143 // also allow void functions
1146 propDef.inPrivateClass += "()";
1151 createPropertyDef(propDef);
1153 if(!propDef.notify.isEmpty())
1154 def->notifyableProperties++;
1155 if (propDef.revision > 0)
1156 ++def->revisionedProperties;
1158 def->propertyList += propDef;
1161 void Moc::parseEnumOrFlag(ClassDef *def, bool isFlag)
1164 QByteArray identifier;
1165 while (test(IDENTIFIER)) {
1166 identifier = lexem();
1167 while (test(SCOPE) && test(IDENTIFIER)) {
1169 identifier += lexem();
1171 def->enumDeclarations[identifier] = isFlag;
1176 void Moc::parseFlag(ClassDef *def)
1179 QByteArray flagName, enumName;
1180 while (test(IDENTIFIER)) {
1182 while (test(SCOPE) && test(IDENTIFIER)) {
1184 flagName += lexem();
1188 while (test(IDENTIFIER)) {
1190 while (test(SCOPE) && test(IDENTIFIER)) {
1192 enumName += lexem();
1196 def->flagAliases.insert(enumName, flagName);
1200 void Moc::parseClassInfo(ClassDef *def)
1203 ClassInfoDef infoDef;
1204 next(STRING_LITERAL);
1205 infoDef.name = symbol().unquotedLexem();
1207 if (test(STRING_LITERAL)) {
1208 infoDef.value = symbol().unquotedLexem();
1210 // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
1213 next(STRING_LITERAL);
1214 infoDef.value = symbol().unquotedLexem();
1218 def->classInfoList += infoDef;
1221 void Moc::parseInterfaces(ClassDef *def)
1224 while (test(IDENTIFIER)) {
1225 QList<ClassDef::Interface> iface;
1226 iface += ClassDef::Interface(lexem());
1227 while (test(SCOPE)) {
1228 iface.last().className += lexem();
1230 iface.last().className += lexem();
1232 while (test(COLON)) {
1234 iface += ClassDef::Interface(lexem());
1235 while (test(SCOPE)) {
1236 iface.last().className += lexem();
1238 iface.last().className += lexem();
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);
1245 error("Undefined interface");
1247 iface[i].interfaceId = iid;
1249 def->interfaceList += iface;
1254 void Moc::parseDeclareInterface()
1257 QByteArray interface;
1259 interface += lexem();
1260 while (test(SCOPE)) {
1261 interface += lexem();
1263 interface += lexem();
1267 if (test(STRING_LITERAL)) {
1273 interface2IdMap.insert(interface, iid);
1277 void Moc::parseDeclareMetatype()
1280 QByteArray typeName = lexemUntil(RPAREN);
1281 typeName.remove(0, 1);
1283 metaTypes.append(typeName);
1286 void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
1289 FunctionDef funcDef;
1291 funcDef.inPrivateClass = lexem();
1292 // also allow void functions
1295 funcDef.inPrivateClass += "()";
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;
1306 if (funcDef.revision > 0)
1307 ++def->revisionedMethods;
1311 QByteArray Moc::lexemUntil(Token target)
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)))
1327 bool Moc::until(Token target) {
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;
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.
1347 while (index < symbols.size()) {
1348 Token t = symbols.at(index++).token;
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;
1365 && (target != RANGLE || angleCount <= 0)) {
1366 if (target != COMMA || angleCount <= 0)
1371 if (target == COMMA && t == EQ && possible != -1) {
1376 if (braceCount < 0 || brackCount < 0 || parenCount < 0
1377 || (target == RANGLE && angleCount < 0)) {
1383 if(target == COMMA && angleCount != 0 && possible != -1) {
1391 void Moc::checkSuperClasses(ClassDef *def)
1393 const QByteArray firstSuperclass = def->superclassList.value(0).first;
1395 if (!knownQObjectClasses.contains(firstSuperclass)) {
1396 // enable once we /require/ include paths
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());
1408 for (int i = 1; i < def->superclassList.count(); ++i) {
1409 const QByteArray superClass = def->superclassList.at(i).first;
1410 if (knownQObjectClasses.contains(superClass)) {
1413 msg += def->classname;
1414 msg += " inherits from two QObject subclasses ";
1415 msg += firstSuperclass;
1418 msg += ". This is not supported!";
1419 warning(msg.constData());
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;
1430 if (!registeredInterface) {
1433 msg += def->classname;
1434 msg += " implements the interface ";
1436 msg += " but does not list it in Q_INTERFACES. qobject_cast to ";
1438 msg += " will not work!";
1439 warning(msg.constData());
1445 void Moc::checkProperties(ClassDef *cdef)
1448 // specify get function, for compatibiliy we accept functions
1449 // returning pointers, or const char * for QByteArray.
1451 for (int i = 0; i < cdef->propertyList.count(); ++i) {
1452 PropertyDef &p = cdef->propertyList[i];
1453 if (p.read.isEmpty())
1455 for (int j = 0; j < cdef->publicList.count(); ++j) {
1456 const FunctionDef &f = cdef->publicList.at(j);
1457 if (f.name != p.read)
1459 if (!f.isConst) // get functions must be const
1461 if (f.arguments.size()) // and must not take any arguments
1463 PropertyDef::Specification spec = PropertyDef::ValueSpec;
1464 QByteArray tmp = f.normalizedType;
1465 if (p.type == "QByteArray" && tmp == "const char *")
1467 if (tmp.left(6) == "const ")
1469 if (p.type != tmp && tmp.endsWith('*')) {
1471 spec = PropertyDef::PointerSpec;
1472 } else if (f.type.name.endsWith('&')) { // raw type, not normalized type
1473 spec = PropertyDef::ReferenceSpec;
1480 if(!p.notify.isEmpty()) {
1482 for (int j = 0; j < cdef->signalList.count(); ++j) {
1483 const FunctionDef &f = cdef->signalList.at(j);
1484 if(f.name != p.notify) {
1487 notifyId = j /* Signal indexes start from 0 */;
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());