diff options
author | Fabian Kosmale <[email protected]> | 2025-06-06 11:21:05 +0200 |
---|---|---|
committer | Fabian Kosmale <[email protected]> | 2025-06-16 20:10:26 +0200 |
commit | dea21545b32cf04337ca470483991dc7da39ba5b (patch) | |
tree | b022cac9b21b767ab93b4a20cae3573e1e448d3e | |
parent | c8c64e75806c52766e3bbd809d3ed4b852a7f17f (diff) |
moc: handle enum / member name conflict
It is possible (and somewhat common on Windows) to have getters and
types with the same name. This so far would cause issues in the moc
generated code.
To fix it, we remember all names of enums found in a class, and prefix
references to such types with "enum". Note that this requires a new set
to track them, as other parts of moc currently treat `eunm Foo { Val }`
and `typedef enum { Val } Foo` the same, but `enum Foo` is only valid
C++ for the former.
A similar issue would also exist with inner structs, but that seems to
be less common, so isn't implemented yet.
We also use the opportunity to drop the typeNameForCast member in
ArgumentDef, as we can't do the enum disambiguation with it easily.
Instead, we do it on-demand, which should also give a beneficial
memory/runtime trade-off in any case.
Fixes: QTBUG-137452
Pick-to: 6.10 6.9
Change-Id: I07341f971c9ca65edecbea890ebc33e007087c43
Reviewed-by: Joerg Bornemann <[email protected]>
Reviewed-by: Ulf Hermann <[email protected]>
-rw-r--r-- | src/tools/moc/generator.cpp | 44 | ||||
-rw-r--r-- | src/tools/moc/generator.h | 2 | ||||
-rw-r--r-- | src/tools/moc/moc.cpp | 11 | ||||
-rw-r--r-- | src/tools/moc/moc.h | 4 | ||||
-rw-r--r-- | tests/auto/tools/moc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/tools/moc/allmocs_baseline_in.json | 53 | ||||
-rw-r--r-- | tests/auto/tools/moc/name_collision.h | 33 | ||||
-rw-r--r-- | tests/auto/tools/moc/tst_moc.cpp | 3 |
8 files changed, 129 insertions, 22 deletions
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index b60b65d5fb5..1c3e529e531 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -628,11 +628,11 @@ void Generator::addFunctions(const QList<FunctionDef> &list, const char *functyp if (f.isConstructor) fprintf(out, "Constructor("); else - fprintf(out, "%s(", f.type.name.constData()); // return type + fprintf(out, "%s(", disambiguatedTypeName(f.type.name).constData()); // return type const char *comma = ""; for (const auto &argument : f.arguments) { - fprintf(out, "%s%s", comma, argument.type.name.constData()); + fprintf(out, "%s%s", comma, disambiguatedTypeName(argument.type.name).constData()); comma = ", "; } @@ -725,7 +725,7 @@ void Generator::addProperties() for (const PropertyDef &p : std::as_const(cdef->propertyList)) { fprintf(out, " // property '%s'\n" " QtMocHelpers::PropertyData<%s%s>(%d, ", - p.name.constData(), cxxTypeTag(p.typeTag), p.type.constData(), stridx(p.name)); + p.name.constData(), cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData(), stridx(p.name)); generateTypeInfo(p.type); fputc(',', out); @@ -810,7 +810,7 @@ void Generator::addEnums() fprintf(out, " // %s '%s'\n" " QtMocHelpers::EnumData<%s>(%d, %d,", e.flags & EnumIsFlag ? "flag" : "enum", e.name.constData(), - e.name.constData(), stridx(e.name), stridx(typeName)); + disambiguatedTypeName(e.name).constData(), stridx(e.name), stridx(typeName)); if (e.flags) { const char *separator = ""; @@ -959,7 +959,7 @@ void Generator::generateStaticMetacall() if (it != begin) fprintf(out, ","); fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))", - a.typeNameForCast.constData(), offset++); + disambiguatedTypeNameForCast(a.normalizedType).constData(), offset++); } }; @@ -1006,7 +1006,7 @@ void Generator::generateStaticMetacall() Q_ASSERT(!f.normalizedType.isEmpty()); fprintf(out, " case %d: ", methodindex); if (f.normalizedType != "void") - fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); + fprintf(out, "{ %s _r = ", disambiguatedTypeName(noRef(f.normalizedType)).constData()); fprintf(out, "_t->"); if (f.inPrivateClass.size()) fprintf(out, "%s->", f.inPrivateClass.constData()); @@ -1023,7 +1023,7 @@ void Generator::generateStaticMetacall() const ArgumentDef &a = *it; if (it != begin) fprintf(out, ","); - fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++); + fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", disambiguatedTypeNameForCast(a.normalizedType).constData(), offset++); usedArgs |= UsedA; } if (f.isPrivateSignal) { @@ -1035,7 +1035,7 @@ void Generator::generateStaticMetacall() fprintf(out, ");"); if (f.normalizedType != "void") { fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = std::move(_r); } ", - noRef(f.normalizedType).constData()); + disambiguatedTypeName(noRef(f.normalizedType)).constData()); usedArgs |= UsedA; } fprintf(out, " break;\n"); @@ -1169,19 +1169,19 @@ void Generator::generateStaticMetacall() #if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0) else if (auto eflags = cdef->enumDeclarations.value(p.type); eflags & EnumIsFlag) fprintf(out, " case %d: QtMocHelpers::assignFlags<%s>(_v, %s%s()); break;\n", - propindex, p.type.constData(), prefix.constData(), p.read.constData()); + propindex, disambiguatedTypeName(p.type).constData(), prefix.constData(), p.read.constData()); #endif else if (p.read == "default") fprintf(out, " case %d: *reinterpret_cast<%s%s*>(_v) = %s%s().value(); break;\n", - propindex, cxxTypeTag(p.typeTag), p.type.constData(), + propindex, cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData(), prefix.constData(), p.bind.constData()); else if (!p.read.isEmpty()) fprintf(out, " case %d: *reinterpret_cast<%s%s*>(_v) = %s%s(); break;\n", - propindex, cxxTypeTag(p.typeTag), p.type.constData(), + propindex, cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData(), prefix.constData(), p.read.constData()); else fprintf(out, " case %d: *reinterpret_cast<%s%s*>(_v) = %s%s; break;\n", - propindex, cxxTypeTag(p.typeTag), p.type.constData(), + propindex, cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData(), prefix.constData(), p.member.constData()); } fprintf(out, " default: break;\n"); @@ -1206,21 +1206,21 @@ void Generator::generateStaticMetacall() if (p.write == "default") { fprintf(out, " case %d: {\n", propindex); fprintf(out, " %s%s().setValue(*reinterpret_cast<%s%s*>(_v));\n", - prefix.constData(), p.bind.constData(), cxxTypeTag(p.typeTag), p.type.constData()); + prefix.constData(), p.bind.constData(), cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData()); fprintf(out, " break;\n"); fprintf(out, " }\n"); } else if (!p.write.isEmpty()) { fprintf(out, " case %d: %s%s(*reinterpret_cast<%s%s*>(_v)); break;\n", propindex, prefix.constData(), p.write.constData(), - cxxTypeTag(p.typeTag), p.type.constData()); + cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData()); } else { fprintf(out, " case %d:", propindex); if (p.notify.isEmpty()) { fprintf(out, " QtMocHelpers::setProperty(%s%s, *reinterpret_cast<%s%s*>(_v)); break;\n", - prefix.constData(), p.member.constData(), cxxTypeTag(p.typeTag), p.type.constData()); + prefix.constData(), p.member.constData(), cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData()); } else { fprintf(out, "\n if (QtMocHelpers::setProperty(%s%s, *reinterpret_cast<%s%s*>(_v)))\n", - prefix.constData(), p.member.constData(), cxxTypeTag(p.typeTag), p.type.constData()); + prefix.constData(), p.member.constData(), cxxTypeTag(p.typeTag), disambiguatedTypeName(p.type).constData()); fprintf(out, " Q_EMIT _t->%s(", p.notify.constData()); if (p.notifyId > -1) { const FunctionDef &f = cdef->signalList.at(p.notifyId); @@ -1483,6 +1483,18 @@ void Generator::generatePluginMetaData() fputs("\n", out); } +QByteArray Generator::disambiguatedTypeName(const QByteArray &name) +{ + if (cdef->allEnumNames.contains(name)) + return "enum " + name; + return name; +} + +QByteArray Generator::disambiguatedTypeNameForCast(const QByteArray &name) +{ + return QByteArray("std::add_pointer_t<"+ disambiguatedTypeName(name) +">"); +} + QT_WARNING_DISABLE_GCC("-Wunused-function") QT_WARNING_DISABLE_CLANG("-Wunused-function") QT_WARNING_DISABLE_CLANG("-Wundefined-internal") diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index 0ee2ad23919..a9d65764a2b 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -40,6 +40,8 @@ private: void generateStaticMetacall(); void generateSignal(const FunctionDef *def, int index); void generatePluginMetaData(); + QByteArray disambiguatedTypeName(const QByteArray &name); + QByteArray disambiguatedTypeNameForCast(const QByteArray &name); QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper(); QMap<int, QMultiMap<QByteArray, int>> methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index da0096ebfe1..14280712154 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -243,7 +243,7 @@ enum class IncludeState { NoInclude, }; -bool Moc::parseEnum(EnumDef *def) +bool Moc::parseEnum(EnumDef *def, ClassDef *containingClass) { bool isTypdefEnum = false; // typedef enum { ... } Foo; @@ -252,6 +252,8 @@ bool Moc::parseEnum(EnumDef *def) if (test(IDENTIFIER)) { def->name = lexem(); + if (containingClass) + containingClass->allEnumNames.insert(def->name); } else { if (lookup(-1) != TYPEDEF) return false; // anonymous enum @@ -294,6 +296,8 @@ bool Moc::parseEnum(EnumDef *def) if (!test(IDENTIFIER)) return false; def->name = lexem(); + // used as the name for our enum, but we don't track it, + // because we only care about types that might conflict with members } return true; } @@ -316,7 +320,6 @@ void Moc::parseFunctionArguments(FunctionDef *def) arg.rightType += lexem(); } arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType)); - arg.typeNameForCast = QByteArray("std::add_pointer_t<"+arg.normalizedType+">"); if (test(EQ)) arg.isDefault = true; def->arguments += arg; @@ -772,7 +775,7 @@ void Moc::parse() break; case ENUM: { EnumDef enumDef; - if (parseEnum(&enumDef)) + if (parseEnum(&enumDef, nullptr)) def.enumList += enumDef; } break; case CLASS: @@ -979,7 +982,7 @@ void Moc::parse() break; case ENUM: { EnumDef enumDef; - if (parseEnum(&enumDef)) + if (parseEnum(&enumDef, &def)) def.enumList += enumDef; } break; case SEMIC: diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index 6ee48b83df1..f08edb3f0d2 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -66,7 +66,6 @@ struct ArgumentDef ArgumentDef() : isDefault(false) {} Type type; QByteArray rightType, normalizedType, name; - QByteArray typeNameForCast; // type name to be used in cast from void * in metacall bool isDefault; QJsonObject toJson() const; @@ -198,6 +197,7 @@ struct ClassDef : BaseDef { QList<FunctionDef> signalList, slotList, methodList, publicList; QList<QByteArray> nonClassSignalList; QList<PropertyDef> propertyList; + QSet<QByteArray> allEnumNames; int revisionedMethods = 0; bool hasQObject = false; @@ -260,7 +260,7 @@ public: Type parseType(); - bool parseEnum(EnumDef *def); + bool parseEnum(EnumDef *def, ClassDef *containingClass); bool parseFunction(FunctionDef *def, bool inMacro = false); bool parseMaybeFunction(const ClassDef *cdef, FunctionDef *def); diff --git a/tests/auto/tools/moc/CMakeLists.txt b/tests/auto/tools/moc/CMakeLists.txt index 11dbc52411e..c8344dad081 100644 --- a/tests/auto/tools/moc/CMakeLists.txt +++ b/tests/auto/tools/moc/CMakeLists.txt @@ -28,6 +28,7 @@ set(JSON_HEADERS gadgetwithnoenums.h grand-parent-gadget-class.h moc_include.h + name_collision.h namespace.h namespaced-flags.h namespaced-base-class.h diff --git a/tests/auto/tools/moc/allmocs_baseline_in.json b/tests/auto/tools/moc/allmocs_baseline_in.json index c3425c6d15f..4ad1d2b53c5 100644 --- a/tests/auto/tools/moc/allmocs_baseline_in.json +++ b/tests/auto/tools/moc/allmocs_baseline_in.json @@ -1380,6 +1380,59 @@ { "classes": [ { + "className": "NameCollision", + "lineNumber": 11, + "object": true, + "properties": [ + { + "constant": false, + "designable": true, + "final": false, + "index": 0, + "name": "Status", + "read": "Status", + "required": false, + "scriptable": true, + "stored": true, + "type": "Status", + "user": false, + "write": "setStatus" + } + ], + "qualifiedClassName": "myns::NameCollision", + "slots": [ + { + "access": "public", + "arguments": [ + { + "type": "Status" + } + ], + "index": 0, + "name": "setStatus", + "returnType": "void" + }, + { + "access": "public", + "index": 1, + "name": "Status", + "returnType": "Status" + } + ], + "superClasses": [ + { + "access": "public", + "name": "QObject" + } + ] + } + ], + "inputFile": "name_collision.h", + "outputRevision": 69 + }, + { + "classes": [ + { "className": "FooNamespace", "enums": [ { diff --git a/tests/auto/tools/moc/name_collision.h b/tests/auto/tools/moc/name_collision.h new file mode 100644 index 00000000000..ad67a3cba71 --- /dev/null +++ b/tests/auto/tools/moc/name_collision.h @@ -0,0 +1,33 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef NAME_COLLISION_H +#define NAME_COLLISION_H + +#include <QObject> + +namespace myns { + +class NameCollision : public QObject +{ + Q_OBJECT + + // intentionally not fully qualified + Q_PROPERTY(Status Status READ Status WRITE setStatus) + + int m_status = 0; + +public: + enum Status {}; + + void statusChanged(Status status); + +public Q_SLOTS: + void setStatus(Status ) {} + Status Status() { return {}; } +}; + +} + + +#endif // TECH_PREVIEW_H diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 9c345093854..9a428e59f06 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -67,6 +67,8 @@ #include "tech-preview.h" +#include "name_collision.h" + using namespace Qt::StringLiterals; #ifdef Q_MOC_RUN @@ -987,6 +989,7 @@ void tst_Moc::initTestCase() QVERIFY(QmlMacro::staticMetaObject.className()); QVERIFY(SignalWithDefaultArg::staticMetaObject.className()); QVERIFY(TestPointeeCanBeIncomplete::staticMetaObject.className()); + QVERIFY(myns::NameCollision::staticMetaObject.className()); } void tst_Moc::hasIncludeSupport() |