Moc: fix generated code for nested enum class corner case

Fixes an issue with generated code where the name of an enclosing
namespace is identical to an enum class type, when Q_ENUM_NS is used.

Consider:
namespace a {
    Q_NAMESPACE
    namespace b {
        enum class b { Key, Key2 };
        Q_ENUM_NS(b);
    }
}

moc generated code such as:
Q_CONSTINIT const QMetaObject a:🅱️:staticMetaObject = { {
    ...
    qt_incomplete_metaTypeArray<qt_meta_stringdata_CLASSaSCOPEbENDCLASS_t,
        // enum 'TestEnum'
        QtPrivate::TypeAndForceComplete<b::b, std::true_type>,
        // Q_OBJECT / Q_GADGET
        QtPrivate::TypeAndForceComplete<void, std::true_type>
    >,
    nullptr
} };

which confused the compiler:
error: ‘b’ is not a member of ‘a:🅱️:b
   83 |         QtPrivate::TypeAndForceComplete<b::b, std::true_type>,

Fixes: QTBUG-112996
Pick-to: 6.6
Change-Id: I37aee83c32efe96cc9d6c2bd0bdb9ba80bb7b8a7
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ahmad Samir 2023-06-07 16:34:45 +03:00
parent e603661c48
commit 7779400ba6
4 changed files with 65 additions and 2 deletions

View File

@ -525,8 +525,7 @@ void Generator::generateCode()
// metatypes for enums
for (const EnumDef &e : std::as_const(cdef->enumList)) {
fprintf(out, "%s\n // enum '%s'\n %s",
comma, e.name.constData(),
stringForType(cdef->classname % "::" % e.name, true).constData());
comma, e.name.constData(), stringForType(e.qualifiedType(cdef), true).constData());
comma = ",";
}

View File

@ -2187,4 +2187,23 @@ QJsonObject EnumDef::toJson(const ClassDef &cdef) const
return def;
}
QByteArray EnumDef::qualifiedType(const ClassDef *cdef) const
{
if (name == cdef->classname) {
// The name of the enclosing namespace is the same as the enum class name
if (cdef->qualified.contains("::")) {
// QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum
// class name and enclosing namespace, e.g.:
// namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } }
return cdef->qualified % "::" % name;
} else {
// Just "B"; otherwise the compiler complains about the type "B::B" inside
// "B::staticMetaObject" in the generated code; e.g.:
// namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) }
return name;
}
}
return cdef->classname % "::" % name;
}
QT_END_NAMESPACE

View File

@ -48,6 +48,7 @@ struct EnumDef
bool isEnumClass; // c++11 enum class
EnumDef() : isEnumClass(false) {}
QJsonObject toJson(const ClassDef &cdef) const;
QByteArray qualifiedType(const ClassDef *cdef) const;
};
Q_DECLARE_TYPEINFO(EnumDef, Q_RELOCATABLE_TYPE);

View File

@ -203,6 +203,27 @@ namespace TestQNamespace {
Q_FLAG_NS(TestFlag2)
}
namespace TestSameEnumNamespace {
Q_NAMESPACE
enum class TestSameEnumNamespace {
Key1 = 1,
Key2 = 2,
};
Q_ENUM_NS(TestSameEnumNamespace)
}
namespace TestNestedSameEnumNamespace {
namespace a {
Q_NAMESPACE
// enum class with the same name as the enclosing nested namespace
enum class a {
Key11 = 11,
Key12 = 12,
};
Q_ENUM_NS(a)
}
}
namespace TestExportNamespace {
Q_NAMESPACE_EXPORT(TESTEXPORTMACRO)
@ -789,6 +810,7 @@ private slots:
void optionsFileError_data();
void optionsFileError();
void testQNamespace();
void testNestedQNamespace();
void cxx17Namespaces();
void cxxAttributes();
void mocJsonOutput();
@ -4081,6 +4103,28 @@ public:
FooNamespace::Enum1 prop() { return FooNamespace::Enum1::Key2; }
};
void tst_Moc::testNestedQNamespace()
{
QCOMPARE(TestSameEnumNamespace::staticMetaObject.enumeratorCount(), 1);
checkEnum(TestSameEnumNamespace::staticMetaObject.enumerator(0), "TestSameEnumNamespace",
{{"Key1", 1}, {"Key2", 2}});
QMetaEnum meta1 = QMetaEnum::fromType<TestSameEnumNamespace::TestSameEnumNamespace>();
QVERIFY(meta1.isValid());
QCOMPARE(meta1.name(), "TestSameEnumNamespace");
QCOMPARE(meta1.enclosingMetaObject(), &TestSameEnumNamespace::staticMetaObject);
QCOMPARE(meta1.keyCount(), 2);
// QTBUG-112996
QCOMPARE(TestNestedSameEnumNamespace::a::staticMetaObject.enumeratorCount(), 1);
checkEnum(TestNestedSameEnumNamespace::a::staticMetaObject.enumerator(0), "a",
{{"Key11", 11}, {"Key12", 12}});
QMetaEnum meta2 = QMetaEnum::fromType<TestNestedSameEnumNamespace::a::a>();
QVERIFY(meta2.isValid());
QCOMPARE(meta2.name(), "a");
QCOMPARE(meta2.enclosingMetaObject(), &TestNestedSameEnumNamespace::a::staticMetaObject);
QCOMPARE(meta2.keyCount(), 2);
}
void tst_Moc::testQNamespace()
{
QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 5);