diff options
Diffstat (limited to 'sources/shiboken6/generator/shiboken/shibokengenerator.cpp')
-rw-r--r-- | sources/shiboken6/generator/shiboken/shibokengenerator.cpp | 2649 |
1 files changed, 2649 insertions, 0 deletions
diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp new file mode 100644 index 000000000..a1417e5d9 --- /dev/null +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp @@ -0,0 +1,2649 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "shibokengenerator.h" +#include "generatorstrings.h" +#include "generatorargument.h" +#include "defaultvalue.h" +#include "generatorcontext.h" +#include "apiextractorresult.h" +#include "codesnip.h" +#include "customconversion.h" +#include "ctypenames.h" +#include <abstractmetabuilder.h> +#include <abstractmetaenum.h> +#include <abstractmetafield.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetalang_helpers.h> +#include <usingmember.h> +#include <exception.h> +#include <messages.h> +#include <modifications.h> +#include "overloaddata.h" +#include <optionsparser.h> +#include "propertyspec.h" +#include "pytypenames.h" +#include <reporthandler.h> +#include <textstream.h> +#include <typedatabase.h> +#include <containertypeentry.h> +#include <customtypenentry.h> +#include <enumtypeentry.h> +#include <flagstypeentry.h> +#include <namespacetypeentry.h> +#include <primitivetypeentry.h> +#include <pythontypeentry.h> +#include <smartpointertypeentry.h> +#include <valuetypeentry.h> + +#include <iostream> + +#include "qtcompat.h" + +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QRegularExpression> + +#include <algorithm> +#include <limits> +#include <memory> +#include <utility> + +using namespace Qt::StringLiterals; + +static constexpr auto PARENT_CTOR_HEURISTIC = "enable-parent-ctor-heuristic"_L1; +static constexpr auto RETURN_VALUE_HEURISTIC = "enable-return-value-heuristic"_L1; +static constexpr auto DISABLE_VERBOSE_ERROR_MESSAGES = "disable-verbose-error-messages"_L1; +static constexpr auto USE_ISNULL_AS_NB_BOOL = "use-isnull-as-nb-bool"_L1; +// FIXME PYSIDE 7: Remove USE_ISNULL_AS_NB_NONZERO/USE_OPERATOR_BOOL_AS_NB_NONZERO +static constexpr auto USE_ISNULL_AS_NB_NONZERO = "use-isnull-as-nb_nonzero"_L1; +static constexpr auto USE_OPERATOR_BOOL_AS_NB_BOOL = "use-operator-bool-as-nb-bool"_L1; +static constexpr auto USE_OPERATOR_BOOL_AS_NB_NONZERO = "use-operator-bool-as-nb-nonzero"_L1; +static constexpr auto WRAPPER_DIAGNOSTICS = "wrapper-diagnostics"_L1; +static constexpr auto NO_IMPLICIT_CONVERSIONS = "no-implicit-conversions"_L1; +static constexpr auto LEAN_HEADERS = "lean-headers"_L1; + +QString CPP_ARG_N(int i) +{ + return CPP_ARG + QString::number(i); +} + +constexpr auto CPP_ARG_REMOVED_PREFIX = "removed_cppArg"_L1; + +QString CPP_ARG_REMOVED(int i) +{ + return CPP_ARG_REMOVED_PREFIX + QString::number(i); +} + +const char *const METHOD_DEF_SENTINEL = "{nullptr, nullptr, 0, nullptr} // Sentinel\n"; +const char *const PYTHON_TO_CPPCONVERSION_STRUCT = "Shiboken::Conversions::PythonToCppConversion"; + +const char *const openTargetExternC = R"( +// Target --------------------------------------------------------- + +extern "C" { +)"; +const char *const closeExternC = "} // extern \"C\"\n\n"; +const char *const richCompareComment = + "// PYSIDE-74: By default, we redirect to object's tp_richcompare (which is `==`, `!=`).\n"; + +struct ShibokenGeneratorOptions +{ + bool useCtorHeuristic = false; + bool userReturnValueHeuristic = false; + bool verboseErrorMessagesDisabled = false; + bool useIsNullAsNbBool = false; + // FIXME PYSIDE 7 Flip m_leanHeaders default or remove? + bool leanHeaders = false; + bool useOperatorBoolAsNbBool = false; + // FIXME PYSIDE 7 Flip generateImplicitConversions default or remove? + bool generateImplicitConversions = true; + bool wrapperDiagnostics = false; +}; + +struct GeneratorClassInfoCacheEntry +{ + ShibokenGenerator::FunctionGroups functionGroups; + QList<AbstractMetaFunctionCList> numberProtocolOperators; + BoolCastFunctionOptional boolCastFunctionO; + bool needsGetattroFunction = false; +}; + +using GeneratorClassInfoCache = QHash<AbstractMetaClassCPtr, GeneratorClassInfoCacheEntry>; + +Q_GLOBAL_STATIC(GeneratorClassInfoCache, generatorClassInfoCache) + +static const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()"; +static const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()"; +static const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()"; +// Capture a '*' leading the variable name into the target +// so that "*valuePtr = %CONVERTTOCPP..." works as expected. +static const char CONVERTTOCPP_REGEX[] = + R"((\*?%?[a-zA-Z_][\w\.]*(?:\[[^\[^<^>]+\])*)(?:\s+)=(?:\s+)%CONVERTTOCPP\[([^\[]*)\]\()"; + +const ShibokenGenerator::TypeSystemConverterRegExps & + ShibokenGenerator::typeSystemConvRegExps() +{ + static const TypeSystemConverterRegExps result = { + QRegularExpression(QLatin1StringView(CHECKTYPE_REGEX)), + QRegularExpression(QLatin1StringView(ISCONVERTIBLE_REGEX)), + QRegularExpression(QLatin1StringView(CONVERTTOCPP_REGEX)), + QRegularExpression(QLatin1StringView(CONVERTTOPYTHON_REGEX)) + }; + return result; +} + +// Options are static to avoid duplicated handling since ShibokenGenerator +// is instantiated for HeaderGenerator and CppGenerator. +ShibokenGeneratorOptions ShibokenGenerator::m_options; + +ShibokenGenerator::ShibokenGenerator() = default; + +ShibokenGenerator::~ShibokenGenerator() = default; + +// Correspondences between primitive and Python types. +static const QHash<QString, QString> &primitiveTypesCorrespondences() +{ + static const QHash<QString, QString> result = { + {u"bool"_s, pyBoolT}, + {u"char"_s, sbkCharT}, + {u"signed char"_s, sbkCharT}, + {u"unsigned char"_s, sbkCharT}, + {intT, pyLongT}, + {u"signed int"_s, pyLongT}, + {u"uint"_s, pyLongT}, + {u"unsigned int"_s, pyLongT}, + {shortT, pyLongT}, + {u"ushort"_s, pyLongT}, + {u"signed short"_s, pyLongT}, + {u"signed short int"_s, pyLongT}, + {unsignedShortT, pyLongT}, + {u"unsigned short int"_s, pyLongT}, + {longT, pyLongT}, + {doubleT, pyFloatT}, + {floatT, pyFloatT}, + {u"unsigned long"_s, pyLongT}, + {u"signed long"_s, pyLongT}, + {u"ulong"_s, pyLongT}, + {u"unsigned long int"_s, pyLongT}, + {u"long long"_s, pyLongT}, + {u"__int64"_s, pyLongT}, + {u"unsigned long long"_s, pyLongT}, + {u"unsigned __int64"_s, pyLongT}, + {u"size_t"_s, pyLongT} + }; + return result; +} + +const QHash<QString, QChar> &ShibokenGenerator::formatUnits() +{ + static const QHash<QString, QChar> result = { + {u"char"_s, u'b'}, + {u"unsigned char"_s, u'B'}, + {intT, u'i'}, + {u"unsigned int"_s, u'I'}, + {shortT, u'h'}, + {unsignedShortT, u'H'}, + {longT, u'l'}, + {unsignedLongLongT, u'k'}, + {longLongT, u'L'}, + {u"__int64"_s, u'L'}, + {unsignedLongLongT, u'K'}, + {u"unsigned __int64"_s, u'K'}, + {doubleT, u'd'}, + {floatT, u'f'}, + }; + return result; +} + +QString ShibokenGenerator::translateTypeForWrapperMethod(const AbstractMetaType &cType, + const AbstractMetaClassCPtr &context, + Options options) const +{ + if (cType.isArray()) { + return translateTypeForWrapperMethod(*cType.arrayElementType(), context, options) + + u"[]"_s; + } + + if (avoidProtectedHack() && cType.isEnum()) { + auto metaEnum = api().findAbstractMetaEnum(cType.typeEntry()); + if (metaEnum && metaEnum->isProtected()) + return protectedEnumSurrogateName(metaEnum.value()); + } + + return translateType(cType, context, options); +} + +bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClassCPtr &metaClass) +{ + const auto wrapper = metaClass->cppWrapper(); + return wrapper.testFlag(AbstractMetaClass::CppVirtualMethodWrapper) + || (avoidProtectedHack() + && wrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper)); +} + +bool ShibokenGenerator::shouldGenerateMetaObjectFunctions(const AbstractMetaClassCPtr &metaClass) +{ + return usePySideExtensions() + && (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) + && !metaClass->typeEntry()->typeFlags() + .testFlag(ComplexTypeEntry::DisableQtMetaObjectFunctions) + && isQObject(metaClass); +} + +ShibokenGenerator::FunctionGeneration ShibokenGenerator::functionGeneration( + const AbstractMetaFunctionCPtr &func) +{ + FunctionGeneration result; + + const auto functionType = func->functionType(); + switch (functionType) { + case AbstractMetaFunction::ConversionOperator: + case AbstractMetaFunction::AssignmentOperatorFunction: + case AbstractMetaFunction::MoveAssignmentOperatorFunction: + case AbstractMetaFunction::DestructorFunction: + case AbstractMetaFunction::SignalFunction: + case AbstractMetaFunction::GetAttroFunction: + case AbstractMetaFunction::SetAttroFunction: + return result; + default: + if (func->isUserAdded() || func->usesRValueReferences() || !func->isWhiteListed()) + return result; + break; + } + + const bool notModifiedRemoved = !func->isModifiedRemoved(); + const bool isPrivate = func->isPrivate() && !func->isVisibilityModifiedToPrivate(); + switch (functionType) { + case AbstractMetaFunction::ConstructorFunction: + if (!isPrivate && notModifiedRemoved) + result.setFlag(FunctionGenerationFlag::WrapperConstructor); + return result; + case AbstractMetaFunction::CopyConstructorFunction: + if (!isPrivate && notModifiedRemoved) + result.setFlag(FunctionGenerationFlag::WrapperSpecialCopyConstructor); + return result; + case AbstractMetaFunction::NormalFunction: + case AbstractMetaFunction::SlotFunction: + if (avoidProtectedHack() && func->isProtected()) + result.setFlag(FunctionGenerationFlag::ProtectedWrapper); + break; + default: + break; + } + + // Check on virtuals (including operators). + const bool isAbstract = func->isAbstract(); + if (!(isAbstract || func->isVirtual()) + || func->cppAttributes().testFlag(FunctionAttribute::Final) + || func->isModifiedFinal()) { + return result; + } + + // MetaObject virtuals only need to be declared; CppGenerator creates a + // special implementation. + if (functionType == AbstractMetaFunction::NormalFunction + && usePySideExtensions() && isQObject(func->ownerClass())) { + const QString &name = func->name(); + if (name == u"metaObject"_s || name == u"qt_metacall") { + result.setFlag(FunctionGenerationFlag::QMetaObjectMethod); + return result; + } + } + + // Pure virtual functions need a default implementation even if private. + if (isAbstract || (notModifiedRemoved && !isPrivate)) + result.setFlag(FunctionGenerationFlag::VirtualMethod); + + return result; +} + +AbstractMetaFunctionCList ShibokenGenerator::implicitConversions(const TypeEntryCPtr &t) const +{ + if (!generateImplicitConversions() || !t->isValue()) + return {}; + auto vte = std::static_pointer_cast<const ValueTypeEntry>(t); + auto customConversion = vte->customConversion(); + if (customConversion && customConversion->replaceOriginalTargetToNativeConversions()) + return {}; + + auto result = api().implicitConversions(t); + auto end = std::remove_if(result.begin(), result.end(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isUserAdded(); + }); + result.erase(end, result.end()); + return result; +} + +QString ShibokenGenerator::wrapperName(const AbstractMetaClassCPtr &metaClass) +{ + Q_ASSERT(shouldGenerateCppWrapper(metaClass)); + QString result = metaClass->name(); + if (metaClass->enclosingClass()) // is a inner class + result.replace(u"::"_s, u"_"_s); + return result + u"Wrapper"_s; +} + +QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClassCPtr &metaClass) +{ + QString fullClassName = metaClass->name(); + auto enclosing = metaClass->enclosingClass(); + while (enclosing) { + if (NamespaceTypeEntry::isVisibleScope(enclosing->typeEntry())) + fullClassName.prepend(enclosing->name() + u'.'); + enclosing = enclosing->enclosingClass(); + } + fullClassName.prepend(metaClass->typeEntry()->targetLangPackage() + u'.'); + return fullClassName; +} + +QString ShibokenGenerator::headerFileNameForContext(const GeneratorContext &context) +{ + return fileNameForContextHelper(context, u"_wrapper.h"_s); +} + +// PYSIDE-500: When avoiding the protected hack, also include the inherited +// wrapper classes of the *current* module, because without the protected hack, +// we sometimes need to cast inherited wrappers. Inherited classes +// of *other* modules are completely regenerated by the header generator +// since the wrapper headers are not installed. + +IncludeGroup ShibokenGenerator::baseWrapperIncludes(const GeneratorContext &classContext) const +{ + IncludeGroup result{u"Wrappers"_s, {}}; + if (!classContext.useWrapper() || !avoidProtectedHack() + || classContext.forSmartPointer()) { + return result; + } + + const auto moduleEntry = TypeDatabase::instance()->defaultTypeSystemType(); + const auto &baseClasses = allBaseClasses(classContext.metaClass()); + for (const auto &base : baseClasses) { + const auto te = base->typeEntry(); + if (te->codeGeneration() == TypeEntry::GenerateCode) { // current module + const auto context = contextForClass(base); + if (context.useWrapper()) { + const QString header = headerFileNameForContext(context); + const auto type = typeSystemTypeEntry(te) == moduleEntry + ? Include::LocalPath : Include::IncludePath; + result.append(Include(type, header)); + } + } + } + return result; +} + +QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunctionCPtr &func, bool forceFunc) +{ + QString funcName; + if (func->isOperatorOverload()) + funcName = ShibokenGenerator::pythonOperatorFunctionName(func); + else + funcName = func->name(); + if (func->ownerClass()) { + QString fullClassName = fullPythonClassName(func->ownerClass()); + if (func->isConstructor()) { + funcName = fullClassName; + if (forceFunc) + funcName.append(u".__init__"_s); + } + else { + funcName.prepend(fullClassName + u'.'); + } + } + else { + funcName = packageName() + u'.' + func->name(); + } + return funcName; +} + +bool ShibokenGenerator::wrapperDiagnostics() +{ + return m_options.wrapperDiagnostics; +} + +QString ShibokenGenerator::protectedEnumSurrogateName(const AbstractMetaEnum &metaEnum) +{ + QString result = metaEnum.fullName(); + result.replace(u'.', u'_'); + result.replace(u"::"_s, u"_"_s); + return result + u"_Surrogate"_s; +} + +QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &func) +{ + QString result; + + // PYSIDE-331: For inherited functions, we need to find the same labels. + // Therefore we use the implementing class. + if (func->implementingClass()) { + result = cpythonBaseName(func->implementingClass()->typeEntry()); + if (func->isConstructor()) { + result += u"_Init"_s; + } else { + result += u"Func_"_s; + if (func->isOperatorOverload()) + result += ShibokenGenerator::pythonOperatorFunctionName(func); + else + result += func->name(); + } + } else { + result = u"Sbk"_s + moduleName() + u"Module_"_s + func->name(); + } + + return result; +} + +QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func) +{ + if (!func->ownerClass()) + return {}; + return cpythonBaseName(func->ownerClass()->typeEntry()) + u"Method_"_s + + func->name(); +} + +QString ShibokenGenerator::cpythonGettersSettersDefinitionName(const AbstractMetaClassCPtr &metaClass) +{ + return cpythonBaseName(metaClass) + u"_getsetlist"_s; +} + +QString ShibokenGenerator::cpythonSetattroFunctionName(const AbstractMetaClassCPtr &metaClass) +{ + return cpythonBaseName(metaClass) + u"_setattro"_s; +} + + +QString ShibokenGenerator::cpythonGetattroFunctionName(const AbstractMetaClassCPtr &metaClass) +{ + return cpythonBaseName(metaClass) + u"_getattro"_s; +} + +QString ShibokenGenerator::cpythonGetterFunctionName(const QString &name, + const AbstractMetaClassCPtr &enclosingClass) +{ + return cpythonBaseName(enclosingClass) + "_get_"_L1 + name; +} + +QString ShibokenGenerator::cpythonSetterFunctionName(const QString &name, + const AbstractMetaClassCPtr &enclosingClass) +{ + return cpythonBaseName(enclosingClass) + "_set_"_L1 + name; +} + +QString ShibokenGenerator::cpythonGetterFunctionName(const AbstractMetaField &metaField) +{ + return cpythonGetterFunctionName(metaField.name(), metaField.enclosingClass()); +} + +QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField &metaField) +{ + return cpythonSetterFunctionName(metaField.name(), metaField.enclosingClass()); +} + +QString ShibokenGenerator::cpythonGetterFunctionName(const QPropertySpec &property, + const AbstractMetaClassCPtr &metaClass) +{ + return cpythonGetterFunctionName(property.name(), metaClass); +} + +QString ShibokenGenerator::cpythonSetterFunctionName(const QPropertySpec &property, + const AbstractMetaClassCPtr &metaClass) +{ + return cpythonSetterFunctionName(property.name(), metaClass); +} + +static QString cpythonEnumFlagsName(const QString &moduleName, + const QString &qualifiedCppName) +{ + QString result = u"Sbk"_s + moduleName + u'_' + qualifiedCppName; + result.replace(u"::"_s, u"_"_s); + return result; +} + +QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntryCPtr &enumEntry) +{ + QString p = enumEntry->targetLangPackage(); + p.replace(u'.', u'_'); + return cpythonEnumFlagsName(p, enumEntry->qualifiedCppName()); +} + +QString ShibokenGenerator::cpythonEnumName(const AbstractMetaEnum &metaEnum) +{ + return cpythonEnumName(metaEnum.typeEntry()); +} + +QString ShibokenGenerator::cpythonFlagsName(const FlagsTypeEntryCPtr &flagsEntry) +{ + QString p = flagsEntry->targetLangPackage(); + p.replace(u'.', u'_'); + return cpythonEnumFlagsName(p, flagsEntry->originalName()); +} + +QString ShibokenGenerator::cpythonFlagsName(const AbstractMetaEnum *metaEnum) +{ + const auto flags = metaEnum->typeEntry()->flags(); + return flags ? cpythonFlagsName(flags) : QString{}; +} + +QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClassCPtr &metaClass) +{ + return cpythonBaseName(metaClass->typeEntry()) + u"SpecialCastFunction"_s; +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClassCPtr &metaClass, + const QString &argName) +{ + return cpythonWrapperCPtr(metaClass->typeEntry(), argName); +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType &metaType, + const QString &argName) +{ + if (!metaType.isWrapperType()) + return {}; + return u"reinterpret_cast< ::"_s + metaType.cppSignature() + + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(metaType) + + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s; +} + +QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntryCPtr &type, + const QString &argName) +{ + if (!type->isWrapperType()) + return QString(); + return u"reinterpret_cast< "_s + getFullTypeName(type) + + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(type) + + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s; +} + +void ShibokenGenerator::writeToPythonConversion(TextStream & s, const AbstractMetaType &type, + const AbstractMetaClassCPtr & /* context */, + const QString &argumentName) +{ + s << cpythonToPythonConversionFunction(type) << argumentName << ')'; +} + +void ShibokenGenerator::writeToCppConversion(TextStream &s, + const AbstractMetaClassCPtr &metaClass, + const QString &inArgName, const QString &outArgName) +{ + s << cpythonToCppConversionFunction(metaClass) << inArgName << ", &" << outArgName << ')'; +} + +void ShibokenGenerator::writeToCppConversion(TextStream &s, const AbstractMetaType &type, + const QString &inArgName, + const QString &outArgName) +{ + s << cpythonToCppConversionFunction(type) << inArgName << ", &" << outArgName << ')'; +} + +bool ShibokenGenerator::shouldRejectNullPointerArgument(const AbstractMetaFunctionCPtr &func, + int argIndex) +{ + if (argIndex < 0 || argIndex >= func->arguments().size()) + return false; + + const AbstractMetaArgument &arg = func->arguments().at(argIndex); + if (arg.type().isValueTypeWithCopyConstructorOnly()) + return true; + + // Argument type is not a pointer, a None rejection should not be + // necessary because the type checking would handle that already. + if (!arg.type().isPointer()) + return false; + if (arg.isModifiedRemoved()) + return false; + for (const auto &funcMod : func->modifications()) { + for (const ArgumentModification &argMod : funcMod.argument_mods()) { + if (argMod.index() == argIndex + 1 && argMod.noNullPointers()) + return true; + } + } + return false; +} + +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType &type) +{ + if (type.isCString()) + return u"PyString"_s; + return cpythonBaseName(type.typeEntry()); +} + +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaClassCPtr &metaClass) +{ + return cpythonBaseName(metaClass->typeEntry()); +} + +QString ShibokenGenerator::containerCpythonBaseName(const ContainerTypeEntryCPtr &ctype) +{ + switch (ctype->containerKind()) { + case ContainerTypeEntry::SetContainer: + return u"PySet"_s; + case ContainerTypeEntry::MapContainer: + case ContainerTypeEntry::MultiMapContainer: + return u"PyDict"_s; + case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::PairContainer: + case ContainerTypeEntry::SpanContainer: + break; + default: + Q_ASSERT(false); + } + return cPySequenceT; +} + +QString ShibokenGenerator::cpythonBaseName(const TypeEntryCPtr &type) +{ + QString baseName; + if (type->isWrapperType() || type->isNamespace()) { // && type->referenceType() == NoReference) { + baseName = u"Sbk_"_s + type->name(); + } else if (type->isPrimitive()) { + const auto ptype = basicReferencedTypeEntry(type); + baseName = ptype->hasTargetLangApiType() + ? ptype->targetLangApiName() : pythonPrimitiveTypeName(ptype->name()); + } else if (type->isEnum()) { + baseName = cpythonEnumName(std::static_pointer_cast<const EnumTypeEntry>(type)); + } else if (type->isFlags()) { + baseName = cpythonFlagsName(std::static_pointer_cast<const FlagsTypeEntry>(type)); + } else if (type->isContainer()) { + const auto ctype = std::static_pointer_cast<const ContainerTypeEntry>(type); + baseName = containerCpythonBaseName(ctype); + } else { + baseName = cPyObjectT; + } + return baseName.replace(u"::"_s, u"_"_s); +} + +QString ShibokenGenerator::cpythonTypeName(const AbstractMetaClassCPtr &metaClass) +{ + return cpythonTypeName(metaClass->typeEntry()); +} + +QString ShibokenGenerator::cpythonTypeName(const TypeEntryCPtr &type) +{ + return cpythonBaseName(type) + u"_TypeF()"_s; +} + +QString ShibokenGenerator::converterObject(const AbstractMetaType &type) +{ + if (type.isCString()) + return u"Shiboken::Conversions::PrimitiveTypeConverter<const char *>()"_s; + if (type.isVoidPointer()) + return u"Shiboken::Conversions::PrimitiveTypeConverter<void *>()"_s; + const AbstractMetaTypeList nestedArrayTypes = type.nestedArrayTypes(); + if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) { + return "Shiboken::Conversions::ArrayTypeConverter<"_L1 + + nestedArrayTypes.constLast().minimalSignature() + + u">("_s + QString::number(nestedArrayTypes.size()) + + u')'; + } + + auto typeEntry = type.typeEntry(); + if (typeEntry->isContainer() || typeEntry->isSmartPointer()) { + return convertersVariableName(typeEntry->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + u']'; + } + return converterObject(typeEntry); +} + +QString ShibokenGenerator::converterObject(const TypeEntryCPtr &type) +{ + if (isExtendedCppPrimitive(type)) + return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()") + .arg(type->qualifiedCppName()); + if (type->isWrapperType()) + return QString::fromLatin1("PepType_SOTP(reinterpret_cast<PyTypeObject *>(%1))->converter") + .arg(cpythonTypeNameExt(type)); + if (type->isEnum() || type->isFlags()) + return QString::fromLatin1("PepType_SETP(reinterpret_cast<SbkEnumType *>(%1))->converter") + .arg(cpythonTypeNameExt(type)); + + if (type->isArray()) { + qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName(); + return {}; + } + + /* the typedef'd primitive types case */ + auto pte = std::dynamic_pointer_cast<const PrimitiveTypeEntry>(type); + if (!pte) { + qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName(); + return {}; + } + pte = basicReferencedTypeEntry(pte); + if (pte->isPrimitive() && !isCppPrimitive(pte) && !pte->customConversion()) { + return u"Shiboken::Conversions::PrimitiveTypeConverter<"_s + + pte->qualifiedCppName() + u">()"_s; + } + + return convertersVariableName(type->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + u']'; +} + +QString ShibokenGenerator::cpythonTypeNameExtSet(const TypeEntryCPtr &type) +{ + return cppApiVariableName(type->targetLangPackage()) + u'[' + + getTypeIndexVariableName(type) + "].type"_L1; +} + +QString ShibokenGenerator::cpythonTypeNameExtSet(const AbstractMetaType &type) +{ + return cppApiVariableName(type.typeEntry()->targetLangPackage()) + u'[' + + getTypeIndexVariableName(type) + "].type"_L1; +} + +QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntryCPtr &type) +{ + return "Shiboken::Module::get("_L1 + cppApiVariableName(type->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + "])"_L1; +} + +QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type) +{ + return u"Shiboken::Module::get("_s + cppApiVariableName(type.typeEntry()->targetLangPackage()) + + u'[' + getTypeIndexVariableName(type) + "])"_L1; +} + +QString ShibokenGenerator::fixedCppTypeName(const TargetToNativeConversion &toNative) +{ + if (toNative.sourceType()) + return fixedCppTypeName(toNative.sourceType()); + return toNative.sourceTypeName(); +} +QString ShibokenGenerator::fixedCppTypeName(const AbstractMetaType &type) +{ + return fixedCppTypeName(type.typeEntry(), type.cppSignature()); +} + +static QString _fixedCppTypeName(QString typeName) +{ + typeName.remove(u' '); + typeName.replace(u'.', u'_'); + typeName.replace(u',', u'_'); + typeName.replace(u'<', u'_'); + typeName.replace(u'>', u'_'); + typeName.replace(u"::"_s, u"_"_s); + typeName.replace(u"*"_s, u"PTR"_s); + typeName.replace(u"&"_s, u"REF"_s); + return typeName; +} +QString ShibokenGenerator::fixedCppTypeName(const TypeEntryCPtr &type, QString typeName) +{ + if (typeName.isEmpty()) + typeName = type->qualifiedCppName(); + if (!type->generateCode()) { + typeName.prepend(u'_'); + typeName.prepend(type->targetLangPackage()); + } + return _fixedCppTypeName(typeName); +} + +QString ShibokenGenerator::pythonPrimitiveTypeName(const QString &cppTypeName) +{ + const auto &mapping = primitiveTypesCorrespondences(); + const auto it = mapping.constFind(cppTypeName); + if (it == mapping.cend()) + throw Exception(u"Primitive type not found: "_s + cppTypeName); + return it.value(); +} + +QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunctionCPtr &func) +{ + QString op = Generator::pythonOperatorFunctionName(func->originalName()); + if (op.isEmpty()) { + qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func.get()); + return "__UNKNOWN_OPERATOR__"_L1; + } + if (func->arguments().isEmpty()) { + if (op == u"__sub__") + op = u"__neg__"_s; + else if (op == u"__add__") + op = u"__pos__"_s; + } else if (func->isStatic() && func->arguments().size() == 2) { + // If a operator overload function has 2 arguments and + // is static we assume that it is a reverse operator. + op = op.insert(2, u'r'); + } + return op; +} + +bool ShibokenGenerator::isNumber(const QString &cpythonApiName) +{ + return cpythonApiName == pyFloatT || cpythonApiName == pyLongT + || cpythonApiName == pyBoolT; +} + +static std::optional<TypeSystem::CPythonType> + targetLangApiCPythonType(const PrimitiveTypeEntryCPtr &t) +{ + if (!t->hasTargetLangApiType()) + return {}; + const auto cte = t->targetLangApiType(); + if (cte->type() != TypeEntry::PythonType) + return {}; + return std::static_pointer_cast<const PythonTypeEntry>(cte)->cPythonType(); +} + +bool ShibokenGenerator::isNumber(const TypeEntryCPtr &type) +{ + if (!type->isPrimitive()) + return false; + const auto pte = basicReferencedTypeEntry(type); + const auto cPythonTypeOpt = targetLangApiCPythonType(pte); + // FIXME PYSIDE-1660: Return false here after making primitive types built-in? + if (!cPythonTypeOpt.has_value()) { + const auto &mapping = primitiveTypesCorrespondences(); + const auto it = mapping.constFind(pte->name()); + return it != mapping.cend() && isNumber(it.value()); + } + const auto cPythonType = cPythonTypeOpt.value(); + return cPythonType == TypeSystem::CPythonType::Bool + || cPythonType == TypeSystem::CPythonType::Float + || cPythonType == TypeSystem::CPythonType::Integer; +} + +bool ShibokenGenerator::isNumber(const AbstractMetaType &type) +{ + return isNumber(type.typeEntry()); +} + +bool ShibokenGenerator::isPyInt(const TypeEntryCPtr &type) +{ + if (!type->isPrimitive()) + return false; + const auto pte = basicReferencedTypeEntry(type); + const auto cPythonTypeOpt = targetLangApiCPythonType(pte); + // FIXME PYSIDE-1660: Return false here after making primitive types built-in? + if (!cPythonTypeOpt.has_value()) { + const auto &mapping = primitiveTypesCorrespondences(); + const auto it = mapping.constFind(pte->name()); + return it != mapping.cend() && it.value() == pyLongT; + } + return cPythonTypeOpt.value() == TypeSystem::CPythonType::Integer; +} + +bool ShibokenGenerator::isPyInt(const AbstractMetaType &type) +{ + return isPyInt(type.typeEntry()); +} + +bool ShibokenGenerator::isNullPtr(const QString &value) +{ + return value == u"0" || value == u"nullptr" + || value == u"NULLPTR" || value == u"{}"; +} + +QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType) +{ + const auto typeEntry = metaType.typeEntry(); + if (typeEntry->isCustom()) { + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry); + if (cte->hasCheckFunction()) + return cte->checkFunction(); + throw Exception(msgUnknownCheckFunction(typeEntry)); + } + + if (metaType.isExtendedCppPrimitive()) { + if (metaType.isCString()) + return u"Shiboken::String::check"_s; + if (metaType.isVoidPointer()) + return u"true"_s; + return cpythonCheckFunction(typeEntry); + } + + if (typeEntry->isContainer()) { + QString typeCheck = u"Shiboken::Conversions::"_s; + ContainerTypeEntry::ContainerKind type = + std::static_pointer_cast<const ContainerTypeEntry>(typeEntry)->containerKind(); + if (type == ContainerTypeEntry::ListContainer + || type == ContainerTypeEntry::SetContainer) { + const QString containerType = type == ContainerTypeEntry::SetContainer + ? u"Iterable"_s : u"Sequence"_s; + const AbstractMetaType &type = metaType.instantiations().constFirst(); + if (type.isPointerToWrapperType()) { + typeCheck += u"check"_s + containerType + u"Types("_s + + cpythonTypeNameExt(type) + u", "_s; + } else if (type.isWrapperType()) { + typeCheck += u"convertible"_s + containerType + + u"Types("_s + cpythonTypeNameExt(type) + u", "_s; + } else { + typeCheck += u"convertible"_s + containerType + + u"Types("_s + converterObject(type) + u", "_s; + } + } else if (type == ContainerTypeEntry::MapContainer + || type == ContainerTypeEntry::MultiMapContainer + || type == ContainerTypeEntry::PairContainer) { + + QString pyType; + if (type == ContainerTypeEntry::PairContainer) + pyType = u"Pair"_s; + else if (type == ContainerTypeEntry::MultiMapContainer) + pyType = u"MultiDict"_s; + else + pyType = u"Dict"_s; + + const AbstractMetaType &firstType = metaType.instantiations().constFirst(); + const AbstractMetaType &secondType = metaType.instantiations().constLast(); + if (firstType.isPointerToWrapperType() && secondType.isPointerToWrapperType()) { + QTextStream(&typeCheck) << "check" << pyType << "Types(" + << cpythonTypeNameExt(firstType) << ", " + << cpythonTypeNameExt(secondType) << ", "; + } else { + QTextStream(&typeCheck) << "convertible" << pyType << "Types(" + << converterObject(firstType) << ", " + << (firstType.isPointerToWrapperType() ? "true" : "false") + << ", " << converterObject(secondType) << ", " + << (secondType.isPointerToWrapperType() ? "true" :"false") + << ", "; + } + } + return typeCheck; + } + return cpythonCheckFunction(typeEntry); +} + +QString ShibokenGenerator::cpythonCheckFunction(TypeEntryCPtr type) +{ + if (type->isCustom()) { + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(type); + if (cte->hasCheckFunction()) + return cte->checkFunction(); + throw Exception(msgUnknownCheckFunction(type)); + } + + if (type->isEnum() || type->isFlags() || type->isWrapperType()) + return u"SbkObject_TypeCheck("_s + cpythonTypeNameExt(type) + u", "_s; + + if (type->isPrimitive()) + type = basicReferencedTypeEntry(type); + + if (auto tla = type->targetLangApiType()) { + if (tla->hasCheckFunction()) + return tla->checkFunction(); + } + + if (isExtendedCppPrimitive(type)) + return pythonPrimitiveTypeName(type->name()) + u"_Check"_s; + + return cpythonIsConvertibleFunction(type); +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const TypeEntryCPtr &type) +{ + if (type->isWrapperType()) { + QString result = u"Shiboken::Conversions::"_s; + bool isValue = false; + if (type->isValue()) { + const auto cte = std::static_pointer_cast<const ComplexTypeEntry>(type); + isValue = !cte->isValueTypeWithCopyConstructorOnly(); + } + result += isValue ? u"isPythonToCppValueConvertible"_s + : u"isPythonToCppPointerConvertible"_s; + result += u"("_s + cpythonTypeNameExt(type) + u", "_s; + return result; + } + return QString::fromLatin1("Shiboken::Conversions::isPythonToCppConvertible(%1, ") + .arg(converterObject(type)); +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType &metaType) +{ + const auto typeEntry = metaType.typeEntry(); + if (typeEntry->isCustom()) { + const auto cte = std::static_pointer_cast<const CustomTypeEntry>(typeEntry); + if (cte->hasCheckFunction()) + return cte->checkFunction(); + throw Exception(msgUnknownCheckFunction(typeEntry)); + } + + QString result = u"Shiboken::Conversions::"_s; + if (metaType.generateOpaqueContainer()) { + result += u"pythonToCppReferenceConversion("_s + + converterObject(metaType) + u", "_s; + return result; + } + if (metaType.isWrapperType()) { + if (metaType.isPointer() || metaType.isValueTypeWithCopyConstructorOnly()) { + result += u"pythonToCppPointerConversion"_s; + } else if (metaType.referenceType() == LValueReference + || (metaType.referenceType() == RValueReference && typeEntry->isObject())) { + result += u"pythonToCppReferenceConversion"_s; + } else { + result += u"pythonToCppValueConversion"_s; + } + result += u'(' + cpythonTypeNameExt(metaType) + u", "_s; + return result; + } + result += u"pythonToCppConversion("_s + converterObject(metaType); + // Write out array sizes if known + const AbstractMetaTypeList nestedArrayTypes = metaType.nestedArrayTypes(); + if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) { + const int dim1 = metaType.arrayElementCount(); + const int dim2 = nestedArrayTypes.constFirst().isArray() + ? nestedArrayTypes.constFirst().arrayElementCount() : -1; + result += u", "_s + QString::number(dim1) + + u", "_s + QString::number(dim2); + } + result += u", "_s; + return result; +} + +QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg) +{ + return cpythonIsConvertibleFunction(metaArg.type()); +} + +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaClassCPtr &metaClass) +{ + return u"Shiboken::Conversions::pythonToCppPointer("_s + + cpythonTypeNameExt(metaClass->typeEntry()) + u", "_s; +} + +QString ShibokenGenerator::cpythonToCppConversionFunction(const AbstractMetaType &type) +{ + if (type.isWrapperType()) { + return u"Shiboken::Conversions::pythonToCpp"_s + + (type.isPointer() ? u"Pointer"_s : u"Copy"_s) + + u'(' + cpythonTypeNameExt(type) + u", "_s; + } + return "Shiboken::Conversions::pythonToCppCopy("_L1 + + converterObject(type) + ", "_L1; +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType &type) +{ + if (type.isWrapperType()) { + QString conversion; + if (type.referenceType() == LValueReference + && !(type.isValue() && type.isConstant()) && !type.isPointer()) { + conversion = u"reference"_s; + } else if (type.isValue() || type.isSmartPointer()) { + conversion = u"copy"_s; + } else { + conversion = u"pointer"_s; + } + QString result = u"Shiboken::Conversions::"_s + conversion + + u"ToPython("_s + + cpythonTypeNameExt(type) + u", "_s; + if (conversion != u"pointer") + result += u'&'; + return result; + } + + const auto indirections = type.indirections() - 1; + return u"Shiboken::Conversions::copyToPython("_s + converterObject(type) + + u", "_s + AbstractMetaType::dereferencePrefix(indirections); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass) +{ + return cpythonToPythonConversionFunction(metaClass->typeEntry()); +} + +QString ShibokenGenerator::cpythonToPythonConversionFunction(const TypeEntryCPtr &type) +{ + if (type->isWrapperType()) { + const QString conversion = type->isValue() ? u"copy"_s : u"pointer"_s; + QString result = u"Shiboken::Conversions::"_s + conversion + + u"ToPython("_s + cpythonTypeNameExt(type) + + u", "_s; + if (conversion != u"pointer") + result += u'&'; + return result; + } + + return u"Shiboken::Conversions::copyToPython("_s + + converterObject(type) + u", &"_s; +} + +QString ShibokenGenerator::argumentString(const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &argument, + Options options) const +{ + auto type = options.testFlag(OriginalTypeDescription) + ? argument.type() : argument.modifiedType(); + + + QString arg = translateType(type, func->implementingClass(), options); + + if (argument.isTypeModified()) + arg.replace(u'$', u'.'); // Haehh? + + // "int a", "int a[]" + const auto arrayPos = arg.indexOf(u'['); + if (arrayPos != -1) + arg.insert(arrayPos, u' ' + argument.name()); + else + arg.append(u' ' + argument.name()); + + if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues && + !argument.originalDefaultValueExpression().isEmpty()) + { + QString default_value = argument.originalDefaultValueExpression(); + if (default_value == u"NULL") + default_value = NULL_PTR; + + //WORKAROUND: fix this please + if (default_value.startsWith(u"new ")) + default_value.remove(0, 4); + + arg += u" = "_s + default_value; + } + + return arg; +} + +void ShibokenGenerator::writeArgument(TextStream &s, + const AbstractMetaFunctionCPtr &func, + const AbstractMetaArgument &argument, + Options options) const +{ + s << argumentString(func, argument, options); +} + +void ShibokenGenerator::writeFunctionArguments(TextStream &s, + const AbstractMetaFunctionCPtr &func, + Options options) const +{ + int argUsed = 0; + if (func->isUserAddedPythonOverride()) { + s << "Shiboken::GilState &gil, PyObject *" << PYTHON_OVERRIDE_VAR; + argUsed += 2; + } + for (const auto &arg : func->arguments()) { + if (options.testFlag(Generator::SkipRemovedArguments) && arg.isModifiedRemoved()) + continue; + + if (argUsed != 0) + s << ", "; + writeArgument(s, func, arg, options); + argUsed++; + } +} + +GeneratorContext ShibokenGenerator::contextForClass(const AbstractMetaClassCPtr &c) const +{ + GeneratorContext result = Generator::contextForClass(c); + if (shouldGenerateCppWrapper(c)) { + result.m_type = GeneratorContext::WrappedClass; + result.m_wrappername = wrapperName(c); + } + return result; +} + +QString ShibokenGenerator::functionReturnType(const AbstractMetaFunctionCPtr &func, Options options) const +{ + if (func->isTypeModified() && !options.testFlag(OriginalTypeDescription)) + return func->modifiedTypeName(); + return translateType(func->type(), func->implementingClass(), options); +} + +QString ShibokenGenerator::functionSignature(const AbstractMetaFunctionCPtr &func, + const QString &prepend, + const QString &append, + Options options, + int /* argCount */) const +{ + StringStream s(TextStream::Language::Cpp); + // The actual function + if (!options.testFlag(Option::SkipDefaultValues) && func->isStatic()) // Declaration + s << "static "; + if (func->isEmptyFunction() || func->needsReturnType()) + s << functionReturnType(func, options) << ' '; + else + options |= Generator::SkipReturnType; + + // name + QString name(func->originalName()); + if (func->isConstructor()) + name = wrapperName(func->ownerClass()); + + s << prepend << name << append << '('; + writeFunctionArguments(s, func, options); + s << ')'; + + if (func->isConstant()) + s << " const"; + + if (func->exceptionSpecification() == ExceptionSpecification::NoExcept) + s << " noexcept"; + + return s; +} + +void ShibokenGenerator::writeArgumentNames(TextStream &s, + const AbstractMetaFunctionCPtr &func, + Options options) +{ + const AbstractMetaArgumentList arguments = func->arguments(); + int argCount = 0; + for (const auto &argument : arguments) { + const int index = argument.argumentIndex() + 1; + if (options.testFlag(Generator::SkipRemovedArguments) && argument.isModifiedRemoved()) + continue; + const auto &type = argument.type(); + if (argCount > 0) + s << ", "; + const bool isVirtualCall = options.testFlag(Option::VirtualCall); + const bool useStdMove = isVirtualCall && type.isUniquePointer() && type.passByValue(); + s << (useStdMove ? stdMove(argument.name()) : argument.name()); + + if (!isVirtualCall + && (func->hasConversionRule(TypeSystem::NativeCode, index) + || func->hasConversionRule(TypeSystem::TargetLangCode, index)) + && !func->isConstructor()) { + s << CONV_RULE_OUT_VAR_SUFFIX; + } + + argCount++; + } +} + +void ShibokenGenerator::writeFunctionCall(TextStream &s, + const AbstractMetaFunctionCPtr &func, + Options options) +{ + s << (func->isConstructor() ? func->ownerClass()->qualifiedCppName() : func->originalName()) + << '('; + writeArgumentNames(s, func, options); + s << ')'; +} + +ShibokenGenerator::ExtendedConverterData ShibokenGenerator::getExtendedConverters() const +{ + ExtendedConverterData extConvs; + for (const auto &metaClass : api().classes()) { + // Use only the classes for the current module. + if (!shouldGenerate(metaClass->typeEntry())) + continue; + const auto &overloads = metaClass->operatorOverloads(OperatorQueryOption::ConversionOp); + for (const auto &convOp : overloads) { + // Get only the conversion operators that return a type from another module, + // that are value-types and were not removed in the type system. + const auto convType = convOp->type().typeEntry(); + if (convType->generateCode() || !convType->isValue() + || convOp->isModifiedRemoved()) + continue; + extConvs[convType].append(convOp->ownerClass()); + } + } + return extConvs; +} + +QList<CustomConversionPtr> ShibokenGenerator::getPrimitiveCustomConversions() +{ + QList<CustomConversionPtr> conversions; + const auto &primitiveTypeList = primitiveTypes(); + for (const auto &type : primitiveTypeList) { + if (type->shouldGenerate() && isUserPrimitive(type) && type->hasCustomConversion()) + conversions << type->customConversion(); + } + return conversions; +} + +static QString getArgumentsFromMethodCall(const QString &str) +{ + // It would be way nicer to be able to use a Perl like + // regular expression that accepts temporary variables + // to count the parenthesis. + // For more information check this: + // http://perl.plover.com/yak/regex/samples/slide083.html + static QLatin1String funcCall("%CPPSELF.%FUNCTION_NAME"); + auto pos = str.indexOf(funcCall); + if (pos == -1) + return QString(); + pos = pos + funcCall.size(); + while (str.at(pos) == u' ' || str.at(pos) == u'\t') + ++pos; + if (str.at(pos) == u'(') + ++pos; + int begin = pos; + int counter = 1; + while (counter != 0) { + if (str.at(pos) == u'(') + ++counter; + else if (str.at(pos) == u')') + --counter; + ++pos; + } + return str.mid(begin, pos-begin-1); +} + +QString ShibokenGenerator::getCodeSnippets(const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) +{ + QString code; + for (const CodeSnip &snip : codeSnips) { + if ((position != TypeSystem::CodeSnipPositionAny && snip.position != position) || !(snip.language & language)) + continue; + code.append(snip.code()); + } + return code; +} + +void ShibokenGenerator::processClassCodeSnip(QString &code, const GeneratorContext &context) const +{ + auto metaClass = context.metaClass(); + // Replace template variable by the Python Type object + // for the class context in which the variable is used. + code.replace(u"%PYTHONTYPEOBJECT"_s, + u"(*"_s + cpythonTypeName(metaClass) + u')'); + const QString className = context.effectiveClassName(); + code.replace(u"%TYPE"_s, className); + code.replace(u"%CPPTYPE"_s, metaClass->name()); + + processCodeSnip(code, context.effectiveClassName()); +} + +void ShibokenGenerator::processCodeSnip(QString &code) const +{ + // replace "toPython" converters + replaceConvertToPythonTypeSystemVariable(code); + + // replace "toCpp" converters + replaceConvertToCppTypeSystemVariable(code); + + // replace "isConvertible" check + replaceIsConvertibleToCppTypeSystemVariable(code); + + // replace "checkType" check + replaceTypeCheckTypeSystemVariable(code); +} + +void ShibokenGenerator::processCodeSnip(QString &code, const QString &context) const +{ + try { + processCodeSnip(code); + } catch (const std::exception &e) { + throw Exception(msgSnippetError(context, e.what())); + } +} + +ShibokenGenerator::ArgumentVarReplacementList + ShibokenGenerator::getArgumentReplacement(const AbstractMetaFunctionCPtr &func, + bool usePyArgs, TypeSystem::Language language, + const AbstractMetaArgument *lastArg) +{ + ArgumentVarReplacementList argReplacements; + TypeSystem::Language convLang = (language == TypeSystem::TargetLangCode) + ? TypeSystem::NativeCode : TypeSystem::TargetLangCode; + int removed = 0; + for (qsizetype i = 0; i < func->arguments().size(); ++i) { + const AbstractMetaArgument &arg = func->arguments().at(i); + QString argValue; + if (language == TypeSystem::TargetLangCode) { + const bool hasConversionRule = func->hasConversionRule(convLang, i + 1); + const bool argRemoved = arg.isModifiedRemoved(); + if (argRemoved) + ++removed; + if (argRemoved && hasConversionRule) + argValue = arg.name() + CONV_RULE_OUT_VAR_SUFFIX; + else if (argRemoved || (lastArg && arg.argumentIndex() > lastArg->argumentIndex())) + argValue = CPP_ARG_REMOVED(i); + if (!argRemoved && argValue.isEmpty()) { + int argPos = i - removed; + AbstractMetaType type = arg.modifiedType(); + if (type.typeEntry()->isCustom()) { + argValue = usePyArgs + ? pythonArgsAt(argPos) : PYTHON_ARG; + } else { + argValue = hasConversionRule + ? arg.name() + CONV_RULE_OUT_VAR_SUFFIX + : CPP_ARG_N(argPos); + const auto generatorArg = GeneratorArgument::fromMetaType(type); + AbstractMetaType::applyDereference(&argValue, generatorArg.indirections); + } + } + } else { + argValue = arg.name(); + } + if (!argValue.isEmpty()) + argReplacements << ArgumentVarReplacementPair(arg, argValue); + + } + return argReplacements; +} + +void ShibokenGenerator::writeClassCodeSnips(TextStream &s, + const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language, + const GeneratorContext &context) const +{ + QString code = getCodeSnippets(codeSnips, position, language); + if (code.isEmpty()) + return; + processClassCodeSnip(code, context); + s << "// Begin code injection\n" << code << "// End of code injection\n\n"; +} + +void ShibokenGenerator::writeCodeSnips(TextStream &s, + const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language) const +{ + QString code = getCodeSnippets(codeSnips, position, language); + if (code.isEmpty()) + return; + processCodeSnip(code); + s << "// Begin code injection\n" << code << "// End of code injection\n\n"; +} + +static void replacePyArg0(TypeSystem::Language language, QString *code) +{ + static constexpr auto pyArg0 = "%PYARG_0"_L1; + + if (!code->contains(pyArg0)) + return; + if (language != TypeSystem::NativeCode) { + code->replace(pyArg0, PYTHON_RETURN_VAR); + return; + } + + // pyResult is an AutoDecRef in overridden methods of wrapper classes which + // has a cast operator for PyObject *. This may however not work in all + // situations (fex _PyVarObject_CAST(op) defined as ((PyVarObject*)(op))). + // Append ".object()" unless it is followed by a '.' indicating explicit + // AutoDecRef member invocation. + static const QString pyObject = PYTHON_RETURN_VAR + u".object()"_s; + qsizetype pos{}; + while ( (pos = code->indexOf(pyArg0)) >= 0) { + const auto next = pos + pyArg0.size(); + const bool memberInvocation = next < code->size() && code->at(next) == u'.'; + code->replace(pos, pyArg0.size(), + memberInvocation ? PYTHON_RETURN_VAR : pyObject); + } +} + +void ShibokenGenerator::writeCodeSnips(TextStream &s, + const CodeSnipList &codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language, + const AbstractMetaFunctionCPtr &func, + bool usePyArgs, + const AbstractMetaArgument *lastArg) const +{ + QString code = getCodeSnippets(codeSnips, position, language); + if (code.isEmpty()) + return; + + // Replace %PYARG_# variables. + replacePyArg0(language, &code); + + static const QRegularExpression pyArgsRegex("%PYARG_(\\d+)"_L1); + Q_ASSERT(pyArgsRegex.isValid()); + if (language == TypeSystem::TargetLangCode) { + if (usePyArgs) { + code.replace(pyArgsRegex, PYTHON_ARGS + u"[\\1-1]"_s); + } else { + static const QRegularExpression pyArgsRegexCheck("%PYARG_([2-9]+)"_L1); + Q_ASSERT(pyArgsRegexCheck.isValid()); + const QRegularExpressionMatch match = pyArgsRegexCheck.match(code); + if (match.hasMatch()) { + qCWarning(lcShiboken).noquote().nospace() + << msgWrongIndex("%PYARG", match.captured(1), func.get()); + return; + } + code.replace(u"%PYARG_1"_s, PYTHON_ARG); + } + } else { + // Replaces the simplest case of attribution to a + // Python argument on the binding virtual method. + static const QRegularExpression pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"_L1); + Q_ASSERT(pyArgsAttributionRegex.isValid()); + code.replace(pyArgsAttributionRegex, u"PyTuple_SET_ITEM("_s + + PYTHON_ARGS + u".object(), \\1-1, \\2)"_s); + code.replace(pyArgsRegex, u"PyTuple_GET_ITEM("_s + + PYTHON_ARGS + u".object(), \\1-1)"_s); + } + + // Replace %ARG#_TYPE variables. + const AbstractMetaArgumentList &arguments = func->arguments(); + for (const AbstractMetaArgument &arg : arguments) { + QString argTypeVar = u"%ARG"_s + QString::number(arg.argumentIndex() + 1) + + u"_TYPE"_s; + QString argTypeVal = arg.type().cppSignature(); + code.replace(argTypeVar, argTypeVal); + } + + static const QRegularExpression cppArgTypeRegexCheck("%ARG(\\d+)_TYPE"_L1); + Q_ASSERT(cppArgTypeRegexCheck.isValid()); + QRegularExpressionMatchIterator rit = cppArgTypeRegexCheck.globalMatch(code); + while (rit.hasNext()) { + QRegularExpressionMatch match = rit.next(); + qCWarning(lcShiboken).noquote().nospace() + << msgWrongIndex("%ARG#_TYPE", match.captured(1), func.get()); + } + + // Replace template variable for return variable name. + if (func->isConstructor()) { + code.replace(u"%0."_s, u"cptr->"_s); + code.replace(u"%0"_s, u"cptr"_s); + } else if (!func->isVoid()) { + QString returnValueOp = func->type().isPointerToWrapperType() + ? u"%1->"_s : u"%1."_s; + if (func->type().isWrapperType()) + code.replace(u"%0."_s, returnValueOp.arg(CPP_RETURN_VAR)); + code.replace(u"%0"_s, CPP_RETURN_VAR); + } + + // Replace template variable for self Python object. + QString pySelf = language == TypeSystem::NativeCode + ? u"pySelf"_s : u"self"_s; + code.replace(u"%PYSELF"_s, pySelf); + + // Replace template variable for a pointer to C++ of this object. + if (func->implementingClass()) { + QString replacement = func->isStatic() ? u"%1::"_s : u"%1->"_s; + QString cppSelf; + if (func->isStatic()) + cppSelf = func->ownerClass()->qualifiedCppName(); + else if (language == TypeSystem::NativeCode) + cppSelf = u"this"_s; + else + cppSelf = CPP_SELF_VAR; + + // On comparison operator CPP_SELF_VAR is always a reference. + if (func->isComparisonOperator()) + replacement = u"%1."_s; + + if (func->isVirtual() && !func->isAbstract() && (!avoidProtectedHack() || !func->isProtected())) { + QString methodCallArgs = getArgumentsFromMethodCall(code); + if (!methodCallArgs.isEmpty()) { + const QString pattern = u"%CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u')'; + QString replacement = u"(Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>("_s + + pySelf + u")) ? "_s; + if (func->name() == u"metaObject") { + QString wrapperClassName = wrapperName(func->ownerClass()); + QString cppSelfVar = avoidProtectedHack() + ? u"%CPPSELF"_s + : u"reinterpret_cast<"_s + wrapperClassName + u" *>(%CPPSELF)"_s; + replacement += cppSelfVar + u"->::"_s + wrapperClassName + + u"::%FUNCTION_NAME("_s + methodCallArgs + + u") : %CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u"))"_s; + } else { + replacement += u"%CPPSELF->::%TYPE::%FUNCTION_NAME("_s + methodCallArgs + + u") : %CPPSELF.%FUNCTION_NAME("_s + methodCallArgs + u"))"_s; + } + code.replace(pattern, replacement); + } + } + + code.replace(u"%CPPSELF."_s, replacement.arg(cppSelf)); + code.replace(u"%CPPSELF"_s, cppSelf); + + if (code.indexOf(u"%BEGIN_ALLOW_THREADS") > -1) { + if (code.count(u"%BEGIN_ALLOW_THREADS"_s) == code.count(u"%END_ALLOW_THREADS"_s)) { + code.replace(u"%BEGIN_ALLOW_THREADS"_s, BEGIN_ALLOW_THREADS); + code.replace(u"%END_ALLOW_THREADS"_s, END_ALLOW_THREADS); + } else { + qCWarning(lcShiboken) << "%BEGIN_ALLOW_THREADS and %END_ALLOW_THREADS mismatch"; + } + } + + // replace template variable for the Python Type object for the + // class implementing the method in which the code snip is written + if (func->isStatic()) { + code.replace(u"%PYTHONTYPEOBJECT"_s, + u"(*"_s + cpythonTypeName(func->implementingClass()) + u')'); + } else { + code.replace(u"%PYTHONTYPEOBJECT."_s, pySelf + u"->ob_type->"_s); + code.replace(u"%PYTHONTYPEOBJECT"_s, pySelf + u"->ob_type"_s); + } + } + + // Replaces template %ARGUMENT_NAMES and %# variables by argument variables and values. + // Replaces template variables %# for individual arguments. + const ArgumentVarReplacementList &argReplacements = getArgumentReplacement(func, usePyArgs, language, lastArg); + + QStringList args; + for (const ArgumentVarReplacementPair &pair : argReplacements) { + if (pair.second.startsWith(CPP_ARG_REMOVED_PREFIX)) + continue; + args << pair.second; + } + code.replace(u"%ARGUMENT_NAMES"_s, args.join(u", "_s)); + + for (const ArgumentVarReplacementPair &pair : argReplacements) { + const AbstractMetaArgument &arg = pair.first; + int idx = arg.argumentIndex() + 1; + AbstractMetaType type = arg.modifiedType(); + if (type.isWrapperType()) { + QString replacement = pair.second; + const auto generatorArg = GeneratorArgument::fromMetaType(type); + if (generatorArg.indirections > 0) + AbstractMetaType::stripDereference(&replacement); + if (type.referenceType() == LValueReference || type.isPointer()) + code.replace(u'%' + QString::number(idx) + u'.', replacement + u"->"_s); + } + code.replace(CodeSnipAbstract::placeHolderRegex(idx), pair.second); + } + + if (language == TypeSystem::NativeCode) { + // Replaces template %PYTHON_ARGUMENTS variable with a pointer to the Python tuple + // containing the converted virtual method arguments received from C++ to be passed + // to the Python override. + code.replace(u"%PYTHON_ARGUMENTS"_s, PYTHON_ARGS); + + // replace variable %PYTHON_METHOD_OVERRIDE for a pointer to the Python method + // override for the C++ virtual method in which this piece of code was inserted + code.replace(u"%PYTHON_METHOD_OVERRIDE"_s, PYTHON_OVERRIDE_VAR); + } + + if (avoidProtectedHack()) { + // If the function being processed was added by the user via type system, + // Shiboken needs to find out if there are other overloads for the same method + // name and if any of them is of the protected visibility. This is used to replace + // calls to %FUNCTION_NAME on user written custom code for calls to the protected + // dispatcher. + bool isProtected = func->isProtected(); + auto owner = func->ownerClass(); + if (!isProtected && func->isUserAdded() && owner != nullptr) { + const auto &funcs = getFunctionGroups(owner).value(func->name()); + isProtected = std::any_of(funcs.cbegin(), funcs.cend(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isProtected(); + }); + } + + if (isProtected) { + code.replace(u"%TYPE::%FUNCTION_NAME"_s, + wrapperName(func->ownerClass()) + "::"_L1 + + func->originalName() + "_protected"_L1); + code.replace(u"%FUNCTION_NAME"_s, + func->originalName() + u"_protected"_s); + } + } + + if (func->isConstructor() && shouldGenerateCppWrapper(func->ownerClass())) + code.replace(u"%TYPE"_s, wrapperName(func->ownerClass())); + + if (func->ownerClass()) + code.replace(u"%CPPTYPE"_s, func->ownerClass()->name()); + + replaceTemplateVariables(code, func); + + processCodeSnip(code, func->classQualifiedSignature()); + s << "// Begin code injection\n" << code << "// End of code injection\n\n"; +} + +// Returns true if the string is an expression, +// and false if it is a variable. +static bool isVariable(const QString &code) +{ + static const QRegularExpression expr("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$"_L1); + Q_ASSERT(expr.isValid()); + return expr.match(code.trimmed()).hasMatch(); +} + +// A miniature normalizer that puts a type string into a format +// suitable for comparison with AbstractMetaType::cppSignature() +// result. +static QString miniNormalizer(const QString &varType) +{ + QString normalized = varType.trimmed(); + if (normalized.isEmpty()) + return normalized; + if (normalized.startsWith(u"::")) + normalized.remove(0, 2); + QString suffix; + while (normalized.endsWith(u'*') || normalized.endsWith(u'&')) { + suffix.prepend(normalized.at(normalized.size() - 1)); + normalized.chop(1); + normalized = normalized.trimmed(); + } + const QString result = normalized + u' ' + suffix; + return result.trimmed(); +} +// The position must indicate the first character after the opening '('. +// ATTENTION: do not modify this function to trim any resulting string! +// This must be done elsewhere. +static QString getConverterTypeSystemVariableArgument(const QString &code, int pos) +{ + QString arg; + int parenthesisDepth = 0; + int count = 0; + while (pos + count < code.size()) { + char c = code.at(pos+count).toLatin1(); // toAscii is gone + if (c == '(') { + ++parenthesisDepth; + } else if (c == ')') { + if (parenthesisDepth == 0) { + arg = code.mid(pos, count).trimmed(); + break; + } + --parenthesisDepth; + } + ++count; + } + if (parenthesisDepth != 0) + throw Exception("Unbalanced parenthesis on type system converter variable call."); + return arg; +} + +const QHash<int, QString> &ShibokenGenerator::typeSystemConvName() +{ + static const QHash<int, QString> result = { + {TypeSystemCheckFunction, u"checkType"_s}, + {TypeSystemIsConvertibleFunction, u"isConvertible"_s}, + {TypeSystemToCppFunction, u"toCpp"_s}, + {TypeSystemToPythonFunction, u"toPython"_s} + }; + return result; +} + +using StringPair = std::pair<QString, QString>; + +void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, + QString &code) const +{ + QList<StringPair> replacements; + QRegularExpressionMatchIterator rit = typeSystemConvRegExps()[converterVariable].globalMatch(code); + while (rit.hasNext()) { + const QRegularExpressionMatch match = rit.next(); + const QStringList list = match.capturedTexts(); + QString conversionString = list.constFirst(); + const QString &conversionTypeName = list.constLast(); + QString message; + const auto conversionTypeO = AbstractMetaType::fromString(conversionTypeName, &message); + if (!conversionTypeO.has_value()) { + throw Exception(msgCannotFindType(conversionTypeName, + typeSystemConvName().value(converterVariable), + message)); + } + const auto conversionType = conversionTypeO.value(); + QString conversion; + switch (converterVariable) { + case TypeSystemToCppFunction: { + StringStream c(TextStream::Language::Cpp); + int end = match.capturedStart(); + int start = end; + while (start > 0 && code.at(start) != u'\n') + --start; + while (code.at(start).isSpace()) + ++start; + QString varType = code.mid(start, end - start); + conversionString = varType + list.constFirst(); + varType = miniNormalizer(varType); + QString varName = list.at(1).trimmed(); + if (!varType.isEmpty()) { + c << getFullTypeName(conversionType) << ' ' << varName + << minimalConstructorExpression(api(), conversionType) << ";\n"; + } + c << cpythonToCppConversionFunction(conversionType); + QString prefix; + if (!AbstractMetaType::stripDereference(&varName)) + prefix = u'&'; + QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); + conversionString += arg; + c << arg << ", " << prefix << '(' << varName << ')'; + conversion = c.toString(); + break; + } + case TypeSystemCheckFunction: + conversion = cpythonCheckFunction(conversionType); + if (conversionType.typeEntry()->isPrimitive() + && (conversionType.typeEntry()->name() == cPyObjectT + || !conversion.endsWith(u' '))) { + conversion += u'('; + break; + } + Q_FALLTHROUGH(); + case TypeSystemIsConvertibleFunction: + if (conversion.isEmpty()) + conversion = cpythonIsConvertibleFunction(conversionType); + Q_FALLTHROUGH(); + case TypeSystemToPythonFunction: + if (conversion.isEmpty()) + conversion = cpythonToPythonConversionFunction(conversionType); + Q_FALLTHROUGH(); + default: { + QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); + conversionString += arg; + if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) { + QString m; + QTextStream(&m) << "Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '" + << code << '\''; + throw Exception(m); + } + if (conversion.contains(u"%in")) { + conversion.prepend(u'('); + conversion.replace(u"%in"_s, arg); + } else { + conversion += arg; + } + } + } + replacements.append(std::make_pair(conversionString, conversion)); + } + for (const StringPair &rep : std::as_const(replacements)) + code.replace(rep.first, rep.second); +} + +bool ShibokenGenerator::injectedCodeCallsCppFunction(const GeneratorContext &context, + const AbstractMetaFunctionCPtr &func) +{ + if (func->injectedCodeContains(u"%FUNCTION_NAME(")) + return true; + QString funcCall = func->originalName() + u'('; + if (func->isConstructor()) + funcCall.prepend(u"new "_s); + if (func->injectedCodeContains(funcCall)) + return true; + if (!func->isConstructor()) + return false; + if (func->injectedCodeContains(u"new %TYPE(")) + return true; + const auto owner = func->ownerClass(); + if (!owner->isPolymorphic()) + return false; + const QString wrappedCtorCall = u"new "_s + context.effectiveClassName() + u'('; + return func->injectedCodeContains(wrappedCtorCall); +} + +bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClassCPtr &metaClass) +{ + return metaClass->isPolymorphic(); +} + +ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds( + const AbstractMetaClassCPtr &metaClass) +{ + AttroCheck result; + if (metaClass->typeEntry()->isSmartPointer()) { + result |= AttroCheckFlag::GetattroSmartPointer | AttroCheckFlag::SetattroSmartPointer; + } else { + if (getGeneratorClassInfo(metaClass).needsGetattroFunction) + result |= AttroCheckFlag::GetattroOverloads; + if (metaClass->queryFirstFunction(metaClass->functions(), + FunctionQueryOption::GetAttroFunction)) { + result |= AttroCheckFlag::GetattroUser; + } + if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT) + result |= AttroCheckFlag::SetattroQObject; + if (useOverrideCaching(metaClass)) + result |= AttroCheckFlag::SetattroMethodOverride; + if (metaClass->queryFirstFunction(metaClass->functions(), + FunctionQueryOption::SetAttroFunction)) { + result |= AttroCheckFlag::SetattroUser; + } + // PYSIDE-1255: If setattro is generated for a class inheriting + // QObject, the property code needs to be generated, too. + if ((result & AttroCheckFlag::SetattroMask) != 0 + && !result.testFlag(AttroCheckFlag::SetattroQObject) + && isQObject(metaClass)) { + result |= AttroCheckFlag::SetattroQObject; + } + } + return result; +} + +bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass) +{ + if (!metaClass) + return false; + if (metaClass->typeEntry()->isSmartPointer()) + return true; + const auto &functionGroup = getFunctionGroups(metaClass); + for (auto it = functionGroup.cbegin(), end = functionGroup.cend(); it != end; ++it) { + AbstractMetaFunctionCList overloads; + for (const auto &func : std::as_const(it.value())) { + if (func->isAssignmentOperator() || func->isConversionOperator() + || func->isModifiedRemoved() + || func->isPrivate() || func->ownerClass() != func->implementingClass() + || func->isConstructor() || func->isOperatorOverload()) + continue; + overloads.append(func); + } + if (overloads.isEmpty()) + continue; + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) + return true; + } + return false; +} + +AbstractMetaFunctionCList + ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClassCPtr &metaClass) +{ + AbstractMetaFunctionCList methods; + if (metaClass) { + const auto &functionGroups = getFunctionGroups(metaClass); + for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { + AbstractMetaFunctionCList overloads; + for (const auto &func : std::as_const(it.value())) { + if (func->isAssignmentOperator() || func->isConversionOperator() + || func->isModifiedRemoved() + || func->isPrivate() || func->ownerClass() != func->implementingClass() + || func->isConstructor() || func->isOperatorOverload()) + continue; + overloads.append(func); + } + if (overloads.isEmpty()) + continue; + if (OverloadData::hasStaticAndInstanceFunctions(overloads)) + methods.append(overloads.constFirst()); + } + } + return methods; +} + +AbstractMetaClassCPtr + ShibokenGenerator::getMultipleInheritingClass(const AbstractMetaClassCPtr &metaClass) +{ + if (!metaClass || metaClass->baseClassNames().isEmpty()) + return nullptr; + if (metaClass->baseClassNames().size() > 1) + return metaClass; + return getMultipleInheritingClass(metaClass->baseClass()); +} + +QString ShibokenGenerator::getModuleHeaderFileBaseName(const QString &moduleName) +{ + return moduleCppPrefix(moduleName).toLower() + "_python"_L1; +} + +QString ShibokenGenerator::getModuleHeaderFileName(const QString &moduleName) +{ + return getModuleHeaderFileBaseName(moduleName) + ".h"_L1; +} + +QString ShibokenGenerator::getPrivateModuleHeaderFileName(const QString &moduleName) +{ + return getModuleHeaderFileBaseName(moduleName) + "_p.h"_L1; +} + +IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClassCPtr &metaClass) const +{ + IncludeGroupList result; + const auto typeEntry = metaClass->typeEntry(); + //Extra includes + result.append(IncludeGroup{u"Extra includes"_s, + typeEntry->extraIncludes()}); + + result.append({u"Enum includes"_s, {}}); + for (const auto &cppEnum : metaClass->enums()) + result.back().includes.append(cppEnum.typeEntry()->extraIncludes()); + + result.append({u"Argument includes"_s, typeEntry->argumentIncludes()}); + const auto implicitConvs = implicitConversions(typeEntry); + for (const auto &f : implicitConvs) { + if (f->isConversionOperator()) { + const auto source = f->ownerClass(); + Q_ASSERT(source); + result.back().append(source->typeEntry()->include()); + } + } + return result; +} + +/* +static void dumpFunction(AbstractMetaFunctionList lst) +{ + qDebug() << "DUMP FUNCTIONS: "; + for (AbstractMetaFunction *func : std::as_const(lst)) + qDebug() << "*" << func->ownerClass()->name() + << func->signature() + << "Private: " << func->isPrivate() + << "Empty: " << func->isEmptyFunction() + << "Static:" << func->isStatic() + << "Signal:" << func->isSignal() + << "ClassImplements: " << (func->ownerClass() != func->implementingClass()) + << "is operator:" << func->isOperatorOverload() + << "is global:" << func->isInGlobalScope(); +} +*/ + +static bool isGroupable(const AbstractMetaFunctionCPtr &func) +{ + switch (func->functionType()) { + case AbstractMetaFunction::DestructorFunction: + case AbstractMetaFunction::SignalFunction: + case AbstractMetaFunction::GetAttroFunction: + case AbstractMetaFunction::SetAttroFunction: + case AbstractMetaFunction::ArrowOperator: // weird operator overloads + case AbstractMetaFunction::SubscriptOperator: + return false; + default: + break; + } + if (func->isModifiedRemoved() && !func->isAbstract()) + return false; + return true; +} + +static void insertIntoFunctionGroups(const AbstractMetaFunctionCList &lst, + ShibokenGenerator::FunctionGroups *results) +{ + for (const auto &func : lst) { + if (isGroupable(func)) + (*results)[func->name()].append(func); + } +} + +ShibokenGenerator::FunctionGroups ShibokenGenerator::getGlobalFunctionGroups() const +{ + FunctionGroups results; + insertIntoFunctionGroups(api().globalFunctions(), &results); + for (const auto &nsp : invisibleTopNamespaces()) + insertIntoFunctionGroups(nsp->functions(), &results); + return results; +} + +const GeneratorClassInfoCacheEntry & + ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClassCPtr &scope) +{ + auto cache = generatorClassInfoCache(); + auto it = cache->find(scope); + if (it == cache->end()) { + it = cache->insert(scope, {}); + auto &entry = it.value(); + entry.functionGroups = getFunctionGroupsImpl(scope); + entry.needsGetattroFunction = classNeedsGetattroFunctionImpl(scope); + entry.numberProtocolOperators = getNumberProtocolOperators(scope); + entry.boolCastFunctionO = getBoolCast(scope); + } + return it.value(); +} + +ShibokenGenerator::FunctionGroups + ShibokenGenerator::getFunctionGroups(const AbstractMetaClassCPtr &scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).functionGroups; +} + +QList<AbstractMetaFunctionCList> + ShibokenGenerator::numberProtocolOperators(const AbstractMetaClassCPtr &scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).numberProtocolOperators; +} + +BoolCastFunctionOptional ShibokenGenerator::boolCast(const AbstractMetaClassCPtr &scope) +{ + Q_ASSERT(scope); + return getGeneratorClassInfo(scope).boolCastFunctionO; +} + +// Use non-const overloads only, for example, "foo()" and "foo()const" +// the second is removed. +static void removeConstOverloads(AbstractMetaFunctionCList *overloads) +{ + for (qsizetype i = overloads->size() - 1; i >= 0; --i) { + const auto &f = overloads->at(i); + if (f->isConstant()) { + for (qsizetype c = 0, size = overloads->size(); c < size; ++c) { + if (f->isConstOverloadOf(overloads->at(c).get())) { + overloads->removeAt(i); + break; + } + } + } + } +} + +ShibokenGenerator::FunctionGroups + ShibokenGenerator::getFunctionGroupsImpl(const AbstractMetaClassCPtr &scope) +{ + AbstractMetaFunctionCList lst = scope->functions(); + scope->getFunctionsFromInvisibleNamespacesToBeGenerated(&lst); + + FunctionGroups results; + for (const auto &func : lst) { + if (isGroupable(func) + && func->ownerClass() == func->implementingClass() + && func->generateBinding()) { + auto it = results.find(func->name()); + if (it == results.end()) { + it = results.insert(func->name(), AbstractMetaFunctionCList(1, func)); + } else { + // If there are virtuals methods in the mix (PYSIDE-570, + // QFileSystemModel::index(QString,int) and + // QFileSystemModel::index(int,int,QModelIndex)) override, make sure + // the overriding method of the most-derived class is seen first + // and inserted into the "seenSignatures" set. + if (func->isVirtual()) + it.value().prepend(func); + else + it.value().append(func); + } + getInheritedOverloads(scope, &it.value()); + removeConstOverloads(&it.value()); + } + } + return results; +} + +static bool removeNumberProtocolOperator(const AbstractMetaFunctionCPtr &f) +{ + return !f->generateBinding() + || (f->ownerClass() != f->implementingClass() && !f->isAbstract()); +} + +QList<AbstractMetaFunctionCList> + ShibokenGenerator::getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass) +{ + QList<AbstractMetaFunctionCList> result; + if (metaClass->isNamespace()) + return result; + result = filterGroupedOperatorFunctions( + metaClass, + OperatorQueryOption::ArithmeticOp + | OperatorQueryOption::IncDecrementOp + | OperatorQueryOption::LogicalOp + | OperatorQueryOption::BitwiseOp + | OperatorQueryOption::ConversionOp); + + for (auto i = result.size() - 1; i >= 0; --i) { + AbstractMetaFunctionCList &l = result[i]; + auto rend = std::remove_if(l.begin(), l.end(), removeNumberProtocolOperator); + l.erase(rend, l.end()); + if (l.isEmpty()) + result.removeAt(i); + } + + return result; +} + +BoolCastFunctionOptional +ShibokenGenerator::getBoolCast(const AbstractMetaClassCPtr &metaClass) +{ + if (metaClass->isNamespace()) + return std::nullopt; + + const auto te = metaClass->typeEntry(); + if (te->isSmartPointer()) { + auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(te); + + auto valueCheckMethod = ste->valueCheckMethod(); + if (!valueCheckMethod.isEmpty()) { + const auto func = metaClass->findFunction(valueCheckMethod); + if (!func) + throw Exception(msgMethodNotFound(metaClass, valueCheckMethod)); + return BoolCastFunction{func, false}; + } + + auto nullCheckMethod = ste->nullCheckMethod(); + if (!nullCheckMethod.isEmpty()) { + const auto func = metaClass->findFunction(nullCheckMethod); + if (!func) + throw Exception(msgMethodNotFound(metaClass, nullCheckMethod)); + return BoolCastFunction{func, true}; + } + } + + auto mode = te->operatorBoolMode(); + if (useOperatorBoolAsNbBool() + ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { + const auto func = metaClass->findOperatorBool(); + if (func) + return BoolCastFunction{func, false}; + } + + mode = te->isNullMode(); + if (useIsNullAsNbBool() + ? mode != TypeSystem::BoolCast::Disabled : mode == TypeSystem::BoolCast::Enabled) { + const auto func = metaClass->findQtIsNullMethod(); + if (func) + return BoolCastFunction{func, true}; + } + return std::nullopt; +} + +static bool isInplaceAdd(const AbstractMetaFunctionCPtr &func) +{ + return func->name() == u"operator+="; +} + +static bool isIncrementOperator(const AbstractMetaFunctionCPtr &func) +{ + return func->functionType() == AbstractMetaFunction::IncrementOperator; +} + +static bool isDecrementOperator(const AbstractMetaFunctionCPtr &func) +{ + return func->functionType() == AbstractMetaFunction::DecrementOperator; +} + +// Filter predicate for operator functions +static bool skipOperatorFunc(const AbstractMetaFunctionCPtr &func) +{ + if (func->isModifiedRemoved() || func->usesRValueReferences()) + return true; + const auto &name = func->name(); + return name == u"operator[]" || name == u"operator->" || name == u"operator!" + || name == u"operator/="; // __idiv__ is not needed in Python3 +} + +QList<AbstractMetaFunctionCList> +ShibokenGenerator::filterGroupedOperatorFunctions(const AbstractMetaClassCPtr &metaClass, + OperatorQueryOptions query) +{ + // ( func_name, num_args ) => func_list + QMap<std::pair<QString, int>, AbstractMetaFunctionCList> results; + auto funcs = metaClass->operatorOverloads(query); + auto end = std::remove_if(funcs.begin(), funcs.end(), skipOperatorFunc); + funcs.erase(end, funcs.end()); + // If we have operator+=, we remove the operator++/-- which would + // otherwise be used for emulating __iadd__, __isub__. + if (std::any_of(funcs.cbegin(), funcs.cend(), isInplaceAdd)) { + end = std::remove_if(funcs.begin(), funcs.end(), + [] (const AbstractMetaFunctionCPtr &func) { + return func->isIncDecrementOperator(); + }); + funcs.erase(end, funcs.end()); + } else { + // If both prefix/postfix ++/-- are present, remove one + if (std::count_if(funcs.begin(), funcs.end(), isIncrementOperator) > 1) + funcs.erase(std::find_if(funcs.begin(), funcs.end(), isIncrementOperator)); + if (std::count_if(funcs.begin(), funcs.end(), isDecrementOperator) > 1) + funcs.erase(std::find_if(funcs.begin(), funcs.end(), isDecrementOperator)); + } + for (const auto &func : funcs) { + int args; + if (func->isComparisonOperator()) { + args = -1; + } else { + args = func->arguments().size(); + } + auto op = std::make_pair(func->name(), args); + results[op].append(func); + } + QList<AbstractMetaFunctionCList> result; + result.reserve(results.size()); + for (auto it = results.cbegin(), end = results.cend(); it != end; ++it) + result.append(it.value()); + return result; +} + +static bool hidesBaseClassFunctions(const AbstractMetaFunctionCPtr &f) +{ + auto attributes = f->cppAttributes(); + return !attributes.testFlag(FunctionAttribute::Override) + && !attributes.testFlag(FunctionAttribute::Final); +} + +void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClassCPtr &scope, + AbstractMetaFunctionCList *overloads) +{ + if (overloads->isEmpty() || scope->isNamespace() || scope->baseClasses().isEmpty()) + return; + + // PYSIDE-331: look also into base classes. Check for any non-overriding + // function hiding the base class functions. + const bool hideBaseClassFunctions = + std::any_of(overloads->cbegin(), overloads->cend(), hidesBaseClassFunctions); + + const QString &functionName = overloads->constFirst()->name(); + const bool hasUsingDeclarations = scope->hasUsingMemberFor(functionName); + if (hideBaseClassFunctions && !hasUsingDeclarations) + return; // No base function is visible + + // Collect base candidates by name and signature + bool staticEncountered = false; + QSet<QString> seenSignatures; + for (const auto &func : *overloads) { + seenSignatures.insert(func->minimalSignature()); + staticEncountered |= func->isStatic(); + } + + AbstractMetaFunctionCList baseCandidates; + + auto basePredicate = [&functionName, &seenSignatures, &baseCandidates] + (const AbstractMetaClassCPtr &b) { + for (const auto &f : b->functions()) { + if (f->generateBinding() && f->name() == functionName) { + const QString signature = f->minimalSignature(); + if (!seenSignatures.contains(signature)) { + seenSignatures.insert(signature); + baseCandidates.append(f); + } + } + } + return false; // Keep going + }; + + for (const auto &baseClass : scope->baseClasses()) + recurseClassHierarchy(baseClass, basePredicate); + + // Remove the ones that are not made visible with using declarations + if (hideBaseClassFunctions && hasUsingDeclarations) { + const auto pred = [scope](const AbstractMetaFunctionCPtr &f) { + return !scope->isUsingMember(f->ownerClass(), f->name(), f->access()); + }; + auto end = std::remove_if(baseCandidates.begin(), baseCandidates.end(), pred); + baseCandidates.erase(end, baseCandidates.end()); + } + + // PYSIDE-886: If the method does not have any static overloads declared + // in the class in question, remove all inherited static methods as setting + // METH_STATIC in that case can cause crashes for the instance methods. + // Manifested as crash when calling QPlainTextEdit::find() (clash with + // static QWidget::find(WId)). + if (!staticEncountered) { + auto end = std::remove_if(baseCandidates.begin(), baseCandidates.end(), + [](const AbstractMetaFunctionCPtr &f) { return f->isStatic(); }); + baseCandidates.erase(end, baseCandidates.end()); + } + + for (const auto &baseCandidate : baseCandidates) { + AbstractMetaFunction *newFunc = baseCandidate->copy(); + newFunc->setImplementingClass(scope); + overloads->append(AbstractMetaFunctionCPtr(newFunc)); + } +} + +QList<OptionDescription> ShibokenGenerator::options() +{ + return { + {DISABLE_VERBOSE_ERROR_MESSAGES, + u"Disable verbose error messages. Turn the python code hard to debug\n" + "but safe few kB on the generated bindings."_s}, + {PARENT_CTOR_HEURISTIC, + u"Enable heuristics to detect parent relationship on constructors."_s}, + {RETURN_VALUE_HEURISTIC, + u"Enable heuristics to detect parent relationship on return values\n" + "(USE WITH CAUTION!)"_s}, + {USE_ISNULL_AS_NB_BOOL, + u"If a class have an isNull() const method, it will be used to compute\n" + "the value of boolean casts"_s}, + {LEAN_HEADERS, + u"Forward declare classes in module headers"_s}, + {USE_OPERATOR_BOOL_AS_NB_BOOL, + u"If a class has an operator bool, it will be used to compute\n" + "the value of boolean casts"_s}, + {NO_IMPLICIT_CONVERSIONS, + u"Do not generate implicit_conversions for function arguments."_s}, + {WRAPPER_DIAGNOSTICS, + u"Generate diagnostic code around wrappers"_s} + }; +} + +class ShibokenGeneratorOptionsParser : public OptionsParser +{ +public: + explicit ShibokenGeneratorOptionsParser(ShibokenGeneratorOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString & key, OptionSource source) override; + +private: + ShibokenGeneratorOptions *m_options; +}; + +bool ShibokenGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; + if (key == PARENT_CTOR_HEURISTIC) + return (m_options->useCtorHeuristic = true); + if (key == RETURN_VALUE_HEURISTIC) + return (m_options->userReturnValueHeuristic = true); + if (key == DISABLE_VERBOSE_ERROR_MESSAGES) + return (m_options->verboseErrorMessagesDisabled = true); + if (key == USE_ISNULL_AS_NB_BOOL || key == USE_ISNULL_AS_NB_NONZERO) { + return (m_options->useIsNullAsNbBool = true); + } + if (key == LEAN_HEADERS) + return (m_options->leanHeaders= true); + if (key == USE_OPERATOR_BOOL_AS_NB_BOOL || key == USE_OPERATOR_BOOL_AS_NB_NONZERO) { + return (m_options->useOperatorBoolAsNbBool = true); + } + if (key == NO_IMPLICIT_CONVERSIONS) { + m_options->generateImplicitConversions = false; + return true; + } + if (key == WRAPPER_DIAGNOSTICS) + return (m_options->wrapperDiagnostics = true); + return false; +} + +std::shared_ptr<OptionsParser> ShibokenGenerator::createOptionsParser() +{ + return std::make_shared<ShibokenGeneratorOptionsParser>(&m_options); +} + +bool ShibokenGenerator::doSetup() +{ + return true; +} + +bool ShibokenGenerator::useCtorHeuristic() +{ + return m_options.useCtorHeuristic; +} + +bool ShibokenGenerator::useReturnValueHeuristic() +{ + return m_options.userReturnValueHeuristic; +} + +bool ShibokenGenerator::useIsNullAsNbBool() +{ + return m_options.useIsNullAsNbBool; +} + +bool ShibokenGenerator::leanHeaders() +{ + return m_options.leanHeaders; +} + +bool ShibokenGenerator::useOperatorBoolAsNbBool() +{ + return m_options.useOperatorBoolAsNbBool; +} + +bool ShibokenGenerator::generateImplicitConversions() +{ + return m_options.generateImplicitConversions; +} + +QString ShibokenGenerator::moduleCppPrefix(const QString &moduleName) + { + QString result = moduleName.isEmpty() ? packageName() : moduleName; + result.replace(u'.', u'_'); + return result; +} + +QString ShibokenGenerator::cppApiVariableNameOld(const QString &moduleName) +{ + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "Types"_L1; +} + +QString ShibokenGenerator::cppApiVariableName(const QString &moduleName) +{ + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "TypeStructs"_L1; +} + +QString ShibokenGenerator::pythonModuleObjectName(const QString &moduleName) +{ + return "Sbk"_L1 + moduleCppPrefix(moduleName) + "ModuleObject"_L1; +} + +QString ShibokenGenerator::convertersVariableName(const QString &moduleName) +{ + QString result = cppApiVariableNameOld(moduleName); + result.chop(1); + result.append(u"Converters"_s); + return result; +} + +static QString processInstantiationsVariableName(const AbstractMetaType &type) +{ + QString res = u'_' + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()); + for (const auto &instantiation : type.instantiations()) { + res += instantiation.isContainer() + ? processInstantiationsVariableName(instantiation) + : u'_' + _fixedCppTypeName(instantiation.cppSignature()); + } + return res; +} + +static void appendIndexSuffix(QString *s) +{ + if (!s->endsWith(u'_')) + s->append(u'_'); + s->append("IDX"_L1); +} + +QString + ShibokenGenerator::getTypeAlternateTemplateIndexVariableName(const AbstractMetaClassCPtr &metaClass) +{ + const auto templateBaseClass = metaClass->templateBaseClass(); + Q_ASSERT(templateBaseClass); + QString result = u"SBK_"_s + + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()); + for (const auto &instantiation : metaClass->templateBaseClassInstantiations()) + result += processInstantiationsVariableName(instantiation); + appendIndexSuffix(&result); + return result; +} + +QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClassCPtr &metaClass) +{ + return getTypeIndexVariableName(metaClass->typeEntry()); +} +QString ShibokenGenerator::getTypeIndexVariableName(TypeEntryCPtr type) +{ + if (isCppPrimitive(type)) + type = basicReferencedTypeEntry(type); + QString result = u"SBK_"_s; + // Disambiguate namespaces per module to allow for extending them. + if (type->isNamespace()) { + QString package = type->targetLangPackage(); + const int dot = package.lastIndexOf(u'.'); + result += QStringView{package}.right(package.size() - (dot + 1)); + } + result += _fixedCppTypeName(type->qualifiedCppName()); + appendIndexSuffix(&result); + return result; +} +QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType &type) +{ + QString result = u"SBK"_s; + if (type.typeEntry()->isContainer()) + result += u'_' + moduleName(); + result += processInstantiationsVariableName(type); + appendIndexSuffix(&result); + return result; +} + +void collectfromTypeEntry(TypeEntryCPtr entry, QStringList &typeNames) +{ + if (entry->shouldGenerate()) { + typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName(); + if (entry->isEnum()) { + auto ete = std::static_pointer_cast<const EnumTypeEntry>(entry); + if (ete->flags()) { + auto entry = ete->flags(); + typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName(); + } + } + } +} + +void ShibokenGenerator::collectFullTypeNamesArray(QStringList &typeNames) +{ + for (const auto &metaClass : api().classes()) { + collectfromTypeEntry(metaClass->typeEntry(), typeNames); + + for (const AbstractMetaEnum &metaEnum : metaClass->enums()) + collectfromTypeEntry(metaEnum.typeEntry(), typeNames); + + int smartPointerCountIndex = getMaxTypeIndex(); + for (const auto &smp : api().instantiatedSmartPointers()) { + auto entry = smp.type.typeEntry(); + typeNames[smartPointerCountIndex] = entry->qualifiedTargetLangName(); + ++smartPointerCountIndex; + } + } + for (const AbstractMetaEnum &metaEnum : api().globalEnums()) + collectfromTypeEntry(metaEnum.typeEntry(), typeNames); +} + +bool ShibokenGenerator::verboseErrorMessagesDisabled() +{ + return m_options.verboseErrorMessagesDisabled; +} + +bool ShibokenGenerator::pythonFunctionWrapperUsesListOfArguments(const AbstractMetaFunctionCPtr &func) const +{ + const auto &groups = func->implementingClass() + ? getFunctionGroups(func->implementingClass()) + : getGlobalFunctionGroups(); + OverloadData od(groups.value(func->name()), api()); + return od.pythonFunctionWrapperUsesListOfArguments(); +} + +QString ShibokenGenerator::minimalConstructorExpression(const ApiExtractorResult &api, + const AbstractMetaType &type) +{ + if (type.isExtendedCppPrimitive() || type.isSmartPointer()) + return {}; + QString errorMessage; + const auto ctor = minimalConstructor(api, type, &errorMessage); + if (ctor.has_value()) + return ctor->initialization(); + + const QString message = + msgCouldNotFindMinimalConstructor(QLatin1StringView(__FUNCTION__), + type.cppSignature(), errorMessage); + qCWarning(lcShiboken()).noquote() << message; + return u";\n#error "_s + message + u'\n'; +} + +QString ShibokenGenerator::minimalConstructorExpression(const ApiExtractorResult &api, + const TypeEntryCPtr &type) +{ + if (isExtendedCppPrimitive(type)) + return {}; + const auto ctor = minimalConstructor(api, type); + if (ctor.has_value()) + return ctor->initialization(); + + const QString message = + msgCouldNotFindMinimalConstructor(QLatin1StringView(__FUNCTION__), + type->qualifiedCppName()); + qCWarning(lcShiboken()).noquote() << message; + return u";\n#error "_s + message + u'\n'; +} + +QString ShibokenGenerator::pythonArgsAt(int i) +{ + return PYTHON_ARGS + u'[' + QString::number(i) + u']'; +} + +void ShibokenGenerator::replaceTemplateVariables(QString &code, + const AbstractMetaFunctionCPtr &func) const +{ + const auto cpp_class = func->ownerClass(); + if (cpp_class) + code.replace(u"%TYPE"_s, cpp_class->name()); + + const AbstractMetaArgumentList &argument = func->arguments(); + for (const AbstractMetaArgument &arg : argument) + code.replace(u'%' + QString::number(arg.argumentIndex() + 1), arg.name()); + + //template values + code.replace(u"%RETURN_TYPE"_s, translateType(func->type(), cpp_class)); + code.replace(u"%FUNCTION_NAME"_s, func->originalName()); + + if (code.contains(u"%ARGUMENT_NAMES")) { + StringStream aux_stream; + writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments); + code.replace(u"%ARGUMENT_NAMES"_s, aux_stream); + } + + if (code.contains(u"%ARGUMENTS")) { + StringStream aux_stream; + writeFunctionArguments(aux_stream, func, Options(SkipDefaultValues) | SkipRemovedArguments); + code.replace(u"%ARGUMENTS"_s, aux_stream); + } +} + +QString ShibokenGenerator::stdMove(const QString &c) +{ + return u"std::move("_s + c + u')'; +} |