aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/typesystemparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/typesystemparser.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/typesystemparser.cpp3648
1 files changed, 3648 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
new file mode 100644
index 000000000..2b686e997
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp
@@ -0,0 +1,3648 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "typesystemparser_p.h"
+#include "anystringview_helpers.h"
+#include "addedfunction.h"
+#include "codesnip.h"
+#include "enumtypeentry.h"
+#include "containertypeentry.h"
+#include "customconversion.h"
+#include "customtypenentry.h"
+#include "flagstypeentry.h"
+#include "functiontypeentry.h"
+#include "namespacetypeentry.h"
+#include "objecttypeentry.h"
+#include "primitivetypeentry.h"
+#include "smartpointertypeentry.h"
+#include "typedefentry.h"
+#include "typesystemtypeentry.h"
+#include "valuetypeentry.h"
+#include "modifications.h"
+#include "typedatabase.h"
+#include "messages.h"
+#include "reporthandler.h"
+#include "sourcelocation.h"
+#include "conditionalstreamreader.h"
+
+#include "qtcompat.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QSet>
+#include <QtCore/QStringView>
+#include <QtCore/QStringAlgorithms>
+#include <QtCore/QVersionNumber>
+#include <QtCore/QXmlStreamAttributes>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamEntityResolver>
+
+#include <algorithm>
+#include <optional>
+#include <memory>
+
+using namespace Qt::StringLiterals;
+
+constexpr auto allowThreadAttribute = "allow-thread"_L1;
+constexpr auto checkFunctionAttribute = "check-function"_L1;
+constexpr auto copyableAttribute = "copyable"_L1;
+constexpr auto accessAttribute = "access"_L1;
+constexpr auto actionAttribute = "action"_L1;
+constexpr auto quoteAfterLineAttribute = "quote-after-line"_L1;
+constexpr auto quoteBeforeLineAttribute = "quote-before-line"_L1;
+constexpr auto textAttribute = "text"_L1;
+constexpr auto nameAttribute = "name"_L1;
+constexpr auto sinceAttribute = "since"_L1;
+constexpr auto untilAttribute = "until"_L1;
+constexpr auto defaultSuperclassAttribute = "default-superclass"_L1;
+constexpr auto deleteInMainThreadAttribute = "delete-in-main-thread"_L1;
+constexpr auto deprecatedAttribute = "deprecated"_L1;
+constexpr auto disableWrapperAttribute = "disable-wrapper"_L1;
+constexpr auto docFileAttribute = "doc-file"_L1;
+constexpr auto exceptionHandlingAttribute = "exception-handling"_L1;
+constexpr auto extensibleAttribute = "extensible"_L1;
+constexpr auto fileNameAttribute = "file-name"_L1;
+constexpr auto fileAttribute = "file"_L1;
+constexpr auto flagsAttribute = "flags"_L1;
+constexpr auto forceAbstractAttribute = "force-abstract"_L1;
+constexpr auto forceIntegerAttribute = "force-integer"_L1;
+constexpr auto formatAttribute = "format"_L1;
+constexpr auto generateUsingAttribute = "generate-using"_L1;
+constexpr auto generateFunctionsAttribute = "generate-functions"_L1;
+constexpr auto classAttribute = "class"_L1;
+constexpr auto generateAttribute = "generate"_L1;
+constexpr auto generateGetSetDefAttribute = "generate-getsetdef"_L1;
+constexpr auto genericClassAttribute = "generic-class"_L1;
+constexpr auto indexAttribute = "index"_L1;
+constexpr auto invalidateAfterUseAttribute = "invalidate-after-use"_L1;
+constexpr auto isNullAttribute = "isNull"_L1;
+constexpr auto locationAttribute = "location"_L1;
+constexpr auto modifiedTypeAttribute = "modified-type"_L1;
+constexpr auto opaqueContainerAttribute = "opaque-containers"_L1;
+constexpr auto operatorBoolAttribute = "operator-bool"_L1;
+constexpr auto parentManagementAttribute = "parent-management"_L1;
+constexpr auto pyiTypeAttribute = "pyi-type"_L1;
+constexpr auto overloadNumberAttribute = "overload-number"_L1;
+constexpr auto ownershipAttribute = "owner"_L1;
+constexpr auto packageAttribute = "package"_L1;
+constexpr auto polymorphicBaseAttribute = "polymorphic-base"_L1;
+constexpr auto positionAttribute = "position"_L1;
+constexpr auto preferredConversionAttribute = "preferred-conversion"_L1;
+constexpr auto preferredTargetLangTypeAttribute = "preferred-target-lang-type"_L1;
+constexpr auto pythonEnumTypeAttribute = "python-type"_L1;
+constexpr auto pythonOverrideAttribute = "python-override"_L1;
+constexpr auto cppEnumTypeAttribute = "cpp-type"_L1;
+constexpr auto qtMetaObjectFunctionsAttribute = "qt-metaobject"_L1;
+constexpr auto qtMetaTypeAttribute = "qt-register-metatype"_L1;
+constexpr auto removeAttribute = "remove"_L1;
+constexpr auto renameAttribute = "rename"_L1;
+constexpr auto readAttribute = "read"_L1;
+constexpr auto targetLangNameAttribute = "target-lang-name"_L1;
+constexpr auto writeAttribute = "write"_L1;
+constexpr auto opaqueContainerFieldAttribute = "opaque-container"_L1;
+constexpr auto replaceAttribute = "replace"_L1;
+constexpr auto toAttribute = "to"_L1;
+constexpr auto signatureAttribute = "signature"_L1;
+constexpr auto snippetAttribute = "snippet"_L1;
+constexpr auto snakeCaseAttribute = "snake-case"_L1;
+constexpr auto staticAttribute = "static"_L1;
+constexpr auto classmethodAttribute = "classmethod"_L1;
+constexpr auto threadAttribute = "thread"_L1;
+constexpr auto sourceAttribute = "source"_L1;
+constexpr auto streamAttribute = "stream"_L1;
+constexpr auto privateAttribute = "private"_L1;
+constexpr auto xPathAttribute = "xpath"_L1;
+constexpr auto virtualSlotAttribute = "virtual-slot"_L1;
+constexpr auto visibleAttribute = "visible"_L1;
+constexpr auto enumIdentifiedByValueAttribute = "identified-by-value"_L1;
+constexpr auto subModuleOfAttribute = "submodule-of"_L1;
+
+constexpr auto noAttributeValue = "no"_L1;
+constexpr auto yesAttributeValue = "yes"_L1;
+constexpr auto trueAttributeValue = "true"_L1;
+constexpr auto falseAttributeValue = "false"_L1;
+
+static bool isTypeEntry(StackElement el)
+{
+ return el >= StackElement::FirstTypeEntry && el <= StackElement::LastTypeEntry;
+}
+
+static bool isComplexTypeEntry(StackElement el)
+{
+ return el >= StackElement::FirstTypeEntry && el <= StackElement::LastComplexTypeEntry;
+}
+
+static bool isDocumentation(StackElement el)
+{
+ return el >= StackElement::FirstDocumentation && el <= StackElement::LastDocumentation;
+}
+
+static QList<CustomConversionPtr> customConversionsForReview;
+
+// Set a regular expression for rejection from text. By legacy, those are fixed
+// strings, except for '*' meaning 'match all'. Enclosing in "^..$"
+// indicates regular expression.
+static bool setRejectionRegularExpression(const QString &patternIn,
+ QRegularExpression *re,
+ QString *errorMessage)
+{
+ QString pattern;
+ if (patternIn.startsWith(u'^') && patternIn.endsWith(u'$'))
+ pattern = patternIn;
+ else if (patternIn == u"*")
+ pattern = "^.*$"_L1;
+ else
+ pattern = u'^' + QRegularExpression::escape(patternIn) + u'$';
+ re->setPattern(pattern);
+ if (!re->isValid()) {
+ *errorMessage = msgInvalidRegularExpression(patternIn, re->errorString());
+ return false;
+ }
+ return true;
+}
+
+static inline bool hasFileSnippetAttributes(const QXmlStreamAttributes *attributes)
+{
+ return attributes->hasAttribute(fileAttribute);
+}
+
+// Extract a snippet from a file within annotation "// @snippet label".
+std::optional<QString>
+ extractSnippet(const QString &code, const QString &snippetLabel)
+{
+ if (snippetLabel.isEmpty())
+ return code;
+ const QString pattern = R"(^\s*//\s*@snippet\s+)"_L1
+ + QRegularExpression::escape(snippetLabel)
+ + R"(\s*$)"_L1;
+ const QRegularExpression snippetRe(pattern);
+ Q_ASSERT(snippetRe.isValid());
+
+ bool useLine = false;
+ bool foundLabel = false;
+ QString result;
+ const auto lines = QStringView{code}.split(u'\n');
+ for (const auto &line : lines) {
+ if (snippetRe.matchView(line).hasMatch()) {
+ foundLabel = true;
+ useLine = !useLine;
+ if (!useLine)
+ break; // End of snippet reached
+ } else if (useLine)
+ result += line.toString() + u'\n';
+ }
+ if (!foundLabel)
+ return {};
+ return CodeSnipAbstract::fixSpaces(result);
+}
+
+template <class EnumType>
+struct EnumLookup
+{
+ QStringView name;
+ EnumType value;
+};
+
+// Helper macros to define lookup functions that take a QStringView needle
+// and an optional default return value.
+#define ENUM_LOOKUP_BEGIN(EnumType, caseSensitivity, functionName) \
+static std::optional<EnumType> functionName(QStringView needle) \
+{ \
+ using HaystackEntry = EnumLookup<EnumType>; \
+ constexpr auto cs = caseSensitivity; \
+ static constexpr HaystackEntry haystack[] =
+
+#define ENUM_LOOKUP_LINEAR_SEARCH \
+ auto pred = [cs, needle](const HaystackEntry &he) { \
+ return he.name.compare(needle, cs) == 0; \
+ }; \
+ auto end = std::cend(haystack); \
+ auto it = std::find_if(std::cbegin(haystack), end, pred); \
+ if (it != end) \
+ return it->value; \
+ return std::nullopt; \
+}
+
+ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive,
+ allowThreadFromAttribute)
+ {
+ {u"yes", TypeSystem::AllowThread::Allow},
+ {u"true", TypeSystem::AllowThread::Allow},
+ {u"auto", TypeSystem::AllowThread::Auto},
+ {u"no", TypeSystem::AllowThread::Disallow},
+ {u"false", TypeSystem::AllowThread::Disallow},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+
+ENUM_LOOKUP_BEGIN(TypeSystem::BoolCast, Qt::CaseInsensitive,
+ boolCastFromAttribute)
+ {
+ {u"yes", TypeSystem::BoolCast::Enabled},
+ {u"true", TypeSystem::BoolCast::Enabled},
+ {u"no", TypeSystem::BoolCast::Disabled},
+ {u"false", TypeSystem::BoolCast::Disabled},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::PythonEnumType, Qt::CaseSensitive,
+ pythonEnumTypeFromAttribute)
+ {
+ {u"Enum", TypeSystem::PythonEnumType::Enum},
+ {u"IntEnum", TypeSystem::PythonEnumType::IntEnum},
+ {u"Flag", TypeSystem::PythonEnumType::Flag},
+ {u"IntFlag", TypeSystem::PythonEnumType::IntFlag},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::QtMetaTypeRegistration, Qt::CaseSensitive,
+ qtMetaTypeFromAttribute)
+ {
+ {u"yes", TypeSystem::QtMetaTypeRegistration::Enabled},
+ {u"true", TypeSystem::QtMetaTypeRegistration::Enabled},
+ {u"base", TypeSystem::QtMetaTypeRegistration::BaseEnabled},
+ {u"no", TypeSystem::QtMetaTypeRegistration::Disabled},
+ {u"false", TypeSystem::QtMetaTypeRegistration::Disabled},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive,
+ languageFromAttribute)
+ {
+ {u"all", TypeSystem::All}, // sorted!
+ {u"native", TypeSystem::NativeCode}, // em algum lugar do cpp
+ {u"shell", TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe
+ {u"target", TypeSystem::TargetLangCode} // em algum lugar do cpp
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive,
+ ownershipFromFromAttribute)
+ {
+ {u"target", TypeSystem::TargetLangOwnership},
+ {u"c++", TypeSystem::CppOwnership},
+ {u"default", TypeSystem::DefaultOwnership}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive,
+ addedFunctionAccessFromAttribute)
+ {
+ {u"public", AddedFunction::Public},
+ {u"protected", AddedFunction::Protected},
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(FunctionModification::ModifierFlag, Qt::CaseSensitive,
+ modifierFromAttribute)
+ {
+ {u"private", FunctionModification::Private},
+ {u"public", FunctionModification::Public},
+ {u"protected", FunctionModification::Protected},
+ {u"rename", FunctionModification::Rename},
+ {u"final", FunctionModification::Final},
+ {u"non-final", FunctionModification::NonFinal}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive,
+ referenceCountFromAttribute)
+ {
+ {u"add", ReferenceCount::Add},
+ {u"add-all", ReferenceCount::AddAll},
+ {u"remove", ReferenceCount::Remove},
+ {u"set", ReferenceCount::Set},
+ {u"ignore", ReferenceCount::Ignore}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive,
+ argumentOwnerActionFromAttribute)
+ {
+ {u"add", ArgumentOwner::Add},
+ {u"remove", ArgumentOwner::Remove}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::CodeSnipPosition, Qt::CaseInsensitive,
+ codeSnipPositionFromAttribute)
+ {
+ {u"beginning", TypeSystem::CodeSnipPositionBeginning},
+ {u"end", TypeSystem::CodeSnipPositionEnd},
+ {u"declaration", TypeSystem::CodeSnipPositionDeclaration},
+ {u"override", TypeSystem::CodeSnipPositionPyOverride}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive,
+ locationFromAttribute)
+ {
+ {u"global", Include::IncludePath},
+ {u"local", Include::LocalPath},
+ {u"target", Include::TargetLangImport}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive,
+ docModificationFromAttribute)
+ {
+ {u"append", TypeSystem::DocModificationAppend},
+ {u"prepend", TypeSystem::DocModificationPrepend},
+ {u"replace", TypeSystem::DocModificationReplace}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(ContainerTypeEntry::ContainerKind, Qt::CaseSensitive,
+ containerTypeFromAttribute)
+ {
+ {u"list", ContainerTypeEntry::ListContainer},
+ {u"string-list", ContainerTypeEntry::ListContainer},
+ {u"linked-list", ContainerTypeEntry::ListContainer},
+ {u"vector", ContainerTypeEntry::ListContainer},
+ {u"stack", ContainerTypeEntry::ListContainer},
+ {u"queue", ContainerTypeEntry::ListContainer},
+ {u"set", ContainerTypeEntry::SetContainer},
+ {u"map", ContainerTypeEntry::MapContainer},
+ {u"multi-map", ContainerTypeEntry::MultiMapContainer},
+ {u"hash", ContainerTypeEntry::MapContainer},
+ {u"multi-hash", ContainerTypeEntry::MultiMapContainer},
+ {u"pair", ContainerTypeEntry::PairContainer},
+ {u"span", ContainerTypeEntry::SpanContainer}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive,
+ typeRejectionFromAttribute)
+ {
+ {u"class", TypeRejection::ExcludeClass},
+ {u"function-name", TypeRejection::Function},
+ {u"field-name", TypeRejection::Field},
+ {u"enum-name", TypeRejection::Enum },
+ {u"argument-type", TypeRejection::ArgumentType},
+ {u"return-type", TypeRejection::ReturnType}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive,
+ exceptionHandlingFromAttribute)
+{
+ {u"no", TypeSystem::ExceptionHandling::Off},
+ {u"false", TypeSystem::ExceptionHandling::Off},
+ {u"auto-off", TypeSystem::ExceptionHandling::AutoDefaultToOff},
+ {u"auto-on", TypeSystem::ExceptionHandling::AutoDefaultToOn},
+ {u"yes", TypeSystem::ExceptionHandling::On},
+ {u"true", TypeSystem::ExceptionHandling::On},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::SmartPointerType, Qt::CaseSensitive,
+ smartPointerTypeFromAttribute)
+{
+ {u"handle", TypeSystem::SmartPointerType::Handle},
+ {u"unique", TypeSystem::SmartPointerType::Unique},
+ {u"value-handle", TypeSystem::SmartPointerType::ValueHandle},
+ {u"shared", TypeSystem::SmartPointerType::Shared}
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+template <class EnumType>
+static std::optional<EnumType>
+ lookupHashElement(const QHash<QStringView, EnumType> &hash,
+ QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive)
+{
+ auto end = hash.cend();
+ auto it = hash.constFind(needle);
+ if (it != end)
+ return it.value();
+ if (cs == Qt::CaseInsensitive) { // brute force search for the unlikely case mismatch
+ for (it = hash.cbegin(); it != end; ++it) {
+ if (it.key().compare(needle, cs) == 0)
+ return it.value();
+ }
+ }
+ return std::nullopt;
+}
+
+using StackElementHash = QHash<QStringView, StackElement>;
+
+static const StackElementHash &stackElementHash()
+{
+ static const StackElementHash result{
+ {u"add-conversion", StackElement::AddConversion},
+ {u"add-function", StackElement::AddFunction},
+ {u"add-pymethoddef", StackElement::AddPyMethodDef},
+ {u"array", StackElement::Array},
+ {u"configuration", StackElement::Configuration},
+ {u"container-type", StackElement::ContainerTypeEntry},
+ {u"conversion-rule", StackElement::ConversionRule},
+ {u"custom-constructor", StackElement::Unimplemented},
+ {u"custom-destructor", StackElement::Unimplemented},
+ {u"custom-type", StackElement::CustomTypeEntry},
+ {u"declare-function", StackElement::DeclareFunction},
+ {u"define-ownership", StackElement::DefineOwnership},
+ {u"enum-type", StackElement::EnumTypeEntry},
+ {u"extra-includes", StackElement::ExtraIncludes},
+ {u"function", StackElement::FunctionTypeEntry},
+ {u"import-file", StackElement::ImportFile},
+ {u"include", StackElement::Include},
+ {u"inject-code", StackElement::InjectCode},
+ {u"inject-documentation", StackElement::InjectDocumentation},
+ {u"insert-template", StackElement::InsertTemplate},
+ {u"interface-type", StackElement::InterfaceTypeEntry},
+ {u"load-typesystem", StackElement::LoadTypesystem},
+ {u"modify-argument", StackElement::ModifyArgument},
+ {u"modify-documentation", StackElement::ModifyDocumentation},
+ {u"modify-field", StackElement::ModifyField},
+ {u"modify-function", StackElement::ModifyFunction},
+ {u"namespace-type", StackElement::NamespaceTypeEntry},
+ {u"native-to-target", StackElement::NativeToTarget},
+ {u"no-null-pointer", StackElement::NoNullPointers},
+ {u"object-type", StackElement::ObjectTypeEntry},
+ {u"opaque-container", StackElement::OpaqueContainer},
+ {u"parent", StackElement::ParentOwner},
+ {u"primitive-type", StackElement::PrimitiveTypeEntry},
+ {u"property", StackElement::Property},
+ {u"reference-count", StackElement::ReferenceCount},
+ {u"reject-enum-value", StackElement::RejectEnumValue},
+ {u"rejection", StackElement::Rejection},
+ {u"remove-argument", StackElement::RemoveArgument},
+ {u"remove-default-expression", StackElement::RemoveDefaultExpression},
+ {u"rename", StackElement::Rename}, // ### fixme PySide7: remove
+ {u"replace", StackElement::Replace},
+ {u"replace-default-expression", StackElement::ReplaceDefaultExpression},
+ {u"replace-type", StackElement::ReplaceType},
+ {u"smart-pointer-type", StackElement::SmartPointerTypeEntry},
+ {u"suppress-warning", StackElement::SuppressedWarning},
+ {u"system-include", StackElement::SystemInclude},
+ {u"target-to-native", StackElement::TargetToNative},
+ {u"template", StackElement::Template},
+ {u"typedef-type", StackElement::TypedefTypeEntry},
+ {u"typesystem", StackElement::Root},
+ {u"value-type", StackElement::ValueTypeEntry},
+ };
+ return result;
+}
+
+static std::optional<StackElement> elementFromTag(QStringView needle)
+{
+ return lookupHashElement(stackElementHash(), needle,
+ Qt::CaseInsensitive); // FIXME PYSIDE-7: case sensitive
+}
+
+static QStringView tagFromElement(StackElement st)
+{
+ return stackElementHash().key(st);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, StackElement st)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << tagFromElement(st);
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+ENUM_LOOKUP_BEGIN(TypeSystem::SnakeCase, Qt::CaseSensitive,
+ snakeCaseFromAttribute)
+{
+ {u"no", TypeSystem::SnakeCase::Disabled},
+ {u"false", TypeSystem::SnakeCase::Disabled},
+ {u"yes", TypeSystem::SnakeCase::Enabled},
+ {u"true", TypeSystem::SnakeCase::Enabled},
+ {u"both", TypeSystem::SnakeCase::Both},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(TypeSystem::Visibility, Qt::CaseSensitive,
+ visibilityFromAttribute)
+{
+ {u"no", TypeSystem::Visibility::Invisible},
+ {u"false", TypeSystem::Visibility::Invisible},
+ {u"auto", TypeSystem::Visibility::Auto},
+ {u"yes", TypeSystem::Visibility::Visible},
+ {u"true", TypeSystem::Visibility::Visible},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
+static int indexOfAttribute(const QXmlStreamAttributes &atts,
+ QAnyStringView name)
+{
+ for (qsizetype i = 0, size = atts.size(); i < size; ++i) {
+ if (atts.at(i).qualifiedName() == name)
+ return i;
+ }
+ return -1;
+}
+
+static QString msgMissingAttribute(const QString &a)
+{
+ return u"Required attribute '"_s + a
+ + u"' missing."_s;
+}
+
+QTextStream &operator<<(QTextStream &str, const QXmlStreamAttribute &attribute)
+{
+ str << attribute.qualifiedName() << "=\"" << attribute.value() << '"';
+ return str;
+}
+
+static QString msgInvalidAttributeValue(const QXmlStreamAttribute &attribute)
+{
+ QString result;
+ QTextStream(&result) << "Invalid attribute value:" << attribute;
+ return result;
+}
+
+static QString msgUnusedAttributes(QStringView tag, const QXmlStreamAttributes &attributes)
+{
+ QString result;
+ QTextStream str(&result);
+ str << attributes.size() << " attributes(s) unused on <" << tag << ">: ";
+ for (qsizetype i = 0, size = attributes.size(); i < size; ++i) {
+ if (i)
+ str << ", ";
+ str << attributes.at(i);
+ }
+ return result;
+}
+
+// QXmlStreamEntityResolver::resolveEntity(publicId, systemId) is not
+// implemented; resolve via undeclared entities instead.
+class TypeSystemEntityResolver : public QXmlStreamEntityResolver
+{
+public:
+ explicit TypeSystemEntityResolver(const QString &currentPath) :
+ m_currentPath(currentPath) {}
+
+ QString resolveUndeclaredEntity(const QString &name) override;
+
+private:
+ QString readFile(const QString &entityName, QString *errorMessage) const;
+
+ const QString m_currentPath;
+};
+
+QString TypeSystemEntityResolver::readFile(const QString &entityName, QString *errorMessage) const
+{
+ QString fileName = entityName;
+ if (!fileName.contains(u'.'))
+ fileName += u".xml"_s;
+ QString path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath);
+ if (!QFileInfo::exists(path)) // PySide6-specific hack
+ fileName.prepend(u"typesystem_"_s);
+ path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath);
+ if (!QFileInfo::exists(path)) {
+ *errorMessage = u"Unable to resolve: "_s + entityName;
+ return {};
+ }
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ *errorMessage = msgCannotOpenForReading(file);
+ return {};
+ }
+ QString result = QString::fromUtf8(file.readAll()).trimmed();
+ // Remove license header comments on which QXmlStreamReader chokes
+ if (result.startsWith(u"<!--")) {
+ const auto commentEnd = result.indexOf(u"-->");
+ if (commentEnd != -1) {
+ result.remove(0, commentEnd + 3);
+ result = result.trimmed();
+ }
+ }
+ return result;
+}
+
+QString TypeSystemEntityResolver::resolveUndeclaredEntity(const QString &name)
+{
+ QString errorMessage;
+ const QString result = readFile(name, &errorMessage);
+ if (result.isEmpty()) { // The parser will fail and display the line number.
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgCannotResolveEntity(name, errorMessage)));
+ }
+ return result;
+}
+
+// State depending on element stack
+enum class ParserState
+{
+ None,
+ PrimitiveTypeNativeToTargetConversion,
+ PrimitiveTypeTargetToNativeConversion,
+ ArgumentConversion, // Argument conversion rule with class attribute
+ ArgumentNativeToTargetConversion,
+ ArgumentTargetToNativeConversion,
+ FunctionCodeInjection,
+ TypeEntryCodeInjection,
+ TypeSystemCodeInjection,
+ Template
+};
+
+TypeSystemParser::TypeSystemParser(const std::shared_ptr<TypeDatabaseParserContext> &context,
+ bool generate) :
+ m_context(context),
+ m_generate(generate ? TypeEntry::GenerateCode : TypeEntry::GenerateForSubclass)
+{
+}
+
+TypeSystemParser::~TypeSystemParser() = default;
+
+static QString readerFileName(const ConditionalStreamReader &reader)
+{
+ const auto *file = qobject_cast<const QFile *>(reader.device());
+ return file != nullptr ? file->fileName() : QString();
+}
+
+static QString msgReaderMessage(const ConditionalStreamReader &reader,
+ const char *type,
+ const QString &what)
+{
+ QString message;
+ QTextStream str(&message);
+ const QString fileName = readerFileName(reader);
+ if (fileName.isEmpty())
+ str << "<stdin>:";
+ else
+ str << QDir::toNativeSeparators(fileName) << ':';
+ // Use a tab separator like SourceLocation for suppression detection
+ str << reader.lineNumber() << ':' << reader.columnNumber()
+ << ":\t" << type << ": " << what;
+ return message;
+}
+
+static QString msgReaderWarning(const ConditionalStreamReader &reader, const QString &what)
+{
+ return msgReaderMessage(reader, "Warning", what);
+}
+
+static QString msgReaderError(const ConditionalStreamReader &reader, const QString &what)
+{
+ return msgReaderMessage(reader, "Error", what);
+}
+
+static QString msgUnimplementedElementWarning(const ConditionalStreamReader &reader,
+ QAnyStringView name)
+{
+ QString message;
+ QTextStream(&message) << "The element \"" << name
+ << "\" is not implemented.";
+ return msgReaderMessage(reader, "Warning", message);
+}
+
+static QString msgUnimplementedAttributeWarning(const ConditionalStreamReader &reader,
+ QStringView name)
+{
+ QString message;
+ QTextStream(&message) << "The attribute \"" << name
+ << "\" is not implemented.";
+ return msgReaderMessage(reader, "Warning", message);
+}
+
+static inline QString msgUnimplementedAttributeWarning(const ConditionalStreamReader &reader,
+ const QXmlStreamAttribute &attribute)
+{
+ return msgUnimplementedAttributeWarning(reader, attribute.qualifiedName());
+}
+
+static QString
+ msgUnimplementedAttributeValueWarning(const ConditionalStreamReader &reader,
+ QAnyStringView name, QAnyStringView value)
+{
+ QString message;
+ QTextStream(&message) << "The value \"" << value
+ << "\" of the attribute \"" << name << "\" is not implemented.";
+ return msgReaderMessage(reader, "Warning", message);
+}
+
+static inline
+ QString msgUnimplementedAttributeValueWarning(const ConditionalStreamReader &reader,
+ const QXmlStreamAttribute &attribute)
+{
+ return msgUnimplementedAttributeValueWarning(reader,
+ attribute.qualifiedName(),
+ attribute.value());
+}
+
+static bool addRejection(TypeDatabase *database, bool generate, QXmlStreamAttributes *attributes,
+ QString *errorMessage)
+{
+ const auto classIndex = indexOfAttribute(*attributes, classAttribute);
+ if (classIndex == -1) {
+ *errorMessage = msgMissingAttribute(classAttribute);
+ return false;
+ }
+
+ TypeRejection rejection;
+ rejection.generate = generate;
+ const QString className = attributes->takeAt(classIndex).value().toString();
+ if (!setRejectionRegularExpression(className, &rejection.className, errorMessage))
+ return false;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto &attribute = attributes->at(i);
+ const auto name = attribute.qualifiedName();
+ const auto typeOpt = typeRejectionFromAttribute(name);
+ if (!typeOpt.has_value()) {
+ *errorMessage = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ switch (typeOpt.value()) {
+ case TypeRejection::Function:
+ case TypeRejection::Field:
+ case TypeRejection::Enum:
+ case TypeRejection::ArgumentType:
+ case TypeRejection::ReturnType: {
+ const QString pattern = attributes->takeAt(i).value().toString();
+ if (!setRejectionRegularExpression(pattern, &rejection.pattern, errorMessage))
+ return false;
+ rejection.matchType = typeOpt.value();
+ database->addRejection(rejection);
+ return true;
+ }
+ case TypeRejection::ExcludeClass:
+ break;
+ }
+ }
+
+ // Special case: When all fields except class are empty, completely exclude class
+ if (className == u"*") {
+ *errorMessage = u"bad reject entry, neither 'class', 'function-name'"
+ " nor 'field' specified"_s;
+ return false;
+ }
+ rejection.matchType = TypeRejection::ExcludeClass;
+ database->addRejection(rejection);
+ return true;
+}
+
+bool TypeSystemParser::parse(ConditionalStreamReader &reader)
+{
+ m_error.clear();
+ m_currentPath.clear();
+ m_currentFile.clear();
+ return parseXml(reader);
+}
+
+bool TypeSystemParser::parseXml(ConditionalStreamReader &reader)
+{
+ const QString fileName = readerFileName(reader);
+ if (!fileName.isEmpty()) {
+ QFileInfo fi(fileName);
+ m_currentPath = fi.absolutePath();
+ m_currentFile = fi.absoluteFilePath();
+ }
+ m_entityResolver.reset(new TypeSystemEntityResolver(m_currentPath));
+ reader.setEntityResolver(m_entityResolver.data());
+
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::NoToken:
+ case QXmlStreamReader::Invalid:
+ m_error = msgReaderError(reader, reader.errorString());
+ return false;
+ case QXmlStreamReader::StartElement: {
+ const auto elementTypeOpt = elementFromTag(reader.name());
+ if (!elementTypeOpt.has_value()) {
+ m_error = u"Unknown tag name: '"_s + reader.name().toString() + u'\'';
+ return false;
+ }
+ m_stack.push(elementTypeOpt.value());
+ if (!startElement(reader, m_stack.top())) {
+ m_error = msgReaderError(reader, m_error);
+ return false;
+ }
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (!endElement(m_stack.top())) {
+ m_error = msgReaderError(reader, m_error);
+ return false;
+ }
+ m_stack.pop();
+ break;
+ case QXmlStreamReader::Characters:
+ if (!characters(reader.text())) {
+ m_error = msgReaderError(reader, m_error);
+ return false;
+ }
+ break;
+ case QXmlStreamReader::StartDocument:
+ case QXmlStreamReader::EndDocument:
+ case QXmlStreamReader::Comment:
+ case QXmlStreamReader::DTD:
+ case QXmlStreamReader::EntityReference:
+ case QXmlStreamReader::ProcessingInstruction:
+ break;
+ }
+ }
+ return true;
+}
+
+bool TypeSystemParser::endElement(StackElement element)
+{
+ if (m_ignoreDepth) {
+ --m_ignoreDepth;
+ return true;
+ }
+
+ if (m_currentDroppedEntryDepth != 0) {
+ --m_currentDroppedEntryDepth;
+ return true;
+ }
+
+ if (element == StackElement::ImportFile)
+ return true;
+
+ if (m_contextStack.isEmpty())
+ return true;
+
+ const auto &top = m_contextStack.top();
+
+ switch (element) {
+ case StackElement::Unimplemented:
+ return true;
+ case StackElement::Root:
+ if (m_generate == TypeEntry::GenerateCode) {
+ TypeDatabase::instance()->addGlobalUserFunctions(top->addedFunctions);
+ TypeDatabase::instance()->addGlobalUserFunctionModifications(top->functionMods);
+ for (const auto &customConversion : std::as_const(customConversionsForReview)) {
+ TargetToNativeConversions &toNatives =
+ customConversion->targetToNativeConversions();
+ for (auto &toNative : toNatives)
+ toNative.setSourceType(m_context->db->findType(toNative.sourceTypeName()));
+ }
+ }
+ purgeEmptyCodeSnips(&std::static_pointer_cast<TypeSystemTypeEntry>(top->entry)->codeSnips());
+ break;
+ case StackElement::FunctionTypeEntry:
+ TypeDatabase::instance()->addGlobalUserFunctionModifications(top->functionMods);
+ break;
+ case StackElement::ObjectTypeEntry:
+ case StackElement::ValueTypeEntry:
+ case StackElement::InterfaceTypeEntry:
+ case StackElement::ContainerTypeEntry:
+ case StackElement::NamespaceTypeEntry: {
+ Q_ASSERT(top->entry);
+ Q_ASSERT(top->entry->isComplex());
+ auto centry = std::static_pointer_cast<ComplexTypeEntry>(top->entry);
+ purgeEmptyCodeSnips(&centry->codeSnips());
+ centry->setAddedFunctions(top->addedFunctions);
+ centry->setFunctionModifications(top->functionMods);
+ centry->setFieldModifications(top->fieldMods);
+ centry->setDocModification(top->docModifications);
+ }
+ break;
+
+ case StackElement::TypedefTypeEntry: {
+ auto centry = std::static_pointer_cast<TypedefEntry>(top->entry)->target();
+ centry->setAddedFunctions(centry->addedFunctions() + top->addedFunctions);
+ centry->setFunctionModifications(centry->functionModifications() + top->functionMods);
+ centry->setFieldModifications(centry->fieldModifications() + top->fieldMods);
+ centry->setDocModification(centry->docModifications() + top->docModifications);
+ if (top->entry->isComplex()) {
+ auto cte = std::static_pointer_cast<const ComplexTypeEntry>(top->entry);
+ centry->setCodeSnips(centry->codeSnips() + cte->codeSnips());
+ }
+ }
+ break;
+
+ case StackElement::AddFunction:
+ case StackElement::DeclareFunction: {
+ // Leaving add-function: Assign all modifications to the added function
+ const int modIndex = top->addedFunctionModificationIndex;
+ top->addedFunctionModificationIndex = -1;
+ Q_ASSERT(modIndex >= 0);
+ Q_ASSERT(!top->addedFunctions.isEmpty());
+ while (modIndex < top->functionMods.size())
+ top->addedFunctions.last()->modifications().append(top->functionMods.takeAt(modIndex));
+ }
+ break;
+ case StackElement::NativeToTarget:
+ case StackElement::AddConversion:
+ switch (parserState()) {
+ case ParserState::PrimitiveTypeNativeToTargetConversion:
+ case ParserState::PrimitiveTypeTargetToNativeConversion: {
+ auto customConversion = CustomConversion::getCustomConversion(top->entry);
+ if (!customConversion) {
+ m_error = msgMissingCustomConversion(top->entry);
+ return false;
+ }
+ QString code = top->conversionCodeSnips.constLast().code();
+ if (element == StackElement::AddConversion) {
+ if (customConversion->targetToNativeConversions().isEmpty()) {
+ m_error = u"CustomConversion's target to native conversions missing."_s;
+ return false;
+ }
+ customConversion->targetToNativeConversions().last().setConversion(code);
+ } else {
+ customConversion->setNativeToTargetConversion(code);
+ }
+ }
+ break;
+
+ case ParserState::ArgumentNativeToTargetConversion: {
+ top->conversionCodeSnips.last().language = TypeSystem::TargetLangCode;
+ auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ lastArgMod.conversionRules().append(top->conversionCodeSnips.constLast());
+ }
+ break;
+ case ParserState::ArgumentTargetToNativeConversion: {
+ top->conversionCodeSnips.last().language = TypeSystem::NativeCode;
+ auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ lastArgMod.conversionRules().append(top->conversionCodeSnips.constLast());
+ }
+ break;
+ default:
+ break;
+ }
+ top->conversionCodeSnips.clear();
+ break;
+
+ case StackElement::EnumTypeEntry:
+ m_currentEnum = nullptr;
+ break;
+ case StackElement::Template:
+ m_context->db->addTemplate(m_templateEntry);
+ m_templateEntry = nullptr;
+ break;
+ case StackElement::InsertTemplate:
+ if (auto *snip = injectCodeTarget(1))
+ snip->addTemplateInstance(m_templateInstance);
+ m_templateInstance.reset();
+ break;
+
+ case StackElement::ModifyArgument:
+ purgeEmptyCodeSnips(&top->functionMods.last().argument_mods().last().conversionRules());
+ break;
+
+ default:
+ break;
+ }
+
+ if (isTypeEntry(element) || element == StackElement::Root)
+ m_contextStack.pop();
+
+ return true;
+}
+
+ParserState TypeSystemParser::parserState(qsizetype offset) const
+{
+ const auto stackSize = m_stack.size() - offset;
+ if (stackSize <= 0 || m_contextStack.isEmpty())
+ return ParserState::None;
+
+ const auto last = stackSize - 1;
+
+ switch (m_stack.at(last)) {
+ // Primitive entry with conversion rule
+ case StackElement::NativeToTarget: // <conversion-rule><native-to-target>
+ if (stackSize > 2 && m_stack.at(last - 2) == StackElement::ModifyArgument)
+ return ParserState::ArgumentNativeToTargetConversion;
+ return ParserState::PrimitiveTypeNativeToTargetConversion;
+
+ case StackElement::AddConversion: // <conversion-rule><target-to-native><add-conversion>
+ if (stackSize > 3 && m_stack.at(last - 3) == StackElement::ModifyArgument)
+ return ParserState::ArgumentTargetToNativeConversion;
+ return ParserState::PrimitiveTypeTargetToNativeConversion;
+
+ case StackElement::ConversionRule:
+ if (stackSize > 1 && m_stack.at(last - 1) == StackElement::ModifyArgument)
+ return ParserState::ArgumentConversion;
+ break;
+
+ case StackElement::InjectCode:
+ switch (m_stack.value(last - 1, StackElement::None)) {
+ case StackElement::Root:
+ return ParserState::TypeSystemCodeInjection;
+ case StackElement::ModifyFunction:
+ case StackElement::AddFunction:
+ return ParserState::FunctionCodeInjection;
+ case StackElement::NamespaceTypeEntry:
+ case StackElement::ObjectTypeEntry:
+ case StackElement::ValueTypeEntry:
+ case StackElement::InterfaceTypeEntry:
+ return ParserState::TypeEntryCodeInjection;
+ default:
+ break;
+ }
+ break;
+
+ case StackElement::Template:
+ return ParserState::Template;
+
+ default:
+ break;
+ }
+
+ return ParserState::None;
+}
+
+// Return where to add injected code depending on elements.
+CodeSnipAbstract *TypeSystemParser::injectCodeTarget(qsizetype offset) const
+{
+ const auto state = parserState(offset);
+ if (state == ParserState::None)
+ return nullptr;
+
+ const auto &top = m_contextStack.top();
+ switch (state) {
+ case ParserState::PrimitiveTypeNativeToTargetConversion:
+ case ParserState::PrimitiveTypeTargetToNativeConversion:
+ case ParserState::ArgumentNativeToTargetConversion:
+ case ParserState::ArgumentTargetToNativeConversion:
+ return &top->conversionCodeSnips.last();
+ case ParserState::ArgumentConversion:
+ return &top->functionMods.last().argument_mods().last().conversionRules().last();
+ case ParserState::FunctionCodeInjection: {
+ auto &funcMod = top->functionMods.last();
+ funcMod.setModifierFlag(FunctionModification::CodeInjection);
+ return &funcMod.snips().last();
+ }
+ case ParserState::TypeEntryCodeInjection:
+ Q_ASSERT(top->entry->isComplex());
+ return &std::static_pointer_cast<ComplexTypeEntry>(top->entry)->codeSnips().last();
+ case ParserState::TypeSystemCodeInjection:
+ Q_ASSERT(top->entry->isTypeSystem());
+ return &std::static_pointer_cast<TypeSystemTypeEntry>(top->entry)->codeSnips().last();
+ case ParserState::Template:
+ return m_templateEntry.get();
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+template <class String> // QString/QStringRef
+bool TypeSystemParser::characters(const String &ch)
+{
+ const auto stackSize = m_stack.size();
+ if (m_currentDroppedEntryDepth != 0 || m_ignoreDepth != 0
+ || stackSize == 0 || m_stack.top() == StackElement::Unimplemented) {
+ return true;
+ }
+
+ const StackElement type = m_stack.top();
+
+ if (type == StackElement::Template) {
+ m_templateEntry->addCode(ch);
+ return true;
+ }
+
+ if (m_contextStack.isEmpty()) {
+ m_error = msgNoRootTypeSystemEntry();
+ return false;
+ }
+
+ if (auto *snip = injectCodeTarget()) {
+ snip->addCode(ch);
+ return true;
+ }
+
+ if (isDocumentation(type)) {
+ const bool isAddedFunction = m_stack.value(m_stack.size() - 2, StackElement::None)
+ == StackElement::AddFunction;
+ const auto &top = m_contextStack.top();
+ auto &docModifications = isAddedFunction
+ ? top->addedFunctions.last()->docModifications()
+ : top->docModifications;
+ docModifications.last().setCode(ch);
+ }
+
+ return true;
+}
+
+bool TypeSystemParser::importFileElement(const QXmlStreamAttributes &atts)
+{
+ const QString fileName = atts.value(nameAttribute).toString();
+ if (fileName.isEmpty()) {
+ m_error = u"Required attribute 'name' missing for include-file tag."_s;
+ return false;
+ }
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ file.setFileName(u":/trolltech/generator/"_s + fileName);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ m_error = msgCannotOpenForReading(file);
+ return false;
+ }
+ }
+
+ const auto quoteFrom = atts.value(quoteAfterLineAttribute);
+ bool foundFromOk = quoteFrom.isEmpty();
+ bool from = quoteFrom.isEmpty();
+
+ const auto quoteTo = atts.value(quoteBeforeLineAttribute);
+ bool foundToOk = quoteTo.isEmpty();
+ bool to = true;
+
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (from && to && line.contains(quoteTo)) {
+ to = false;
+ foundToOk = true;
+ break;
+ }
+ if (from && to)
+ characters(line + u'\n');
+ if (!from && line.contains(quoteFrom)) {
+ from = true;
+ foundFromOk = true;
+ }
+ }
+ if (!foundFromOk || !foundToOk) {
+ QString fromError = QString::fromLatin1("Could not find quote-after-line='%1' in file '%2'.")
+ .arg(quoteFrom.toString(), fileName);
+ QString toError = QString::fromLatin1("Could not find quote-before-line='%1' in file '%2'.")
+ .arg(quoteTo.toString(), fileName);
+
+ if (!foundToOk)
+ m_error = toError;
+ if (!foundFromOk)
+ m_error = fromError;
+ if (!foundFromOk && !foundToOk)
+ m_error = fromError + u' ' + toError;
+ return false;
+ }
+
+ return true;
+}
+
+static bool convertBoolean(QStringView value, QAnyStringView attributeName, bool defaultValue)
+{
+ if (value.compare(trueAttributeValue, Qt::CaseInsensitive) == 0
+ || value.compare(yesAttributeValue, Qt::CaseInsensitive) == 0) {
+ return true;
+ }
+ if (value.compare(falseAttributeValue, Qt::CaseInsensitive) == 0
+ || value.compare(noAttributeValue, Qt::CaseInsensitive) == 0) {
+ return false;
+ }
+ qCWarning(lcShiboken).noquote().nospace() << "Boolean value '" << value
+ << "' not supported in attribute '" << attributeName
+ << "'. Use 'yes' or 'no'. Defaulting to '"
+ << (defaultValue ? yesAttributeValue : noAttributeValue) << "'.";
+ return defaultValue;
+}
+
+static bool convertRemovalAttribute(QStringView value)
+{
+ return value == u"all" // Legacy
+ || convertBoolean(value, removeAttribute, false);
+}
+
+// Check whether an entry should be dropped, allowing for dropping the module
+// name (match 'Class' and 'Module.Class').
+static bool shouldDropTypeEntry(const TypeDatabase *db,
+ const TypeSystemParser::ContextStack &stack ,
+ QString name)
+{
+ for (auto i = stack.size() - 1; i >= 0; --i) {
+ if (auto entry = stack.at(i)->entry) {
+ if (entry->type() == TypeEntry::TypeSystemType) {
+ if (db->shouldDropTypeEntry(name)) // Unqualified
+ return true;
+ }
+ name.prepend(u'.');
+ name.prepend(entry->name());
+ }
+ }
+ return db->shouldDropTypeEntry(name);
+}
+
+// Returns empty string if there's no error.
+static QString checkSignatureError(const QString& signature, const QString& tag)
+{
+ QString funcName = signature.left(signature.indexOf(u'(')).trimmed();
+ static const QRegularExpression whiteSpace("\\s"_L1);
+ Q_ASSERT(whiteSpace.isValid());
+ if (!funcName.startsWith(u"operator ") && funcName.contains(whiteSpace)) {
+ return QString::fromLatin1("Error in <%1> tag signature attribute '%2'.\n"
+ "White spaces aren't allowed in function names, "
+ "and return types should not be part of the signature.")
+ .arg(tag, signature);
+ }
+ return QString();
+}
+
+inline TypeEntryCPtr TypeSystemParser::currentParentTypeEntry() const
+{
+ const auto size = m_contextStack.size();
+ return size > 1 ? m_contextStack.at(size - 2)->entry : nullptr;
+}
+
+bool TypeSystemParser::checkRootElement()
+{
+ for (auto i = m_contextStack.size() - 1; i >= 0; --i) {
+ auto e = m_contextStack.at(i)->entry;
+ if (e && e->isTypeSystem())
+ return true;
+ }
+ m_error = msgNoRootTypeSystemEntry();
+ return false;
+}
+
+static TypeEntryPtr findViewedType(const QString &name)
+{
+ const auto range = TypeDatabase::instance()->entries().equal_range(name);
+ for (auto i = range.first; i != range.second; ++i) {
+ switch (i.value()->type()) {
+ case TypeEntry::BasicValueType:
+ case TypeEntry::PrimitiveType:
+ case TypeEntry::ContainerType:
+ case TypeEntry::ObjectType:
+ return i.value();
+ default:
+ break;
+ }
+ }
+ return nullptr;
+}
+
+bool TypeSystemParser::applyCommonAttributes(const ConditionalStreamReader &reader,
+ const TypeEntryPtr &type,
+ QXmlStreamAttributes *attributes)
+{
+ type->setSourceLocation(SourceLocation(m_currentFile,
+ reader.lineNumber()));
+ type->setCodeGeneration(m_generate);
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"revision") {
+ type->setRevision(attributes->takeAt(i).value().toInt());
+ } else if (name == u"view-on") {
+ const QString name = attributes->takeAt(i).value().toString();
+ TypeEntryPtr views = findViewedType(name);
+ if (!views) {
+ m_error = msgCannotFindView(name, type->name());
+ return false;
+ }
+ type->setViewOn(views);
+ }
+ }
+ return true;
+}
+
+CustomTypeEntryPtr TypeSystemParser::parseCustomTypeEntry(const ConditionalStreamReader &,
+ const QString &name,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto result = std::make_shared<CustomTypeEntry>(name, since, m_contextStack.top()->entry);
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == checkFunctionAttribute)
+ result->setCheckFunction(attributes->takeAt(i).value().toString());
+ }
+ return result;
+}
+
+FlagsTypeEntryPtr
+ TypeSystemParser::parseFlagsEntry(const ConditionalStreamReader &reader,
+ const EnumTypeEntryPtr &enumEntry, QString flagName,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto ftype = std::make_shared<FlagsTypeEntry>(u"QFlags<"_s + enumEntry->name() + u'>',
+ since,
+ typeSystemTypeEntry(currentParentTypeEntry()));
+ ftype->setOriginator(enumEntry);
+ ftype->setTargetLangPackage(enumEntry->targetLangPackage());
+ // Try toenumEntry get the guess the qualified flag name
+ if (!flagName.contains(u"::"_s)) {
+ auto eq = enumEntry->qualifier();
+ if (!eq.isEmpty())
+ flagName.prepend(eq + u"::"_s);
+ }
+
+ ftype->setOriginalName(flagName);
+ if (!applyCommonAttributes(reader, ftype, attributes))
+ return nullptr;
+
+ QStringList lst = flagName.split(u"::"_s);
+ const QString name = lst.takeLast();
+ const QString targetLangFlagName = lst.join(u'.');
+ const QString &targetLangQualifier = enumEntry->targetLangQualifier();
+ if (targetLangFlagName != targetLangQualifier) {
+ qCWarning(lcShiboken, "enum %s and flags %s (%s) differ in qualifiers",
+ qPrintable(targetLangQualifier), qPrintable(lst.value(0)),
+ qPrintable(targetLangFlagName));
+ }
+
+ ftype->setFlagsName(name);
+ enumEntry->setFlags(ftype);
+
+ m_context->db->addFlagsType(ftype);
+ m_context->db->addType(ftype);
+
+ const int revisionIndex =
+ indexOfAttribute(*attributes, u"flags-revision");
+ ftype->setRevision(revisionIndex != -1
+ ? attributes->takeAt(revisionIndex).value().toInt()
+ : enumEntry->revision());
+ return ftype;
+}
+
+SmartPointerTypeEntryPtr
+ TypeSystemParser::parseSmartPointerEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ TypeSystem::SmartPointerType smartPointerType = TypeSystem::SmartPointerType::Shared;
+ QString getter;
+ QString refCountMethodName;
+ QString valueCheckMethod;
+ QString nullCheckMethod;
+ QString resetMethod;
+ QString instantiations;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"type") {
+ const auto attribute = attributes->takeAt(i);
+ const auto typeOpt = smartPointerTypeFromAttribute(attribute.value());
+ if (!typeOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return nullptr;
+ }
+ smartPointerType = typeOpt.value();
+ } else if (name == u"getter") {
+ getter = attributes->takeAt(i).value().toString();
+ } else if (name == u"ref-count-method") {
+ refCountMethodName = attributes->takeAt(i).value().toString();
+ } else if (name == u"instantiations") {
+ instantiations = attributes->takeAt(i).value().toString();
+ } else if (name == u"value-check-method") {
+ valueCheckMethod = attributes->takeAt(i).value().toString();
+ } else if (name == u"null-check-method") {
+ nullCheckMethod = attributes->takeAt(i).value().toString();
+ } else if (name == u"reset-method") {
+ resetMethod = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (getter.isEmpty()) {
+ m_error = u"No function getter name specified for getting the raw pointer held by the smart pointer."_s;
+ return nullptr;
+ }
+
+ QString signature = getter + u"()"_s;
+ signature = TypeDatabase::normalizedSignature(signature);
+ if (signature.isEmpty()) {
+ m_error = u"No signature for the smart pointer getter found."_s;
+ return nullptr;
+ }
+
+ QString errorString = checkSignatureError(signature,
+ u"smart-pointer-type"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return nullptr;
+ }
+
+ if (smartPointerType == TypeSystem::SmartPointerType::Unique && resetMethod.isEmpty()) {
+ m_error = u"Unique pointers require a reset() method."_s;
+ return nullptr;
+ }
+
+ auto type = std::make_shared<SmartPointerTypeEntry>(name, getter, smartPointerType,
+ refCountMethodName, since,
+ currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, type, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, type, attributes);
+ type->setNullCheckMethod(nullCheckMethod);
+ type->setValueCheckMethod(valueCheckMethod);
+ type->setResetMethod(resetMethod);
+ m_context->smartPointerInstantiations.insert(type, instantiations);
+ return type;
+}
+
+PrimitiveTypeEntryPtr
+ TypeSystemParser::parsePrimitiveTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto type = std::make_shared<PrimitiveTypeEntry>(name, since, currentParentTypeEntry());
+ QString targetLangApiName;
+ if (!applyCommonAttributes(reader, type, attributes))
+ return nullptr;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == targetLangNameAttribute) {
+ type->setTargetLangName(attributes->takeAt(i).value().toString());
+ } else if (name == u"target-lang-api-name") {
+ targetLangApiName = attributes->takeAt(i).value().toString();
+ } else if (name == preferredConversionAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == preferredTargetLangTypeAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ preferredTargetLangTypeAttribute, true);
+ type->setPreferredTargetLangType(v);
+ } else if (name == u"default-constructor") {
+ type->setDefaultConstructor(attributes->takeAt(i).value().toString());
+ }
+ }
+
+ if (!targetLangApiName.isEmpty()) {
+ auto e = m_context->db->findType(targetLangApiName);
+ if (!e || !e->isCustom()) {
+ m_error = msgInvalidTargetLanguageApiName(targetLangApiName);
+ return nullptr;
+ }
+ type->setTargetLangApiType(std::static_pointer_cast<CustomTypeEntry>(e));
+ }
+ type->setTargetLangPackage(m_defaultPackage);
+ return type;
+}
+
+// "int:QList_int;QString:QList_QString"
+bool TypeSystemParser::parseOpaqueContainers(QStringView s, OpaqueContainers *result)
+{
+ const auto entries = s.split(u';');
+ for (const auto &entry : entries) {
+ const auto values = entry.split(u':');
+ if (values.size() != 2) {
+ m_error = u"Error parsing the opaque container attribute: \""_s
+ + s.toString() + u"\"."_s;
+ return false;
+ }
+ OpaqueContainer oc;
+ oc.name = values.at(1).trimmed().toString();
+ const auto instantiations = values.at(0).split(u',', Qt::SkipEmptyParts);
+ for (const auto &instantiationV : instantiations) {
+ QString instantiation = instantiationV.trimmed().toString();
+ // Fix to match AbstractMetaType::signature() which is used for matching
+ // "Foo*" -> "Foo *"
+ const auto asteriskPos = instantiation.indexOf(u'*');
+ if (asteriskPos > 0 && !instantiation.at(asteriskPos - 1).isSpace())
+ instantiation.insert(asteriskPos, u' ');
+ oc.instantiations.append(instantiation);
+ }
+ result->append(oc);
+ }
+ return true;
+}
+
+ContainerTypeEntryPtr
+ TypeSystemParser::parseContainerTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ const auto typeIndex = indexOfAttribute(*attributes, u"type");
+ if (typeIndex == -1) {
+ m_error = u"no 'type' attribute specified"_s;
+ return nullptr;
+ }
+ const auto typeName = attributes->at(typeIndex).value();
+ const auto containerTypeOpt = containerTypeFromAttribute(typeName);
+ if (!containerTypeOpt.has_value()) {
+ m_error = u"there is no container of type "_s + typeName.toString();
+ return nullptr;
+ }
+ attributes->removeAt(typeIndex);
+ auto type = std::make_shared<ContainerTypeEntry>(name, containerTypeOpt.value(),
+ since, currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, type, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, type, attributes);
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == opaqueContainerAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ OpaqueContainers oc;
+ if (!parseOpaqueContainers(attribute.value(), &oc))
+ return nullptr;
+ type->appendOpaqueContainers(oc);
+ }
+ }
+
+ return type;
+}
+
+bool TypeSystemParser::parseOpaqueContainerElement(QXmlStreamAttributes *attributes)
+{
+ QString containerName;
+ OpaqueContainers oc;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ containerName = attributes->takeAt(i).value().toString();
+ } else if (name == opaqueContainerAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ if (!parseOpaqueContainers(attribute.value(), &oc))
+ return false;
+ }
+ }
+ if (containerName.isEmpty()) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_context->opaqueContainerHash[containerName].append(oc);
+ return true;
+}
+
+EnumTypeEntryPtr
+ TypeSystemParser::parseEnumTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto entry = std::make_shared<EnumTypeEntry>(name, since, currentParentTypeEntry());
+ applyCommonAttributes(reader, entry, attributes);
+ entry->setTargetLangPackage(m_defaultPackage);
+
+ QString flagNames;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"upper-bound") {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == u"lower-bound") {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == docFileAttribute) {
+ entry->setDocFile(attributes->takeAt(i).value().toString());
+ } else if (name == forceIntegerAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == pythonEnumTypeAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto typeOpt = pythonEnumTypeFromAttribute(attribute.value());
+ if (typeOpt.has_value()) {
+ entry->setPythonEnumType(typeOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == cppEnumTypeAttribute) {
+ entry->setCppType(attributes->takeAt(i).value().toString());
+ } else if (name == extensibleAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == flagsAttribute) {
+ flagNames = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ // put in the flags parallel...
+ if (!flagNames.isEmpty()) {
+ const QStringList &flagNameList = flagNames.split(u',');
+ for (const QString &flagName : flagNameList)
+ parseFlagsEntry(reader, entry, flagName.trimmed(), since, attributes);
+ }
+ return entry;
+}
+
+
+NamespaceTypeEntryPtr
+ TypeSystemParser::parseNamespaceTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto result = std::make_shared<NamespaceTypeEntry>(name, since, currentParentTypeEntry());
+ auto visibility = TypeSystem::Visibility::Unspecified;
+ applyCommonAttributes(reader, result, attributes);
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto attributeName = attributes->at(i).qualifiedName();
+ if (attributeName == u"files") {
+ const QString pattern = attributes->takeAt(i).value().toString();
+ QRegularExpression re(pattern);
+ if (!re.isValid()) {
+ m_error = msgInvalidRegularExpression(pattern, re.errorString());
+ return nullptr;
+ }
+ result->setFilePattern(re);
+ } else if (attributeName == u"extends") {
+ const auto extendsPackageName = attributes->at(i).value();
+ auto allEntries = TypeDatabase::instance()->findNamespaceTypes(name);
+ auto extendsIt = std::find_if(allEntries.cbegin(), allEntries.cend(),
+ [extendsPackageName] (const NamespaceTypeEntryCPtr &e) {
+ return e->targetLangPackage() == extendsPackageName;
+ });
+ if (extendsIt == allEntries.cend()) {
+ m_error = msgCannotFindNamespaceToExtend(name, extendsPackageName.toString());
+ return nullptr;
+ }
+ result->setExtends(*extendsIt);
+ attributes->removeAt(i);
+ } else if (attributeName == visibleAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto visibilityOpt = visibilityFromAttribute(attribute.value());
+ if (!visibilityOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return nullptr;
+ }
+ visibility = visibilityOpt.value();
+ } else if (attributeName == generateAttribute) {
+ if (!convertBoolean(attributes->takeAt(i).value(), generateAttribute, true))
+ visibility = TypeSystem::Visibility::Invisible;
+ } else if (attributeName == generateUsingAttribute) {
+ result->setGenerateUsing(convertBoolean(attributes->takeAt(i).value(),
+ generateUsingAttribute, true));
+ }
+ }
+
+ if (visibility != TypeSystem::Visibility::Unspecified)
+ result->setVisibility(visibility);
+ // Handle legacy "generate" before the common handling
+ applyComplexTypeAttributes(reader, result, attributes);
+
+ if (result->extends() && !result->hasPattern()) {
+ m_error = msgExtendingNamespaceRequiresPattern(name);
+ return {};
+ }
+
+ return result;
+}
+
+ValueTypeEntryPtr
+ TypeSystemParser::parseValueTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ auto typeEntry = std::make_shared<ValueTypeEntry>(name, since, currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, typeEntry, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, typeEntry, attributes);
+ const int defaultCtIndex =
+ indexOfAttribute(*attributes, u"default-constructor");
+ if (defaultCtIndex != -1)
+ typeEntry->setDefaultConstructor(attributes->takeAt(defaultCtIndex).value().toString());
+ return typeEntry;
+}
+
+FunctionTypeEntryPtr
+ TypeSystemParser::parseFunctionTypeEntry(const ConditionalStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+
+ FunctionModification mod;
+ const auto oldAttributesSize = attributes->size();
+ if (!parseModifyFunctionAttributes(attributes, &mod))
+ return nullptr;
+ const bool hasModification = attributes->size() < oldAttributesSize;
+
+ QString originalSignature;
+ QString docFile;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute)
+ originalSignature = attributes->takeAt(i).value().toString().simplified();
+ else if (name == docFileAttribute)
+ docFile = attributes->takeAt(i).value().toString();
+ }
+
+ const QString signature = TypeDatabase::normalizedSignature(originalSignature);
+ if (signature.isEmpty()) {
+ m_error = msgMissingAttribute(signatureAttribute);
+ return nullptr;
+ }
+
+ if (hasModification) {
+ mod.setOriginalSignature(originalSignature);
+ mod.setSignature(signature);
+ m_contextStack.top()->functionMods << mod;
+ }
+
+ TypeEntryPtr existingType = m_context->db->findType(name);
+
+ if (!existingType) {
+ auto result = std::make_shared<FunctionTypeEntry>(name, signature, since,
+ currentParentTypeEntry());
+ result->setTargetLangPackage(m_defaultPackage);
+ result->setDocFile(docFile);
+ applyCommonAttributes(reader, result, attributes);
+ return result;
+ }
+
+ if (existingType->type() != TypeEntry::FunctionType) {
+ m_error = name + " expected to be a function, but isn't! Maybe it was already declared as a class or something else."_L1;
+ return nullptr;
+ }
+
+ auto result = std::static_pointer_cast<FunctionTypeEntry>(existingType);
+ result->addSignature(signature);
+ return result;
+}
+
+TypedefEntryPtr
+ TypeSystemParser::parseTypedefEntry(const ConditionalStreamReader &reader,
+ const QString &name, StackElement topElement,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ if (!checkRootElement())
+ return nullptr;
+ if (topElement != StackElement::Root
+ && topElement != StackElement::NamespaceTypeEntry) {
+ m_error = u"typedef entries must be nested in namespaces or type system."_s;
+ return nullptr;
+ }
+ const auto sourceIndex = indexOfAttribute(*attributes, sourceAttribute);
+ if (sourceIndex == -1) {
+ m_error = msgMissingAttribute(sourceAttribute);
+ return nullptr;
+ }
+ const QString sourceType = attributes->takeAt(sourceIndex).value().toString();
+ auto result = std::make_shared<TypedefEntry>(name, sourceType, since,
+ currentParentTypeEntry());
+ if (!applyCommonAttributes(reader, result, attributes))
+ return nullptr;
+ applyComplexTypeAttributes(reader, result, attributes);
+ return result;
+}
+
+void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader &reader,
+ const ComplexTypeEntryPtr &ctype,
+ QXmlStreamAttributes *attributes) const
+{
+ bool generate = true;
+ ctype->setCopyable(ComplexTypeEntry::Unknown);
+ auto exceptionHandling = m_exceptionHandling;
+ auto allowThread = m_allowThread;
+
+ QString package = m_defaultPackage;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == streamAttribute) {
+ ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute, false));
+ } else if (name == privateAttribute) {
+ ctype->setPrivate(convertBoolean(attributes->takeAt(i).value(),
+ privateAttribute, false));
+ } else if (name == generateAttribute) {
+ generate = convertBoolean(attributes->takeAt(i).value(), generateAttribute, true);
+ } else if (name ==packageAttribute) {
+ package = attributes->takeAt(i).value().toString();
+ } else if (name == defaultSuperclassAttribute) {
+ ctype->setDefaultSuperclass(attributes->takeAt(i).value().toString());
+ } else if (name == genericClassAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ genericClassAttribute, false);
+ ctype->setGenericClass(v);
+ } else if (name == targetLangNameAttribute) {
+ ctype->setTargetLangName(attributes->takeAt(i).value().toString());
+ } else if (name == polymorphicBaseAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ polymorphicBaseAttribute, false);
+ ctype->setIsPolymorphicBase(v);
+ } else if (name == u"polymorphic-name-function") {
+ ctype->setPolymorphicNameFunction(attributes->takeAt(i).value().toString());
+ } else if (name == u"polymorphic-id-expression") {
+ ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString());
+ } else if (name == copyableAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ copyableAttribute, false);
+ ctype->setCopyable(v ? ComplexTypeEntry::CopyableSet : ComplexTypeEntry::NonCopyableSet);
+ } else if (name == exceptionHandlingAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
+ if (exceptionOpt.has_value()) {
+ exceptionHandling = exceptionOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == allowThreadAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
+ if (allowThreadOpt.has_value()) {
+ allowThread = allowThreadOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == u"held-type") {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ } else if (name == u"hash-function") {
+ ctype->setHashFunction(attributes->takeAt(i).value().toString());
+ } else if (name == forceAbstractAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), forceAbstractAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract);
+ } else if (name == deprecatedAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), deprecatedAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated);
+ } else if (name == disableWrapperAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), disableWrapperAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DisableWrapper);
+ } else if (name == deleteInMainThreadAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute, false))
+ ctype->setDeleteInMainThread(true);
+ } else if (name == qtMetaObjectFunctionsAttribute) {
+ if (!convertBoolean(attributes->takeAt(i).value(),
+ qtMetaObjectFunctionsAttribute, true)) {
+ ctype->setTypeFlags(ctype->typeFlags()
+ | ComplexTypeEntry::DisableQtMetaObjectFunctions);
+ }
+ } else if (name == generateFunctionsAttribute) {
+ const auto names = attributes->takeAt(i).value();
+ const auto nameList = names.split(u';', Qt::SkipEmptyParts);
+ QSet<QString> nameSet;
+ for (const auto &name : nameList)
+ nameSet.insert(name.trimmed().toString());
+ ctype->setGenerateFunctions(nameSet);
+ } else if (name == u"target-type") {
+ ctype->setTargetType(attributes->takeAt(i).value().toString());
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (snakeCaseOpt.has_value()) {
+ ctype->setSnakeCase(snakeCaseOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == isNullAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto boolCastOpt = boolCastFromAttribute(attribute.value());
+ if (boolCastOpt.has_value()) {
+ ctype->setIsNullMode(boolCastOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == operatorBoolAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto boolCastOpt = boolCastFromAttribute(attribute.value());
+ if (boolCastOpt.has_value()) {
+ ctype->setOperatorBoolMode(boolCastOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == qtMetaTypeAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto qtMetaTypeOpt = qtMetaTypeFromAttribute(attribute.value());
+ if (qtMetaTypeOpt.has_value()) {
+ ctype->setQtMetaTypeRegistration(qtMetaTypeOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == parentManagementAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ if (convertBoolean(attribute.value(), parentManagementAttribute, false))
+ ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ParentManagement);
+ ComplexTypeEntry::setParentManagementEnabled(true);
+ }
+ }
+
+ if (exceptionHandling != TypeSystem::ExceptionHandling::Unspecified)
+ ctype->setExceptionHandling(exceptionHandling);
+ if (allowThread != TypeSystem::AllowThread::Unspecified)
+ ctype->setAllowThread(allowThread);
+
+ // The generator code relies on container's package being empty.
+ if (ctype->type() != TypeEntry::ContainerType)
+ ctype->setTargetLangPackage(package);
+
+ if (generate)
+ ctype->setCodeGeneration(m_generate);
+ else
+ ctype->setCodeGeneration(TypeEntry::GenerationDisabled);
+}
+
+bool TypeSystemParser::parseConfiguration(StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)
+ && topElement != StackElement::EnumTypeEntry) {
+ m_error = u"<configuration> must be nested into a complex or enum type entry."_s;
+ return false;
+ }
+ QString condition;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"condition") {
+ condition = attributes->takeAt(i).value().toString();
+ }
+ }
+ if (condition.isEmpty()) {
+ m_error = u"<configuration> requires a \"condition\" attribute."_s;
+ return false;
+ }
+ const auto topEntry = m_contextStack.top()->entry;
+ const auto configurableEntry = std::dynamic_pointer_cast<ConfigurableTypeEntry>(topEntry);
+ Q_ASSERT(configurableEntry);
+ configurableEntry->setConfigCondition(condition);
+ return true;
+}
+
+bool TypeSystemParser::parseRenameFunction(const ConditionalStreamReader &,
+ QString *name, QXmlStreamAttributes *attributes)
+{
+ QString signature;
+ QString rename;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute) {
+ // Do not remove as it is needed for the type entry later on
+ signature = attributes->at(i).value().toString().simplified();
+ } else if (name == renameAttribute) {
+ rename = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (signature.isEmpty()) {
+ m_error = msgMissingAttribute(signatureAttribute);
+ return false;
+ }
+
+ *name = signature.left(signature.indexOf(u'(')).trimmed();
+
+ QString errorString = checkSignatureError(signature, u"function"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ if (!rename.isEmpty()) {
+ static const QRegularExpression functionNameRegExp(u"^[a-zA-Z_][a-zA-Z0-9_]*$"_s);
+ Q_ASSERT(functionNameRegExp.isValid());
+ if (!functionNameRegExp.match(rename).hasMatch()) {
+ m_error = u"can not rename '"_s + signature + u"', '"_s
+ + rename + u"' is not a valid function name"_s;
+ return false;
+ }
+ FunctionModification mod;
+ if (!mod.setSignature(signature, &m_error))
+ return false;
+ mod.setRenamedToName(rename);
+ mod.setModifierFlag(FunctionModification::Rename);
+ m_contextStack.top()->functionMods << mod;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ const bool isAddFunction = topElement == StackElement::AddFunction;
+ const bool validParent = isTypeEntry(topElement)
+ || topElement == StackElement::ModifyFunction
+ || topElement == StackElement::ModifyField
+ || isAddFunction;
+ if (!validParent) {
+ m_error = u"inject-documentation must be inside modify-function, add-function"
+ "modify-field or other tags that creates a type"_s;
+ return false;
+ }
+
+ TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace;
+ TypeSystem::Language lang = TypeSystem::NativeCode;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"mode") {
+ const auto attribute = attributes->takeAt(i);
+ const auto modeOpt = docModificationFromAttribute(attribute.value());
+ if (!modeOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mode = modeOpt.value();
+ } else if (name == formatAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(attribute.value());
+ if (!langOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ lang = langOpt.value();
+ }
+ }
+
+ QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature;
+ DocModification mod(mode, signature);
+ mod.setFormat(lang);
+ if (hasFileSnippetAttributes(attributes)) {
+ const auto snippetOptional = readFileSnippet(attributes);
+ if (!snippetOptional.has_value())
+ return false;
+ mod.setCode(snippetOptional.value().content);
+ }
+ auto &top = m_contextStack.top();
+ if (isAddFunction)
+ top->addedFunctions.last()->addDocModification(mod);
+ else
+ top->docModifications << mod;
+ return true;
+}
+
+bool TypeSystemParser::parseModifyDocumentation(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ const bool validParent = isTypeEntry(topElement)
+ || topElement == StackElement::ModifyFunction
+ || topElement == StackElement::ModifyField;
+ if (!validParent) {
+ m_error = u"modify-documentation must be inside modify-function, "
+ "modify-field or other tags that creates a type"_s;
+ return false;
+ }
+
+ const auto xpathIndex = indexOfAttribute(*attributes, xPathAttribute);
+ if (xpathIndex == -1) {
+ m_error = msgMissingAttribute(xPathAttribute);
+ return false;
+ }
+
+ const QString xpath = attributes->takeAt(xpathIndex).value().toString();
+ QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature;
+ m_contextStack.top()->docModifications
+ << DocModification(xpath, signature);
+ return true;
+}
+
+// m_exceptionHandling
+TypeSystemTypeEntryPtr TypeSystemParser::parseRootElement(const ConditionalStreamReader &,
+ const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
+ QString subModuleOf;
+ QString namespaceBegin;
+ QString namespaceEnd;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == packageAttribute) {
+ m_defaultPackage = attributes->takeAt(i).value().toString();
+ } else if (name == defaultSuperclassAttribute) {
+ m_defaultSuperclass = attributes->takeAt(i).value().toString();
+ } else if (name == exceptionHandlingAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
+ if (exceptionOpt.has_value()) {
+ m_exceptionHandling = exceptionOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == allowThreadAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
+ if (allowThreadOpt.has_value()) {
+ m_allowThread = allowThreadOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (snakeCaseOpt.has_value()) {
+ snakeCase = snakeCaseOpt.value();
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ } else if (name == subModuleOfAttribute) {
+ subModuleOf = attributes->takeAt(i).value().toString();
+ } else if (name == "namespace-begin"_L1) {
+ namespaceBegin = attributes->takeAt(i).value().toString();
+ } else if (name == "namespace-end"_L1) {
+ namespaceEnd = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (m_defaultPackage.isEmpty()) { // Extending default, see addBuiltInContainerTypes()
+ auto moduleEntry = std::const_pointer_cast<TypeSystemTypeEntry>(m_context->db->defaultTypeSystemType());
+ Q_ASSERT(moduleEntry);
+ m_defaultPackage = moduleEntry->name();
+ return moduleEntry;
+ }
+
+ auto moduleEntry =
+ std::const_pointer_cast<TypeSystemTypeEntry>(m_context->db->findTypeSystemType(m_defaultPackage));
+ const bool add = !moduleEntry;
+ if (add) {
+ moduleEntry.reset(new TypeSystemTypeEntry(m_defaultPackage, since,
+ currentParentTypeEntry()));
+ moduleEntry->setSubModule(subModuleOf);
+ }
+ moduleEntry->setCodeGeneration(m_generate);
+ moduleEntry->setSnakeCase(snakeCase);
+ if (!namespaceBegin.isEmpty())
+ moduleEntry->setNamespaceBegin(namespaceBegin);
+ if (!namespaceEnd.isEmpty())
+ moduleEntry->setNamespaceEnd(namespaceEnd);
+
+ if ((m_generate == TypeEntry::GenerateForSubclass ||
+ m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty())
+ TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage);
+
+ if (add)
+ m_context->db->addTypeSystemType(moduleEntry);
+ return moduleEntry;
+}
+
+bool TypeSystemParser::loadTypesystem(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ QString typeSystemName;
+ bool generateChild = true;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute)
+ typeSystemName = attributes->takeAt(i).value().toString();
+ else if (name == generateAttribute)
+ generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute, true);
+ }
+ if (typeSystemName.isEmpty()) {
+ m_error = u"No typesystem name specified"_s;
+ return false;
+ }
+ const bool result =
+ m_context->db->parseFile(m_context, typeSystemName, m_currentPath,
+ generateChild && m_generate == TypeEntry::GenerateCode);
+ if (!result)
+ m_error = u"Failed to parse: '"_s + typeSystemName + u'\'';
+ return result;
+}
+
+bool TypeSystemParser::parseRejectEnumValue(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ if (!m_currentEnum) {
+ m_error = u"<reject-enum-value> node must be used inside a <enum-type> node"_s;
+ return false;
+ }
+ const auto nameIndex = indexOfAttribute(*attributes, nameAttribute);
+ if (nameIndex == -1) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString());
+ return true;
+}
+
+bool TypeSystemParser::parseReplaceArgumentType(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"Type replacement can only be specified for argument modifications"_s;
+ return false;
+ }
+ const auto modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute);
+ if (modifiedTypeIndex == -1) {
+ m_error = u"Type replacement requires 'modified-type' attribute"_s;
+ return false;
+ }
+ m_contextStack.top()->functionMods.last().argument_mods().last().setModifiedType(
+ attributes->takeAt(modifiedTypeIndex).value().toString());
+ return true;
+}
+
+bool TypeSystemParser::parseCustomConversion(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument
+ && topElement != StackElement::ValueTypeEntry
+ && topElement != StackElement::PrimitiveTypeEntry
+ && topElement != StackElement::ContainerTypeEntry) {
+ m_error = u"Conversion rules can only be specified for argument modification, "
+ "value-type, primitive-type or container-type conversion."_s;
+ return false;
+ }
+
+ QString sourceFile;
+ QString snippetLabel;
+ TypeSystem::Language lang = TypeSystem::NativeCode;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == classAttribute) {
+ const auto languageAttribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(languageAttribute.value());
+ if (!langOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(languageAttribute);
+ return false;
+ }
+ lang = langOpt.value();
+ } else if (name == u"file") {
+ sourceFile = attributes->takeAt(i).value().toString();
+ } else if (name == snippetAttribute) {
+ snippetLabel = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ const auto &top = m_contextStack.top();
+ if (topElement == StackElement::ModifyArgument) {
+ CodeSnip snip;
+ snip.language = lang;
+ top->functionMods.last().argument_mods().last().conversionRules().append(snip);
+ return true;
+ }
+
+ ValueTypeEntryPtr valueTypeEntry;
+ if (top->entry->isValue()) {
+ valueTypeEntry = std::static_pointer_cast<ValueTypeEntry>(top->entry);
+ if (valueTypeEntry->hasTargetConversionRule() || valueTypeEntry->hasCustomConversion()) {
+ m_error = u"Types can have only one conversion rule"_s;
+ return false;
+ }
+ }
+
+ // The old conversion rule tag that uses a file containing the conversion
+ // will be kept temporarily for compatibility reasons. FIXME PYSIDE7: Remove
+ if (valueTypeEntry != nullptr && !sourceFile.isEmpty()) {
+ if (m_generate != TypeEntry::GenerateForSubclass
+ && m_generate != TypeEntry::GenerateNothing) {
+ qWarning(lcShiboken, "Specifying conversion rules by \"file\" is deprecated.");
+ if (lang != TypeSystem::TargetLangCode)
+ return true;
+
+ QFile conversionSource(sourceFile);
+ if (!conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ m_error = msgCannotOpenForReading(conversionSource);
+ return false;
+ }
+ const auto conversionRuleOptional =
+ extractSnippet(QString::fromUtf8(conversionSource.readAll()), snippetLabel);
+ if (!conversionRuleOptional.has_value()) {
+ m_error = msgCannotFindSnippet(sourceFile, snippetLabel);
+ return false;
+ }
+ valueTypeEntry->setTargetConversionRule(conversionRuleOptional.value());
+ }
+ return true;
+ }
+
+ auto customConversion = std::make_shared<CustomConversion>(top->entry);
+ if (top->entry->isPrimitive())
+ std::static_pointer_cast<PrimitiveTypeEntry>(top->entry)->setCustomConversion(customConversion);
+ else if (top->entry->isContainer())
+ std::static_pointer_cast<ContainerTypeEntry>(top->entry)->setCustomConversion(customConversion);
+ else if (top->entry->isValue())
+ std::static_pointer_cast<ValueTypeEntry>(top->entry)->setCustomConversion(customConversion);
+ customConversionsForReview.append(customConversion);
+ return true;
+}
+
+bool TypeSystemParser::parseNativeToTarget(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ConversionRule) {
+ m_error = u"Native to Target conversion code can only be specified for custom conversion rules."_s;
+ return false;
+ }
+ CodeSnip snip;
+ if (!readCodeSnippet(attributes, &snip))
+ return false;
+ m_contextStack.top()->conversionCodeSnips.append(snip);
+ return true;
+}
+
+bool TypeSystemParser::parseAddConversion(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::TargetToNative) {
+ m_error = u"Target to Native conversions can only be added inside 'target-to-native' tags."_s;
+ return false;
+ }
+ QString sourceTypeName;
+ QString typeCheck;
+ CodeSnip snip;
+ if (!readCodeSnippet(attributes, &snip))
+ return false;
+
+ const auto &top = m_contextStack.top();
+ top->conversionCodeSnips.append(snip);
+
+ if (parserState() == ParserState::ArgumentTargetToNativeConversion)
+ return true;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"type")
+ sourceTypeName = attributes->takeAt(i).value().toString();
+ else if (name == u"check")
+ typeCheck = attributes->takeAt(i).value().toString();
+ }
+
+ if (sourceTypeName.isEmpty()) {
+ m_error = u"Target to Native conversions must specify the input type with the 'type' attribute."_s;
+ return false;
+ }
+ auto customConversion = CustomConversion::getCustomConversion(top->entry);
+ if (!customConversion) {
+ m_error = msgMissingCustomConversion(top->entry);
+ return false;
+ }
+ customConversion->addTargetToNativeConversion(sourceTypeName, typeCheck);
+ return true;
+}
+
+static bool parseIndex(const QString &index, int *result, QString *errorMessage)
+{
+ bool ok = false;
+ *result = index.toInt(&ok);
+ if (!ok)
+ *errorMessage = QString::fromLatin1("Cannot convert '%1' to integer").arg(index);
+ return ok;
+}
+
+static bool parseArgumentIndex(const QString &index, int *result, QString *errorMessage)
+{
+ if (index == u"return") {
+ *result = 0;
+ return true;
+ }
+ if (index == u"this") {
+ *result = -1;
+ return true;
+ }
+ return parseIndex(index, result, errorMessage);
+}
+
+bool TypeSystemParser::parseModifyArgument(const ConditionalStreamReader &,
+ StackElement topElement, QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyFunction
+ && topElement != StackElement::AddFunction
+ && topElement != StackElement::DeclareFunction) {
+ m_error = u"Argument modification requires <modify-function>,"
+ " <add-function> or <declare-function> as parent, was "_s
+ + tagFromElement(topElement).toString();
+ return false;
+ }
+
+ QString index;
+ QString renameTo;
+ QString pyiType;
+ bool resetAfterUse = false;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == indexAttribute) {
+ index = attributes->takeAt(i).value().toString();
+ } else if (name == invalidateAfterUseAttribute) {
+ resetAfterUse = convertBoolean(attributes->takeAt(i).value(),
+ invalidateAfterUseAttribute, false);
+ } else if (name == renameAttribute) {
+ renameTo = attributes->takeAt(i).value().toString();
+ } else if (name == pyiTypeAttribute) {
+ pyiType = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ if (index.isEmpty()) {
+ m_error = msgMissingAttribute(indexAttribute);
+ return false;
+ }
+
+ int idx;
+ if (!parseArgumentIndex(index, &idx, &m_error))
+ return false;
+
+ ArgumentModification argumentModification = ArgumentModification(idx);
+ argumentModification.setResetAfterUse(resetAfterUse);
+ argumentModification.setRenamedToName(renameTo);
+ argumentModification.setPyiType(pyiType);
+ m_contextStack.top()->functionMods.last().argument_mods().append(argumentModification);
+ return true;
+}
+
+bool TypeSystemParser::parseNoNullPointer(const ConditionalStreamReader &reader,
+ StackElement topElement, QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"no-null-pointer requires argument modification as parent"_s;
+ return false;
+ }
+
+ ArgumentModification &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ lastArgMod.setNoNullPointers(true);
+
+ const int defaultValueIndex =
+ indexOfAttribute(*attributes, u"default-value");
+ if (defaultValueIndex != -1) {
+ const QXmlStreamAttribute attribute = attributes->takeAt(defaultValueIndex);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, attribute)));
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseDefineOwnership(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"define-ownership requires argument modification as parent"_s;
+ return false;
+ }
+
+ TypeSystem::Language lang = TypeSystem::TargetLangCode;
+ std::optional<TypeSystem::Ownership> ownershipOpt;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == classAttribute) {
+ const auto classAttribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(classAttribute.value());
+ if (!langOpt.has_value() || langOpt.value() == TypeSystem::ShellCode) {
+ m_error = msgInvalidAttributeValue(classAttribute);
+ return false;
+ }
+ lang = langOpt.value();
+ } else if (name == ownershipAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ ownershipOpt = ownershipFromFromAttribute(attribute.value());
+ if (!ownershipOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ }
+ }
+
+ if (!ownershipOpt.has_value()) {
+ m_error = "unspecified ownership"_L1;
+ return false;
+ }
+ auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
+ switch (lang) {
+ case TypeSystem::TargetLangCode:
+ lastArgMod.setTargetOwnerShip(ownershipOpt.value());
+ break;
+ case TypeSystem::NativeCode:
+ lastArgMod.setNativeOwnership(ownershipOpt.value());
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+// ### fixme PySide7: remove (replaced by attribute).
+bool TypeSystemParser::parseRename(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"Argument modification parent required"_s;
+ return false;
+ }
+
+ const auto toIndex = indexOfAttribute(*attributes, toAttribute);
+ if (toIndex == -1) {
+ m_error = msgMissingAttribute(toAttribute);
+ return false;
+ }
+ const QString renamed_to = attributes->takeAt(toIndex).value().toString();
+ m_contextStack.top()->functionMods.last().argument_mods().last().setRenamedToName(renamed_to);
+ return true;
+}
+
+bool TypeSystemParser::parseModifyField(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ FieldModification fm;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ fm.setName(attributes->takeAt(i).value().toString());
+ } else if (name == removeAttribute) {
+ fm.setRemoved(convertRemovalAttribute(attributes->takeAt(i).value()));
+ } else if (name == opaqueContainerFieldAttribute) {
+ fm.setOpaqueContainer(convertBoolean(attributes->takeAt(i).value(),
+ opaqueContainerFieldAttribute, false));
+ } else if (name == readAttribute) {
+ fm.setReadable(convertBoolean(attributes->takeAt(i).value(), readAttribute, true));
+ } else if (name == writeAttribute) {
+ fm.setWritable(convertBoolean(attributes->takeAt(i).value(), writeAttribute, true));
+ } else if (name == renameAttribute) {
+ fm.setRenamedToName(attributes->takeAt(i).value().toString());
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (snakeCaseOpt.has_value()) {
+ fm.setSnakeCase(snakeCaseOpt.value());
+ } else {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
+ }
+ }
+ if (fm.name().isEmpty()) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_contextStack.top()->fieldMods << fm;
+ return true;
+}
+
+static bool parseOverloadNumber(const QXmlStreamAttribute &attribute, int *overloadNumber,
+ QString *errorMessage)
+{
+ bool ok;
+ *overloadNumber = attribute.value().toInt(&ok);
+ if (!ok || *overloadNumber < 0) {
+ *errorMessage = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseAddFunction(const ConditionalStreamReader &,
+ StackElement topElement,
+ StackElement t,
+ QXmlStreamAttributes *attributes)
+{
+ const bool validParent = isComplexTypeEntry(topElement)
+ || topElement == StackElement::Root
+ || topElement == StackElement::ContainerTypeEntry;
+ if (!validParent) {
+ m_error = QString::fromLatin1("Add/Declare function requires a complex/container type or a root tag as parent"
+ ", was=%1").arg(tagFromElement(topElement));
+ return false;
+ }
+
+ FunctionModification mod;
+ if (!(t == StackElement::AddFunction
+ ? parseBasicModifyFunctionAttributes(attributes, &mod)
+ : parseModifyFunctionAttributes(attributes, &mod))) {
+ return false;
+ }
+
+ QString originalSignature;
+ QString returnType;
+ bool staticFunction = false;
+ bool classMethod = false;
+ bool pythonOverride = false;
+ QString access;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute) {
+ originalSignature = attributes->takeAt(i).value().toString().simplified();
+ } else if (name == u"return-type") {
+ returnType = attributes->takeAt(i).value().toString();
+ } else if (name == staticAttribute) {
+ staticFunction = convertBoolean(attributes->takeAt(i).value(),
+ staticAttribute, false);
+ } else if (name == classmethodAttribute) {
+ classMethod = convertBoolean(attributes->takeAt(i).value(),
+ classmethodAttribute, false);
+ } else if (name == accessAttribute) {
+ access = attributes->takeAt(i).value().toString();
+ } else if (name == pythonOverrideAttribute) {
+ pythonOverride = convertBoolean(attributes->takeAt(i).value(),
+ pythonOverrideAttribute, false);
+ }
+ }
+
+ QString signature = TypeDatabase::normalizedAddedFunctionSignature(originalSignature);
+ if (signature.isEmpty()) {
+ m_error = u"No signature for the added function"_s;
+ return false;
+ }
+
+ QString errorString = checkSignatureError(signature, u"add-function"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ AddedFunctionPtr func = AddedFunction::createAddedFunction(signature, returnType, &errorString);
+ if (!func) {
+ m_error = errorString;
+ return false;
+ }
+
+ func->setStatic(staticFunction);
+ func->setClassMethod(classMethod);
+ func->setPythonOverride(pythonOverride);
+ func->setTargetLangPackage(m_defaultPackage);
+
+ // Create signature for matching modifications
+ signature = TypeDatabase::normalizedSignature(originalSignature);
+ if (!signature.contains(u'('))
+ signature += u"()"_s;
+ m_currentSignature = signature;
+
+ if (!access.isEmpty()) {
+ const auto acessOpt = addedFunctionAccessFromAttribute(access);
+ if (!acessOpt.has_value()) {
+ m_error = u"Bad access type '"_s + access + u'\'';
+ return false;
+ }
+ func->setAccess(acessOpt.value());
+ }
+ func->setDeclaration(t == StackElement::DeclareFunction);
+
+ m_contextStack.top()->addedFunctions << func;
+ m_contextStack.top()->addedFunctionModificationIndex =
+ m_contextStack.top()->functionMods.size();
+
+ if (!mod.setSignature(m_currentSignature, &m_error))
+ return false;
+ mod.setOriginalSignature(originalSignature);
+ m_contextStack.top()->functionMods << mod;
+ return true;
+}
+
+bool TypeSystemParser::parseAddPyMethodDef(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)) {
+ m_error = u"add-pymethoddef requires a complex type as parent, was="_s
+ + tagFromElement(topElement).toString();
+ return false;
+ }
+
+ TypeSystemPyMethodDefEntry def;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ def.name = attributes->takeAt(i).value().toString();
+ } else if (name == u"doc") {
+ def.doc = attributes->takeAt(i).value().toString();
+ } else if (name == u"function") {
+ def.function = attributes->takeAt(i).value().toString();
+ } else if (name == u"flags") {
+ auto attribute = attributes->takeAt(i);
+ const auto flags = attribute.value().split(u'|', Qt::SkipEmptyParts);
+ for (const auto &flag : flags)
+ def.methFlags.append(flag.toString().toUtf8());
+ } else if (name == u"signatures") {
+ auto attribute = attributes->takeAt(i);
+ const auto signatures = attribute.value().split(u';', Qt::SkipEmptyParts);
+ for (const auto &signature : signatures)
+ def.signatures.append(signature.toString());
+ }
+ }
+
+ if (def.name.isEmpty() || def.function.isEmpty()) {
+ m_error = u"add-pymethoddef requires at least a name and a function attribute"_s;
+ return false;
+ }
+ std::static_pointer_cast<ComplexTypeEntry>(m_contextStack.top()->entry)->addPyMethodDef(def);
+ return true;
+}
+
+bool TypeSystemParser::parseProperty(const ConditionalStreamReader &, StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)) {
+ m_error = QString::fromLatin1("Add property requires a complex type as parent"
+ ", was=%1").arg(tagFromElement(topElement));
+ return false;
+ }
+
+ TypeSystemProperty property;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == nameAttribute) {
+ property.name = attributes->takeAt(i).value().toString();
+ } else if (name == u"get") {
+ property.read = attributes->takeAt(i).value().toString();
+ } else if (name == u"type") {
+ property.type = attributes->takeAt(i).value().toString();
+ } else if (name == u"set") {
+ property.write = attributes->takeAt(i).value().toString();
+ } else if (name == generateGetSetDefAttribute) {
+ property.generateGetSetDef =
+ convertBoolean(attributes->takeAt(i).value(),
+ generateGetSetDefAttribute, false);
+ }
+ }
+ if (!property.isValid()) {
+ m_error = u"<property> element is missing required attibutes (name/type/get)."_s;
+ return false;
+ }
+ std::static_pointer_cast<ComplexTypeEntry>(m_contextStack.top()->entry)->addProperty(property);
+ return true;
+}
+
+// Parse basic attributes applicable to <add-function>/<declare-function>/<function>
+// and <modify-function> (all that is not done by injected code).
+bool TypeSystemParser::parseBasicModifyFunctionAttributes(QXmlStreamAttributes *attributes,
+ FunctionModification *mod)
+{
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == overloadNumberAttribute) {
+ int overloadNumber = TypeSystem::OverloadNumberUnset;
+ if (!parseOverloadNumber(attributes->takeAt(i), &overloadNumber, &m_error))
+ return false;
+ mod->setOverloadNumber(overloadNumber);
+ }
+ }
+ return true;
+}
+
+// Parse attributes applicable to <declare-function>/<function>
+// and <modify-function>.
+bool TypeSystemParser::parseModifyFunctionAttributes(QXmlStreamAttributes *attributes,
+ FunctionModification *mod)
+{
+ if (!parseBasicModifyFunctionAttributes(attributes, mod))
+ return false;
+
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == allowThreadAttribute) {
+ const QXmlStreamAttribute attribute = attributes->takeAt(i);
+ const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
+ if (!allowThreadOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mod->setAllowThread(allowThreadOpt.value());
+ } else if (name == exceptionHandlingAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
+ if (!exceptionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mod->setExceptionHandling(exceptionOpt.value());
+ } else if (name == snakeCaseAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
+ if (!snakeCaseOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ mod->setSnakeCase(snakeCaseOpt.value());
+ } else if (name == deprecatedAttribute) {
+ const bool deprecated = convertBoolean(attributes->takeAt(i).value(),
+ deprecatedAttribute, false);
+ mod->setModifierFlag(deprecated ? FunctionModification::Deprecated
+ : FunctionModification::Undeprecated);
+ }
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseModifyFunction(const ConditionalStreamReader &reader,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ const bool validParent = isComplexTypeEntry(topElement)
+ || topElement == StackElement::TypedefTypeEntry
+ || topElement == StackElement::FunctionTypeEntry;
+ if (!validParent) {
+ m_error = QString::fromLatin1("Modify function requires complex type as parent"
+ ", was=%1").arg(tagFromElement(topElement));
+ return false;
+ }
+
+ QString originalSignature;
+ FunctionModification mod;
+ if (!parseModifyFunctionAttributes(attributes, &mod))
+ return false;
+
+ QString access;
+ bool removed = false;
+ QString rename;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == signatureAttribute) {
+ originalSignature = attributes->takeAt(i).value().toString().simplified();
+ } else if (name == accessAttribute) {
+ access = attributes->takeAt(i).value().toString();
+ } else if (name == renameAttribute) {
+ rename = attributes->takeAt(i).value().toString();
+ } else if (name == removeAttribute) {
+ removed = convertRemovalAttribute(attributes->takeAt(i).value());
+ } else if (name == virtualSlotAttribute || name == threadAttribute) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeWarning(reader, name)));
+ }
+ }
+
+ // Child of global <function>
+ const auto &top = m_contextStack.top();
+ if (originalSignature.isEmpty() && top->entry->isFunction()) {
+ auto f = std::static_pointer_cast<const FunctionTypeEntry>(top->entry);
+ originalSignature = f->signatures().value(0);
+ }
+
+ const QString signature = TypeDatabase::normalizedSignature(originalSignature);
+ if (signature.isEmpty()) {
+ m_error = u"No signature for modified function"_s;
+ return false;
+ }
+
+ QString errorString = checkSignatureError(signature, u"modify-function"_s);
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ if (!mod.setSignature(signature, &m_error))
+ return false;
+ mod.setOriginalSignature(originalSignature);
+ m_currentSignature = signature;
+
+ if (!access.isEmpty()) {
+ const auto modifierFlagOpt = modifierFromAttribute(access);
+ if (!modifierFlagOpt.has_value()) {
+ m_error = u"Bad access type '"_s + access + u'\'';
+ return false;
+ }
+ const FunctionModification::ModifierFlag m = modifierFlagOpt.value();
+ if (m == FunctionModification::NonFinal) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeValueWarning(reader,
+ accessAttribute, access)));
+ }
+ mod.setModifierFlag(m);
+ }
+
+ mod.setRemoved(removed);
+
+ if (!rename.isEmpty()) {
+ mod.setRenamedToName(rename);
+ mod.setModifierFlag(FunctionModification::Rename);
+ }
+
+ top->functionMods << mod;
+ return true;
+}
+
+bool TypeSystemParser::parseReplaceDefaultExpression(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!(topElement & StackElement::ModifyArgument)) {
+ m_error = u"Replace default expression only allowed as child of argument modification"_s;
+ return false;
+ }
+ const auto withIndex = indexOfAttribute(*attributes, u"with");
+ if (withIndex == -1 || attributes->at(withIndex).value().isEmpty()) {
+ m_error = u"Default expression replaced with empty string. Use remove-default-expression instead."_s;
+ return false;
+ }
+
+ m_contextStack.top()->functionMods.last().argument_mods().last().setReplacedDefaultExpression(
+ attributes->takeAt(withIndex).value().toString());
+ return true;
+}
+
+bool TypeSystemParser::parseReferenceCount(const ConditionalStreamReader &reader,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"reference-count must be child of modify-argument"_s;
+ return false;
+ }
+
+ ReferenceCount rc;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == actionAttribute) {
+ const QXmlStreamAttribute attribute = attributes->takeAt(i);
+ const auto actionOpt = referenceCountFromAttribute(attribute.value());
+ if (!actionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ rc.action = actionOpt.value();
+ switch (rc.action) {
+ case ReferenceCount::AddAll:
+ case ReferenceCount::Ignore:
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedAttributeValueWarning(reader, attribute)));
+ break;
+ default:
+ break;
+ }
+ } else if (name == u"variable-name") {
+ rc.varName = attributes->takeAt(i).value().toString();
+ }
+ }
+
+ m_contextStack.top()->functionMods.last().argument_mods().last().addReferenceCount(rc);
+ return true;
+}
+
+bool TypeSystemParser::parseParentOwner(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"parent-policy must be child of modify-argument"_s;
+ return false;
+ }
+ ArgumentOwner ao;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == indexAttribute) {
+ const QString index = attributes->takeAt(i).value().toString();
+ if (!parseArgumentIndex(index, &ao.index, &m_error))
+ return false;
+ } else if (name == actionAttribute) {
+ const auto action = attributes->takeAt(i);
+ const auto actionOpt = argumentOwnerActionFromAttribute(action.value());
+ if (!actionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(action);
+ return false;
+ }
+ ao.action = actionOpt.value();
+ }
+ }
+ m_contextStack.top()->functionMods.last().argument_mods().last().setOwner(ao);
+ return true;
+}
+
+std::optional<TypeSystemParser::Snippet>
+ TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes)
+{
+ Snippet result;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == fileAttribute) {
+ result.fileName = attributes->takeAt(i).value().toString();
+ } else if (name == snippetAttribute) {
+ result.snippetLabel = attributes->takeAt(i).value().toString();
+ }
+ }
+ if (result.fileName.isEmpty()) {
+ m_error = "Snippet missing file name"_L1;
+ return std::nullopt;
+ }
+ const QString resolved = m_context->db->modifiedTypesystemFilepath(result.fileName,
+ m_currentPath);
+ if (!QFile::exists(resolved)) {
+ m_error = u"File for inject code not exist: "_s
+ + QDir::toNativeSeparators(result.fileName);
+ return std::nullopt;
+ }
+ QFile codeFile(resolved);
+ if (!codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ m_error = msgCannotOpenForReading(codeFile);
+ return std::nullopt;
+ }
+ const auto contentOptional = extractSnippet(QString::fromUtf8(codeFile.readAll()),
+ result.snippetLabel);
+ codeFile.close();
+ if (!contentOptional.has_value()) {
+ m_error = msgCannotFindSnippet(resolved, result.snippetLabel);
+ return std::nullopt;
+ }
+ result.content = contentOptional.value();
+ return result;
+}
+
+bool TypeSystemParser::readCodeSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip)
+{
+ if (!hasFileSnippetAttributes(attributes))
+ return true; // Expecting inline content.
+ const auto snippetOptional = readFileSnippet(attributes);
+ if (!snippetOptional.has_value())
+ return false;
+ const auto snippet = snippetOptional.value();
+
+ QString source = snippet.fileName;
+ if (!snippet.snippetLabel.isEmpty())
+ source += " ("_L1 + snippet.snippetLabel + u')';
+ QString content;
+ QTextStream str(&content);
+ str << "// ========================================================================\n"
+ "// START of custom code block [file: "
+ << source << "]\n" << snippet.content
+ << "// END of custom code block [file: " << source
+ << "]\n// ========================================================================\n";
+ snip->addCode(content);
+ return true;
+}
+
+bool TypeSystemParser::parseInjectCode(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if (!isComplexTypeEntry(topElement)
+ && (topElement != StackElement::AddFunction)
+ && (topElement != StackElement::ModifyFunction)
+ && (topElement != StackElement::Root)) {
+ m_error = u"wrong parent type for code injection"_s;
+ return false;
+ }
+
+ TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionBeginning;
+ TypeSystem::Language lang = TypeSystem::TargetLangCode;
+ CodeSnip snip;
+ if (!readCodeSnippet(attributes, &snip))
+ return false;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == classAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto langOpt = languageFromAttribute(attribute.value());
+ if (!langOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ lang = langOpt.value();
+ } else if (name == positionAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto positionOpt = codeSnipPositionFromAttribute(attribute.value());
+ if (!positionOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ position = positionOpt.value();
+ }
+ }
+
+ snip.position = position;
+ snip.language = lang;
+
+ switch (topElement) {
+ case StackElement::ModifyFunction:
+ case StackElement::AddFunction: {
+ FunctionModification &mod = m_contextStack.top()->functionMods.last();
+ mod.appendSnip(snip);
+ if (!snip.code().isEmpty())
+ mod.setModifierFlag(FunctionModification::CodeInjection);
+ }
+ break;
+ case StackElement::Root:
+ std::static_pointer_cast<TypeSystemTypeEntry>(m_contextStack.top()->entry)->addCodeSnip(snip);
+ break;
+ default:
+ std::static_pointer_cast<ComplexTypeEntry>(m_contextStack.top()->entry)->addCodeSnip(snip);
+ break;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseInclude(const ConditionalStreamReader &,
+ StackElement topElement,
+ const TypeEntryPtr &entry, QXmlStreamAttributes *attributes)
+{
+ QString fileName;
+ Include::IncludeType location = Include::IncludePath;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == fileNameAttribute) {
+ fileName = attributes->takeAt(i).value().toString();
+ } else if (name == locationAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ const auto locationOpt = locationFromAttribute(attribute.value());
+ if (!locationOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ location = locationOpt.value();
+ }
+ }
+
+ Include inc(location, fileName);
+ if (isComplexTypeEntry(topElement)
+ || topElement == StackElement::PrimitiveTypeEntry
+ || topElement == StackElement::ContainerTypeEntry
+ || topElement == StackElement::SmartPointerTypeEntry
+ || topElement == StackElement::TypedefTypeEntry) {
+ entry->setInclude(inc);
+ } else if (topElement == StackElement::ExtraIncludes) {
+ entry->addExtraInclude(inc);
+ } else {
+ m_error = u"Only supported parent tags are primitive-type, complex types or extra-includes"_s;
+ return false;
+ }
+ return true;
+}
+
+bool TypeSystemParser::parseSystemInclude(const ConditionalStreamReader &,
+ QXmlStreamAttributes *attributes)
+{
+ const auto index = indexOfAttribute(*attributes, fileNameAttribute);
+ if (index == -1) {
+ m_error = msgMissingAttribute(fileNameAttribute);
+ return false;
+ }
+ TypeDatabase::instance()->addForceProcessSystemInclude(attributes->takeAt(index).value().toString());
+ return true;
+}
+
+TemplateInstance *
+ TypeSystemParser::parseInsertTemplate(const ConditionalStreamReader &,
+ StackElement topElement,
+ QXmlStreamAttributes *attributes)
+{
+ if ((topElement != StackElement::InjectCode) &&
+ (topElement != StackElement::Template) &&
+ (topElement != StackElement::NativeToTarget) &&
+ (topElement != StackElement::AddConversion) &&
+ (topElement != StackElement::ConversionRule)) {
+ m_error = u"Can only insert templates into code snippets, templates, "\
+ "conversion-rule, native-to-target or add-conversion tags."_s;
+ return nullptr;
+ }
+ const auto nameIndex = indexOfAttribute(*attributes, nameAttribute);
+ if (nameIndex == -1) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return nullptr;
+ }
+ return new TemplateInstance(attributes->takeAt(nameIndex).value().toString());
+}
+
+bool TypeSystemParser::parseReplace(const ConditionalStreamReader &,
+ StackElement topElement, QXmlStreamAttributes *attributes)
+{
+ if (topElement != StackElement::InsertTemplate) {
+ m_error = u"Can only insert replace rules into insert-template."_s;
+ return false;
+ }
+ QString from;
+ QString to;
+ for (auto i = attributes->size() - 1; i >= 0; --i) {
+ const auto name = attributes->at(i).qualifiedName();
+ if (name == u"from")
+ from = attributes->takeAt(i).value().toString();
+ else if (name == toAttribute)
+ to = attributes->takeAt(i).value().toString();
+ }
+ m_templateInstance->addReplaceRule(from, to);
+ return true;
+}
+
+// Check for a duplicated type entry and return whether to add the new one.
+// We need to be able to have duplicate primitive type entries,
+// or it's not possible to cover all primitive target language
+// types (which we need to do in order to support fake meta objects)
+bool TypeSystemParser::checkDuplicatedTypeEntry(const ConditionalStreamReader &reader,
+ StackElement t,
+ const QString &name) const
+{
+ if (t == StackElement::PrimitiveTypeEntry || t == StackElement::FunctionTypeEntry)
+ return true;
+ const auto duplicated = m_context->db->findType(name);
+ if (!duplicated || duplicated->isNamespace())
+ return true;
+ if (duplicated->isBuiltIn()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgReaderMessage(reader, "Warning",
+ msgDuplicateBuiltInTypeEntry(name))));
+ return false;
+ }
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgReaderMessage(reader, "Warning",
+ msgDuplicateTypeEntry(name))));
+ return true;
+}
+
+static bool parseVersion(const QString &versionSpec, const QString &package,
+ QVersionNumber *result, QString *errorMessage)
+{
+ *result = QVersionNumber::fromString(versionSpec);
+ if (result->isNull()) {
+ *errorMessage = msgInvalidVersion(versionSpec, package);
+ return false;
+ }
+ return true;
+}
+
+bool TypeSystemParser::startElement(const ConditionalStreamReader &reader, StackElement element)
+{
+ if (m_ignoreDepth) {
+ ++m_ignoreDepth;
+ return true;
+ }
+
+ const auto tagName = reader.name();
+ QXmlStreamAttributes attributes = reader.attributes();
+
+ VersionRange versionRange;
+ for (auto i = attributes.size() - 1; i >= 0; --i) {
+ const auto name = attributes.at(i).qualifiedName();
+ if (name == sinceAttribute) {
+ if (!parseVersion(attributes.takeAt(i).value().toString(),
+ m_defaultPackage, &versionRange.since, &m_error)) {
+ return false;
+ }
+ } else if (name == untilAttribute) {
+ if (!parseVersion(attributes.takeAt(i).value().toString(),
+ m_defaultPackage, &versionRange.until, &m_error)) {
+ return false;
+ }
+ }
+ }
+
+ if (!m_defaultPackage.isEmpty() && !versionRange.isNull()) {
+ auto *td = TypeDatabase::instance();
+ if (!td->checkApiVersion(m_defaultPackage, versionRange)) {
+ ++m_ignoreDepth;
+ return true;
+ }
+ }
+
+ if (element == StackElement::ImportFile)
+ return importFileElement(attributes);
+
+ if (m_currentDroppedEntryDepth) {
+ ++m_currentDroppedEntryDepth;
+ return true;
+ }
+
+ if (element == StackElement::Root && m_generate == TypeEntry::GenerateCode)
+ customConversionsForReview.clear();
+
+ if (element == StackElement::Unimplemented) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnimplementedElementWarning(reader, tagName)));
+ return true;
+ }
+
+ if (isTypeEntry(element) || element == StackElement::Root)
+ m_contextStack.push(std::make_shared<StackElementContext>());
+
+ if (m_contextStack.isEmpty()) {
+ m_error = msgNoRootTypeSystemEntry();
+ return false;
+ }
+
+ const auto &top = m_contextStack.top();
+ const StackElement topElement = m_stack.value(m_stack.size() - 2, StackElement::None);
+
+ if (isTypeEntry(element)) {
+ QString name;
+ if (element != StackElement::FunctionTypeEntry) {
+ const auto nameIndex = indexOfAttribute(attributes, nameAttribute);
+ if (nameIndex != -1) {
+ name = attributes.takeAt(nameIndex).value().toString();
+ } else if (element != StackElement::EnumTypeEntry) { // anonymous enum?
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ }
+ // Allow for primitive and/or std:: types only, else require proper nesting.
+ if (element != StackElement::PrimitiveTypeEntry && name.contains(u':')
+ && !name.contains(u"std::")) {
+ m_error = msgIncorrectlyNestedName(name);
+ return false;
+ }
+
+ if (m_context->db->hasDroppedTypeEntries()) {
+ const QString identifier = element == StackElement::FunctionTypeEntry
+ ? attributes.value(signatureAttribute).toString().simplified() : name;
+ if (shouldDropTypeEntry(m_context->db, m_contextStack, identifier)) {
+ m_currentDroppedEntryDepth = 1;
+ if (ReportHandler::isDebug(ReportHandler::SparseDebug)) {
+ qCInfo(lcShiboken, "Type system entry '%s' was intentionally dropped from generation.",
+ qPrintable(identifier));
+ }
+ m_contextStack.pop();
+ return true;
+ }
+ }
+
+ // The top level tag 'function' has only the 'signature' tag
+ // and we should extract the 'name' value from it.
+ if (element == StackElement::FunctionTypeEntry
+ && !parseRenameFunction(reader, &name, &attributes)) {
+ return false;
+ }
+
+ // We need to be able to have duplicate primitive type entries,
+ // or it's not possible to cover all primitive target language
+ // types (which we need to do in order to support fake meta objects)
+ if (element != StackElement::PrimitiveTypeEntry
+ && element != StackElement::FunctionTypeEntry) {
+ TypeEntryPtr tmp = m_context->db->findType(name);
+ if (tmp && !tmp->isNamespace())
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Duplicate type entry: '" << name << '\'';
+ }
+
+ if (element == StackElement::EnumTypeEntry) {
+ const auto enumIdentifiedByIndex =
+ indexOfAttribute(attributes, enumIdentifiedByValueAttribute);
+ const QString identifiedByValue = enumIdentifiedByIndex != -1
+ ? attributes.takeAt(enumIdentifiedByIndex).value().toString() : QString();
+ if (name.isEmpty()) {
+ name = identifiedByValue;
+ } else if (!identifiedByValue.isEmpty()) {
+ m_error = u"can't specify both 'name' and 'identified-by-value' attributes"_s;
+ return false;
+ }
+ }
+
+ if (name.isEmpty()) {
+ m_error = u"no 'name' attribute specified"_s;
+ return false;
+ }
+
+ switch (element) {
+ case StackElement::CustomTypeEntry:
+ top->entry = parseCustomTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!top->entry))
+ return false;
+ break;
+ case StackElement::PrimitiveTypeEntry:
+ top->entry = parsePrimitiveTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!top->entry))
+ return false;
+ break;
+ case StackElement::ContainerTypeEntry:
+ top->entry = parseContainerTypeEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+
+ case StackElement::SmartPointerTypeEntry:
+ top->entry = parseSmartPointerEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ case StackElement::EnumTypeEntry:
+ m_currentEnum = parseEnumTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!m_currentEnum))
+ return false;
+ top->entry = m_currentEnum;
+ break;
+
+ case StackElement::ValueTypeEntry:
+ top->entry = parseValueTypeEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ case StackElement::NamespaceTypeEntry:
+ top->entry = parseNamespaceTypeEntry(reader, name, versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ case StackElement::ObjectTypeEntry:
+ case StackElement::InterfaceTypeEntry: {
+ if (!checkRootElement())
+ return false;
+ auto ce = std::make_shared<ObjectTypeEntry>(name, versionRange.since, currentParentTypeEntry());
+ top->entry = ce;
+ applyCommonAttributes(reader, top->entry, &attributes);
+ applyComplexTypeAttributes(reader, ce, &attributes);
+ }
+ break;
+ case StackElement::FunctionTypeEntry:
+ top->entry = parseFunctionTypeEntry(reader, name, versionRange.since, &attributes);
+ if (Q_UNLIKELY(!top->entry))
+ return false;
+ break;
+ case StackElement::TypedefTypeEntry:
+ top->entry = parseTypedefEntry(reader, name, topElement,
+ versionRange.since, &attributes);
+ if (top->entry == nullptr)
+ return false;
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if (top->entry) {
+ if (checkDuplicatedTypeEntry(reader, element, top->entry->name())
+ && !m_context->db->addType(top->entry, &m_error)) {
+ return false;
+ }
+ } else {
+ qCWarning(lcShiboken).noquote().nospace()
+ << u"Type: "_s + name + u" was rejected by typesystem"_s;
+ }
+
+ } else if (element == StackElement::InjectDocumentation) {
+ if (!parseInjectDocumentation(reader, topElement, &attributes))
+ return false;
+ } else if (element == StackElement::ModifyDocumentation) {
+ if (!parseModifyDocumentation(reader, topElement, &attributes))
+ return false;
+ } else if (element != StackElement::None) {
+ bool topLevel = element == StackElement::Root
+ || element == StackElement::SuppressedWarning
+ || element == StackElement::Rejection
+ || element == StackElement::LoadTypesystem
+ || element == StackElement::InjectCode
+ || element == StackElement::ExtraIncludes
+ || element == StackElement::SystemInclude
+ || element == StackElement::ConversionRule
+ || element == StackElement::AddFunction
+ || element == StackElement::DeclareFunction
+ || element == StackElement::Template
+ || element == StackElement::OpaqueContainer;
+
+ if (!topLevel && m_stack.at(m_stack.size() - 2) == StackElement::Root) {
+ m_error = u"Tag requires parent: '"_s + tagName.toString() + u'\'';
+ return false;
+ }
+
+ switch (element) {
+ case StackElement::Root:
+ top->entry = parseRootElement(reader, versionRange.since, &attributes);
+ break;
+ case StackElement::LoadTypesystem:
+ if (!loadTypesystem(reader, &attributes))
+ return false;
+ break;
+ case StackElement::RejectEnumValue:
+ if (!parseRejectEnumValue(reader, &attributes))
+ return false;
+ break;
+ case StackElement::ReplaceType:
+ if (!parseReplaceArgumentType(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ConversionRule:
+ if (!TypeSystemParser::parseCustomConversion(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::NativeToTarget:
+ if (!parseNativeToTarget(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::TargetToNative: {
+ if (topElement != StackElement::ConversionRule) {
+ m_error = u"Target to Native conversions can only be specified for custom conversion rules."_s;
+ return false;
+ }
+
+ const auto topParent = m_stack.value(m_stack.size() - 3, StackElement::None);
+ if (isTypeEntry(topParent)) {
+ const auto replaceIndex = indexOfAttribute(attributes, replaceAttribute);
+ const bool replace = replaceIndex == -1
+ || convertBoolean(attributes.takeAt(replaceIndex).value(),
+ replaceAttribute, true);
+ auto customConversion = CustomConversion::getCustomConversion(top->entry);
+ if (!customConversion) {
+ m_error = msgMissingCustomConversion(top->entry);
+ return false;
+ }
+ customConversion->setReplaceOriginalTargetToNativeConversions(replace);
+ }
+ }
+ break;
+ case StackElement::AddConversion:
+ if (!parseAddConversion(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ModifyArgument:
+ if (!parseModifyArgument(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::NoNullPointers:
+ if (!parseNoNullPointer(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::DefineOwnership:
+ if (!parseDefineOwnership(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::SuppressedWarning: {
+ const auto textIndex = indexOfAttribute(attributes, textAttribute);
+ if (textIndex == -1) {
+ qCWarning(lcShiboken) << "Suppressed warning with no text specified";
+ } else {
+ const QString suppressedWarning =
+ attributes.takeAt(textIndex).value().toString();
+ if (!m_context->db->addSuppressedWarning(suppressedWarning,
+ m_generate == TypeEntry::GenerateCode,
+ &m_error)) {
+ return false;
+ }
+ }
+ }
+ break;
+ case StackElement::Rename:
+ if (!parseRename(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::RemoveArgument:
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"Removing argument requires argument modification as parent"_s;
+ return false;
+ }
+
+ top->functionMods.last().argument_mods().last().setRemoved(true);
+ break;
+
+ case StackElement::ModifyField:
+ if (!parseModifyField(reader, &attributes))
+ return false;
+ break;
+ case StackElement::DeclareFunction:
+ case StackElement::AddFunction:
+ if (!parseAddFunction(reader, topElement, element, &attributes))
+ return false;
+ break;
+ case StackElement::AddPyMethodDef:
+ if (!parseAddPyMethodDef(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::Property:
+ if (!parseProperty(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ModifyFunction:
+ if (!parseModifyFunction(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ReplaceDefaultExpression:
+ if (!parseReplaceDefaultExpression(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::RemoveDefaultExpression:
+ top->functionMods.last().argument_mods().last().setRemovedDefaultExpression(true);
+ break;
+ case StackElement::ReferenceCount:
+ if (!parseReferenceCount(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::ParentOwner:
+ if (!parseParentOwner(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::Array:
+ if (topElement != StackElement::ModifyArgument) {
+ m_error = u"array must be child of modify-argument"_s;
+ return false;
+ }
+ top->functionMods.last().argument_mods().last().setArray(true);
+ break;
+ case StackElement::InjectCode:
+ if (!parseInjectCode(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::Include:
+ if (!parseInclude(reader, topElement, top->entry, &attributes))
+ return false;
+ break;
+ case StackElement::Rejection:
+ if (!addRejection(m_context->db, m_generate == TypeEntry::GenerateCode,
+ &attributes, &m_error)) {
+ return false;
+ }
+ break;
+ case StackElement::SystemInclude:
+ if (!parseSystemInclude(reader, &attributes))
+ return false;
+ break;
+ case StackElement::Template: {
+ const auto nameIndex = indexOfAttribute(attributes, nameAttribute);
+ if (nameIndex == -1) {
+ m_error = msgMissingAttribute(nameAttribute);
+ return false;
+ }
+ m_templateEntry.reset(new TemplateEntry(attributes.takeAt(nameIndex).value().toString()));
+ }
+ break;
+ case StackElement::InsertTemplate:
+ m_templateInstance.reset(parseInsertTemplate(reader, topElement, &attributes));
+ if (!m_templateInstance)
+ return false;
+ break;
+ case StackElement::Replace:
+ if (!parseReplace(reader, topElement, &attributes))
+ return false;
+ break;
+ case StackElement::OpaqueContainer:
+ if (!parseOpaqueContainerElement(&attributes))
+ case StackElement::Configuration:
+ if (!parseConfiguration(topElement, &attributes))
+ return false;
+ break;
+ default:
+ break; // nada
+ }
+ }
+
+ if (!attributes.isEmpty()) {
+ const QString message = msgUnusedAttributes(tagName, attributes);
+ qCWarning(lcShiboken, "%s", qPrintable(msgReaderWarning(reader, message)));
+ }
+
+ return true;
+}