summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <[email protected]>2025-06-06 11:21:05 +0200
committerFabian Kosmale <[email protected]>2025-06-16 20:10:26 +0200
commitdea21545b32cf04337ca470483991dc7da39ba5b (patch)
treeb022cac9b21b767ab93b4a20cae3573e1e448d3e
parentc8c64e75806c52766e3bbd809d3ed4b852a7f17f (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.cpp44
-rw-r--r--src/tools/moc/generator.h2
-rw-r--r--src/tools/moc/moc.cpp11
-rw-r--r--src/tools/moc/moc.h4
-rw-r--r--tests/auto/tools/moc/CMakeLists.txt1
-rw-r--r--tests/auto/tools/moc/allmocs_baseline_in.json53
-rw-r--r--tests/auto/tools/moc/name_collision.h33
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp3
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()