aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-11-30 18:39:25 +0100
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2020-12-07 12:31:15 +0000
commited87e710dd14bc9612dcfdba2e09143201768556 (patch)
tree0c7dd4b49e4a891c57d514fca39642abfaad91f6
parenteb16797b9a54970dbc0c461ebeddda57b060af22 (diff)
shiboken6: Add support for a snake case typesystem attribute
Add a snake case attribute to type system, complex type entry, function type entry as well as to function and field modifications. Add a function definitionNames() to AbstractMetaFunction/Field returning the names under which the function/field will be registered. Change the code writing the registration accordingly. Fixes: PYSIDE-1441 Change-Id: I178390bb80fa25aad9f8a56e99e4cc70064178eb Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp49
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.h6
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafield.cpp23
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafield.h5
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.cpp46
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.h5
-rw-r--r--sources/shiboken6/ApiExtractor/modifications.cpp24
-rw-r--r--sources/shiboken6/ApiExtractor/modifications.h6
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp22
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h2
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem.cpp64
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem.h13
-rw-r--r--sources/shiboken6/ApiExtractor/typesystem_enums.h7
-rw-r--r--sources/shiboken6/ApiExtractor/typesystemparser.cpp45
-rw-r--r--sources/shiboken6/doc/typesystem_manipulating_objects.rst10
-rw-r--r--sources/shiboken6/doc/typesystem_specifying_types.rst36
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.cpp58
-rw-r--r--sources/shiboken6/generator/shiboken/cppgenerator.h5
-rw-r--r--sources/shiboken6/tests/libsample/CMakeLists.txt1
-rw-r--r--sources/shiboken6/tests/libsample/snakecasetest.cpp69
-rw-r--r--sources/shiboken6/tests/libsample/snakecasetest.h65
-rw-r--r--sources/shiboken6/tests/samplebinding/CMakeLists.txt2
-rw-r--r--sources/shiboken6/tests/samplebinding/global.h1
-rw-r--r--sources/shiboken6/tests/samplebinding/snakecase_test.py86
-rw-r--r--sources/shiboken6/tests/samplebinding/typesystem_sample.xml8
25 files changed, 625 insertions, 33 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
index a3c2d3731..41e42ac2a 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
@@ -451,13 +451,11 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom)
if (!funcEntry->hasSignature(metaFunc->minimalSignature()))
continue;
+ metaFunc->setTypeEntry(funcEntry);
applyFunctionModifications(metaFunc);
setInclude(funcEntry, func->fileName());
- if (metaFunc->typeEntry())
- delete metaFunc->typeEntry();
- metaFunc->setTypeEntry(funcEntry);
m_globalFunctions << metaFuncPtr;
}
@@ -1368,6 +1366,51 @@ void AbstractMetaBuilderPrivate::fillAddedFunctions(AbstractMetaClass *metaClass
}
}
+QString AbstractMetaBuilder::getSnakeCaseName(const QString &name)
+{
+ const int size = name.size();
+ if (size < 3)
+ return name;
+ QString result;
+ result.reserve(size + 4);
+ for (int i = 0; i < size; ++i) {
+ const QChar c = name.at(i);
+ if (c.isUpper()) {
+ if (i > 0) {
+ if (name.at(i - 1).isUpper())
+ return name; // Give up at consecutive upper chars
+ result.append(u'_');
+ }
+ result.append(c.toLower());
+ } else {
+ result.append(c);
+ }
+ }
+ return result;
+}
+
+// Names under which an item will be registered to Python depending on snakeCase
+QStringList AbstractMetaBuilder::definitionNames(const QString &name,
+ TypeSystem::SnakeCase snakeCase)
+{
+ QStringList result;
+ switch (snakeCase) {
+ case TypeSystem::SnakeCase::Unspecified:
+ case TypeSystem::SnakeCase::Disabled:
+ result.append(name);
+ break;
+ case TypeSystem::SnakeCase::Enabled:
+ result.append(AbstractMetaBuilder::getSnakeCaseName(name));
+ break;
+ case TypeSystem::SnakeCase::Both:
+ result.append(AbstractMetaBuilder::getSnakeCaseName(name));
+ if (name != result.constFirst())
+ result.append(name);
+ break;
+ }
+ return result;
+}
+
void AbstractMetaBuilderPrivate::applyFunctionModifications(AbstractMetaFunction *func)
{
AbstractMetaFunction& funcRef = *func;
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.h b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h
index cd9f36ca9..5b0414cd0 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.h
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.h
@@ -32,6 +32,7 @@
#include "abstractmetalang_typedefs.h"
#include "header_paths.h"
#include "dependency.h"
+#include "typesystem_enums.h"
#include "clangparser/compilersupport.h"
@@ -106,6 +107,11 @@ public:
translateType(const QString &t, AbstractMetaClass *currentClass = nullptr,
TranslateTypeFlags flags = {}, QString *errorMessage = nullptr);
+ static QString getSnakeCaseName(const QString &name);
+ // Names under which an item will be registered to Python depending on snakeCase
+ static QStringList definitionNames(const QString &name,
+ TypeSystem::SnakeCase snakeCase);
+
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &d) const;
#endif
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafield.cpp b/sources/shiboken6/ApiExtractor/abstractmetafield.cpp
index 3c294c3c7..23c21d2a5 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafield.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetafield.cpp
@@ -27,6 +27,7 @@
****************************************************************************/
#include "abstractmetafield.h"
+#include "abstractmetabuilder.h"
#include "abstractmetalang.h"
#include "abstractmetatype.h"
#include "documentation.h"
@@ -111,6 +112,11 @@ QString AbstractMetaField::qualifiedCppName() const
+ originalName();
}
+QStringList AbstractMetaField::definitionNames() const
+{
+ return AbstractMetaBuilder::definitionNames(d->m_name, snakeCase());
+}
+
QString AbstractMetaField::originalName() const
{
return d->m_originalName.isEmpty() ? d->m_name : d->m_originalName;
@@ -166,6 +172,23 @@ bool AbstractMetaField::canGenerateSetter() const
&& (!d->m_type.isConstant() || d->m_type.isPointerToConst());
}
+TypeSystem::SnakeCase AbstractMetaField::snakeCase() const
+{
+ // Renamed?
+ if (!d->m_originalName.isEmpty() && d->m_originalName != d->m_name)
+ return TypeSystem::SnakeCase::Disabled;
+
+ for (const auto &mod : modifications()) {
+ if (mod.snakeCase() != TypeSystem::SnakeCase::Unspecified)
+ return mod.snakeCase();
+ }
+
+ auto typeEntry = enclosingClass()->typeEntry();
+ const auto snakeCase = typeEntry->snakeCase();
+ return snakeCase != TypeSystem::SnakeCase::Unspecified
+ ? snakeCase : typeEntry->typeSystemTypeEntry()->snakeCase();
+}
+
FieldModificationList AbstractMetaField::modifications() const
{
const FieldModificationList &mods = enclosingClass()->typeEntry()->fieldModifications();
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafield.h b/sources/shiboken6/ApiExtractor/abstractmetafield.h
index 7964d5b57..52e08e2af 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafield.h
+++ b/sources/shiboken6/ApiExtractor/abstractmetafield.h
@@ -66,6 +66,9 @@ public:
QString qualifiedCppName() const;
+ // Names under which the field will be registered to Python.
+ QStringList definitionNames() const;
+
QString originalName() const;
void setOriginalName(const QString& name);
@@ -80,6 +83,8 @@ public:
bool canGenerateGetter() const;
bool canGenerateSetter() const;
+ TypeSystem::SnakeCase snakeCase() const;
+
static std::optional<AbstractMetaField>
find(const AbstractMetaFieldList &haystack, const QString &needle);
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
index e2d0fb9dc..9594cfdb6 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
@@ -27,6 +27,7 @@
****************************************************************************/
#include "abstractmetafunction.h"
+#include "abstractmetabuilder.h"
#include "abstractmetalang.h"
#include "abstractmetalang_helpers.h"
#include "abstractmetatype.h"
@@ -144,6 +145,11 @@ void AbstractMetaFunction::setOriginalName(const QString &name)
d->m_originalName = name;
}
+QStringList AbstractMetaFunction::definitionNames() const
+{
+ return AbstractMetaBuilder::definitionNames(d->m_name, snakeCase());
+}
+
const Documentation &AbstractMetaFunction::documentation() const
{
return d->m_doc;
@@ -1132,6 +1138,46 @@ int AbstractMetaFunction::overloadNumber() const
return d->overloadNumber(this);
}
+TypeSystem::SnakeCase AbstractMetaFunction::snakeCase() const
+{
+ if (isUserAdded())
+ return TypeSystem::SnakeCase::Disabled;
+ // Renamed?
+ if (!d->m_originalName.isEmpty() && d->m_originalName != d->m_name)
+ return TypeSystem::SnakeCase::Disabled;
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::NormalFunction:
+ case AbstractMetaFunction::SignalFunction:
+ case AbstractMetaFunction::EmptyFunction:
+ case AbstractMetaFunction::SlotFunction:
+ case AbstractMetaFunction::GlobalScopeFunction:
+ if (isOperatorOverload())
+ return TypeSystem::SnakeCase::Disabled;
+ break;
+ default:
+ return TypeSystem::SnakeCase::Disabled;
+ }
+
+ for (const auto &mod : modifications()) {
+ if (mod.snakeCase() != TypeSystem::SnakeCase::Unspecified)
+ return mod.snakeCase();
+ }
+
+ if (d->m_typeEntry) { // Global function
+ const auto snakeCase = d->m_typeEntry->snakeCase();
+ return snakeCase != TypeSystem::SnakeCase::Unspecified
+ ? snakeCase : d->m_typeEntry->typeSystemTypeEntry()->snakeCase();
+ }
+
+ if (d->m_class) {
+ auto typeEntry = d->m_class->typeEntry();
+ const auto snakeCase = typeEntry->snakeCase();
+ return snakeCase != TypeSystem::SnakeCase::Unspecified
+ ? snakeCase : typeEntry->typeSystemTypeEntry()->snakeCase();
+ }
+ return TypeSystem::SnakeCase::Disabled;
+}
+
// Query functions for generators
bool AbstractMetaFunction::injectedCodeUsesPySelf() const
{
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.h b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
index 1a29fcefe..99174b47f 100644
--- a/sources/shiboken6/ApiExtractor/abstractmetafunction.h
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.h
@@ -95,6 +95,9 @@ public:
QString name() const;
void setName(const QString &name);
+ // Names under which the function will be registered to Python.
+ QStringList definitionNames() const;
+
QString originalName() const;
void setOriginalName(const QString &name);
@@ -283,6 +286,8 @@ public:
int overloadNumber() const;
+ TypeSystem::SnakeCase snakeCase() const;
+
// Query functions for generators
/// Verifies if any of the function's code injections of the "native"
/// type needs the type system variable "%PYSELF".
diff --git a/sources/shiboken6/ApiExtractor/modifications.cpp b/sources/shiboken6/ApiExtractor/modifications.cpp
index eba1437dc..9e5071a33 100644
--- a/sources/shiboken6/ApiExtractor/modifications.cpp
+++ b/sources/shiboken6/ApiExtractor/modifications.cpp
@@ -173,6 +173,7 @@ public:
bool m_readable = true;
bool m_writable = true;
bool m_removed = false;
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
};
FieldModification::FieldModification() : d(new FieldModificationData)
@@ -245,6 +246,17 @@ void FieldModification::setRemoved(bool r)
d->m_removed = r;
}
+TypeSystem::SnakeCase FieldModification::snakeCase() const
+{
+ return d->snakeCase;
+}
+
+void FieldModification::setSnakeCase(TypeSystem::SnakeCase s)
+{
+ if (d->snakeCase != s)
+ d->snakeCase = s;
+}
+
// Helpers to split a parameter list of <add-function>, <declare-function>
// (@ denoting names), like
// "void foo(QList<X,Y> &@list@ = QList<X,Y>{1,2}, int @b@=5, ...)"
@@ -716,6 +728,7 @@ public:
bool m_thread = false;
TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified;
TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified;
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
};
FunctionModification::FunctionModification() : d(new FunctionModificationData)
@@ -763,6 +776,17 @@ void FunctionModification::setArgument_mods(const QList<ArgumentModification> &a
d->m_argument_mods = argument_mods;
}
+TypeSystem::SnakeCase FunctionModification::snakeCase() const
+{
+ return d->snakeCase;
+}
+
+void FunctionModification::setSnakeCase(TypeSystem::SnakeCase s)
+{
+ if (d->snakeCase != s)
+ d->snakeCase = s;
+}
+
const CodeSnipList &FunctionModification::snips() const
{
return d->m_snips;
diff --git a/sources/shiboken6/ApiExtractor/modifications.h b/sources/shiboken6/ApiExtractor/modifications.h
index 1d217d0cb..de47c15c2 100644
--- a/sources/shiboken6/ApiExtractor/modifications.h
+++ b/sources/shiboken6/ApiExtractor/modifications.h
@@ -382,6 +382,9 @@ public:
QList<ArgumentModification> &argument_mods();
void setArgument_mods(const QList<ArgumentModification> &argument_mods);
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase s);
+
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &d) const;
#endif
@@ -423,6 +426,9 @@ public:
bool isRemoved() const;
void setRemoved(bool r);
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase s);
+
private:
QSharedDataPointer<FieldModificationData> d;
};
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp
index 7c7bb6115..906b8a4b4 100644
--- a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp
@@ -29,6 +29,7 @@
#include "testmodifyfunction.h"
#include <QtTest/QTest>
#include "testutil.h"
+#include <abstractmetabuilder_p.h>
#include <abstractmetafunction.h>
#include <abstractmetalang.h>
#include <modifications.h>
@@ -469,4 +470,25 @@ void TestModifyFunction::testScopedModifications()
QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing);
}
+void TestModifyFunction::testSnakeCaseRenaming_data()
+{
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("expected");
+ QTest::newRow("s1")
+ << QStringLiteral("snakeCaseFunc") << QStringLiteral("snake_case_func");
+ QTest::newRow("s2")
+ << QStringLiteral("SnakeCaseFunc") << QStringLiteral("snake_case_func");
+ QTest::newRow("consecutive-uppercase")
+ << QStringLiteral("snakeCAseFunc") << QStringLiteral("snakeCAseFunc");
+}
+
+void TestModifyFunction::testSnakeCaseRenaming()
+{
+ QFETCH(QString, name);
+ QFETCH(QString, expected);
+
+ const QString actual = AbstractMetaBuilder::getSnakeCaseName(name);
+ QCOMPARE(actual, expected);
+}
+
QTEST_APPLESS_MAIN(TestModifyFunction)
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h
index 375111e03..a9a13a82b 100644
--- a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h
+++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h
@@ -44,6 +44,8 @@ class TestModifyFunction : public QObject
void testGlobalFunctionModification();
void testScopedModifications_data();
void testScopedModifications();
+ void testSnakeCaseRenaming_data();
+ void testSnakeCaseRenaming();
};
#endif
diff --git a/sources/shiboken6/ApiExtractor/typesystem.cpp b/sources/shiboken6/ApiExtractor/typesystem.cpp
index 372d36cfb..b76582b9f 100644
--- a/sources/shiboken6/ApiExtractor/typesystem.cpp
+++ b/sources/shiboken6/ApiExtractor/typesystem.cpp
@@ -637,9 +637,18 @@ void TypeEntry::useAsTypedef(const TypeEntry *source)
m_d->m_version = source->m_d->m_version;
}
+// ----------------- TypeSystemTypeEntry
+class TypeSystemTypeEntryPrivate : public TypeEntryPrivate
+{
+public:
+ using TypeEntryPrivate::TypeEntryPrivate;
+
+ TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Disabled;
+};
+
TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &entryName, const QVersionNumber &vr,
const TypeEntry *parent) :
- TypeEntry(entryName, TypeSystemType, vr, parent)
+ TypeEntry(new TypeSystemTypeEntryPrivate(entryName, TypeSystemType, vr, parent))
{
}
@@ -650,7 +659,20 @@ TypeSystemTypeEntry::TypeSystemTypeEntry(TypeEntryPrivate *d) :
TypeEntry *TypeSystemTypeEntry::clone() const
{
- return new TypeSystemTypeEntry(new TypeEntryPrivate(*d_func()));
+ S_D(const TypeSystemTypeEntry);
+ return new TypeSystemTypeEntry(new TypeSystemTypeEntryPrivate(*d));
+}
+
+TypeSystem::SnakeCase TypeSystemTypeEntry::snakeCase() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_snakeCase;
+}
+
+void TypeSystemTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_snakeCase = sc;
}
// ----------------- VoidTypeEntry
@@ -1148,6 +1170,7 @@ public:
// For class functions
TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified;
TypeSystem::AllowThread m_allowThread = TypeSystem::AllowThread::Unspecified;
+ TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Unspecified;
};
ComplexTypeEntry::ComplexTypeEntry(const QString &entryName, TypeEntry::Type t,
@@ -1407,6 +1430,18 @@ bool ComplexTypeEntry::hasDefaultConstructor() const
return !d->m_defaultConstructor.isEmpty();
}
+TypeSystem::SnakeCase ComplexTypeEntry::snakeCase() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_snakeCase;
+}
+
+void ComplexTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc)
+{
+ S_D(ComplexTypeEntry);
+ d->m_snakeCase = sc;
+}
+
TypeEntry *ComplexTypeEntry::clone() const
{
S_D(const ComplexTypeEntry);
@@ -1954,6 +1989,7 @@ public:
}
QStringList m_signatures;
+ TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Unspecified;
};
FunctionTypeEntry::FunctionTypeEntry(const QString &entryName, const QString &signature,
@@ -1981,6 +2017,18 @@ bool FunctionTypeEntry::hasSignature(const QString &signature) const
return d->m_signatures.contains(signature);
}
+TypeSystem::SnakeCase FunctionTypeEntry::snakeCase() const
+{
+ S_D(const FunctionTypeEntry);
+ return d->m_snakeCase;
+}
+
+void FunctionTypeEntry::setSnakeCase(TypeSystem::SnakeCase sc)
+{
+ S_D(FunctionTypeEntry);
+ d->m_snakeCase = sc;
+}
+
TypeEntry *FunctionTypeEntry::clone() const
{
S_D(const FunctionTypeEntry);
@@ -2074,7 +2122,8 @@ void ComplexTypeEntry::formatDebug(QDebug &debug) const
if (d->m_typeFlags != 0)
debug << ", typeFlags=" << d->m_typeFlags;
debug << ", copyableFlag=" << d->m_copyableFlag
- << ", except=" << int(d->m_exceptionHandling);
+ << ", except=" << int(d->m_exceptionHandling)
+ << ", snakeCase=" << int(d->m_snakeCase);
FORMAT_NONEMPTY_STRING("defaultSuperclass", d->m_defaultSuperclass)
FORMAT_NONEMPTY_STRING("polymorphicIdValue", d->m_polymorphicIdValue)
FORMAT_NONEMPTY_STRING("targetType", d->m_targetType)
@@ -2084,6 +2133,15 @@ void ComplexTypeEntry::formatDebug(QDebug &debug) const
FORMAT_LIST_SIZE("fieldMods", d->m_fieldMods)
}
+void FunctionTypeEntry::formatDebug(QDebug &debug) const
+{
+ S_D(const FunctionTypeEntry);
+
+ TypeEntry::formatDebug(debug);
+ debug << "signatures=" << d->m_signatures
+ << ", snakeCase=" << int(d->m_snakeCase);
+}
+
void TypedefEntry::formatDebug(QDebug &debug) const
{
S_D(const TypedefEntry);
diff --git a/sources/shiboken6/ApiExtractor/typesystem.h b/sources/shiboken6/ApiExtractor/typesystem.h
index a2b52bb0d..a823814f7 100644
--- a/sources/shiboken6/ApiExtractor/typesystem.h
+++ b/sources/shiboken6/ApiExtractor/typesystem.h
@@ -296,6 +296,9 @@ public:
TypeEntry *clone() const override;
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase sc);
+
protected:
explicit TypeSystemTypeEntry(TypeEntryPrivate *d);
};
@@ -573,6 +576,9 @@ public:
void useAsTypedef(const ComplexTypeEntry *source);
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase sc);
+
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &debug) const override;
#endif
@@ -741,8 +747,15 @@ public:
bool hasSignature(const QString& signature) const;
void addSignature(const QString& signature);
+ TypeSystem::SnakeCase snakeCase() const;
+ void setSnakeCase(TypeSystem::SnakeCase sc);
+
TypeEntry *clone() const override;
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
protected:
explicit FunctionTypeEntry(FunctionTypeEntryPrivate *d);
};
diff --git a/sources/shiboken6/ApiExtractor/typesystem_enums.h b/sources/shiboken6/ApiExtractor/typesystem_enums.h
index 0d7f279c4..60832b64e 100644
--- a/sources/shiboken6/ApiExtractor/typesystem_enums.h
+++ b/sources/shiboken6/ApiExtractor/typesystem_enums.h
@@ -81,6 +81,13 @@ enum class ExceptionHandling {
On
};
+enum class SnakeCase {
+ Unspecified,
+ Disabled,
+ Enabled,
+ Both
+};
+
enum Visibility { // For namespaces
Unspecified,
Visible,
diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
index 4c754f680..6762bef21 100644
--- a/sources/shiboken6/ApiExtractor/typesystemparser.cpp
+++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
@@ -97,6 +97,7 @@ static inline QString replaceAttribute() { return QStringLiteral("replace"); }
static inline QString toAttribute() { return QStringLiteral("to"); }
static inline QString signatureAttribute() { return QStringLiteral("signature"); }
static inline QString snippetAttribute() { return QStringLiteral("snippet"); }
+static inline QString snakeCaseAttribute() { return QStringLiteral("snake-case"); }
static inline QString staticAttribute() { return QStringLiteral("static"); }
static inline QString threadAttribute() { return QStringLiteral("thread"); }
static inline QString sourceAttribute() { return QStringLiteral("source"); }
@@ -392,6 +393,18 @@ ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive,
};
ENUM_LOOKUP_BINARY_SEARCH()
+
+ENUM_LOOKUP_BEGIN(TypeSystem::SnakeCase, Qt::CaseSensitive,
+ snakeCaseFromAttribute, TypeSystem::SnakeCase::Unspecified)
+{
+ {u"no", TypeSystem::SnakeCase::Disabled},
+ {u"false", TypeSystem::SnakeCase::Disabled},
+ {u"yes", TypeSystem::SnakeCase::Enabled},
+ {u"true", TypeSystem::SnakeCase::Enabled},
+ {u"both", TypeSystem::SnakeCase::Both},
+};
+ENUM_LOOKUP_LINEAR_SEARCH()
+
ENUM_LOOKUP_BEGIN(TypeSystem::Visibility, Qt::CaseSensitive,
visibilityFromAttribute, TypeSystem::Visibility::Unspecified)
{
@@ -1442,18 +1455,29 @@ FunctionTypeEntry *
{
if (!checkRootElement())
return nullptr;
- const int signatureIndex = indexOfAttribute(*attributes, signatureAttribute());
- if (signatureIndex == -1) {
+
+ QString signature;
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Disabled;
+
+ for (int i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute()) {
+ signature = TypeDatabase::normalizedSignature(attributes->takeAt(i).value().toString());
+ } else if (name == snakeCaseAttribute()) {
+ snakeCase = snakeCaseFromAttribute(attributes->takeAt(i).value());
+ }
+ }
+
+ if (signature.isEmpty()) {
m_error = msgMissingAttribute(signatureAttribute());
return nullptr;
}
- const QString signature =
- TypeDatabase::normalizedSignature(attributes->takeAt(signatureIndex).value().toString());
TypeEntry *existingType = m_database->findType(name);
if (!existingType) {
auto *result = new FunctionTypeEntry(name, signature, since, currentParentTypeEntry());
+ result->setSnakeCase(snakeCase);
applyCommonAttributes(reader, result, attributes);
return result;
}
@@ -1564,6 +1588,8 @@ void TypeSystemParser::applyComplexTypeAttributes(const QXmlStreamReader &reader
ctype->setDeleteInMainThread(true);
} else if (name == QLatin1String("target-type")) {
ctype->setTargetType(attributes->takeAt(i).value().toString());
+ } else if (name == snakeCaseAttribute()) {
+ ctype->setSnakeCase(snakeCaseFromAttribute(attributes->takeAt(i).value()));
}
}
@@ -1700,6 +1726,8 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const QXmlStreamReader &
const QVersionNumber &since,
QXmlStreamAttributes *attributes)
{
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
+
for (int i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
if (name == packageAttribute()) {
@@ -1724,6 +1752,8 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const QXmlStreamReader &
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
+ } else if (name == snakeCaseAttribute()) {
+ snakeCase = snakeCaseFromAttribute(attributes->takeAt(i).value());
}
}
@@ -1735,6 +1765,7 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const QXmlStreamReader &
currentParentTypeEntry());
}
moduleEntry->setCodeGeneration(m_generate);
+ moduleEntry->setSnakeCase(snakeCase);
if ((m_generate == TypeEntry::GenerateForSubclass ||
m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty())
@@ -2078,6 +2109,8 @@ bool TypeSystemParser::parseModifyField(const QXmlStreamReader &reader,
fm.setWritable(convertBoolean(attributes->takeAt(i).value(), writeAttribute(), true));
} else if (name == renameAttribute()) {
fm.setRenamedToName(attributes->takeAt(i).value().toString());
+ } else if (name == snakeCaseAttribute()) {
+ fm.setSnakeCase(snakeCaseFromAttribute(attributes->takeAt(i).value()));
}
}
if (fm.name().isEmpty()) {
@@ -2232,6 +2265,7 @@ bool TypeSystemParser::parseModifyFunction(const QXmlStreamReader &reader,
int overloadNumber = TypeSystem::OverloadNumberUnset;
TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified;
TypeSystem::AllowThread allowThread = TypeSystem::AllowThread::Unspecified;
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
for (int i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
if (name == QLatin1String("signature")) {
@@ -2265,6 +2299,8 @@ bool TypeSystemParser::parseModifyFunction(const QXmlStreamReader &reader,
} else if (name == overloadNumberAttribute()) {
if (!parseOverloadNumber(attributes->takeAt(i), &overloadNumber, &m_error))
return false;
+ } else if (name == snakeCaseAttribute()) {
+ snakeCase = snakeCaseFromAttribute(attributes->takeAt(i).value());
} else if (name == virtualSlotAttribute()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
@@ -2295,6 +2331,7 @@ bool TypeSystemParser::parseModifyFunction(const QXmlStreamReader &reader,
mod.setOriginalSignature(originalSignature);
mod.setExceptionHandling(exceptionHandling);
mod.setOverloadNumber(overloadNumber);
+ mod.setSnakeCase(snakeCase);
m_currentSignature = signature;
if (!access.isEmpty()) {
diff --git a/sources/shiboken6/doc/typesystem_manipulating_objects.rst b/sources/shiboken6/doc/typesystem_manipulating_objects.rst
index dcb3a46b1..ea8c6247c 100644
--- a/sources/shiboken6/doc/typesystem_manipulating_objects.rst
+++ b/sources/shiboken6/doc/typesystem_manipulating_objects.rst
@@ -78,7 +78,8 @@ modify-field
<modify-field name="..."
write="true | false"
read="true | false"
- remove="true | false" />
+ remove="true | false"
+ snake-case="yes | no | both" />
</object-type>
The ``name`` attribute is the name of the field, the *optional* ``write``
@@ -91,6 +92,8 @@ modify-field
The *optional* ``rename`` attribute can be used to change the name of the
given field in the generated target language API.
+ The *optional* **snake-case** attribute allows for overriding the value
+ specified on the class entry or **typesystem** element.
.. _modify-function:
@@ -112,7 +115,8 @@ modify-function
allow-thread="true | auto | false"
exception-handling="off | auto-off | auto-on | on"
overload-number="number"
- rename="..." />
+ rename="..."
+ snake-case="yes | no | both" />
</object-type>
The ``signature`` attribute is a normalized C++ signature, excluding return
@@ -191,6 +195,8 @@ modify-function
The *optional* ``access`` attribute changes the access privileges of the
given function in the generated target language API.
+ The *optional* **snake-case** attribute allows for overriding the value
+ specified on the class entry or **typesystem** element.
.. _add-function:
diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst
index 405469ea2..243af9603 100644
--- a/sources/shiboken6/doc/typesystem_specifying_types.rst
+++ b/sources/shiboken6/doc/typesystem_specifying_types.rst
@@ -35,7 +35,8 @@ typesystem
.. code-block:: xml
- <typesystem package="..." default-superclass="..." allow-thread="..." exception-handling="...">
+ <typesystem package="..." default-superclass="..." allow-thread="..."
+ exception-handling="..." snake-case="yes | no | both" >
</typesystem>
The **package** attribute is a string describing the package to be used,
@@ -47,6 +48,22 @@ typesystem
specify the default handling for the corresponding function modification
(see :ref:`modify-function`).
+ The *optional* **snake-case** attribute specifies whether function
+ and field names will be automatically changed to the snake case
+ style that is common in Python (for example, ``snakeCase`` will be
+ changed to ``snake_case``).
+
+ The value ``both`` means that the function or field will be exposed
+ under both its original name and the snake case version. There are
+ limitations to this though:
+
+ - When overriding a virtual function of a C++ class in Python,
+ the snake case name must be used.
+
+ - When static and non-static overloads of a class member function
+ exist (as is the case for example for ``QFileInfo::exists()``),
+ the snake case name must be used.
+
load-typesystem
^^^^^^^^^^^^^^^
@@ -280,7 +297,8 @@ value-type
hash-function="..."
stream="yes | no"
default-constructor="..."
- revision="..." />
+ revision="..."
+ snake-case="yes | no | both" />
</typesystem>
The **name** attribute is the fully qualified C++ class name, such as
@@ -310,6 +328,9 @@ value-type
specify the default handling for the corresponding function modification
(see :ref:`modify-function`).
+ The *optional* **snake-case** attribute allows for overriding the value
+ specified on the **typesystem** element.
+
.. _object-type:
object-type
@@ -330,7 +351,8 @@ object-type
exception-handling="..."
hash-function="..."
stream="yes | no"
- revision="..." />
+ revision="..."
+ snake-case="yes | no | both" />
</typesystem>
The **name** attribute is the fully qualified C++ class name. If there is no
@@ -360,6 +382,9 @@ object-type
specify the default handling for the corresponding function modification
(see :ref:`modify-function`).
+ The *optional* **snake-case** attribute allows for overriding the value
+ specified on the **typesystem** element.
+
interface-type
^^^^^^^^^^^^^^
@@ -480,7 +505,7 @@ function
.. code-block:: xml
<typesystem>
- <function signature="..." rename="..." since="..."/>
+ <function signature="..." rename="..." since="..." snake-case="yes | no | both" />
</typesystem>
This tag has some limitations, it doesn't support function modifications, besides you
@@ -490,6 +515,9 @@ function
The function tag has two *optional* attributes: **since**, whose value is used to specify
the API version of this function, and **rename**, to modify the function name.
+ The *optional* **snake-case** attribute allows for overriding the value
+ specified on the **typesystem** element.
+
.. _system_include:
system-include
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
index 0f1fe18f4..dc92e2432 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp
@@ -575,11 +575,15 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon
writeMethodWrapper(s, overloads, classContext);
writeSignatureInfo(signatureStream, overloads);
+ // For a mixture of static and member function overloads,
+ // a separate PyMethodDef entry is written which is referenced
+ // in the PyMethodDef list and later in getattro() for handling
+ // the non-static case.
if (OverloadData::hasStaticAndInstanceFunctions(overloads)) {
QString methDefName = cpythonMethodDefinitionName(rfunc);
- smd << "static PyMethodDef " << methDefName << " = {\n" << indent;
- writeMethodDefinitionEntry(smd, overloads);
- smd << outdent << "\n};\n\n";
+ smd << "static PyMethodDef " << methDefName << " = " << indent;
+ writeMethodDefinitionEntries(smd, overloads, 1);
+ smd << outdent << ";\n\n";
}
writeMethodDefinition(md, overloads);
}
@@ -716,12 +720,16 @@ void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classCon
s << "static PyGetSetDef " << cpythonGettersSettersDefinitionName(metaClass)
<< "[] = {\n" << indent;
for (const AbstractMetaField &metaField : fields) {
- if (!metaField.isStatic()) {
- const QString getter = metaField.canGenerateGetter()
+ const bool canGenerateGetter = metaField.canGenerateGetter();
+ const bool canGenerateSetter = metaField.canGenerateSetter();
+ if (canGenerateGetter || canGenerateSetter) {
+ const QString getter = canGenerateGetter
? cpythonGetterFunctionName(metaField) : QString();
- const QString setter = metaField.canGenerateSetter()
+ const QString setter = canGenerateSetter
? cpythonSetterFunctionName(metaField) : QString();
- writePyGetSetDefEntry(s, metaField.name(), getter, setter);
+ const auto names = metaField.definitionNames();
+ for (const auto &name : names)
+ writePyGetSetDefEntry(s, name, getter, setter);
}
}
@@ -953,7 +961,8 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s,
return;
const TypeEntry *retType = func->type().typeEntry();
- const QString funcName = func->isOperatorOverload() ? pythonOperatorFunctionName(func) : func->name();
+ const QString funcName = func->isOperatorOverload()
+ ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst();
QString prefix = wrapperName(func->ownerClass()) + QLatin1String("::");
s << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues|Generator::OriginalTypeDescription)
@@ -4881,16 +4890,16 @@ void CppGenerator::writeRichCompareFunction(TextStream &s,
<< outdent << "}\n\n";
}
-void CppGenerator::writeMethodDefinitionEntry(TextStream &s, const AbstractMetaFunctionCList &overloads) const
+QString CppGenerator::methodDefinitionParameters(const OverloadData &overloadData) const
{
- Q_ASSERT(!overloads.isEmpty());
- OverloadData overloadData(overloads, this);
bool usePyArgs = pythonFunctionWrapperUsesListOfArguments(overloadData);
const auto func = overloadData.referenceFunction();
int min = overloadData.minArgs();
int max = overloadData.maxArgs();
- s << '"' << func->name() << "\", reinterpret_cast<PyCFunction>("
+ QString result;
+ QTextStream s(&result);
+ s << "reinterpret_cast<PyCFunction>("
<< cpythonFunctionName(func) << "), ";
if ((min == max) && (max < 2) && !usePyArgs) {
if (max == 0)
@@ -4910,6 +4919,24 @@ void CppGenerator::writeMethodDefinitionEntry(TextStream &s, const AbstractMetaF
&& overloadData.hasStaticFunction()) {
s << "|METH_STATIC";
}
+ return result;
+}
+
+void CppGenerator::writeMethodDefinitionEntries(TextStream &s,
+ const AbstractMetaFunctionCList &overloads,
+ qsizetype maxEntries) const
+{
+ Q_ASSERT(!overloads.isEmpty());
+ OverloadData overloadData(overloads, this);
+ const QStringList names = overloadData.referenceFunction()->definitionNames();
+ const QString parameters = methodDefinitionParameters(overloadData);
+ const qsizetype count = maxEntries > 0
+ ? qMin(names.size(), maxEntries) : names.size();
+ for (qsizetype i = 0; i < count; ++i) {
+ if (i)
+ s << ",\n";
+ s << "{\"" << names.at(i) << "\", " << parameters << '}';
+ }
}
void CppGenerator::writeMethodDefinition(TextStream &s, const AbstractMetaFunctionCList &overloads) const
@@ -4922,9 +4949,7 @@ void CppGenerator::writeMethodDefinition(TextStream &s, const AbstractMetaFuncti
if (OverloadData::hasStaticAndInstanceFunctions(overloads)) {
s << cpythonMethodDefinitionName(func);
} else {
- s << '{';
- writeMethodDefinitionEntry(s, overloads);
- s << '}';
+ writeMethodDefinitionEntries(s, overloads);
}
s << ',' << '\n';
}
@@ -5778,7 +5803,8 @@ void CppGenerator::writeGetattroFunction(TextStream &s, AttroCheck attroCheck,
<< defName << ".ml_doc,\n";
}
s << "};\n"
- << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)\n";
+ << "if (Shiboken::String::compare(name, \""
+ << func->definitionNames().constFirst() << "\") == 0)\n";
Indentation indent(s);
s << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);\n";
}
diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h
index 0539a19ec..a81df17c9 100644
--- a/sources/shiboken6/generator/shiboken/cppgenerator.h
+++ b/sources/shiboken6/generator/shiboken/cppgenerator.h
@@ -286,7 +286,10 @@ private:
void writeClassDefinition(TextStream &s,
const AbstractMetaClass *metaClass,
const GeneratorContext &classContext);
- void writeMethodDefinitionEntry(TextStream &s, const AbstractMetaFunctionCList &overloads) const;
+ QString methodDefinitionParameters(const OverloadData &overloadData) const;
+ void writeMethodDefinitionEntries(TextStream &s,
+ const AbstractMetaFunctionCList &overloads,
+ qsizetype maxEntries = -1) const;
void writeMethodDefinition(TextStream &s, const AbstractMetaFunctionCList &overloads) const;
void writeSignatureInfo(TextStream &s, const AbstractMetaFunctionCList &overloads) const;
/// Writes the implementation of all methods part of python sequence protocol
diff --git a/sources/shiboken6/tests/libsample/CMakeLists.txt b/sources/shiboken6/tests/libsample/CMakeLists.txt
index 3bf119e9f..af4f3318f 100644
--- a/sources/shiboken6/tests/libsample/CMakeLists.txt
+++ b/sources/shiboken6/tests/libsample/CMakeLists.txt
@@ -43,6 +43,7 @@ samplenamespace.cpp
sbkdate.cpp
simplefile.cpp
size.cpp
+snakecasetest.cpp
sometime.cpp
str.cpp
strlist.cpp
diff --git a/sources/shiboken6/tests/libsample/snakecasetest.cpp b/sources/shiboken6/tests/libsample/snakecasetest.cpp
new file mode 100644
index 000000000..d82984e6f
--- /dev/null
+++ b/sources/shiboken6/tests/libsample/snakecasetest.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "snakecasetest.h"
+
+int SnakeCaseGlobalFunction()
+{
+ return 42;
+}
+
+SnakeCaseTest::SnakeCaseTest() = default;
+SnakeCaseTest::~SnakeCaseTest() = default;
+
+int SnakeCaseTest::testFunction1() const
+{
+ return 42;
+}
+
+int SnakeCaseTest::testFunctionDisabled() const
+{
+ return 42;
+}
+
+int SnakeCaseTest::testFunctionBoth() const
+{
+ return 42;
+}
+
+int SnakeCaseTest::callVirtualFunc() const
+{
+ return virtualFunc();
+}
+
+int SnakeCaseTest::virtualFunc() const
+{
+ return 42;
+}
+
+SnakeCaseDerivedTest::SnakeCaseDerivedTest() = default;
+
+int SnakeCaseDerivedTest::virtualFunc() const
+{
+ return 43;
+}
diff --git a/sources/shiboken6/tests/libsample/snakecasetest.h b/sources/shiboken6/tests/libsample/snakecasetest.h
new file mode 100644
index 000000000..8900c2eec
--- /dev/null
+++ b/sources/shiboken6/tests/libsample/snakecasetest.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SNAKECASETEST_H
+#define SNAKECASETEST_H
+
+#include "libsamplemacros.h"
+
+LIBSAMPLE_API int SnakeCaseGlobalFunction();
+
+class LIBSAMPLE_API SnakeCaseTest
+{
+public:
+ SnakeCaseTest();
+ virtual ~SnakeCaseTest();
+
+ int testFunction1() const;
+ int testFunctionDisabled() const;
+ int testFunctionBoth() const;
+
+ int callVirtualFunc() const;
+
+ int testField = 42;
+ int testFieldDisabled = 42;
+ int testFieldBoth = 42;
+
+protected:
+ virtual int virtualFunc() const;
+};
+
+class LIBSAMPLE_API SnakeCaseDerivedTest : public SnakeCaseTest
+{
+public:
+ SnakeCaseDerivedTest();
+
+protected:
+ int virtualFunc() const override;
+};
+
+#endif // SNAKECASETEST_H
diff --git a/sources/shiboken6/tests/samplebinding/CMakeLists.txt b/sources/shiboken6/tests/samplebinding/CMakeLists.txt
index 418e47c0d..2630b9c79 100644
--- a/sources/shiboken6/tests/samplebinding/CMakeLists.txt
+++ b/sources/shiboken6/tests/samplebinding/CMakeLists.txt
@@ -111,6 +111,8 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_derivedfromnamespace_wrapper.
${CMAKE_CURRENT_BINARY_DIR}/sample/simplefile_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/sample/size_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/sample/sizef_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasetest_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasederivedtest_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/sample/sonofmderived1_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/sample/str_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/sample/strlist_wrapper.cpp
diff --git a/sources/shiboken6/tests/samplebinding/global.h b/sources/shiboken6/tests/samplebinding/global.h
index 0a168099b..f647eb795 100644
--- a/sources/shiboken6/tests/samplebinding/global.h
+++ b/sources/shiboken6/tests/samplebinding/global.h
@@ -81,6 +81,7 @@
#include "samplenamespace.h"
#include "simplefile.h"
#include "size.h"
+#include "snakecasetest.h"
#include "str.h"
#include "strlist.h"
#include "sometime.h"
diff --git a/sources/shiboken6/tests/samplebinding/snakecase_test.py b/sources/shiboken6/tests/samplebinding/snakecase_test.py
new file mode 100644
index 000000000..893b9809e
--- /dev/null
+++ b/sources/shiboken6/tests/samplebinding/snakecase_test.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#############################################################################
+##
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the test suite of Qt for Python.
+##
+## $QT_BEGIN_LICENSE:GPL-EXCEPT$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 as published by the Free Software
+## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+'''Test cases for snake case generation'''
+
+import os
+import sys
+import unittest
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from shiboken_paths import init_paths
+init_paths()
+
+from sample import (SnakeCaseTest, SnakeCaseDerivedTest,
+ snake_case_global_function)
+
+
+class OverrideTest(SnakeCaseDerivedTest):
+ def virtual_func(self):
+ return 4711
+
+
+class SnakeCaseTestCase(unittest.TestCase):
+ '''Test for SnakeCaseTest'''
+ def testMemberFunctions(self):
+ s = SnakeCaseTest()
+ self.assertEqual(s.test_function1(), 42)
+
+ self.assertEqual(s.testFunctionDisabled(), 42)
+
+ self.assertEqual(s.testFunctionBoth(), 42)
+ self.assertEqual(s.test_function_both(), 42)
+
+ def virtualFunctions(self):
+ s = OverrideTest()
+ self.assertEqual(s.call_virtual_func(), 4711)
+
+ def testMemberFields(self):
+ s = SnakeCaseTest()
+ old_value = s.test_field
+ s.test_field = old_value + 1
+ self.assertEqual(s.test_field, old_value + 1)
+
+ old_value = s.testFieldDisabled
+ s.testFieldDisabled = old_value + 1
+ self.assertEqual(s.testFieldDisabled, old_value + 1)
+
+ old_value = s.test_field_both
+ s.test_field_both = old_value + 1
+ self.assertEqual(s.test_field_both, old_value + 1)
+ self.assertEqual(s.testFieldBoth, old_value + 1)
+
+ def testGlobalFunction(self):
+ self.assertEqual(snake_case_global_function(), 42)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml
index 6d9c842ac..d5a3edb34 100644
--- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml
+++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml
@@ -1967,6 +1967,14 @@
</add-function>
</value-type>
<value-type name="SizeF"/>
+ <function signature="SnakeCaseGlobalFunction()" snake-case="yes"/>
+ <object-type name="SnakeCaseTest" snake-case="yes">
+ <modify-function signature="testFunctionDisabled()const" snake-case="no"/>
+ <modify-function signature="testFunctionBoth()const" snake-case="both"/>
+ <modify-field name="testFieldDisabled" snake-case="no"/>
+ <modify-field name="testFieldBoth" snake-case="both"/>
+ </object-type>
+ <object-type name="SnakeCaseDerivedTest" snake-case="yes"/>
<value-type name="MapUser"/>
<value-type name="PairUser"/>
<value-type name="ListUser">