diff options
author | Brett Stottlemyer <bstottle@ford.com> | 2019-12-26 19:40:37 -0500 |
---|---|---|
committer | Brett Stottlemyer <bstottle@ford.com> | 2021-09-07 12:26:30 -0400 |
commit | 045cbcbc50d51640142691c8cd78dd868e03ba1b (patch) | |
tree | d66f2e9c8b69e5b5b620bb05bc66232a9868e5e2 | |
parent | 73c2fe37405aac88664681163560d60a17000863 (diff) |
Allow PODs to include enums/flags
The goal is to allow a new syntax:
POD myPOD{
ENUM MyEnum {FOO, BAR}
MyEnum myEnum,
QString myString
}, in addition to the current syntax
POD myPOD(QString myString)
The challenge is that the parsing simplified the detection of parameter
types and parameter names by grabbing everything between the parentheses
and having a separate chunk of code split that up. The new POD syntax
requires the parser to detect and handle parameter types (including
templated types) and handle that syntax.
This converts the enum parsing to use new symbol and value tokens,
rather than the enum_param token, which is necessary to enable a more
generic type detection.
The pod2 type has comparable syntax to the previous version, but uses
brackets ('{' and '}') instead of parentheses for the definition.
While this code detects the parameter type and parameter name distinctly,
it currently merges all of the parameter content into a single string for
processing using the original postprocess method. This ensures the capture
is correct and there aren't any regressions.
The tests were modified for the new POD syntax.
The new code normalizes whitespace, rather than keeping the source
formatting.
Pick-to: 6.2
Change-Id: I671b23852fc2b515bcaea1ed389cfbde122683bd
Reviewed-by: Michael Brasser <michael.brasser@live.com>
-rw-r--r-- | src/remoteobjects/qremoteobjectpacket.cpp | 48 | ||||
-rw-r--r-- | src/remoteobjects/qtremoteobjectglobal.cpp | 23 | ||||
-rw-r--r-- | src/repparser/parser.g | 237 | ||||
-rw-r--r-- | tests/auto/proxy_multiprocess/shared.h | 4 | ||||
-rw-r--r-- | tests/auto/proxy_multiprocess/subclass.rep | 6 | ||||
-rw-r--r-- | tests/auto/repparser/tst_parser.cpp | 57 | ||||
-rw-r--r-- | tools/repc/repcodegenerator.cpp | 50 |
7 files changed, 371 insertions, 54 deletions
diff --git a/src/remoteobjects/qremoteobjectpacket.cpp b/src/remoteobjects/qremoteobjectpacket.cpp index 50f6d57..16a5e31 100644 --- a/src/remoteobjects/qremoteobjectpacket.cpp +++ b/src/remoteobjects/qremoteobjectpacket.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include <QtCore/qabstractitemmodel.h> +#include <QtCore/qbytearrayview.h> #include "qremoteobjectcontainers_p.h" #include "qremoteobjectpendingcall.h" @@ -441,16 +442,34 @@ static ObjectType getObjectType(const QString &typeName) return ObjectType::CLASS; } -// Same method as in QVariant.cpp, as it isn't publicly exposed... +static QByteArrayView resolveEnumName(QMetaType t, bool &isFlag) +{ + // Takes types like `MyPOD::Position` or `QFlags<MyPOD::Position>` and returns 'Position` + QByteArrayView enumName(t.name()); + isFlag = enumName.startsWith("QFlags<"); + auto lastColon = enumName.lastIndexOf(':'); + if (lastColon >= 0) + enumName = QByteArrayView(t.name() + lastColon + 1); + if (isFlag) + enumName.chop(1); + return enumName; +} + static QMetaEnum metaEnumFromType(QMetaType t) { - if (t.flags() & QMetaType::IsEnumeration) { - if (const QMetaObject *metaObject = t.metaObject()) { - const char *enumName = t.name(); - const char *lastColon = std::strrchr(enumName, ':'); - if (lastColon) - enumName = lastColon + 1; - return metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); + if (t.flags().testFlag(QMetaType::IsEnumeration)) { + if (const QMetaObject *m = t.metaObject()) { + bool isFlag; + auto enumName = resolveEnumName(t, isFlag); + if (isFlag) { + for (int i = m->enumeratorOffset(); i < m->enumeratorCount(); i++) { + auto testType = m->enumerator(i); + if (testType.isFlag() && + enumName.compare(QByteArrayView(testType.enumName())) == 0) + return testType; + } + } + return m->enumerator(m->indexOfEnumerator(enumName.data())); } } return QMetaEnum(); @@ -624,7 +643,7 @@ static void serializeGadgets(QDataStream &ds, const QSet<const QMetaObject *> &g int propertyCount = gadgets.contains(meta) ? meta->propertyCount() : 0; ds << quint32(propertyCount); #ifdef QTRO_VERBOSE_PROTOCOL - qDebug(" Gadget %d (name = %s, # properties = %d, # enums = %d):", i++, meta->className(), propertyCount, meta->enumeratorCount()); + qDebug(" Gadget %d (name = %s, # properties = %d, # enums = %d):", i++, meta->className(), propertyCount, meta->enumeratorCount() - meta->enumeratorOffset()); #endif for (int j = 0; j < propertyCount; j++) { auto prop = meta->property(j); @@ -634,9 +653,9 @@ static void serializeGadgets(QDataStream &ds, const QSet<const QMetaObject *> &g ds << QByteArray::fromRawData(prop.name(), qsizetype(qstrlen(prop.name()))); ds << QByteArray::fromRawData(prop.typeName(), qsizetype(qstrlen(prop.typeName()))); } - int enumCount = meta->enumeratorCount(); + int enumCount = meta->enumeratorCount() - meta->enumeratorOffset(); ds << quint32(enumCount); - for (int j = 0; j < enumCount; j++) { + for (int j = meta->enumeratorOffset(); j < meta->enumeratorCount(); j++) { auto const enumMeta = meta->enumerator(j); serializeEnum(ds, enumMeta); } @@ -915,7 +934,12 @@ QRO_::QRO_(const QVariant &value) out << QByteArray::fromRawData(property.name(), qsizetype(qstrlen(property.name()))); out << QByteArray::fromRawData(property.typeName(), qsizetype(qstrlen(property.typeName()))); } - out << quint32(0); // PODs have no enums + int enumCount = meta->enumeratorCount() - meta->enumeratorOffset(); + out << quint32(enumCount); + for (int j = meta->enumeratorOffset(); j < meta->enumeratorCount(); j++) { + auto const enumMeta = meta->enumerator(j); + serializeEnum(out, enumMeta); + } QDataStream ds(¶meters, QIODevice::WriteOnly); ds << value; #ifdef QTRO_VERBOSE_PROTOCOL diff --git a/src/remoteobjects/qtremoteobjectglobal.cpp b/src/remoteobjects/qtremoteobjectglobal.cpp index 8eca928..f83db9b 100644 --- a/src/remoteobjects/qtremoteobjectglobal.cpp +++ b/src/remoteobjects/qtremoteobjectglobal.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qtremoteobjectglobal.h" +#include "qremoteobjectpacket_p.h" #include <QtCore/qdatastream.h> #include <QtCore/qmetaobject.h> @@ -76,7 +77,6 @@ namespace QtRemoteObjects { void copyStoredProperties(const QMetaObject *mo, const void *src, void *dst) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) if (!src) { qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null source"; return; @@ -90,16 +90,10 @@ void copyStoredProperties(const QMetaObject *mo, const void *src, void *dst) const QMetaProperty mp = mo->property(i); mp.writeOnGadget(dst, mp.readOnGadget(src)); } -#else - Q_UNUSED(mo) - Q_UNUSED(src) - Q_UNUSED(dst) -#endif } void copyStoredProperties(const QMetaObject *mo, const void *src, QDataStream &dst) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) if (!src) { qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy from a null source"; return; @@ -107,18 +101,12 @@ void copyStoredProperties(const QMetaObject *mo, const void *src, QDataStream &d for (int i = 0, end = mo->propertyCount(); i != end; ++i) { const QMetaProperty mp = mo->property(i); - dst << mp.readOnGadget(src); + dst << QRemoteObjectPackets::encodeVariant(mp.readOnGadget(src)); } -#else - Q_UNUSED(mo) - Q_UNUSED(src) - Q_UNUSED(dst) -#endif } void copyStoredProperties(const QMetaObject *mo, QDataStream &src, void *dst) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) if (!dst) { qCWarning(QT_REMOTEOBJECT) << Q_FUNC_INFO << ": trying to copy to a null destination"; return; @@ -128,13 +116,8 @@ void copyStoredProperties(const QMetaObject *mo, QDataStream &src, void *dst) const QMetaProperty mp = mo->property(i); QVariant v; src >> v; - mp.writeOnGadget(dst, v); + mp.writeOnGadget(dst, QRemoteObjectPackets::decodeVariant(std::move(v), mp.metaType())); } -#else - Q_UNUSED(mo) - Q_UNUSED(src) - Q_UNUSED(dst) -#endif } } // namespace QtRemoteObjects diff --git a/src/repparser/parser.g b/src/repparser/parser.g index 48fe32a..77ed224 100644 --- a/src/repparser/parser.g +++ b/src/repparser/parser.g @@ -45,22 +45,29 @@ %token semicolon "[semicolon];" %token class "[class]class[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*" %token pod "[pod]POD[ \\t]*(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*\\((?<types>[^\\)]*)\\);?[ \\t]*" +%token pod2 "[pod2]POD[ \\t]*(?<name>[A-Za-z_][A-Za-z0-9_]+)[ \\t]*" %token flag "[flag][ \\t]*FLAG[ \t]*\\([ \t]*(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \t]+(?<enum>[A-Za-z_][A-Za-z0-9_]*)[ \t]*\\)[ \t]*" %token enum "[enum][ \\t]*ENUM[ \t]+(?:(?<class>class[ \t]+))?(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \t]*(?::[ \t]*(?<type>[a-zA-Z0-9 _:]*[a-zA-Z0-9_])[ \t]*)?" -%token enum_param "[enum_param][ \\t]*(?<name>[A-Za-z_][A-Za-z0-9_]*)[ \\t]*(=[ \\t]*(?<value>-\\d+|0[xX][0-9A-Fa-f]+|\\d+))?[ \\t]*" %token prop "[prop][ \\t]*PROP[ \\t]*\\((?<args>[^\\)]+)\\);?[ \\t]*" %token use_enum "[use_enum]USE_ENUM[ \\t]*\\((?<name>[^\\)]*)\\);?[ \\t]*" %token signal "[signal][ \\t]*SIGNAL[ \\t]*\\([ \\t]*(?<name>\\S+)[ \\t]*\\((?<args>[^\\)]*)\\)[ \\t]*\\);?[ \\t]*" %token slot "[slot][ \\t]*SLOT[ \\t]*\\((?<type>[^\\(]*)\\((?<args>[^\\)]*)\\)[ \\t]*\\);?[ \\t]*" %token model "[model][ \\t]*MODEL[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)\\((?<args>[^\\)]+)\\)[ \\t]*;?[ \\t]*" %token childrep "[childrep][ \\t]*CLASS[ \\t]+(?<name>[A-Za-z_][A-Za-z0-9_]+)\\((?<type>[^\\)]+)\\)[ \\t]*;?[ \\t]*" +%token qualifier "[qualifier][ \\t]*(?<value>const|unsigned|signed)[ \\t]*" +%token passbyqual "[passbyqual][ \\t]*(?<value>&)[ \\t]*" +%token symbol "[symbol][ \\t]*(?<symbol>[A-Za-z_][A-Za-z0-9_]*)[ \\t]*" +%token value "[value][ \\t]*(?<value>-\\d+|0[xX][0-9A-Fa-f]+|\\d+)[ \\t]*" %token start "[start][ \\t]*\\{[ \\t]*" %token stop "[stop][ \\t]*\\};?[ \\t]*" %token comma "[comma]," +%token equals "[equals]=" %token comment "[comment](?<comment>[ \\t]*//[^\\n]*\\n)" %token mcomment "[mcomment,M](?<comment>/\\*(.*?)\\*/)" %token preprocessor_directive "[preprocessor_directive](?<preprocessor_directive>#[ \\t]*[^\\n]*\\n)" %token newline "[newline](\\r)?\\n" +%token tstart "[tstart]<" +%token tstop "[tstop]>[ \\t]*" %start TopLevel @@ -262,6 +269,8 @@ struct POD : public SignedType void signature_impl(const AST &ast, QCryptographicHash &checksum) override; QList<PODAttribute> attributes; + QList<ASTEnum> enums; + QList<ASTFlag> flags; }; Q_DECLARE_TYPEINFO(POD, Q_RELOCATABLE_TYPE); @@ -314,6 +323,9 @@ private: AST m_ast; ASTClass m_astClass; + POD m_astPod; + QString m_symbol; + QString m_argString; ASTEnum m_astEnum; int m_astEnumValue; }; @@ -609,6 +621,8 @@ void RepParser::reset() { m_ast = AST(); m_astClass = ASTClass(); + m_astPod = POD(); + m_argString.clear(); m_astEnum = ASTEnum(); //setDebug(); } @@ -870,6 +884,7 @@ Comments: Comment | Comment Comments; Comment: comment | comment Newlines | mcomment | mcomment Newlines; Type: PreprocessorDirective | PreprocessorDirective Newlines; Type: Pod | Pod Newlines; +Type: Pod2; Type: Class; Type: UseEnum | UseEnum Newlines; Type: Comments | Comments Newlines; @@ -886,6 +901,8 @@ Type: Flag | Flag Newlines; Comma: comma | comma Newlines; +Equals: equals; + PreprocessorDirective: preprocessor_directive; /. case $rule_number: @@ -907,6 +924,10 @@ Pod: pod; qWarning() << "[repc] - Ignoring POD with no data members. POD name: " << qPrintable(pod.name); return true; } + if (argString.contains(QLatin1String("ENUM"))) { + setErrorString(QLatin1String("ENUMs are only available in PODs using bracket syntax ('{'), not parentheses")); + return false; + } RepParser::TypeParser parseType; parseType.parseArguments(argString); @@ -948,6 +969,126 @@ ClassType: Enum; break; ./ +Pod2: PodStart Start PodTypes Stop; +/. + case $rule_number: +./ +Pod2: PodStart Start Comments Stop; +/. + case $rule_number: +./ +Pod2: PodStart Start Stop; +/. + case $rule_number: + { + RepParser::TypeParser parseType; + parseType.parseArguments(m_argString); + parseType.appendPods(m_astPod); + m_astPod.generateSignature(m_ast); + m_ast.pods.append(m_astPod); + } + break; +./ + +PodTypes: PodType | PodType Newlines | PodType CaptureComma PodTypes | PodType PodTypes | PodType Newlines PodTypes; +PodType: DecoratedPODFlag | Comments; +PodType: Enum; +/. + case $rule_number: + { + m_astEnum.generateSignature(m_ast); + m_astPod.enums.append(m_astEnum); + } + break; +./ + +PodType: Parameter; +Parameter: DecoratedParameterType ParameterName; + +DecoratedParameterType: ParameterType | Qualified ParameterType | ParameterType PassByReference | Qualified ParameterType PassByReference; + +ParameterType: SimpleType | TemplateType; + +TemplateType: TemplateTypename TStart ParameterTypes TStop; + +TemplateTypename: Symbol; +/. + case $rule_number: + { + m_argString += m_symbol; + } + break; +./ + +TStart: tstart; +/. + case $rule_number: + { + m_argString += QLatin1Char('<'); + } + break; +./ + +TStop: tstop; +/. + case $rule_number: + { + m_argString += QLatin1Char('>'); + } + break; +./ + +Qualified: qualifier; +/. + case $rule_number: + { + m_argString += captured().value(QLatin1String("value")).trimmed() + QLatin1Char(' '); + } + break; +./ + +PassByReference: passbyqual; +/. + case $rule_number: + { + m_argString += QLatin1Char(' ') + captured().value(QLatin1String("value")).trimmed(); + } + break; +./ + +ParameterTypes: DecoratedParameterType | DecoratedParameterType CaptureComma ParameterTypes; + +CaptureComma: comma; +/. + case $rule_number: +./ +CaptureComma: comma Newlines; +/. + case $rule_number: + { + m_argString += QLatin1Char(','); + } + break; +./ + +SimpleType: Symbol; +/. + case $rule_number: + { + m_argString += m_symbol; + } + break; +./ + +ParameterName: Symbol; +/. + case $rule_number: + { + m_argString += QLatin1Char(' ') + m_symbol; + } + break; +./ + DecoratedSlot: Slot | Comments Slot | Slot Newlines | Comments Slot Newlines; DecoratedSignal: Signal | Comments Signal | Signal Newlines | Comments Signal Newlines; DecoratedProp: Prop | Comments Prop | Prop Newlines | Comments Prop Newlines; @@ -956,6 +1097,8 @@ DecoratedClass: ChildRep | Comments ChildRep | ChildRep Newlines | Comments Chil DecoratedEnumParam: EnumParam | Comments EnumParam | EnumParam Newlines | Comments EnumParam Newlines; DecoratedClassFlag: ClassFlag | Comments ClassFlag | ClassFlag Newlines | Comments ClassFlag Newlines; +DecoratedPODFlag: PODFlag | Comments PODFlag | PODFlag Newlines | Comments PODFlag Newlines; + Start: start | Comments start | start Newlines | Comments start Newlines; Stop: stop | stop Newlines; @@ -983,21 +1126,27 @@ EnumStart: enum; EnumParams: DecoratedEnumParam | DecoratedEnumParam Comma EnumParams; -EnumParam: enum_param; +EnumParam: Symbol; /. case $rule_number: { ASTEnumParam param; - param.name = captured().value(QStringLiteral("name")).trimmed(); - QString value = captured().value(QStringLiteral("value")); - value.remove(QLatin1Char('=')); - value = value.trimmed(); - if (value.isEmpty()) - param.value = ++m_astEnumValue; - else if (value.startsWith(QLatin1String("0x"), Qt::CaseInsensitive)) - param.value = m_astEnumValue = value.toInt(0,16); - else - param.value = m_astEnumValue = value.toInt(); + param.name = m_symbol; + param.value = ++m_astEnumValue; + if (m_astEnum.max < param.value) + m_astEnum.max = param.value; + m_astEnum.params << param; + } + break; +./ + +EnumParam: Symbol Equals Value; +/. + case $rule_number: + { + ASTEnumParam param; + param.name = m_symbol; + param.value = m_astEnumValue; if (param.value < 0) { m_astEnum.isSigned = true; if (m_astEnum.max < -param.value) @@ -1009,6 +1158,28 @@ EnumParam: enum_param; break; ./ +Symbol: symbol; +/. + case $rule_number: + { + m_symbol = captured().value(QStringLiteral("symbol")).trimmed(); + } + break; +./ + +Value: value; +/. + case $rule_number: + { + QString value = captured().value(QStringLiteral("value")).trimmed(); + if (value.startsWith(QLatin1String("0x"), Qt::CaseInsensitive)) + m_astEnumValue = value.toInt(0,16); + else + m_astEnumValue = value.toInt(); + } + break; +./ + ClassFlag: flag; /. case $rule_number: @@ -1035,6 +1206,32 @@ ClassFlag: flag; break; ./ +PODFlag: flag; +/. + case $rule_number: + { + const QString name = captured().value(QLatin1String("name")); + const QString _enum = captured().value(QLatin1String("enum")); + int enumIndex = 0; + for (auto &en : m_astPod.enums) { + if (en.name == _enum) { + en.flagIndex = m_astPod.flags.count(); + break; + } + enumIndex++; + } + if (enumIndex == m_astPod.enums.count()) { + setErrorString(QLatin1String("FLAG: Unknown (pod) enum: %1").arg(_enum)); + return false; + } + auto flag = ASTFlag(name, _enum); + flag.scope = m_astPod.name; + flag.generateSignature(m_ast); + m_astPod.flags.append(flag); + } + break; +./ + Prop: prop; /. case $rule_number: @@ -1137,6 +1334,22 @@ ClassStart: class; break; ./ +PodStart: pod2; +/. + case $rule_number: +./ +PodStart: pod2 Newlines; +/. + case $rule_number: + { + // new POD declaration + m_astPod = POD(); + m_astPod.name = captured().value(QLatin1String("name")).trimmed(); + m_argString.clear(); + } + break; +./ + UseEnum: use_enum; /. case $rule_number: diff --git a/tests/auto/proxy_multiprocess/shared.h b/tests/auto/proxy_multiprocess/shared.h index c3bd763..e0a0a73 100644 --- a/tests/auto/proxy_multiprocess/shared.h +++ b/tests/auto/proxy_multiprocess/shared.h @@ -1,5 +1,5 @@ -const MyPOD initialValue(42, 3.14, QStringLiteral("SubClass")); -const MyPOD updatedValue(-1, 123.456, QStringLiteral("Updated")); +const MyPOD initialValue(MyPOD::Position::position1, 3.14, QStringLiteral("SubClass")); +const MyPOD updatedValue(MyPOD::Position::position2 | MyPOD::Position::position3, 123.456, QStringLiteral("Updated")); const VariantPOD podValue(10, 11); const int initialI = 100; const int updatedI = 200; diff --git a/tests/auto/proxy_multiprocess/subclass.rep b/tests/auto/proxy_multiprocess/subclass.rep index ae756e2..5fb03d3 100644 --- a/tests/auto/proxy_multiprocess/subclass.rep +++ b/tests/auto/proxy_multiprocess/subclass.rep @@ -5,7 +5,11 @@ USE_ENUM(Qt::DateFormat) USE_ENUM(NS::NamespaceEnum) USE_ENUM(NS2::NamespaceEnum) -POD MyPOD(int i, float f, QString s) +POD MyPOD{ + ENUM class Position : unsigned short {position1=1, position2=2, position3=4} + FLAG (ActivePositions Position) + ActivePositions positions, float f, QString s +} POD VariantPOD(int i, int j) class SubClass diff --git a/tests/auto/repparser/tst_parser.cpp b/tests/auto/repparser/tst_parser.cpp index 5d2e818..14b93dd 100644 --- a/tests/auto/repparser/tst_parser.cpp +++ b/tests/auto/repparser/tst_parser.cpp @@ -49,6 +49,8 @@ private Q_SLOTS: void testSignals(); void testPods_data(); void testPods(); + void testPods2_data(); + void testPods2(); void testEnums_data(); void testEnums(); void testTypedEnums_data(); @@ -321,6 +323,61 @@ void tst_Parser::testPods() } } +void tst_Parser::testPods2_data() +{ + QTest::addColumn<QString>("podsdeclaration"); + QTest::addColumn<QString>("expectedtypes"); + QTest::addColumn<QString>("expectedvariables"); + + //Variable/Type separate by ";" + QTest::newRow("one pod") << "POD preset{int presetNumber}" << "int" << "presetNumber"; + QTest::newRow("two pod") << "POD preset{int presetNumber, double foo}" << "int;double" << "presetNumber;foo"; + QTest::newRow("two pod with space") << "POD preset { int presetNumber , double foo } " << "int;double" << "presetNumber;foo"; + QTest::newRow("two pod multiline") << "POD preset{\nint presetNumber,\ndouble foo\n}" << "int;double" << "presetNumber;foo"; + //Template + QTest::newRow("pod template") << "POD preset{QMap<QString,int> foo} " << "QMap<QString,int>" << "foo"; + QTest::newRow("pod template (QList)") << "POD preset{QList<QString> foo} " << "QList<QString>" << "foo"; + QTest::newRow("two pod template") << "POD preset{QMap<QString,int> foo, QMap<double,int> bla} " << "QMap<QString,int>;QMap<double,int>" << "foo;bla"; + QTest::newRow("two pod template with space") << "POD preset{ QMap<QString , int > foo , QMap< double , int > bla } " << "QMap<QString,int>;QMap<double,int>" << "foo;bla"; + //Enum + QTest::newRow("enum multiline") << "POD preset{ENUM val {val1 = 1,\nval2,\nval3=12}\nval value,\ndouble foo\n}" << "val;double" << "value;foo"; + +} + +void tst_Parser::testPods2() +{ + QFETCH(QString, podsdeclaration); + QFETCH(QString, expectedtypes); + QFETCH(QString, expectedvariables); + + QTemporaryFile file; + file.open(); + QTextStream stream(&file); + stream << podsdeclaration << Qt::endl; + stream << "class TestClass" << Qt::endl; + stream << "{" << Qt::endl; + stream << "};" << Qt::endl; + file.seek(0); + + RepParser parser(file); + QVERIFY(parser.parse()); + + const AST ast = parser.ast(); + QCOMPARE(ast.classes.count(), 1); + + QCOMPARE(ast.pods.count(), 1); + const POD pods = ast.pods.first(); + const QVector<PODAttribute> podsList = pods.attributes; + const QStringList typeList = expectedtypes.split(QLatin1Char(';')); + const QStringList variableList = expectedvariables.split(QLatin1Char(';')); + QVERIFY(typeList.count() == variableList.count()); + QVERIFY(podsList.count() == variableList.count()); + for (int i=0; i < podsList.count(); ++i) { + QCOMPARE(podsList.at(i).name, variableList.at(i)); + QCOMPARE(podsList.at(i).type, typeList.at(i)); + } +} + void tst_Parser::testEnums_data() { QTest::addColumn<QString>("enumdeclaration"); diff --git a/tools/repc/repcodegenerator.cpp b/tools/repc/repcodegenerator.cpp index 71be53a..175ac43 100644 --- a/tools/repc/repcodegenerator.cpp +++ b/tools/repc/repcodegenerator.cpp @@ -84,6 +84,17 @@ static bool hasScopedEnum(const ASTClass &classContext) return false; } +static bool hasScopedEnum(const POD &pod) +{ + for (const ASTEnum &astEnum : pod.enums) { + if (astEnum.isScoped) { + return true; + } + } + + return false; +} + static QString fullyQualifiedName(const ASTClass& classContext, const QString &className, const QString &typeName) { @@ -156,10 +167,26 @@ void RepCodeGenerator::generate(Mode mode, QString fileName) generatePOD(pod); QSet<QString> metaTypes; + QSet<QString> enumTypes; for (const POD &pod : m_ast.pods) { metaTypes << pod.name; - for (const PODAttribute &attribute : pod.attributes) - metaTypes << attribute.type; + // We register from within the code generated for classes, not PODs + // Thus, for enums/flags in PODs, we need the to prefix with the POD + // name. The enumTypes set is used to make sure we don't try to + // register the non-prefixed name if it is used as a member variable + // type. + for (const ASTEnum &en : pod.enums) { + metaTypes << QLatin1String("%1::%2").arg(pod.name, en.name); + enumTypes << en.name; + } + for (const ASTFlag &flag : pod.flags) { + metaTypes << QLatin1String("%1::%2").arg(pod.name, flag.name); + enumTypes << flag.name; + } + for (const PODAttribute &attribute : pod.attributes) { + if (!enumTypes.contains(attribute.type)) + metaTypes << attribute.type; + } } const QString metaTypeRegistrationCode = generateMetaTypeRegistration(metaTypes); @@ -409,9 +436,16 @@ void RepCodeGenerator::generatePOD(const POD &pod) "{\n" " Q_GADGET\n" << "\n" - << formatQPropertyDeclarations(pod) - << "public:\n" - << formatConstructors(pod) + << formatQPropertyDeclarations(pod); + if (hasScopedEnum(pod)) // See https://bugreports.qt.io/browse/QTBUG-73360 + m_stream << " Q_CLASSINFO(\"RegisterEnumClassesUnscoped\", \"false\")\n"; + m_stream << "public:\n"; + generateDeclarationsForEnums(pod.enums); + for (auto &flag : pod.flags) { + m_stream << " Q_DECLARE_FLAGS(" << flag.name << ", " << flag._enum << ")\n"; + m_stream << " Q_FLAG(" << flag.name << ")\n"; + } + m_stream << formatConstructors(pod) << formatPropertyGettersAndSetters(pod) << "private:\n" << formatDataMembers(pod) @@ -427,8 +461,10 @@ void RepCodeGenerator::generatePOD(const POD &pod) << "}\n" << "\n" << formatDebugOperator(pod) - << formatMarshallingOperators(pod) - << "\n\n"; + << formatMarshallingOperators(pod); + for (auto &flag : pod.flags) + m_stream << "Q_DECLARE_OPERATORS_FOR_FLAGS(" << pod.name << "::" << flag.name << ")\n"; + m_stream << "\n"; } QString getEnumType(const ASTEnum &en) |