aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/abstractmetafunction.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetafunction.cpp1707
1 files changed, 1707 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
new file mode 100644
index 000000000..11a02f154
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
@@ -0,0 +1,1707 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "abstractmetafunction.h"
+#include "abstractmetaargument.h"
+#include "abstractmetabuilder.h"
+#include "abstractmetalang.h"
+#include "abstractmetalang_helpers.h"
+#include "abstractmetatype.h"
+#include "addedfunction.h"
+#include <codemodel.h>
+#include "documentation.h"
+#include "exception.h"
+#include "messages.h"
+#include "codesnip.h"
+#include "modifications.h"
+#include "reporthandler.h"
+#include "sourcelocation.h"
+#include "typedatabase.h"
+#include "complextypeentry.h"
+#include "containertypeentry.h"
+#include "functiontypeentry.h"
+#include "primitivetypeentry.h"
+#include "typesystemtypeentry.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QRegularExpression>
+
+#include <algorithm>
+
+using namespace Qt::StringLiterals;
+
+// Cache FunctionModificationList in a flat list per class (0 for global
+// functions, or typically owner/implementing/declaring class.
+struct ModificationCacheEntry
+{
+ AbstractMetaClassCPtr klass;
+ FunctionModificationList modifications;
+};
+
+using ModificationCache = QList<ModificationCacheEntry>;
+
+class AbstractMetaFunctionPrivate
+{
+public:
+ AbstractMetaFunctionPrivate()
+ : m_constant(false),
+ m_reverse(false),
+ m_pointerOperator(false),
+ m_isCallOperator(false)
+ {
+ }
+
+ QString signature() const;
+ QString formatMinimalSignature(const AbstractMetaFunction *q,
+ bool comment) const;
+ QString modifiedName(const AbstractMetaFunction *q) const;
+ int overloadNumber(const AbstractMetaFunction *q) const;
+
+ const FunctionModificationList &modifications(const AbstractMetaFunction *q,
+ const AbstractMetaClassCPtr &implementor) const;
+
+ bool applyTypeModification(const AbstractMetaFunction *q,
+ const QString &type, int number, QString *errorMessage);
+
+ QString m_name;
+ QString m_originalName;
+ Documentation m_doc;
+ mutable QString m_cachedMinimalSignature;
+ mutable QString m_cachedSignature;
+ mutable QString m_cachedModifiedName;
+ QString m_unresolvedSignature;
+
+ FunctionTypeEntryPtr m_typeEntry;
+ AbstractMetaFunction::FunctionType m_functionType = AbstractMetaFunction::NormalFunction;
+ AbstractMetaType m_type;
+ QString m_modifiedTypeName;
+ AbstractMetaClassCPtr m_class;
+ AbstractMetaClassCPtr m_implementingClass;
+ AbstractMetaClassCPtr m_declaringClass;
+ mutable ModificationCache m_modificationCache;
+ int m_propertySpecIndex = -1;
+ AbstractMetaArgumentList m_arguments;
+ AddedFunctionPtr m_addedFunction;
+ SourceLocation m_sourceLocation;
+ AbstractMetaFunction::Attributes m_attributes;
+ FunctionAttributes m_cppAttributes;
+ AbstractMetaFunction::Flags m_flags;
+ uint m_constant : 1;
+ uint m_reverse : 1;
+ uint m_pointerOperator : 1;
+ uint m_isCallOperator : 1;
+ mutable int m_cachedOverloadNumber = TypeSystem::OverloadNumberUnset;
+ Access m_access = Access::Public;
+ Access m_originalAccess = Access::Public;
+ ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown;
+ TypeSystem::AllowThread m_allowThreadModification = TypeSystem::AllowThread::Unspecified;
+ TypeSystem::ExceptionHandling m_exceptionHandlingModification = TypeSystem::ExceptionHandling::Unspecified;
+};
+
+AbstractMetaFunction::AbstractMetaFunction(const QString &name) :
+ AbstractMetaFunction()
+{
+ d->m_originalName = d->m_name = name;
+}
+
+AbstractMetaFunction::AbstractMetaFunction(const AddedFunctionPtr &addedFunc) :
+ AbstractMetaFunction(addedFunc->name())
+{
+ d->m_addedFunction = addedFunc;
+ setConstant(addedFunc->isConstant());
+ switch (addedFunc->access()) {
+ case AddedFunction::Protected:
+ setAccess(Access::Protected);
+ break;
+ case AddedFunction::Public:
+ setAccess(Access::Public);
+ break;
+ }
+ AbstractMetaFunction::Attributes atts;
+ if (addedFunc->isStatic())
+ setCppAttribute(FunctionAttribute::Static);
+ if (addedFunc->isClassMethod())
+ atts |= AbstractMetaFunction::ClassMethod;
+ setAttributes(atts);
+}
+
+QString AbstractMetaFunction::name() const
+{
+ return d->m_name;
+}
+
+void AbstractMetaFunction::setName(const QString &name)
+{
+ d->m_name = name;
+}
+
+QString AbstractMetaFunction::originalName() const
+{
+ return d->m_originalName.isEmpty() ? name() : d->m_originalName;
+}
+
+void AbstractMetaFunction::setOriginalName(const QString &name)
+{
+ d->m_originalName = name;
+}
+
+Access AbstractMetaFunction::access() const
+{
+ return d->m_access;
+}
+
+void AbstractMetaFunction::setAccess(Access a)
+{
+ d->m_originalAccess = d->m_access = a;
+}
+
+void AbstractMetaFunction::modifyAccess(Access a)
+{
+ d->m_access = a;
+}
+
+bool AbstractMetaFunction::wasPrivate() const
+{
+ return d->m_originalAccess == Access::Private;
+}
+
+bool AbstractMetaFunction::wasProtected() const
+{
+ return d->m_originalAccess == Access::Protected;
+}
+
+bool AbstractMetaFunction::wasPublic() const
+{
+ return d->m_originalAccess == Access::Public;
+}
+
+QStringList AbstractMetaFunction::definitionNames() const
+{
+ return AbstractMetaBuilder::definitionNames(d->m_name, snakeCase());
+}
+
+const Documentation &AbstractMetaFunction::documentation() const
+{
+ return d->m_doc;
+}
+
+void AbstractMetaFunction::setDocumentation(const Documentation &doc)
+{
+ d->m_doc = doc;
+}
+
+bool AbstractMetaFunction::isReverseOperator() const
+{
+ return d->m_reverse;
+}
+
+void AbstractMetaFunction::setReverseOperator(bool reverse)
+{
+ d->m_reverse = reverse;
+}
+
+bool AbstractMetaFunction::isPointerOperator() const
+{
+ return d->m_pointerOperator;
+}
+
+void AbstractMetaFunction::setPointerOperator(bool value)
+{
+ d->m_pointerOperator = value;
+}
+
+bool AbstractMetaFunction::isExplicit() const
+{
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Explicit);
+}
+
+void AbstractMetaFunction::setExplicit(bool isExplicit)
+{
+ d->m_cppAttributes.setFlag(FunctionAttribute::Explicit, isExplicit);
+}
+
+bool AbstractMetaFunction::returnsBool() const
+{
+ if (d->m_type.typeUsagePattern() != AbstractMetaType::PrimitivePattern)
+ return false;
+ return basicReferencedTypeEntry(d->m_type.typeEntry())->name() == u"bool";
+}
+
+bool AbstractMetaFunction::isOperatorBool() const
+{
+ return d->m_functionType == AbstractMetaFunction::ConversionOperator
+ && d->m_constant && returnsBool();
+}
+
+AbstractMetaFunction::AbstractMetaFunction() : d(new AbstractMetaFunctionPrivate)
+{
+}
+
+AbstractMetaFunction::~AbstractMetaFunction() = default;
+
+AbstractMetaFunction::Attributes AbstractMetaFunction::attributes() const
+{
+ return d->m_attributes;
+}
+
+void AbstractMetaFunction::setAttributes(Attributes attributes)
+{
+ d->m_attributes = attributes;
+}
+
+void AbstractMetaFunction::operator+=(AbstractMetaFunction::Attribute attribute)
+{
+ d->m_attributes.setFlag(attribute);
+}
+
+void AbstractMetaFunction::operator-=(AbstractMetaFunction::Attribute attribute)
+{
+ d->m_attributes.setFlag(attribute, false);
+}
+
+FunctionAttributes AbstractMetaFunction::cppAttributes() const
+{
+ return d->m_cppAttributes;
+}
+
+void AbstractMetaFunction::setCppAttributes(FunctionAttributes a)
+{
+ d->m_cppAttributes = a;
+}
+
+void AbstractMetaFunction::setCppAttribute(FunctionAttribute a, bool on)
+{
+ d->m_cppAttributes.setFlag(a, on);
+}
+
+AbstractMetaFunction::Flags AbstractMetaFunction::flags() const
+{
+ return d->m_flags;
+}
+
+void AbstractMetaFunction::setFlags(Flags f)
+{
+ d->m_flags = f;
+}
+
+/*******************************************************************************
+ * Indicates that this function has a modification that removes it
+ */
+bool AbstractMetaFunction::isModifiedRemoved(AbstractMetaClassCPtr cls) const
+{
+ if (!isInGlobalScope() && !cls)
+ cls = d->m_implementingClass;
+ for (const auto &mod : modifications(cls)) {
+ if (mod.isRemoved())
+ return true;
+ }
+
+ return false;
+}
+
+bool AbstractMetaFunction::isModifiedFinal(AbstractMetaClassCPtr cls) const
+{
+ if (!isInGlobalScope() && cls == nullptr)
+ cls = d->m_implementingClass;
+ for (const auto &mod : modifications(cls)) {
+ if (mod.modifiers().testFlag(FunctionModification::Final))
+ return true;
+ }
+ return false;
+}
+
+bool AbstractMetaFunction::isVoid() const
+{
+ return d->m_type.isVoid();
+}
+
+const AbstractMetaType &AbstractMetaFunction::type() const
+{
+ return d->m_type;
+}
+
+void AbstractMetaFunction::setType(const AbstractMetaType &type)
+{
+ d->m_type = type;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::ownerClass() const
+{
+ return d->m_class;
+}
+
+void AbstractMetaFunction::setOwnerClass(const AbstractMetaClassCPtr &cls)
+{
+ d->m_class = cls;
+}
+
+bool AbstractMetaFunction::operator<(const AbstractMetaFunction &other) const
+{
+ return compareTo(&other) & NameLessThan;
+}
+
+
+/*!
+ Returns a mask of CompareResult describing how this function is
+ compares to another function
+*/
+AbstractMetaFunction::CompareResult AbstractMetaFunction::compareTo(const AbstractMetaFunction *other) const
+{
+ CompareResult result;
+
+ // Enclosing class...
+ if (ownerClass() == other->ownerClass())
+ result |= EqualImplementor;
+
+ // Attributes
+ if (attributes() == other->attributes() && cppAttributes() == other->cppAttributes())
+ result |= EqualAttributes;
+
+ // Compare types
+ if (type().name() == other->type().name())
+ result |= EqualReturnType;
+
+ // Compare names
+ int cmp = originalName().compare(other->originalName());
+
+ if (cmp < 0)
+ result |= NameLessThan;
+ else if (!cmp)
+ result |= EqualName;
+
+ // compare name after modification...
+ cmp = modifiedName().compare(other->modifiedName());
+ if (!cmp)
+ result |= EqualModifiedName;
+
+ // Compare arguments...
+ AbstractMetaArgumentList minArguments;
+ AbstractMetaArgumentList maxArguments;
+ if (arguments().size() < other->arguments().size()) {
+ minArguments = arguments();
+ maxArguments = other->arguments();
+ } else {
+ minArguments = other->arguments();
+ maxArguments = arguments();
+ }
+
+ const auto minCount = minArguments.size();
+ const auto maxCount = maxArguments.size();
+ bool same = true;
+ for (qsizetype i = 0; i < maxCount; ++i) {
+ if (i < minCount) {
+ const AbstractMetaArgument &min_arg = minArguments.at(i);
+ const AbstractMetaArgument &max_arg = maxArguments.at(i);
+ if (min_arg.type().name() != max_arg.type().name()
+ && (min_arg.defaultValueExpression().isEmpty() || max_arg.defaultValueExpression().isEmpty())) {
+ same = false;
+ break;
+ }
+ } else {
+ if (maxArguments.at(i).defaultValueExpression().isEmpty()) {
+ same = false;
+ break;
+ }
+ }
+ }
+
+ if (same)
+ result |= minCount == maxCount ? EqualArguments : EqualDefaultValueOverload;
+
+ return result;
+}
+
+// Is this the const overload of another function of equivalent return type?
+bool AbstractMetaFunction::isConstOverloadOf(const AbstractMetaFunction *other) const
+{
+ const auto argumentCount = d->m_arguments.size();
+ if (!isConstant() || other->isConstant() || name() != other->name()
+ || argumentCount != other->arguments().size()) {
+ return false;
+ }
+
+ // Match "const Foo &getFoo() const" / "Foo &getFoo()" / "Foo getFoo() const"
+ const auto otherType = other->type();
+ if (d->m_type.name() != otherType.name()
+ || d->m_type.indirectionsV() != otherType.indirectionsV()) {
+ return false;
+ }
+
+ const auto &otherArguments = other->arguments();
+ for (qsizetype a = 0; a < argumentCount; ++a) {
+ if (d->m_arguments.at(a).type() != otherArguments.at(a).type())
+ return false;
+ }
+ return true;
+}
+
+AbstractMetaFunction *AbstractMetaFunction::copy() const
+{
+ auto *cpy = new AbstractMetaFunction;
+ cpy->setAttributes(attributes());
+ auto ca = cppAttributes();
+ // Historical bug: explicit was not copied! (causing nontypetemplate_test.py fail)
+ ca.setFlag(FunctionAttribute::Explicit, false);
+ cpy->setCppAttributes(ca);
+ cpy->setFlags(flags());
+ cpy->setAccess(access());
+ cpy->setName(name());
+ cpy->setOriginalName(originalName());
+ cpy->setOwnerClass(ownerClass());
+ cpy->setImplementingClass(implementingClass());
+ cpy->setFunctionType(functionType());
+ cpy->setDeclaringClass(declaringClass());
+ cpy->setType(type());
+ cpy->setConstant(isConstant());
+ cpy->setExceptionSpecification(d->m_exceptionSpecification);
+ cpy->setAllowThreadModification(d->m_allowThreadModification);
+ cpy->setExceptionHandlingModification(d->m_exceptionHandlingModification);
+ cpy->d->m_modifiedTypeName = d->m_modifiedTypeName;
+ cpy->d->m_addedFunction = d->m_addedFunction;
+ cpy->d->m_arguments = d->m_arguments;
+
+ return cpy;
+}
+
+bool AbstractMetaFunction::usesRValueReferences() const
+{
+ if (d->m_functionType == MoveConstructorFunction || d->m_functionType == MoveAssignmentOperatorFunction)
+ return true;
+ if (d->m_type.referenceType() == RValueReference)
+ return true;
+ for (const AbstractMetaArgument &a : d->m_arguments) {
+ if (a.type().referenceType() == RValueReference)
+ return true;
+ }
+ return false;
+}
+
+bool AbstractMetaFunction::generateBinding() const
+{
+ switch (d->m_functionType) {
+ case ConversionOperator:
+ if (d->m_name != u"operator int" && d->m_name != u"operator double")
+ return false;
+ break;
+ case AssignmentOperatorFunction:
+ case MoveAssignmentOperatorFunction:
+ case AbstractMetaFunction::MoveConstructorFunction:
+ return false;
+ default:
+ if (!isWhiteListed())
+ return false;
+ break;
+ }
+ // Can we access the wrapper in case of a protected method? If not,
+ // disable for consistency regardless of avoidProtectedHack.
+ if (isProtected()) {
+ const auto typeFlags = ownerClass()->typeEntry()->typeFlags();
+ if (typeFlags.testFlag(ComplexTypeEntry::DisableWrapper))
+ return false;
+ }
+ if (isPrivate() && d->m_functionType != EmptyFunction)
+ return false;
+ // RValue references only for user-specified
+ // functions (<add-function>/<declare-function>/<function>)
+ return d->m_name != u"qt_metacall" &&
+ (!usesRValueReferences() || d->m_addedFunction || d->m_typeEntry)
+ && !isModifiedRemoved();
+}
+
+bool AbstractMetaFunction::isWhiteListed() const
+{
+ switch (d->m_functionType) {
+ case NormalFunction:
+ case SignalFunction:
+ case SlotFunction:
+ if (auto dc = declaringClass()) {
+ const QSet<QString> &whiteList = dc->typeEntry()->generateFunctions();
+ return whiteList.isEmpty() || whiteList.contains(d->m_name)
+ || whiteList.contains(minimalSignature());
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+QString AbstractMetaFunctionPrivate::signature() const
+{
+ if (m_cachedSignature.isEmpty()) {
+ m_cachedSignature = m_originalName;
+
+ m_cachedSignature += u'(';
+
+ for (qsizetype i = 0; i < m_arguments.size(); ++i) {
+ const AbstractMetaArgument &a = m_arguments.at(i);
+ const AbstractMetaType &t = a.type();
+ if (i > 0)
+ m_cachedSignature += u", "_s;
+ m_cachedSignature += t.cppSignature();
+ // We need to have the argument names in the qdoc files
+ m_cachedSignature += u' ';
+ m_cachedSignature += a.name();
+ }
+ m_cachedSignature += u')';
+
+ if (m_constant)
+ m_cachedSignature += u" const"_s;
+ }
+ return m_cachedSignature;
+}
+
+QString AbstractMetaFunction::signature() const
+{
+ return d->signature();
+}
+
+QString AbstractMetaFunction::classQualifiedSignature() const
+{
+ QString result;
+ if (d->m_implementingClass)
+ result += d->m_implementingClass->qualifiedCppName() + u"::"_s;
+ result += signature();
+ return result;
+}
+
+QString AbstractMetaFunction::unresolvedSignature() const
+{
+ return d->m_unresolvedSignature;
+}
+
+void AbstractMetaFunction::setUnresolvedSignature(const QString &s)
+{
+ d->m_unresolvedSignature = s;
+}
+
+bool AbstractMetaFunction::isConstant() const
+{
+ return d->m_constant;
+}
+
+void AbstractMetaFunction::setConstant(bool constant)
+{
+ d->m_constant = constant;
+}
+
+bool AbstractMetaFunction::isUserAdded() const
+{
+ return d->m_addedFunction && !d->m_addedFunction->isDeclaration();
+}
+
+bool AbstractMetaFunction::isUserAddedPythonOverride() const
+{
+ return d->m_addedFunction && d->m_addedFunction->isPythonOverride();
+}
+
+bool AbstractMetaFunction::isUserDeclared() const
+{
+ return d->m_addedFunction && d->m_addedFunction->isDeclaration();
+}
+
+int AbstractMetaFunction::actualMinimumArgumentCount() const
+{
+ int count = 0;
+ for (qsizetype i = 0, size = d->m_arguments.size(); i < size; ++i && ++count) {
+ const auto &arg = d->m_arguments.at(i);
+ if (arg.isModifiedRemoved())
+ --count;
+ else if (!arg.defaultValueExpression().isEmpty())
+ break;
+ }
+
+ return count;
+}
+
+int AbstractMetaFunction::actualArgumentIndex(int index) const
+{
+ if (index < 0 || index >= int(d->m_arguments.size()))
+ throw Exception(msgArgumentIndexOutOfRange(this, index));
+ int result = 0;
+ for (int i = 0; i < index; ++i) {
+ if (!d->m_arguments.at(i).isModifiedRemoved())
+ ++result;
+ }
+ return result;
+}
+
+// Returns reference counts for argument at idx, or all arguments if idx == -2
+QList<ReferenceCount> AbstractMetaFunction::referenceCounts(const AbstractMetaClassCPtr &cls, int idx) const
+{
+ QList<ReferenceCount> returned;
+
+ for (const auto &mod : modifications(cls)) {
+ for (const ArgumentModification &argumentMod : mod.argument_mods()) {
+ if (argumentMod.index() != idx && idx != -2)
+ continue;
+ returned += argumentMod.referenceCounts();
+ }
+ }
+
+ return returned;
+}
+
+ArgumentOwner AbstractMetaFunction::argumentOwner(const AbstractMetaClassCPtr &cls, int idx) const
+{
+ for (const auto &mod : modifications(cls)) {
+ for (const ArgumentModification &argumentMod : mod.argument_mods()) {
+ if (argumentMod.index() != idx)
+ continue;
+ return argumentMod.owner();
+ }
+ }
+ return ArgumentOwner();
+}
+
+QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() != key)
+ continue;
+
+ for (const CodeSnip &snip : argumentModification.conversionRules()) {
+ if (snip.language == language && !snip.code().isEmpty())
+ return snip.code();
+ }
+ }
+ }
+
+ return QString();
+}
+
+bool AbstractMetaFunction::hasConversionRule(TypeSystem::Language language, int idx) const
+{
+ return !conversionRule(language, idx).isEmpty();
+}
+
+// FIXME If we remove a arg. in the method at the base class, it will not reflect here.
+bool AbstractMetaFunction::argumentRemoved(int key) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == key) {
+ if (argumentModification.isRemoved())
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::targetLangOwner() const
+{
+ return d->m_class && d->m_class->isInvisibleNamespace()
+ ? d->m_class->targetLangEnclosingClass() : d->m_class;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::declaringClass() const
+{
+ return d->m_declaringClass;
+}
+
+void AbstractMetaFunction::setDeclaringClass(const AbstractMetaClassCPtr &cls)
+{
+ d->m_declaringClass = cls;
+}
+
+AbstractMetaClassCPtr AbstractMetaFunction::implementingClass() const
+{
+ return d->m_implementingClass;
+}
+
+void AbstractMetaFunction::setImplementingClass(const AbstractMetaClassCPtr &cls)
+{
+ d->m_implementingClass = cls;
+}
+
+const AbstractMetaArgumentList &AbstractMetaFunction::arguments() const
+{
+ return d->m_arguments;
+}
+
+AbstractMetaArgumentList &AbstractMetaFunction::arguments()
+{
+ return d->m_arguments;
+}
+
+void AbstractMetaFunction::setArguments(const AbstractMetaArgumentList &arguments)
+{
+ d->m_arguments = arguments;
+}
+
+void AbstractMetaFunction::addArgument(const AbstractMetaArgument &argument)
+{
+ d->m_arguments << argument;
+}
+
+static bool modifiedDeprecated(const FunctionModification &mod)
+{
+ return mod.modifiers().testFlag(FunctionModification::Deprecated);
+}
+
+static bool modifiedUndeprecated(const FunctionModification &mod)
+{
+ return mod.modifiers().testFlag(FunctionModification::Undeprecated);
+}
+
+bool AbstractMetaFunction::isDeprecated() const
+{
+ const auto &mods = modifications(declaringClass());
+
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Deprecated)
+ ? std::none_of(mods.cbegin(), mods.cend(), modifiedUndeprecated)
+ : std::any_of(mods.cbegin(), mods.cend(), modifiedDeprecated);
+}
+
+bool AbstractMetaFunction::isConstructor() const
+{
+ return d->m_functionType == ConstructorFunction || d->m_functionType == CopyConstructorFunction
+ || d->m_functionType == MoveConstructorFunction;
+}
+
+bool AbstractMetaFunction::isDefaultConstructor() const
+{
+ return d->m_functionType == ConstructorFunction
+ && (d->m_arguments.isEmpty()
+ || d->m_arguments.constFirst().hasDefaultValueExpression());
+}
+
+bool AbstractMetaFunction::needsReturnType() const
+{
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::ConstructorFunction:
+ case AbstractMetaFunction::CopyConstructorFunction:
+ case AbstractMetaFunction::MoveConstructorFunction:
+ case AbstractMetaFunction::DestructorFunction:
+ return false;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool AbstractMetaFunction::isInGlobalScope() const
+{
+ return d->m_class == nullptr;
+}
+
+AbstractMetaFunction::FunctionType AbstractMetaFunction::functionType() const
+{
+ return d->m_functionType;
+}
+
+void AbstractMetaFunction::setFunctionType(AbstractMetaFunction::FunctionType type)
+{
+ d->m_functionType = type;
+}
+
+std::optional<AbstractMetaFunction::ComparisonOperatorType>
+AbstractMetaFunction::comparisonOperatorType() const
+{
+ if (d->m_functionType != ComparisonOperator)
+ return {};
+ static const QHash<QString, ComparisonOperatorType> mapping = {
+ {u"operator=="_s, OperatorEqual},
+ {u"operator!="_s, OperatorNotEqual},
+ {u"operator<"_s, OperatorLess},
+ {u"operator<="_s, OperatorLessEqual},
+ {u"operator>"_s, OperatorGreater},
+ {u"operator>="_s, OperatorGreaterEqual}
+ };
+ const auto it = mapping.constFind(originalName());
+ Q_ASSERT(it != mapping.constEnd());
+ return it.value();
+}
+
+// Auto-detect whether a function should be wrapped into
+// Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, that is, temporarily release
+// the GIL (global interpreter lock). Doing so is required for any thread-wait
+// functions, anything that might call a virtual function (potentially
+// reimplemented in Python), and recommended for lengthy I/O or similar.
+// It has performance costs, though.
+bool AbstractMetaFunction::autoDetectAllowThread() const
+{
+ // Disallow for simple getter functions.
+ return !maybeAccessor();
+}
+
+bool AbstractMetaFunction::maybeAccessor() const
+{
+ return d->m_functionType == NormalFunction && d->m_class != nullptr
+ && d->m_constant != 0 && !isVoid() && d->m_arguments.isEmpty();
+}
+
+SourceLocation AbstractMetaFunction::sourceLocation() const
+{
+ return d->m_sourceLocation;
+}
+
+void AbstractMetaFunction::setSourceLocation(const SourceLocation &sourceLocation)
+{
+ d->m_sourceLocation = sourceLocation;
+}
+
+static inline TypeSystem::AllowThread allowThreadMod(const AbstractMetaClassCPtr &klass)
+{
+ return klass->typeEntry()->allowThread();
+}
+
+static inline bool hasAllowThreadMod(const AbstractMetaClassCPtr &klass)
+{
+ return allowThreadMod(klass) != TypeSystem::AllowThread::Unspecified;
+}
+
+bool AbstractMetaFunction::allowThread() const
+{
+ auto allowThreadModification = d->m_allowThreadModification;
+ // If there is no modification on the function, check for a base class.
+ if (d->m_class && allowThreadModification == TypeSystem::AllowThread::Unspecified) {
+ if (auto base = recurseClassHierarchy(d->m_class, hasAllowThreadMod))
+ allowThreadModification = allowThreadMod(base);
+ }
+
+ bool result = true;
+ switch (allowThreadModification) {
+ case TypeSystem::AllowThread::Disallow:
+ result = false;
+ break;
+ case TypeSystem::AllowThread::Allow:
+ break;
+ case TypeSystem::AllowThread::Auto:
+ result = autoDetectAllowThread();
+ break;
+ case TypeSystem::AllowThread::Unspecified:
+ result = false;
+ break;
+ }
+ if (!result && ReportHandler::isDebug(ReportHandler::MediumDebug))
+ qCInfo(lcShiboken).noquote() << msgDisallowThread(this);
+ return result;
+}
+
+TypeSystem::Ownership AbstractMetaFunction::argumentTargetOwnership(const AbstractMetaClassCPtr &cls, int idx) const
+{
+ for (const auto &modification : modifications(cls)) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == idx)
+ return argumentModification.targetOwnerShip();
+ }
+ }
+
+ return TypeSystem::UnspecifiedOwnership;
+}
+
+const QString &AbstractMetaFunction::modifiedTypeName() const
+{
+ return d->m_modifiedTypeName;
+}
+
+bool AbstractMetaFunction::generateOpaqueContainerReturn() const
+{
+ if (!isTypeModified() || d->m_type.typeUsagePattern() != AbstractMetaType::ContainerPattern)
+ return false;
+ // Needs to be a reference to a container, allow by value only for spans
+ if (d->m_type.referenceType() != LValueReference) {
+ auto cte = std::static_pointer_cast<const ContainerTypeEntry>(d->m_type.typeEntry());
+ if (cte->containerKind() != ContainerTypeEntry::SpanContainer)
+ return false;
+ }
+ return d->m_type.generateOpaqueContainerForGetter(d->m_modifiedTypeName);
+}
+
+bool AbstractMetaFunction::isModifiedToArray(int argumentIndex) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == argumentIndex && argumentModification.isArray())
+ return true;
+ }
+ }
+ return false;
+}
+
+// Note: The declaring class must be correctly set for this to work.
+bool AbstractMetaFunctionPrivate::applyTypeModification(const AbstractMetaFunction *q,
+ const QString &type,
+ int number, QString *errorMessage)
+{
+ if (number < 0 || number > m_arguments.size()) {
+ *errorMessage =
+ msgTypeModificationFailed(type, number, q,
+ msgArgumentOutOfRange(number, 0, m_arguments.size()));
+ return false;
+ }
+
+ // Modified return types may have unparseable types like Python tuples
+ if (number == 0) {
+ m_modifiedTypeName = type;
+ return true;
+ }
+
+ auto typeOpt = AbstractMetaType::fromString(type, errorMessage);
+ if (!typeOpt.has_value()) {
+ *errorMessage = msgTypeModificationFailed(type, number, q, *errorMessage);
+ return false;
+ }
+ m_arguments[number - 1].setModifiedType(typeOpt.value());
+ return true;
+}
+
+void AbstractMetaFunction::applyTypeModifications()
+{
+ QString errorMessage;
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &am : modification.argument_mods()) {
+ const int n = am.index();
+ if (am.isTypeModified()
+ && !d->applyTypeModification(this, am.modifiedType(),
+ n, &errorMessage)) {
+ throw Exception(errorMessage);
+ } else if (am.isRemoved() && n != 0) {
+ if (n < 1 || n > d->m_arguments.size()) {
+ errorMessage =
+ msgArgumentRemovalFailed(this, n,
+ msgArgumentOutOfRange(n, 1, d->m_arguments.size()));
+ throw Exception(errorMessage);
+ }
+ d->m_arguments[n - 1].setModifiedRemoved(true);
+ }
+ }
+ }
+}
+
+QString AbstractMetaFunction::pyiTypeReplaced(int argumentIndex) const
+{
+ for (const auto &modification : modifications(declaringClass())) {
+ for (const ArgumentModification &argumentModification : modification.argument_mods()) {
+ if (argumentModification.index() == argumentIndex) {
+ QString type = argumentModification.pyiType();
+ if (!type.isEmpty())
+ return type;
+ type = argumentModification.modifiedType();
+ if (!type.isEmpty())
+ return type;
+ }
+ }
+ }
+
+ return {};
+}
+
+// Parameter 'comment' indicates usage as a code comment of the overload decisor
+QString AbstractMetaFunctionPrivate::formatMinimalSignature(const AbstractMetaFunction *q,
+ bool comment) const
+{
+ QString result = m_originalName + u'(';
+ for (qsizetype i = 0; i < m_arguments.size(); ++i) {
+ const auto &argument = m_arguments.at(i);
+ if (i > 0)
+ result += u',';
+
+ const auto &type = comment ? argument.modifiedType() : argument.type();
+ result += type.minimalSignature();
+ if (comment && argument.hasDefaultValueExpression())
+ result += u'=';
+ }
+ result += u')';
+ if (m_constant)
+ result += u"const"_s;
+ result = TypeDatabase::normalizedSignature(result);
+
+ if (comment && !q->isVoid()) {
+ result += u"->"_s;
+ result += q->isTypeModified()
+ ? q->modifiedTypeName() : q->type().minimalSignature();
+ }
+ return result;
+}
+
+QString AbstractMetaFunction::minimalSignature() const
+{
+ if (d->m_cachedMinimalSignature.isEmpty())
+ d->m_cachedMinimalSignature = d->formatMinimalSignature(this, false);
+ return d->m_cachedMinimalSignature;
+}
+
+QStringList AbstractMetaFunction::modificationSignatures() const
+{
+ QStringList result{minimalSignature()};
+ if (d->m_unresolvedSignature != result.constFirst())
+ result.append(d->m_unresolvedSignature);
+ return result;
+}
+
+QString AbstractMetaFunction::signatureComment() const
+{
+ return d->formatMinimalSignature(this, true);
+}
+
+QString AbstractMetaFunction::debugSignature() const
+{
+ QString result;
+ const auto attributes = cppAttributes();
+ const bool isOverride = attributes.testFlag(FunctionAttribute::Override);
+ const bool isFinal = attributes.testFlag(FunctionAttribute::Final);
+ if (!isOverride && !isFinal && (attributes.testFlag(FunctionAttribute::Virtual)))
+ result += u"virtual "_s;
+ if (d->m_implementingClass)
+ result += d->m_implementingClass->qualifiedCppName() + u"::"_s;
+ result += minimalSignature();
+ if (isOverride)
+ result += u" override"_s;
+ if (isFinal)
+ result += u" final"_s;
+ return result;
+}
+
+FunctionModificationList AbstractMetaFunction::findClassModifications(const AbstractMetaFunction *f,
+ AbstractMetaClassCPtr implementor)
+{
+ const auto signatures = f->modificationSignatures();
+ FunctionModificationList mods;
+ while (implementor) {
+ mods += implementor->typeEntry()->functionModifications(signatures);
+ if ((implementor == implementor->baseClass()) ||
+ (implementor == f->implementingClass() && !mods.isEmpty())) {
+ break;
+ }
+ implementor = implementor->baseClass();
+ }
+ return mods;
+}
+
+FunctionModificationList AbstractMetaFunction::findGlobalModifications(const AbstractMetaFunction *f)
+{
+ auto *td = TypeDatabase::instance();
+ return td->globalFunctionModifications(f->modificationSignatures());
+}
+
+const FunctionModificationList &
+ AbstractMetaFunctionPrivate::modifications(const AbstractMetaFunction *q,
+ const AbstractMetaClassCPtr &implementor) const
+{
+ if (m_addedFunction)
+ return m_addedFunction->modifications();
+ for (const auto &ce : m_modificationCache) {
+ if (ce.klass == implementor)
+ return ce.modifications;
+ }
+ auto modifications = m_class == nullptr
+ ? AbstractMetaFunction::findGlobalModifications(q)
+ : AbstractMetaFunction::findClassModifications(q, implementor);
+
+ m_modificationCache.append({implementor, modifications});
+ return m_modificationCache.constLast().modifications;
+}
+
+const FunctionModificationList &
+ AbstractMetaFunction::modifications(AbstractMetaClassCPtr implementor) const
+{
+ if (!implementor)
+ implementor = d->m_class;
+ return d->modifications(this, implementor);
+}
+
+void AbstractMetaFunction::clearModificationsCache()
+{
+ d->m_modificationCache.clear();
+}
+
+const DocModificationList AbstractMetaFunction::addedFunctionDocModifications() const
+{
+ return d->m_addedFunction
+ ? d->m_addedFunction->docModifications() : DocModificationList{};
+}
+
+QString AbstractMetaFunction::argumentName(int index,
+ bool /* create */,
+ AbstractMetaClassCPtr /* implementor */) const
+{
+ return d->m_arguments[--index].name();
+}
+
+int AbstractMetaFunction::propertySpecIndex() const
+{
+ return d->m_propertySpecIndex;
+}
+
+void AbstractMetaFunction::setPropertySpecIndex(int i)
+{
+ d->m_propertySpecIndex = i;
+}
+
+FunctionTypeEntryPtr AbstractMetaFunction::typeEntry() const
+{
+ return d->m_typeEntry;
+}
+
+void AbstractMetaFunction::setTypeEntry(const FunctionTypeEntryPtr &typeEntry)
+{
+ d->m_typeEntry = typeEntry;
+}
+
+QString AbstractMetaFunction::targetLangPackage() const
+{
+ if (d->m_addedFunction != nullptr)
+ return d->m_addedFunction->targetLangPackage();
+ if (d->m_class != nullptr)
+ return d->m_class->typeEntry()->targetLangPackage();
+ if (d->m_typeEntry != nullptr)
+ return d->m_typeEntry->targetLangPackage();
+ return {};
+}
+
+bool AbstractMetaFunction::isCallOperator() const
+{
+ return d->m_name == u"operator()";
+}
+
+bool AbstractMetaFunction::hasInjectedCode() const
+{
+ const FunctionModificationList &mods = modifications(ownerClass());
+ for (const FunctionModification &mod : mods) {
+ if (mod.isCodeInjection())
+ return true;
+ }
+ return false;
+}
+
+// Traverse the code snippets, return true if predicate returns true
+template <class Predicate>
+bool AbstractMetaFunction::traverseCodeSnips(Predicate predicate,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ for (const FunctionModification &mod : modifications(ownerClass())) {
+ if (mod.isCodeInjection()) {
+ for (const CodeSnip &snip : mod.snips()) {
+ if ((snip.language & language) != 0
+ && (snip.position == position || position == TypeSystem::CodeSnipPositionAny)
+ && predicate(snip)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+CodeSnipList AbstractMetaFunction::injectedCodeSnips(TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ CodeSnipList result;
+ traverseCodeSnips([&result] (const CodeSnip &s) {
+ result.append(s);
+ return false;
+ }, position, language);
+ return result;
+}
+
+bool AbstractMetaFunction::injectedCodeContains(const QRegularExpression &pattern,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ return traverseCodeSnips([pattern] (const CodeSnip &s) {
+ return s.code().contains(pattern);
+ }, position, language);
+}
+
+bool AbstractMetaFunction::injectedCodeContains(QStringView pattern,
+ TypeSystem::CodeSnipPosition position,
+ TypeSystem::Language language) const
+{
+ return traverseCodeSnips([pattern] (const CodeSnip &s) {
+ return s.code().contains(pattern);
+ }, position, language);
+}
+
+bool AbstractMetaFunction::hasSignatureModifications() const
+{
+ const FunctionModificationList &mods = modifications();
+ for (const FunctionModification &mod : mods) {
+ if (mod.isRenameModifier())
+ return true;
+ for (const ArgumentModification &argmod : mod.argument_mods()) {
+ // since zero represents the return type and we're
+ // interested only in checking the function arguments,
+ // it will be ignored.
+ if (argmod.index() > 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AbstractMetaFunction::isConversionOperator(const QString &funcName)
+{
+ return funcName.startsWith(u"operator ");
+}
+
+ExceptionSpecification AbstractMetaFunction::exceptionSpecification() const
+{
+ return d->m_exceptionSpecification;
+}
+
+void AbstractMetaFunction::setExceptionSpecification(ExceptionSpecification e)
+{
+ d->m_exceptionSpecification = e;
+}
+
+static inline TypeSystem::ExceptionHandling exceptionMod(const AbstractMetaClassCPtr &klass)
+{
+ return klass->typeEntry()->exceptionHandling();
+}
+
+static inline bool hasExceptionMod(const AbstractMetaClassCPtr &klass)
+{
+ return exceptionMod(klass) != TypeSystem::ExceptionHandling::Unspecified;
+}
+
+bool AbstractMetaFunction::generateExceptionHandling() const
+{
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::CopyConstructorFunction:
+ case AbstractMetaFunction::MoveConstructorFunction:
+ case AbstractMetaFunction::AssignmentOperatorFunction:
+ case AbstractMetaFunction::MoveAssignmentOperatorFunction:
+ case AbstractMetaFunction::DestructorFunction:
+ return false;
+ default:
+ break;
+ }
+
+ auto exceptionHandlingModification = d->m_exceptionHandlingModification;
+ // If there is no modification on the function, check for a base class.
+ if (d->m_class && exceptionHandlingModification == TypeSystem::ExceptionHandling::Unspecified) {
+ if (auto base = recurseClassHierarchy(d->m_class, hasExceptionMod))
+ exceptionHandlingModification = exceptionMod(base);
+ }
+
+ bool result = false;
+ switch (exceptionHandlingModification) {
+ case TypeSystem::ExceptionHandling::On:
+ result = true;
+ break;
+ case TypeSystem::ExceptionHandling::AutoDefaultToOn:
+ result = d->m_exceptionSpecification != ExceptionSpecification::NoExcept;
+ break;
+ case TypeSystem::ExceptionHandling::AutoDefaultToOff:
+ result = d->m_exceptionSpecification == ExceptionSpecification::Throws;
+ break;
+ case TypeSystem::ExceptionHandling::Unspecified:
+ case TypeSystem::ExceptionHandling::Off:
+ break;
+ }
+ return result;
+}
+
+bool AbstractMetaFunction::isConversionOperator() const
+{
+ return d->m_functionType == ConversionOperator;
+}
+
+bool AbstractMetaFunction::isOperatorOverload(const QString &funcName)
+{
+ if (isConversionOperator(funcName))
+ return true;
+
+ static const QRegularExpression opRegEx(u"^operator([+\\-\\*/%=&\\|\\^\\<>!][=]?"
+ "|\\+\\+|\\-\\-|&&|\\|\\||<<[=]?|>>[=]?|~"
+ "|\\[\\]|\\s+delete\\[?\\]?"
+ "|\\(\\)"
+ "|\\s+new\\[?\\]?)$"_s);
+ Q_ASSERT(opRegEx.isValid());
+ return opRegEx.match(funcName).hasMatch();
+}
+
+bool AbstractMetaFunction::isOperatorOverload() const
+{
+ return d->m_functionType == AssignmentOperatorFunction
+ || (d->m_functionType >= FirstOperator && d->m_functionType <= LastOperator);
+}
+
+bool AbstractMetaFunction::isArithmeticOperator() const
+{
+ return d->m_functionType == ArithmeticOperator;
+}
+
+bool AbstractMetaFunction::isBitwiseOperator() const
+{
+ return d->m_functionType == BitwiseOperator
+ || d->m_functionType == ShiftOperator;
+}
+
+bool AbstractMetaFunction::isComparisonOperator() const
+{
+ return d->m_functionType == ComparisonOperator;
+}
+
+bool AbstractMetaFunction::isSymmetricalComparisonOperator() const
+{
+ if (d->m_functionType != ComparisonOperator || d->m_class == nullptr)
+ return false;
+ AbstractMetaType classType(d->m_class->typeEntry());
+ classType.decideUsagePattern();
+ return std::all_of(d->m_arguments.constBegin(), d->m_arguments.constEnd(),
+ [classType](const AbstractMetaArgument &a) {
+ return a.type().isEquivalent(classType);});
+}
+
+bool AbstractMetaFunction::isIncDecrementOperator() const
+{
+ return d->m_functionType == IncrementOperator
+ || d->m_functionType == DecrementOperator;
+}
+bool AbstractMetaFunction::isLogicalOperator() const
+{
+ return d->m_functionType == LogicalOperator;
+}
+
+bool AbstractMetaFunction::isAssignmentOperator() const
+{
+ return d->m_functionType == AssignmentOperatorFunction
+ || d->m_functionType == MoveAssignmentOperatorFunction;
+}
+
+bool AbstractMetaFunction::isGetter() const
+{
+ return d->m_functionType == NormalFunction && !isVoid()
+ && d->m_constant && d->m_access == Access::Public
+ && d->m_arguments.isEmpty();
+}
+
+bool AbstractMetaFunction::isQtIsNullMethod() const
+{
+ return isGetter() && d->m_name == u"isNull" && returnsBool();
+}
+
+int AbstractMetaFunction::arityOfOperator() const
+{
+ if (!isOperatorOverload() || isCallOperator())
+ return -1;
+
+ int arity = d->m_arguments.size();
+
+ // Operator overloads that are class members
+ // implicitly includes the instance and have
+ // one parameter less than their arity,
+ // so we increment it.
+ if (ownerClass() && arity < 2)
+ arity++;
+
+ return arity;
+}
+
+bool AbstractMetaFunction::isInplaceOperator() const
+{
+ static const QSet<QStringView> inplaceOperators =
+ {
+ u"operator+=", u"operator&=", u"operator-=", u"operator|=",
+ u"operator*=", u"operator^=", u"operator/=", u"operator<<=",
+ u"operator%=", u"operator>>="
+ };
+
+ return isOperatorOverload() && inplaceOperators.contains(originalName());
+}
+
+bool AbstractMetaFunction::isVirtual() const
+{
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Virtual);
+}
+
+QString AbstractMetaFunctionPrivate::modifiedName(const AbstractMetaFunction *q) const
+{
+ if (m_cachedModifiedName.isEmpty()) {
+ for (const auto &mod : q->modifications(q->implementingClass())) {
+ if (mod.isRenameModifier()) {
+ m_cachedModifiedName = mod.renamedToName();
+ break;
+ }
+ }
+ if (m_cachedModifiedName.isEmpty())
+ m_cachedModifiedName = m_name;
+ }
+ return m_cachedModifiedName;
+}
+
+QString AbstractMetaFunction::modifiedName() const
+{
+ return d->modifiedName(this);
+}
+
+AbstractMetaFunctionCPtr
+AbstractMetaFunction::find(const AbstractMetaFunctionCList &haystack,
+ QAnyStringView needle)
+{
+ for (const auto &f : haystack) {
+ if (f->name() == needle)
+ return f;
+ }
+ return {};
+}
+
+bool AbstractMetaFunction::matches(OperatorQueryOptions query) const
+{
+ bool result = false;
+ switch (d->m_functionType) {
+ case AbstractMetaFunction::AssignmentOperatorFunction:
+ result = query.testFlag(OperatorQueryOption::AssignmentOp);
+ break;
+ case AbstractMetaFunction::ConversionOperator:
+ result = query.testFlag(OperatorQueryOption::ConversionOp);
+ break;
+ case AbstractMetaFunction::ArithmeticOperator:
+ result = query.testFlag(OperatorQueryOption::ArithmeticOp);
+ break;
+ case AbstractMetaFunction::IncrementOperator:
+ case AbstractMetaFunction::DecrementOperator:
+ result = query.testFlag(OperatorQueryOption::IncDecrementOp);
+ break;
+ case AbstractMetaFunction::BitwiseOperator:
+ case AbstractMetaFunction::ShiftOperator:
+ result = query.testFlag(OperatorQueryOption::BitwiseOp);
+ break;
+ case AbstractMetaFunction::LogicalOperator:
+ result = query.testFlag(OperatorQueryOption::LogicalOp);
+ break;
+ case AbstractMetaFunction::SubscriptOperator:
+ result = query.testFlag(OperatorQueryOption::SubscriptionOp);
+ break;
+ case AbstractMetaFunction::ComparisonOperator:
+ result = query.testFlag(OperatorQueryOption::ComparisonOp);
+ if (!result && query.testFlag(OperatorQueryOption::SymmetricalComparisonOp))
+ result = isSymmetricalComparisonOperator();
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+void AbstractMetaFunction::setAllowThreadModification(TypeSystem::AllowThread am)
+{
+ d->m_allowThreadModification = am;
+}
+
+void AbstractMetaFunction::setExceptionHandlingModification(TypeSystem::ExceptionHandling em)
+{
+ d->m_exceptionHandlingModification = em;
+}
+
+int AbstractMetaFunctionPrivate::overloadNumber(const AbstractMetaFunction *q) const
+{
+ if (m_cachedOverloadNumber == TypeSystem::OverloadNumberUnset) {
+ m_cachedOverloadNumber = TypeSystem::OverloadNumberDefault;
+ for (const auto &mod : q->modifications(q->implementingClass())) {
+ if (mod.overloadNumber() != TypeSystem::OverloadNumberUnset) {
+ m_cachedOverloadNumber = mod.overloadNumber();
+ break;
+ }
+ }
+ }
+ return m_cachedOverloadNumber;
+}
+
+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:
+ 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
+ return typeSystemTypeEntry(d->m_typeEntry)->snakeCase();
+
+ if (d->m_class) {
+ auto typeEntry = d->m_class->typeEntry();
+ const auto snakeCase = typeEntry->snakeCase();
+ return snakeCase != TypeSystem::SnakeCase::Unspecified
+ ? snakeCase : typeSystemTypeEntry(typeEntry)->snakeCase();
+ }
+ return TypeSystem::SnakeCase::Disabled;
+}
+
+// Query functions for generators
+bool AbstractMetaFunction::injectedCodeUsesPySelf() const
+{
+ return injectedCodeContains(u"%PYSELF", TypeSystem::CodeSnipPositionAny, TypeSystem::NativeCode);
+}
+
+bool AbstractMetaFunction::injectedCodeCallsPythonOverride() const
+{
+ static const QRegularExpression
+ overrideCallRegexCheck(R"(PyObject_Call\s*\(\s*%PYTHON_METHOD_OVERRIDE\s*,)"_L1);
+ Q_ASSERT(overrideCallRegexCheck.isValid());
+ return injectedCodeContains(overrideCallRegexCheck, TypeSystem::CodeSnipPositionAny,
+ TypeSystem::NativeCode);
+}
+
+bool AbstractMetaFunction::injectedCodeHasReturnValueAttribution(TypeSystem::Language language) const
+{
+ if (language == TypeSystem::TargetLangCode) {
+ static const QRegularExpression
+ retValAttributionRegexCheck_target(R"(%PYARG_0\s*=[^=]\s*.+)"_L1);
+ Q_ASSERT(retValAttributionRegexCheck_target.isValid());
+ return injectedCodeContains(retValAttributionRegexCheck_target, TypeSystem::CodeSnipPositionAny, language);
+ }
+
+ static const QRegularExpression
+ retValAttributionRegexCheck_native(R"(%0\s*=[^=]\s*.+)"_L1);
+ Q_ASSERT(retValAttributionRegexCheck_native.isValid());
+ return injectedCodeContains(retValAttributionRegexCheck_native, TypeSystem::CodeSnipPositionAny, language);
+}
+
+bool AbstractMetaFunction::injectedCodeUsesArgument(int argumentIndex) const
+{
+ const QRegularExpression argRegEx = CodeSnipAbstract::placeHolderRegex(argumentIndex + 1);
+
+ return traverseCodeSnips([argRegEx](const CodeSnip &s) {
+ const QString code = s.code();
+ return code.contains(u"%ARGUMENT_NAMES") || code.contains(argRegEx);
+ }, TypeSystem::CodeSnipPositionAny);
+}
+
+bool AbstractMetaFunction::isVisibilityModifiedToPrivate() const
+{
+ for (const auto &mod : modifications()) {
+ if (mod.modifiers().testFlag(FunctionModification::Private))
+ return true;
+ }
+ return false;
+}
+
+struct ComparisonOperator
+{
+ const char *cppOperator;
+ const char *pythonOpCode;
+};
+
+using ComparisonOperatorMapping =
+ QHash<AbstractMetaFunction::ComparisonOperatorType, ComparisonOperator>;
+
+static const ComparisonOperatorMapping &comparisonOperatorMapping()
+{
+ static const ComparisonOperatorMapping result = {
+ {AbstractMetaFunction::OperatorEqual, {"==", "Py_EQ"}},
+ {AbstractMetaFunction::OperatorNotEqual, {"!=", "Py_NE"}},
+ {AbstractMetaFunction::OperatorLess, {"<", "Py_LT"}},
+ {AbstractMetaFunction::OperatorLessEqual, {"<=", "Py_LE"}},
+ {AbstractMetaFunction::OperatorGreater, {">", "Py_GT"}},
+ {AbstractMetaFunction::OperatorGreaterEqual, {">=", "Py_GE"}}
+ };
+ return result;
+}
+
+const char * AbstractMetaFunction::pythonRichCompareOpCode(ComparisonOperatorType ct)
+{
+ return comparisonOperatorMapping().value(ct).pythonOpCode;
+}
+
+const char * AbstractMetaFunction::cppComparisonOperator(ComparisonOperatorType ct)
+{
+ return comparisonOperatorMapping().value(ct).cppOperator;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+void AbstractMetaFunction::formatDebugBrief(QDebug &debug) const
+{
+ debug << '"' << debugSignature() << '"';
+}
+
+void AbstractMetaFunction::formatDebugVerbose(QDebug &debug) const
+{
+ debug << d->m_functionType << ' ';
+ if (d->m_class)
+ debug << d->m_access << ' ';
+ debug << d->m_type << ' ' << d->m_name;
+ switch (d->m_exceptionSpecification) {
+ case ExceptionSpecification::Unknown:
+ break;
+ case ExceptionSpecification::NoExcept:
+ debug << " noexcept";
+ break;
+ case ExceptionSpecification::Throws:
+ debug << " throw(...)";
+ break;
+ }
+ if (d->m_exceptionHandlingModification != TypeSystem::ExceptionHandling::Unspecified)
+ debug << " exeption-mod " << int(d->m_exceptionHandlingModification);
+ debug << '(';
+ for (qsizetype i = 0, count = d->m_arguments.size(); i < count; ++i) {
+ if (i)
+ debug << ", ";
+ debug << d->m_arguments.at(i);
+ }
+ const QString signature = minimalSignature();
+ debug << "), signature=\"" << signature << '"';
+ if (signature != d->m_unresolvedSignature)
+ debug << ", unresolvedSignature=\"" << d->m_unresolvedSignature << '"';
+ if (d->m_constant)
+ debug << " [const]";
+ if (d->m_reverse)
+ debug << " [reverse]";
+ if (isUserAdded())
+ debug << " [userAdded]";
+ if (isUserDeclared())
+ debug << " [userDeclared]";
+ if (d->m_cppAttributes.testFlag(FunctionAttribute::Explicit))
+ debug << " [explicit]";
+ if (d->m_cppAttributes.testFlag(FunctionAttribute::Deprecated))
+ debug << " [deprecated]";
+ if (d->m_pointerOperator)
+ debug << " [operator->]";
+ if (d->m_isCallOperator)
+ debug << " [operator()]";
+ if (d->m_class)
+ debug << " class: " << d->m_class->name();
+ if (d->m_implementingClass)
+ debug << " implementing class: " << d->m_implementingClass->name();
+ if (d->m_declaringClass)
+ debug << " declaring class: " << d->m_declaringClass->name();
+}
+
+QDebug operator<<(QDebug debug, const AbstractMetaFunction *af)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "AbstractMetaFunction(";
+ if (af) {
+ if (debug.verbosity() > 2) {
+ af->formatDebugVerbose(debug);
+ } else {
+ debug << "signature=";
+ af->formatDebugBrief(debug);
+ }
+ } else {
+ debug << '0';
+ }
+ debug << ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM