diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/typesystemparser.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/typesystemparser.cpp | 2646 |
1 files changed, 1531 insertions, 1115 deletions
diff --git a/sources/shiboken6/ApiExtractor/typesystemparser.cpp b/sources/shiboken6/ApiExtractor/typesystemparser.cpp index ea07b752e..2b686e997 100644 --- a/sources/shiboken6/ApiExtractor/typesystemparser.cpp +++ b/sources/shiboken6/ApiExtractor/typesystemparser.cpp @@ -1,38 +1,33 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "typesystemparser.h" +// 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> @@ -49,74 +44,103 @@ #include <optional> #include <memory> -static inline QString allowThreadAttribute() { return QStringLiteral("allow-thread"); } -static inline QString colonColon() { return QStringLiteral("::"); } -static inline QString checkFunctionAttribute() { return QStringLiteral("check-function"); } -static inline QString copyableAttribute() { return QStringLiteral("copyable"); } -static inline QString accessAttribute() { return QStringLiteral("access"); } -static inline QString actionAttribute() { return QStringLiteral("action"); } -static inline QString quoteAfterLineAttribute() { return QStringLiteral("quote-after-line"); } -static inline QString quoteBeforeLineAttribute() { return QStringLiteral("quote-before-line"); } -static inline QString textAttribute() { return QStringLiteral("text"); } -static inline QString nameAttribute() { return QStringLiteral("name"); } -static inline QString sinceAttribute() { return QStringLiteral("since"); } -static inline QString untilAttribute() { return QStringLiteral("until"); } -static inline QString defaultSuperclassAttribute() { return QStringLiteral("default-superclass"); } -static inline QString deleteInMainThreadAttribute() { return QStringLiteral("delete-in-main-thread"); } -static inline QString deprecatedAttribute() { return QStringLiteral("deprecated"); } -static inline QString disableWrapperAttribute() { return QStringLiteral("disable-wrapper"); } -static inline QString exceptionHandlingAttribute() { return QStringLiteral("exception-handling"); } -static inline QString extensibleAttribute() { return QStringLiteral("extensible"); } -static inline QString fileNameAttribute() { return QStringLiteral("file-name"); } -static inline QString flagsAttribute() { return QStringLiteral("flags"); } -static inline QString forceAbstractAttribute() { return QStringLiteral("force-abstract"); } -static inline QString forceIntegerAttribute() { return QStringLiteral("force-integer"); } -static inline QString formatAttribute() { return QStringLiteral("format"); } -static inline QString generateUsingAttribute() { return QStringLiteral("generate-using"); } -static inline QString classAttribute() { return QStringLiteral("class"); } -static inline QString generateAttribute() { return QStringLiteral("generate"); } -static inline QString generateGetSetDefAttribute() { return QStringLiteral("generate-getsetdef"); } -static inline QString genericClassAttribute() { return QStringLiteral("generic-class"); } -static inline QString indexAttribute() { return QStringLiteral("index"); } -static inline QString invalidateAfterUseAttribute() { return QStringLiteral("invalidate-after-use"); } -static inline QString isNullAttribute() { return QStringLiteral("isNull"); } -static inline QString locationAttribute() { return QStringLiteral("location"); } -static inline QString modifiedTypeAttribute() { return QStringLiteral("modified-type"); } -static inline QString operatorBoolAttribute() { return QStringLiteral("operator-bool"); } -static inline QString pyiTypeAttribute() { return QStringLiteral("pyi-type"); } -static inline QString overloadNumberAttribute() { return QStringLiteral("overload-number"); } -static inline QString ownershipAttribute() { return QStringLiteral("owner"); } -static inline QString packageAttribute() { return QStringLiteral("package"); } -static inline QString positionAttribute() { return QStringLiteral("position"); } -static inline QString preferredConversionAttribute() { return QStringLiteral("preferred-conversion"); } -static inline QString preferredTargetLangTypeAttribute() { return QStringLiteral("preferred-target-lang-type"); } -static inline QString removeAttribute() { return QStringLiteral("remove"); } -static inline QString renameAttribute() { return QStringLiteral("rename"); } -static inline QString readAttribute() { return QStringLiteral("read"); } -static inline QString targetLangNameAttribute() { return QStringLiteral("target-lang-name"); } -static inline QString writeAttribute() { return QStringLiteral("write"); } -static inline QString replaceAttribute() { return QStringLiteral("replace"); } -static inline QString toAttribute() { return QStringLiteral("to"); } -static inline QString signatureAttribute() { return QStringLiteral("signature"); } -static inline QString snippetAttribute() { return QStringLiteral("snippet"); } -static inline QString snakeCaseAttribute() { return QStringLiteral("snake-case"); } -static inline QString staticAttribute() { return QStringLiteral("static"); } -static inline QString classmethodAttribute() { return QStringLiteral("classmethod"); } -static inline QString threadAttribute() { return QStringLiteral("thread"); } -static inline QString sourceAttribute() { return QStringLiteral("source"); } -static inline QString streamAttribute() { return QStringLiteral("stream"); } -static inline QString privateAttribute() { return QStringLiteral("private"); } -static inline QString xPathAttribute() { return QStringLiteral("xpath"); } -static inline QString virtualSlotAttribute() { return QStringLiteral("virtual-slot"); } -static inline QString visibleAttribute() { return QStringLiteral("visible"); } -static inline QString enumIdentifiedByValueAttribute() { return QStringLiteral("identified-by-value"); } - -static inline QString noAttributeValue() { return QStringLiteral("no"); } -static inline QString yesAttributeValue() { return QStringLiteral("yes"); } -static inline QString trueAttributeValue() { return QStringLiteral("true"); } -static inline QString falseAttributeValue() { return QStringLiteral("false"); } - -static QList<CustomConversion *> customConversionsForReview; +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 "^..$" @@ -126,12 +150,12 @@ static bool setRejectionRegularExpression(const QString &patternIn, QString *errorMessage) { QString pattern; - if (patternIn.startsWith(QLatin1Char('^')) && patternIn.endsWith(QLatin1Char('$'))) + if (patternIn.startsWith(u'^') && patternIn.endsWith(u'$')) pattern = patternIn; - else if (patternIn == QLatin1String("*")) - pattern = QStringLiteral("^.*$"); + else if (patternIn == u"*") + pattern = "^.*$"_L1; else - pattern = QLatin1Char('^') + QRegularExpression::escape(patternIn) + QLatin1Char('$'); + pattern = u'^' + QRegularExpression::escape(patternIn) + u'$'; re->setPattern(pattern); if (!re->isValid()) { *errorMessage = msgInvalidRegularExpression(patternIn, re->errorString()); @@ -140,78 +164,66 @@ static bool setRejectionRegularExpression(const QString &patternIn, 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 = QStringLiteral(R"(^\s*//\s*@snippet\s+)") + const QString pattern = R"(^\s*//\s*@snippet\s+)"_L1 + QRegularExpression::escape(snippetLabel) - + QStringLiteral(R"(\s*$)"); + + 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(QLatin1Char('\n')); + const auto lines = QStringView{code}.split(u'\n'); for (const auto &line : lines) { - if (snippetRe.match(line).hasMatch()) { + if (snippetRe.matchView(line).hasMatch()) { foundLabel = true; useLine = !useLine; if (!useLine) break; // End of snippet reached } else if (useLine) - result += line.toString() + QLatin1Char('\n'); + result += line.toString() + u'\n'; } if (!foundLabel) return {}; return CodeSnipAbstract::fixSpaces(result); } -template <class EnumType, Qt::CaseSensitivity cs = Qt::CaseInsensitive> +template <class EnumType> struct EnumLookup { QStringView name; EnumType value; }; -template <class EnumType, Qt::CaseSensitivity cs> -bool operator==(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &e2) -{ - return e1.name.compare(e2.name, cs) == 0; -} - -template <class EnumType, Qt::CaseSensitivity cs> -bool operator<(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &e2) -{ - return e1.name.compare(e2.name, cs) < 0; -} - // 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, caseSensitivity>; \ - static const HaystackEntry haystack[] = - -#define ENUM_LOOKUP_LINEAR_SEARCH() \ - const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ - const auto it = std::find(haystack, end, HaystackEntry{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 {}; \ -} - -#define ENUM_LOOKUP_BINARY_SEARCH() \ - const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ - const HaystackEntry needleEntry{needle, {} }; \ - const auto lb = std::lower_bound(haystack, end, needleEntry); \ - if (lb != end && *lb == needleEntry) \ - return lb->value; \ - return {}; \ + return std::nullopt; \ } ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive, @@ -223,7 +235,7 @@ ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive, {u"no", TypeSystem::AllowThread::Disallow}, {u"false", TypeSystem::AllowThread::Disallow}, }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(TypeSystem::BoolCast, Qt::CaseInsensitive, @@ -234,7 +246,28 @@ ENUM_LOOKUP_BEGIN(TypeSystem::BoolCast, Qt::CaseInsensitive, {u"no", TypeSystem::BoolCast::Disabled}, {u"false", TypeSystem::BoolCast::Disabled}, }; -ENUM_LOOKUP_LINEAR_SEARCH() +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) @@ -244,7 +277,7 @@ ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive, {u"shell", TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe {u"target", TypeSystem::TargetLangCode} // em algum lugar do cpp }; -ENUM_LOOKUP_BINARY_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive, ownershipFromFromAttribute) @@ -253,7 +286,7 @@ ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive, {u"c++", TypeSystem::CppOwnership}, {u"default", TypeSystem::DefaultOwnership} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive, addedFunctionAccessFromAttribute) @@ -261,7 +294,7 @@ ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive, {u"public", AddedFunction::Public}, {u"protected", AddedFunction::Protected}, }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(FunctionModification::ModifierFlag, Qt::CaseSensitive, modifierFromAttribute) @@ -269,12 +302,11 @@ ENUM_LOOKUP_BEGIN(FunctionModification::ModifierFlag, Qt::CaseSensitive, {u"private", FunctionModification::Private}, {u"public", FunctionModification::Public}, {u"protected", FunctionModification::Protected}, - {u"friendly", FunctionModification::Friendly}, {u"rename", FunctionModification::Rename}, {u"final", FunctionModification::Final}, {u"non-final", FunctionModification::NonFinal} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive, referenceCountFromAttribute) @@ -285,7 +317,7 @@ ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive, {u"set", ReferenceCount::Set}, {u"ignore", ReferenceCount::Ignore} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive, argumentOwnerActionFromAttribute) @@ -293,16 +325,17 @@ ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive, {u"add", ArgumentOwner::Add}, {u"remove", ArgumentOwner::Remove} }; -ENUM_LOOKUP_LINEAR_SEARCH() +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"declaration", TypeSystem::CodeSnipPositionDeclaration}, + {u"override", TypeSystem::CodeSnipPositionPyOverride} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive, locationFromAttribute) @@ -311,7 +344,7 @@ ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive, {u"local", Include::LocalPath}, {u"target", Include::TargetLangImport} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive, docModificationFromAttribute) @@ -320,7 +353,7 @@ ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive, {u"prepend", TypeSystem::DocModificationPrepend}, {u"replace", TypeSystem::DocModificationReplace} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(ContainerTypeEntry::ContainerKind, Qt::CaseSensitive, containerTypeFromAttribute) @@ -336,9 +369,10 @@ ENUM_LOOKUP_BEGIN(ContainerTypeEntry::ContainerKind, Qt::CaseSensitive, {u"multi-map", ContainerTypeEntry::MultiMapContainer}, {u"hash", ContainerTypeEntry::MapContainer}, {u"multi-hash", ContainerTypeEntry::MultiMapContainer}, - {u"pair", ContainerTypeEntry::PairContainer} + {u"pair", ContainerTypeEntry::PairContainer}, + {u"span", ContainerTypeEntry::SpanContainer} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive, typeRejectionFromAttribute) @@ -350,7 +384,7 @@ ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive, {u"argument-type", TypeRejection::ArgumentType}, {u"return-type", TypeRejection::ReturnType} }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive, exceptionHandlingFromAttribute) @@ -362,28 +396,61 @@ ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive, {u"yes", TypeSystem::ExceptionHandling::On}, {u"true", TypeSystem::ExceptionHandling::On}, }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH -ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, - elementFromTag) - { - {u"add-conversion", StackElement::AddConversion}, // sorted! +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::CustomMetaConstructor}, - {u"custom-destructor", StackElement::CustomMetaDestructor}, + {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::TemplateInstanceEnum}, + {u"insert-template", StackElement::InsertTemplate}, {u"interface-type", StackElement::InterfaceTypeEntry}, {u"load-typesystem", StackElement::LoadTypesystem}, {u"modify-argument", StackElement::ModifyArgument}, @@ -394,6 +461,7 @@ ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, {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}, @@ -415,8 +483,30 @@ ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, {u"typesystem", StackElement::Root}, {u"value-type", StackElement::ValueTypeEntry}, }; -ENUM_LOOKUP_BINARY_SEARCH() + 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) @@ -427,7 +517,7 @@ ENUM_LOOKUP_BEGIN(TypeSystem::SnakeCase, Qt::CaseSensitive, {u"true", TypeSystem::SnakeCase::Enabled}, {u"both", TypeSystem::SnakeCase::Both}, }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH ENUM_LOOKUP_BEGIN(TypeSystem::Visibility, Qt::CaseSensitive, visibilityFromAttribute) @@ -438,12 +528,12 @@ ENUM_LOOKUP_BEGIN(TypeSystem::Visibility, Qt::CaseSensitive, {u"yes", TypeSystem::Visibility::Visible}, {u"true", TypeSystem::Visibility::Visible}, }; -ENUM_LOOKUP_LINEAR_SEARCH() +ENUM_LOOKUP_LINEAR_SEARCH static int indexOfAttribute(const QXmlStreamAttributes &atts, - QStringView name) + QAnyStringView name) { - for (int i = 0, size = atts.size(); i < size; ++i) { + for (qsizetype i = 0, size = atts.size(); i < size; ++i) { if (atts.at(i).qualifiedName() == name) return i; } @@ -452,8 +542,8 @@ static int indexOfAttribute(const QXmlStreamAttributes &atts, static QString msgMissingAttribute(const QString &a) { - return QLatin1String("Required attribute '") + a - + QLatin1String("' missing."); + return u"Required attribute '"_s + a + + u"' missing."_s; } QTextStream &operator<<(QTextStream &str, const QXmlStreamAttribute &attribute) @@ -474,7 +564,7 @@ static QString msgUnusedAttributes(QStringView tag, const QXmlStreamAttributes & QString result; QTextStream str(&result); str << attributes.size() << " attributes(s) unused on <" << tag << ">: "; - for (int i = 0, size = attributes.size(); i < size; ++i) { + for (qsizetype i = 0, size = attributes.size(); i < size; ++i) { if (i) str << ", "; str << attributes.at(i); @@ -496,31 +586,30 @@ private: QString readFile(const QString &entityName, QString *errorMessage) const; const QString m_currentPath; - QHash<QString, QString> m_cache; }; QString TypeSystemEntityResolver::readFile(const QString &entityName, QString *errorMessage) const { QString fileName = entityName; - if (!fileName.contains(QLatin1Char('.'))) - fileName += QLatin1String(".xml"); + 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(QLatin1String("typesystem_")); + fileName.prepend(u"typesystem_"_s); path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath); if (!QFileInfo::exists(path)) { - *errorMessage = QLatin1String("Unable to resolve: ") + entityName; - return QString(); + *errorMessage = u"Unable to resolve: "_s + entityName; + return {}; } QFile file(path); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { *errorMessage = msgCannotOpenForReading(file); - return QString(); + return {}; } QString result = QString::fromUtf8(file.readAll()).trimmed(); // Remove license header comments on which QXmlStreamReader chokes - if (result.startsWith(QLatin1String("<!--"))) { - const int commentEnd = result.indexOf(QLatin1String("-->")); + if (result.startsWith(u"<!--")) { + const auto commentEnd = result.indexOf(u"-->"); if (commentEnd != -1) { result.remove(0, commentEnd + 3); result = result.trimmed(); @@ -531,20 +620,33 @@ QString TypeSystemEntityResolver::readFile(const QString &entityName, QString *e QString TypeSystemEntityResolver::resolveUndeclaredEntity(const QString &name) { - auto it = m_cache.find(name); - if (it == m_cache.end()) { - QString errorMessage; - it = m_cache.insert(name, readFile(name, &errorMessage)); - if (it.value().isEmpty()) { // The parser will fail and display the line number. - qCWarning(lcShiboken, "%s", - qPrintable(msgCannotResolveEntity(name, errorMessage))); - } + 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 it.value(); + return result; } -TypeSystemParser::TypeSystemParser(TypeDatabase *database, bool generate) : - m_database(database), +// 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) { } @@ -585,7 +687,7 @@ static QString msgReaderError(const ConditionalStreamReader &reader, const QStri } static QString msgUnimplementedElementWarning(const ConditionalStreamReader &reader, - QStringView name) + QAnyStringView name) { QString message; QTextStream(&message) << "The element \"" << name @@ -610,7 +712,7 @@ static inline QString msgUnimplementedAttributeWarning(const ConditionalStreamRe static QString msgUnimplementedAttributeValueWarning(const ConditionalStreamReader &reader, - QStringView name, QStringView value) + QAnyStringView name, QAnyStringView value) { QString message; QTextStream(&message) << "The value \"" << value @@ -627,21 +729,22 @@ static inline attribute.value()); } -static bool addRejection(TypeDatabase *database, QXmlStreamAttributes *attributes, +static bool addRejection(TypeDatabase *database, bool generate, QXmlStreamAttributes *attributes, QString *errorMessage) { - const int classIndex = indexOfAttribute(*attributes, classAttribute()); + const auto classIndex = indexOfAttribute(*attributes, classAttribute); if (classIndex == -1) { - *errorMessage = msgMissingAttribute(classAttribute()); + *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 (int i = attributes->size() - 1; i >= 0; --i) { + 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); @@ -668,9 +771,9 @@ static bool addRejection(TypeDatabase *database, QXmlStreamAttributes *attribute } // Special case: When all fields except class are empty, completely exclude class - if (className == QLatin1String("*")) { - *errorMessage = QLatin1String("bad reject entry, neither 'class', 'function-name'" - " nor 'field' specified"); + if (className == u"*") { + *errorMessage = u"bad reject entry, neither 'class', 'function-name'" + " nor 'field' specified"_s; return false; } rejection.matchType = TypeRejection::ExcludeClass; @@ -683,10 +786,7 @@ bool TypeSystemParser::parse(ConditionalStreamReader &reader) m_error.clear(); m_currentPath.clear(); m_currentFile.clear(); - m_smartPointerInstantiations.clear(); - const bool result = parseXml(reader) && setupSmartPointerInstantiations(); - m_smartPointerInstantiations.clear(); - return result; + return parseXml(reader); } bool TypeSystemParser::parseXml(ConditionalStreamReader &reader) @@ -706,18 +806,25 @@ bool TypeSystemParser::parseXml(ConditionalStreamReader &reader) case QXmlStreamReader::Invalid: m_error = msgReaderError(reader, reader.errorString()); return false; - case QXmlStreamReader::StartElement: - if (!startElement(reader)) { + 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(reader.name())) { + if (!endElement(m_stack.top())) { m_error = msgReaderError(reader, m_error); return false; } + m_stack.pop(); break; case QXmlStreamReader::Characters: if (!characters(reader.text())) { @@ -737,315 +844,302 @@ bool TypeSystemParser::parseXml(ConditionalStreamReader &reader) return true; } -// Split a type list potentially with template types -// "A<B,C>,D" -> ("A<B,C>", "D") -static QStringList splitTypeList(const QString &s) -{ - QStringList result; - int templateDepth = 0; - int lastPos = 0; - const int size = s.size(); - for (int i = 0; i < size; ++i) { - switch (s.at(i).toLatin1()) { - case '<': - ++templateDepth; - break; - case '>': - --templateDepth; - break; - case ',': - if (templateDepth == 0) { - result.append(s.mid(lastPos, i - lastPos).trimmed()); - lastPos = i + 1; - } - break; - } - } - if (lastPos < size) - result.append(s.mid(lastPos, size - lastPos).trimmed()); - return result; -} - -bool TypeSystemParser::setupSmartPointerInstantiations() -{ - for (auto it = m_smartPointerInstantiations.cbegin(), - end = m_smartPointerInstantiations.cend(); it != end; ++it) { - auto smartPointerEntry = it.key(); - const auto instantiationNames = splitTypeList(it.value()); - SmartPointerTypeEntry::Instantiations instantiations; - instantiations.reserve(instantiationNames.size()); - for (const auto &instantiationName : instantiationNames) { - const auto types = m_database->findCppTypes(instantiationName); - if (types.isEmpty()) { - m_error = - msgCannotFindTypeEntryForSmartPointer(instantiationName, - smartPointerEntry->name()); - return false; - } - if (types.size() > 1) { - m_error = msgAmbiguousTypesFound(instantiationName, types); - return false; - } - instantiations.append(types.constFirst()); - } - smartPointerEntry->setInstantiations(instantiations); - } - return true; -} - -bool TypeSystemParser::endElement(QStringView localName) +bool TypeSystemParser::endElement(StackElement element) { if (m_ignoreDepth) { --m_ignoreDepth; return true; } - if (m_currentDroppedEntry) { - if (m_currentDroppedEntryDepth == 1) { - m_current = m_currentDroppedEntry->parent; - delete m_currentDroppedEntry; - m_currentDroppedEntry = nullptr; - m_currentDroppedEntryDepth = 0; - } else { - --m_currentDroppedEntryDepth; - } + if (m_currentDroppedEntryDepth != 0) { + --m_currentDroppedEntryDepth; return true; } - if (!localName.compare(QLatin1String("import-file"), Qt::CaseInsensitive)) + if (element == StackElement::ImportFile) return true; - if (!m_current) + if (m_contextStack.isEmpty()) return true; - switch (m_current->type) { + const auto &top = m_contextStack.top(); + + switch (element) { + case StackElement::Unimplemented: + return true; case StackElement::Root: if (m_generate == TypeEntry::GenerateCode) { - TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions); - TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods); - for (CustomConversion *customConversion : qAsConst(customConversionsForReview)) { - const CustomConversion::TargetToNativeConversions &toNatives = customConversion->targetToNativeConversions(); - for (CustomConversion::TargetToNativeConversion *toNative : toNatives) - toNative->setSourceType(m_database->findType(toNative->sourceTypeName())); + 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: { - auto *centry = static_cast<ComplexTypeEntry *>(m_current->entry); - auto top = m_contextStack.top(); + Q_ASSERT(top->entry); + Q_ASSERT(top->entry->isComplex()); + auto centry = std::static_pointer_cast<ComplexTypeEntry>(top->entry); + purgeEmptyCodeSnips(¢ry->codeSnips()); centry->setAddedFunctions(top->addedFunctions); centry->setFunctionModifications(top->functionMods); centry->setFieldModifications(top->fieldMods); - centry->setCodeSnips(top->codeSnips); centry->setDocModification(top->docModifications); } break; case StackElement::TypedefTypeEntry: { - auto *centry = static_cast<TypedefEntry *>(m_current->entry)->target(); - auto top = m_contextStack.top(); + 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->setCodeSnips(centry->codeSnips() + top->codeSnips); 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::AddFunction: + case StackElement::DeclareFunction: { // Leaving add-function: Assign all modifications to the added function - StackElementContext *top = m_contextStack.top(); 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)); + top->addedFunctions.last()->modifications().append(top->functionMods.takeAt(modIndex)); } - break; + break; case StackElement::NativeToTarget: - case StackElement::AddConversion: { - CustomConversion* customConversion = static_cast<TypeEntry*>(m_current->entry)->customConversion(); - if (!customConversion) { - m_error = QLatin1String("CustomConversion object is missing."); - return false; - } - - QString code = m_contextStack.top()->codeSnips.takeLast().code(); - if (m_current->type == StackElement::AddConversion) { - if (customConversion->targetToNativeConversions().isEmpty()) { - m_error = QLatin1String("CustomConversion's target to native conversions missing."); + 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; } - customConversion->targetToNativeConversions().last()->setConversion(code); - } else { - customConversion->setNativeToTargetConversion(code); - } - } - break; - case StackElement::CustomMetaConstructor: { - m_current->entry->setCustomConstructor(*m_current->value.customFunction); - delete m_current->value.customFunction; - } - break; - case StackElement::CustomMetaDestructor: { - m_current->entry->setCustomDestructor(*m_current->value.customFunction); - delete m_current->value.customFunction; - } - break; - case StackElement::EnumTypeEntry: - m_current->entry->setDocModification(m_contextStack.top()->docModifications); - m_contextStack.top()->docModifications = DocModificationList(); - m_currentEnum = nullptr; - break; - case StackElement::Template: - m_database->addTemplate(m_current->value.templateEntry); - break; - case StackElement::TemplateInstanceEnum: - switch (m_current->parent->type) { - case StackElement::InjectCode: - if (m_current->parent->parent->type == StackElement::Root) { - CodeSnipList snips = m_current->parent->entry->codeSnips(); - CodeSnip snip = snips.takeLast(); - snip.addTemplateInstance(m_current->value.templateInstance); - snips.append(snip); - m_current->parent->entry->setCodeSnips(snips); - break; + 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); } - Q_FALLTHROUGH(); - case StackElement::NativeToTarget: - case StackElement::AddConversion: - m_contextStack.top()->codeSnips.last().addTemplateInstance(m_current->value.templateInstance); - break; - case StackElement::Template: - m_current->parent->value.templateEntry->addTemplateInstance(m_current->value.templateInstance); - break; - case StackElement::CustomMetaConstructor: - case StackElement::CustomMetaDestructor: - m_current->parent->value.customFunction->addTemplateInstance(m_current->value.templateInstance); + } break; - case StackElement::ConversionRule: - m_contextStack.top()->functionMods.last().argument_mods().last().conversionRules().last().addTemplateInstance(m_current->value.templateInstance); + + 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 StackElement::InjectCodeInFunction: - m_contextStack.top()->functionMods.last().snips().last().addTemplateInstance(m_current->value.templateInstance); + 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; // nada + 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; } - switch (m_current->type) { - case StackElement::Root: - case StackElement::NamespaceTypeEntry: - case StackElement::InterfaceTypeEntry: - case StackElement::ObjectTypeEntry: - case StackElement::ValueTypeEntry: - case StackElement::PrimitiveTypeEntry: - case StackElement::TypedefTypeEntry: - case StackElement::ContainerTypeEntry: - delete m_contextStack.pop(); + 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; } - StackElement *child = m_current; - m_current = m_current->parent; - delete(child); + return ParserState::None; +} - return true; +// 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) { - if (m_currentDroppedEntry || m_ignoreDepth) - return true; - - if (m_current->type == StackElement::Template) { - m_current->value.templateEntry->addCode(ch); + const auto stackSize = m_stack.size(); + if (m_currentDroppedEntryDepth != 0 || m_ignoreDepth != 0 + || stackSize == 0 || m_stack.top() == StackElement::Unimplemented) { return true; } - if (m_current->type == StackElement::CustomMetaConstructor || m_current->type == StackElement::CustomMetaDestructor) { - m_current->value.customFunction->addCode(ch); - return true; - } + const StackElement type = m_stack.top(); - if (m_current->type == StackElement::ConversionRule - && m_current->parent->type == StackElement::ModifyArgument) { - m_contextStack.top()->functionMods.last().argument_mods().last().conversionRules().last().addCode(ch); + if (type == StackElement::Template) { + m_templateEntry->addCode(ch); return true; } - if (m_current->type == StackElement::NativeToTarget || m_current->type == StackElement::AddConversion) { - m_contextStack.top()->codeSnips.last().addCode(ch); - return true; + if (m_contextStack.isEmpty()) { + m_error = msgNoRootTypeSystemEntry(); + return false; } - if (m_current->parent) { - if ((m_current->type & StackElement::CodeSnipMask)) { - CodeSnipList snips; - switch (m_current->parent->type) { - case StackElement::Root: - snips = m_current->parent->entry->codeSnips(); - snips.last().addCode(ch); - m_current->parent->entry->setCodeSnips(snips); - break; - case StackElement::ModifyFunction: - case StackElement::AddFunction: - m_contextStack.top()->functionMods.last().snips().last().addCode(ch); - m_contextStack.top()->functionMods.last().setModifierFlag(FunctionModification::CodeInjection); - break; - case StackElement::NamespaceTypeEntry: - case StackElement::ObjectTypeEntry: - case StackElement::ValueTypeEntry: - case StackElement::InterfaceTypeEntry: - m_contextStack.top()->codeSnips.last().addCode(ch); - break; - default: - Q_ASSERT(false); - } - return true; - } + if (auto *snip = injectCodeTarget()) { + snip->addCode(ch); + return true; } - if (m_current->type & StackElement::DocumentationMask) - m_contextStack.top()->docModifications.last().setCode(ch); + 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(); + const QString fileName = atts.value(nameAttribute).toString(); if (fileName.isEmpty()) { - m_error = QLatin1String("Required attribute 'name' missing for include-file tag."); + 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(QLatin1String(":/trolltech/generator/") + fileName); + 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()); + const auto quoteFrom = atts.value(quoteAfterLineAttribute); bool foundFromOk = quoteFrom.isEmpty(); bool from = quoteFrom.isEmpty(); - const auto quoteTo = atts.value(quoteBeforeLineAttribute()); + const auto quoteTo = atts.value(quoteBeforeLineAttribute); bool foundToOk = quoteTo.isEmpty(); bool to = true; @@ -1058,69 +1152,67 @@ bool TypeSystemParser::importFileElement(const QXmlStreamAttributes &atts) break; } if (from && to) - characters(line + QLatin1Char('\n')); + characters(line + u'\n'); if (!from && line.contains(quoteFrom)) { from = true; foundFromOk = true; } } if (!foundFromOk || !foundToOk) { - QString fromError = QStringLiteral("Could not find quote-after-line='%1' in file '%2'.") - .arg(quoteFrom.toString(), fileName); - QString toError = QStringLiteral("Could not find quote-before-line='%1' in file '%2'.") - .arg(quoteTo.toString(), fileName); + 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 + QLatin1Char(' ') + toError; + m_error = fromError + u' ' + toError; return false; } return true; } -static bool convertBoolean(QStringView value, const QString &attributeName, bool defaultValue) +static bool convertBoolean(QStringView value, QAnyStringView attributeName, bool defaultValue) { - if (value.compare(trueAttributeValue(), Qt::CaseInsensitive) == 0 - || value.compare(yesAttributeValue(), Qt::CaseInsensitive) == 0) { + 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) { + if (value.compare(falseAttributeValue, Qt::CaseInsensitive) == 0 + || value.compare(noAttributeValue, Qt::CaseInsensitive) == 0) { return false; } - const QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.") - .arg(value) - .arg(attributeName, - defaultValue ? yesAttributeValue() : noAttributeValue()); - - qCWarning(lcShiboken).noquote().nospace() << warn; + 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); + || 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 StackElement *element, + const TypeSystemParser::ContextStack &stack , QString name) { - for (auto e = element->parent; e ; e = e->parent) { - if (e->entry) { - if (e->entry->type() == TypeEntry::TypeSystemType) { + 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(QLatin1Char('.')); - name.prepend(e->entry->name()); + name.prepend(u'.'); + name.prepend(entry->name()); } } return db->shouldDropTypeEntry(name); @@ -1129,10 +1221,10 @@ static bool shouldDropTypeEntry(const TypeDatabase *db, // Returns empty string if there's no error. static QString checkSignatureError(const QString& signature, const QString& tag) { - QString funcName = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); - static const QRegularExpression whiteSpace(QStringLiteral("\\s")); + QString funcName = signature.left(signature.indexOf(u'(')).trimmed(); + static const QRegularExpression whiteSpace("\\s"_L1); Q_ASSERT(whiteSpace.isValid()); - if (!funcName.startsWith(QLatin1String("operator ")) && funcName.contains(whiteSpace)) { + 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.") @@ -1141,20 +1233,24 @@ static QString checkSignatureError(const QString& signature, const QString& tag) return QString(); } -inline const TypeEntry *TypeSystemParser::currentParentTypeEntry() const +inline TypeEntryCPtr TypeSystemParser::currentParentTypeEntry() const { - return m_current ? m_current->entry : nullptr; + const auto size = m_contextStack.size(); + return size > 1 ? m_contextStack.at(size - 2)->entry : nullptr; } bool TypeSystemParser::checkRootElement() { - const bool ok = currentParentTypeEntry() != nullptr; - if (!ok) - m_error = msgNoRootTypeSystemEntry(); - return ok; + 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 TypeEntry *findViewedType(const QString &name) +static TypeEntryPtr findViewedType(const QString &name) { const auto range = TypeDatabase::instance()->entries().equal_range(name); for (auto i = range.first; i != range.second; ++i) { @@ -1171,20 +1267,21 @@ static TypeEntry *findViewedType(const QString &name) return nullptr; } -bool TypeSystemParser::applyCommonAttributes(const ConditionalStreamReader &reader, TypeEntry *type, +bool TypeSystemParser::applyCommonAttributes(const ConditionalStreamReader &reader, + const TypeEntryPtr &type, QXmlStreamAttributes *attributes) { type->setSourceLocation(SourceLocation(m_currentFile, reader.lineNumber())); type->setCodeGeneration(m_generate); - for (int i = attributes->size() - 1; i >= 0; --i) { + 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(); - TypeEntry *views = findViewedType(name); - if (views == nullptr) { + TypeEntryPtr views = findViewedType(name); + if (!views) { m_error = msgCannotFindView(name, type->name()); return false; } @@ -1194,61 +1291,62 @@ bool TypeSystemParser::applyCommonAttributes(const ConditionalStreamReader &read return true; } -CustomTypeEntry *TypeSystemParser::parseCustomTypeEntry(const ConditionalStreamReader &, +CustomTypeEntryPtr TypeSystemParser::parseCustomTypeEntry(const ConditionalStreamReader &, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *attributes) { if (!checkRootElement()) return nullptr; - auto *result = new CustomTypeEntry(name, since, m_current->entry); - for (int i = attributes->size() - 1; i >= 0; --i) { + 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()) + if (name == checkFunctionAttribute) result->setCheckFunction(attributes->takeAt(i).value().toString()); } return result; } -FlagsTypeEntry * +FlagsTypeEntryPtr TypeSystemParser::parseFlagsEntry(const ConditionalStreamReader &reader, - EnumTypeEntry *enumEntry, QString flagName, + const EnumTypeEntryPtr &enumEntry, QString flagName, const QVersionNumber &since, QXmlStreamAttributes *attributes) { if (!checkRootElement()) return nullptr; - auto ftype = new FlagsTypeEntry(QLatin1String("QFlags<") + enumEntry->name() + QLatin1Char('>'), - since, - currentParentTypeEntry()->typeSystemTypeEntry()); + 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(colonColon())) { + if (!flagName.contains(u"::"_s)) { auto eq = enumEntry->qualifier(); if (!eq.isEmpty()) - flagName.prepend(eq + colonColon()); + flagName.prepend(eq + u"::"_s); } ftype->setOriginalName(flagName); if (!applyCommonAttributes(reader, ftype, attributes)) return nullptr; - QStringList lst = flagName.split(colonColon()); - const QString targetLangFlagName = QStringList(lst.mid(0, lst.size() - 1)).join(QLatin1Char('.')); + 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).noquote().nospace() - << QStringLiteral("enum %1 and flags %2 (%3) differ in qualifiers") - .arg(targetLangQualifier, lst.constFirst(), targetLangFlagName); + qCWarning(lcShiboken, "enum %s and flags %s (%s) differ in qualifiers", + qPrintable(targetLangQualifier), qPrintable(lst.value(0)), + qPrintable(targetLangFlagName)); } - ftype->setFlagsName(lst.constLast()); + ftype->setFlagsName(name); enumEntry->setFlags(ftype); - m_database->addFlagsType(ftype); - m_database->addType(ftype); + m_context->db->addFlagsType(ftype); + m_context->db->addType(ftype); const int revisionIndex = indexOfAttribute(*attributes, u"flags-revision"); @@ -1258,159 +1356,259 @@ FlagsTypeEntry * return ftype; } -SmartPointerTypeEntry * +SmartPointerTypeEntryPtr TypeSystemParser::parseSmartPointerEntry(const ConditionalStreamReader &reader, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *attributes) { if (!checkRootElement()) return nullptr; - QString smartPointerType; + TypeSystem::SmartPointerType smartPointerType = TypeSystem::SmartPointerType::Shared; QString getter; QString refCountMethodName; + QString valueCheckMethod; + QString nullCheckMethod; + QString resetMethod; QString instantiations; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("type")) { - smartPointerType = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("getter")) { + 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 == QLatin1String("ref-count-method")) { + } else if (name == u"ref-count-method") { refCountMethodName = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("instantiations")) { + } 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 (smartPointerType.isEmpty()) { - m_error = QLatin1String("No type specified for the smart pointer. Currently supported types: 'shared',"); - return nullptr; - } - if (smartPointerType != QLatin1String("shared")) { - m_error = QLatin1String("Currently only the 'shared' type is supported."); - return nullptr; - } - if (getter.isEmpty()) { - m_error = QLatin1String("No function getter name specified for getting the raw pointer held by the smart pointer."); + m_error = u"No function getter name specified for getting the raw pointer held by the smart pointer."_s; return nullptr; } - QString signature = getter + QLatin1String("()"); + QString signature = getter + u"()"_s; signature = TypeDatabase::normalizedSignature(signature); if (signature.isEmpty()) { - m_error = QLatin1String("No signature for the smart pointer getter found."); + m_error = u"No signature for the smart pointer getter found."_s; return nullptr; } QString errorString = checkSignatureError(signature, - QLatin1String("smart-pointer-type")); + u"smart-pointer-type"_s); if (!errorString.isEmpty()) { m_error = errorString; return nullptr; } - auto *type = new SmartPointerTypeEntry(name, getter, smartPointerType, - refCountMethodName, since, currentParentTypeEntry()); + 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; - m_smartPointerInstantiations.insert(type, instantiations); + applyComplexTypeAttributes(reader, type, attributes); + type->setNullCheckMethod(nullCheckMethod); + type->setValueCheckMethod(valueCheckMethod); + type->setResetMethod(resetMethod); + m_context->smartPointerInstantiations.insert(type, instantiations); return type; } -PrimitiveTypeEntry * +PrimitiveTypeEntryPtr TypeSystemParser::parsePrimitiveTypeEntry(const ConditionalStreamReader &reader, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *attributes) { if (!checkRootElement()) return nullptr; - auto *type = new PrimitiveTypeEntry(name, since, currentParentTypeEntry()); + auto type = std::make_shared<PrimitiveTypeEntry>(name, since, currentParentTypeEntry()); + QString targetLangApiName; if (!applyCommonAttributes(reader, type, attributes)) return nullptr; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == targetLangNameAttribute()) { + if (name == targetLangNameAttribute) { type->setTargetLangName(attributes->takeAt(i).value().toString()); - } else if (name == QLatin1String("target-lang-api-name")) { - type->setTargetLangApiName(attributes->takeAt(i).value().toString()); - } else if (name == preferredConversionAttribute()) { + } 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()) { + } else if (name == preferredTargetLangTypeAttribute) { const bool v = convertBoolean(attributes->takeAt(i).value(), - preferredTargetLangTypeAttribute(), true); + preferredTargetLangTypeAttribute, true); type->setPreferredTargetLangType(v); - } else if (name == QLatin1String("default-constructor")) { + } else if (name == u"default-constructor") { type->setDefaultConstructor(attributes->takeAt(i).value().toString()); } } - if (type->targetLangApiName().isEmpty()) - type->setTargetLangApiName(type->name()); + 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; } -ContainerTypeEntry * +// "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 int typeIndex = indexOfAttribute(*attributes, u"type"); + const auto typeIndex = indexOfAttribute(*attributes, u"type"); if (typeIndex == -1) { - m_error = QLatin1String("no 'type' attribute specified"); + m_error = u"no 'type' attribute specified"_s; return nullptr; } - const auto typeName = attributes->takeAt(typeIndex).value(); + const auto typeName = attributes->at(typeIndex).value(); const auto containerTypeOpt = containerTypeFromAttribute(typeName); if (!containerTypeOpt.has_value()) { - m_error = QLatin1String("there is no container of type ") + typeName.toString(); + m_error = u"there is no container of type "_s + typeName.toString(); return nullptr; } - auto *type = new ContainerTypeEntry(name, containerTypeOpt.value(), - since, currentParentTypeEntry()); + 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; } -EnumTypeEntry * +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 = new EnumTypeEntry(name, since, currentParentTypeEntry()); + auto entry = std::make_shared<EnumTypeEntry>(name, since, currentParentTypeEntry()); applyCommonAttributes(reader, entry, attributes); entry->setTargetLangPackage(m_defaultPackage); QString flagNames; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("upper-bound")) { + if (name == u"upper-bound") { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == QLatin1String("lower-bound")) { + } else if (name == u"lower-bound") { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == forceIntegerAttribute()) { + } 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 == extensibleAttribute()) { + } 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()) { + } else if (name == flagsAttribute) { flagNames = attributes->takeAt(i).value().toString(); } } // put in the flags parallel... if (!flagNames.isEmpty()) { - const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); + const QStringList &flagNameList = flagNames.split(u','); for (const QString &flagName : flagNameList) parseFlagsEntry(reader, entry, flagName.trimmed(), since, attributes); } @@ -1418,19 +1616,19 @@ EnumTypeEntry * } -NamespaceTypeEntry * +NamespaceTypeEntryPtr TypeSystemParser::parseNamespaceTypeEntry(const ConditionalStreamReader &reader, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *attributes) { if (!checkRootElement()) return nullptr; - std::unique_ptr<NamespaceTypeEntry> result(new NamespaceTypeEntry(name, since, currentParentTypeEntry())); + auto result = std::make_shared<NamespaceTypeEntry>(name, since, currentParentTypeEntry()); auto visibility = TypeSystem::Visibility::Unspecified; - applyCommonAttributes(reader, result.get(), attributes); - for (int i = attributes->size() - 1; i >= 0; --i) { + applyCommonAttributes(reader, result, attributes); + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto attributeName = attributes->at(i).qualifiedName(); - if (attributeName == QLatin1String("files")) { + if (attributeName == u"files") { const QString pattern = attributes->takeAt(i).value().toString(); QRegularExpression re(pattern); if (!re.isValid()) { @@ -1438,11 +1636,11 @@ NamespaceTypeEntry * return nullptr; } result->setFilePattern(re); - } else if (attributeName == QLatin1String("extends")) { - const auto extendsPackageName = attributes->takeAt(i).value(); + } 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 NamespaceTypeEntry *e) { + [extendsPackageName] (const NamespaceTypeEntryCPtr &e) { return e->targetLangPackage() == extendsPackageName; }); if (extendsIt == allEntries.cend()) { @@ -1450,7 +1648,8 @@ NamespaceTypeEntry * return nullptr; } result->setExtends(*extendsIt); - } else if (attributeName == visibleAttribute()) { + attributes->removeAt(i); + } else if (attributeName == visibleAttribute) { const auto attribute = attributes->takeAt(i); const auto visibilityOpt = visibilityFromAttribute(attribute.value()); if (!visibilityOpt.has_value()) { @@ -1458,36 +1657,39 @@ NamespaceTypeEntry * return nullptr; } visibility = visibilityOpt.value(); - } else if (attributeName == generateAttribute()) { - if (!convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true)) + } 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)); + } 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.get(), attributes); + applyComplexTypeAttributes(reader, result, attributes); if (result->extends() && !result->hasPattern()) { m_error = msgExtendingNamespaceRequiresPattern(name); - return nullptr; + return {}; } - return result.release(); + return result; } -ValueTypeEntry * +ValueTypeEntryPtr TypeSystemParser::parseValueTypeEntry(const ConditionalStreamReader &reader, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *attributes) { if (!checkRootElement()) return nullptr; - auto *typeEntry = new ValueTypeEntry(name, since, currentParentTypeEntry()); - applyCommonAttributes(reader, typeEntry, attributes); + 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) @@ -1495,7 +1697,7 @@ ValueTypeEntry * return typeEntry; } -FunctionTypeEntry * +FunctionTypeEntryPtr TypeSystemParser::parseFunctionTypeEntry(const ConditionalStreamReader &reader, const QString &name, const QVersionNumber &since, QXmlStreamAttributes *attributes) @@ -1503,75 +1705,84 @@ FunctionTypeEntry * if (!checkRootElement()) return nullptr; - QString signature; - TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Disabled; + FunctionModification mod; + const auto oldAttributesSize = attributes->size(); + if (!parseModifyFunctionAttributes(attributes, &mod)) + return nullptr; + const bool hasModification = attributes->size() < oldAttributesSize; - for (int i = attributes->size() - 1; i >= 0; --i) { + QString originalSignature; + QString docFile; + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == signatureAttribute()) { - signature = TypeDatabase::normalizedSignature(attributes->takeAt(i).value().toString()); - } else if (name == snakeCaseAttribute()) { - const auto attribute = attributes->takeAt(i); - const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value()); - if (!snakeCaseOpt.has_value()) { - m_error = msgInvalidAttributeValue(attribute); - return nullptr; - } - snakeCase = snakeCaseOpt.value(); - } + 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()); + m_error = msgMissingAttribute(signatureAttribute); return nullptr; } - TypeEntry *existingType = m_database->findType(name); + if (hasModification) { + mod.setOriginalSignature(originalSignature); + mod.setSignature(signature); + m_contextStack.top()->functionMods << mod; + } + + TypeEntryPtr existingType = m_context->db->findType(name); if (!existingType) { - auto *result = new FunctionTypeEntry(name, signature, since, currentParentTypeEntry()); - result->setSnakeCase(snakeCase); + 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 = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.") - .arg(name); + 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 = reinterpret_cast<FunctionTypeEntry *>(existingType); + auto result = std::static_pointer_cast<FunctionTypeEntry>(existingType); result->addSignature(signature); return result; } -TypedefEntry * +TypedefEntryPtr TypeSystemParser::parseTypedefEntry(const ConditionalStreamReader &reader, - const QString &name, + const QString &name, StackElement topElement, const QVersionNumber &since, QXmlStreamAttributes *attributes) { if (!checkRootElement()) return nullptr; - if (m_current && m_current->type != StackElement::Root - && m_current->type != StackElement::NamespaceTypeEntry) { - m_error = QLatin1String("typedef entries must be nested in namespaces or type system."); + if (topElement != StackElement::Root + && topElement != StackElement::NamespaceTypeEntry) { + m_error = u"typedef entries must be nested in namespaces or type system."_s; return nullptr; } - const int sourceIndex = indexOfAttribute(*attributes, sourceAttribute()); + const auto sourceIndex = indexOfAttribute(*attributes, sourceAttribute); if (sourceIndex == -1) { - m_error = msgMissingAttribute(sourceAttribute()); + m_error = msgMissingAttribute(sourceAttribute); return nullptr; } const QString sourceType = attributes->takeAt(sourceIndex).value().toString(); - auto result = new TypedefEntry(name, sourceType, since, currentParentTypeEntry()); - applyCommonAttributes(reader, result, attributes); + 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, - ComplexTypeEntry *ctype, + const ComplexTypeEntryPtr &ctype, QXmlStreamAttributes *attributes) const { bool generate = true; @@ -1580,34 +1791,40 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader auto allowThread = m_allowThread; QString package = m_defaultPackage; - for (int i = attributes->size() - 1; i >= 0; --i) { + 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()) { + 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()) { + 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()) { + } else if (name == defaultSuperclassAttribute) { ctype->setDefaultSuperclass(attributes->takeAt(i).value().toString()); - } else if (name == genericClassAttribute()) { + } else if (name == genericClassAttribute) { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); - const bool v = convertBoolean(attributes->takeAt(i).value(), genericClassAttribute(), false); + const bool v = convertBoolean(attributes->takeAt(i).value(), + genericClassAttribute, false); ctype->setGenericClass(v); - } else if (name == targetLangNameAttribute()) { + } else if (name == targetLangNameAttribute) { ctype->setTargetLangName(attributes->takeAt(i).value().toString()); - } else if (name == QLatin1String("polymorphic-base")) { - ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); - } else if (name == QLatin1String("polymorphic-id-expression")) { + } 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); + } 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()) { + } else if (name == exceptionHandlingAttribute) { const auto attribute = attributes->takeAt(i); const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value()); if (exceptionOpt.has_value()) { @@ -1616,7 +1833,7 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } - } else if (name == allowThreadAttribute()) { + } else if (name == allowThreadAttribute) { const auto attribute = attributes->takeAt(i); const auto allowThreadOpt = allowThreadFromAttribute(attribute.value()); if (allowThreadOpt.has_value()) { @@ -1625,26 +1842,39 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } - } else if (name == QLatin1String("held-type")) { + } else if (name == u"held-type") { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeWarning(reader, name))); - } else if (name == QLatin1String("hash-function")) { + } 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)) + } 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)) + } 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)) + } 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)) + } else if (name == deleteInMainThreadAttribute) { + if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute, false)) ctype->setDeleteInMainThread(true); - } else if (name == QLatin1String("target-type")) { + } 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()) { + } else if (name == snakeCaseAttribute) { const auto attribute = attributes->takeAt(i); const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value()); if (snakeCaseOpt.has_value()) { @@ -1653,7 +1883,7 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } - } else if (name == isNullAttribute()) { + } else if (name == isNullAttribute) { const auto attribute = attributes->takeAt(i); const auto boolCastOpt = boolCastFromAttribute(attribute.value()); if (boolCastOpt.has_value()) { @@ -1662,7 +1892,7 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } - } else if (name == operatorBoolAttribute()) { + } else if (name == operatorBoolAttribute) { const auto attribute = attributes->takeAt(i); const auto boolCastOpt = boolCastFromAttribute(attribute.value()); if (boolCastOpt.has_value()) { @@ -1671,6 +1901,20 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader 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); } } @@ -1689,40 +1933,66 @@ void TypeSystemParser::applyComplexTypeAttributes(const ConditionalStreamReader 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 (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == signatureAttribute()) { + if (name == signatureAttribute) { // Do not remove as it is needed for the type entry later on - signature = attributes->at(i).value().toString(); - } else if (name == renameAttribute()) { + signature = attributes->at(i).value().toString().simplified(); + } else if (name == renameAttribute) { rename = attributes->takeAt(i).value().toString(); } } if (signature.isEmpty()) { - m_error = msgMissingAttribute(signatureAttribute()); + m_error = msgMissingAttribute(signatureAttribute); return false; } - *name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); + *name = signature.left(signature.indexOf(u'(')).trimmed(); - QString errorString = checkSignatureError(signature, QLatin1String("function")); + QString errorString = checkSignatureError(signature, u"function"_s); if (!errorString.isEmpty()) { m_error = errorString; return false; } if (!rename.isEmpty()) { - static const QRegularExpression functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); + static const QRegularExpression functionNameRegExp(u"^[a-zA-Z_][a-zA-Z0-9_]*$"_s); Q_ASSERT(functionNameRegExp.isValid()); if (!functionNameRegExp.match(rename).hasMatch()) { - m_error = QLatin1String("can not rename '") + signature + QLatin1String("', '") - + rename + QLatin1String("' is not a valid function name"); + m_error = u"can not rename '"_s + signature + u"', '"_s + + rename + u"' is not a valid function name"_s; return false; } FunctionModification mod; @@ -1735,23 +2005,25 @@ bool TypeSystemParser::parseRenameFunction(const ConditionalStreamReader &, return true; } -bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &, +bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &, StackElement topElement, QXmlStreamAttributes *attributes) { - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (!m_current->parent || (m_current->parent->type & validParent) == 0) { - m_error = QLatin1String("inject-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + 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 (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("mode")) { + if (name == u"mode") { const auto attribute = attributes->takeAt(i); const auto modeOpt = docModificationFromAttribute(attribute.value()); if (!modeOpt.has_value()) { @@ -1759,7 +2031,7 @@ bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &, return false; } mode = modeOpt.value(); - } else if (name == formatAttribute()) { + } else if (name == formatAttribute) { const auto attribute = attributes->takeAt(i); const auto langOpt = languageFromAttribute(attribute.value()); if (!langOpt.has_value()) { @@ -1770,53 +2042,66 @@ bool TypeSystemParser::parseInjectDocumentation(const ConditionalStreamReader &, } } - QString signature = m_current->type & StackElement::TypeEntryMask - ? QString() : m_currentSignature; + QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature; DocModification mod(mode, signature); mod.setFormat(lang); - m_contextStack.top()->docModifications << mod; + 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 int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (!m_current->parent || (m_current->parent->type & validParent) == 0) { - m_error = QLatin1String("modify-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + 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 int xpathIndex = indexOfAttribute(*attributes, xPathAttribute()); + const auto xpathIndex = indexOfAttribute(*attributes, xPathAttribute); if (xpathIndex == -1) { - m_error = msgMissingAttribute(xPathAttribute()); + m_error = msgMissingAttribute(xPathAttribute); return false; } const QString xpath = attributes->takeAt(xpathIndex).value().toString(); - QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; + QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature; m_contextStack.top()->docModifications << DocModification(xpath, signature); return true; } // m_exceptionHandling -TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const ConditionalStreamReader &, +TypeSystemTypeEntryPtr TypeSystemParser::parseRootElement(const ConditionalStreamReader &, const QVersionNumber &since, QXmlStreamAttributes *attributes) { TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified; + QString subModuleOf; + QString namespaceBegin; + QString namespaceEnd; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == packageAttribute()) { + if (name == packageAttribute) { m_defaultPackage = attributes->takeAt(i).value().toString(); - } else if (name == defaultSuperclassAttribute()) { + } else if (name == defaultSuperclassAttribute) { m_defaultSuperclass = attributes->takeAt(i).value().toString(); - } else if (name == exceptionHandlingAttribute()) { + } else if (name == exceptionHandlingAttribute) { const auto attribute = attributes->takeAt(i); const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value()); if (exceptionOpt.has_value()) { @@ -1825,7 +2110,7 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const ConditionalStreamR qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } - } else if (name == allowThreadAttribute()) { + } else if (name == allowThreadAttribute) { const auto attribute = attributes->takeAt(i); const auto allowThreadOpt = allowThreadFromAttribute(attribute.value()); if (allowThreadOpt.has_value()) { @@ -1834,7 +2119,7 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const ConditionalStreamR qCWarning(lcShiboken, "%s", qPrintable(msgInvalidAttributeValue(attribute))); } - } else if (name == snakeCaseAttribute()) { + } else if (name == snakeCaseAttribute) { const auto attribute = attributes->takeAt(i); const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value()); if (snakeCaseOpt.has_value()) { @@ -1843,25 +2128,43 @@ TypeSystemTypeEntry *TypeSystemParser::parseRootElement(const ConditionalStreamR 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(); } } - auto *moduleEntry = - const_cast<TypeSystemTypeEntry *>(m_database->findTypeSystemType(m_defaultPackage)); - const bool add = moduleEntry == nullptr; + 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 = new TypeSystemTypeEntry(m_defaultPackage, since, - currentParentTypeEntry()); + 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_database->addTypeSystemType(moduleEntry); + m_context->db->addTypeSystemType(moduleEntry); return moduleEntry; } @@ -1870,22 +2173,22 @@ bool TypeSystemParser::loadTypesystem(const ConditionalStreamReader &, { QString typeSystemName; bool generateChild = true; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == nameAttribute()) + if (name == nameAttribute) typeSystemName = attributes->takeAt(i).value().toString(); - else if (name == generateAttribute()) - generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true); + else if (name == generateAttribute) + generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute, true); } if (typeSystemName.isEmpty()) { - m_error = QLatin1String("No typesystem name specified"); + m_error = u"No typesystem name specified"_s; return false; } const bool result = - m_database->parseFile(typeSystemName, m_currentPath, generateChild - && m_generate == TypeEntry::GenerateCode); + m_context->db->parseFile(m_context, typeSystemName, m_currentPath, + generateChild && m_generate == TypeEntry::GenerateCode); if (!result) - m_error = QStringLiteral("Failed to parse: '%1'").arg(typeSystemName); + m_error = u"Failed to parse: '"_s + typeSystemName + u'\''; return result; } @@ -1893,12 +2196,12 @@ bool TypeSystemParser::parseRejectEnumValue(const ConditionalStreamReader &, QXmlStreamAttributes *attributes) { if (!m_currentEnum) { - m_error = QLatin1String("<reject-enum-value> node must be used inside a <enum-type> node"); + m_error = u"<reject-enum-value> node must be used inside a <enum-type> node"_s; return false; } - const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + const auto nameIndex = indexOfAttribute(*attributes, nameAttribute); if (nameIndex == -1) { - m_error = msgMissingAttribute(nameAttribute()); + m_error = msgMissingAttribute(nameAttribute); return false; } m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString()); @@ -1906,16 +2209,16 @@ bool TypeSystemParser::parseRejectEnumValue(const ConditionalStreamReader &, } bool TypeSystemParser::parseReplaceArgumentType(const ConditionalStreamReader &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Type replacement can only be specified for argument modifications"); + if (topElement != StackElement::ModifyArgument) { + m_error = u"Type replacement can only be specified for argument modifications"_s; return false; } - const int modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute()); + const auto modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute); if (modifiedTypeIndex == -1) { - m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); + m_error = u"Type replacement requires 'modified-type' attribute"_s; return false; } m_contextStack.top()->functionMods.last().argument_mods().last().setModifiedType( @@ -1924,24 +2227,24 @@ bool TypeSystemParser::parseReplaceArgumentType(const ConditionalStreamReader &, } bool TypeSystemParser::parseCustomConversion(const ConditionalStreamReader &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ModifyArgument - && topElement.type != StackElement::ValueTypeEntry - && topElement.type != StackElement::PrimitiveTypeEntry - && topElement.type != StackElement::ContainerTypeEntry) { - m_error = QLatin1String("Conversion rules can only be specified for argument modification, " - "value-type, primitive-type or container-type conversion."); + 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 (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == classAttribute()) { + if (name == classAttribute) { const auto languageAttribute = attributes->takeAt(i); const auto langOpt = languageFromAttribute(languageAttribute.value()); if (!langOpt.has_value()) { @@ -1949,28 +2252,33 @@ bool TypeSystemParser::parseCustomConversion(const ConditionalStreamReader &, return false; } lang = langOpt.value(); - } else if (name == QLatin1String("file")) { + } else if (name == u"file") { sourceFile = attributes->takeAt(i).value().toString(); - } else if (name == snippetAttribute()) { + } else if (name == snippetAttribute) { snippetLabel = attributes->takeAt(i).value().toString(); } } - if (topElement.type == StackElement::ModifyArgument) { + const auto &top = m_contextStack.top(); + if (topElement == StackElement::ModifyArgument) { CodeSnip snip; snip.language = lang; - m_contextStack.top()->functionMods.last().argument_mods().last().conversionRules().append(snip); + top->functionMods.last().argument_mods().last().conversionRules().append(snip); return true; } - if (topElement.entry->hasTargetConversionRule() || topElement.entry->hasCustomConversion()) { - m_error = QLatin1String("Types can have only one conversion rule"); - return false; + 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 (!sourceFile.isEmpty()) { + if (valueTypeEntry != nullptr && !sourceFile.isEmpty()) { if (m_generate != TypeEntry::GenerateForSubclass && m_generate != TypeEntry::GenerateNothing) { qWarning(lcShiboken, "Specifying conversion rules by \"file\" is deprecated."); @@ -1988,57 +2296,75 @@ bool TypeSystemParser::parseCustomConversion(const ConditionalStreamReader &, m_error = msgCannotFindSnippet(sourceFile, snippetLabel); return false; } - topElement.entry->setTargetConversionRule(conversionRuleOptional.value()); + valueTypeEntry->setTargetConversionRule(conversionRuleOptional.value()); } return true; } - auto *customConversion = new CustomConversion(m_current->entry); + 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 &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ConversionRule) { - m_error = QLatin1String("Native to Target conversion code can only be specified for custom conversion rules."); + 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 (!readFileSnippet(attributes, &snip)) + if (!readCodeSnippet(attributes, &snip)) return false; - m_contextStack.top()->codeSnips.append(snip); + m_contextStack.top()->conversionCodeSnips.append(snip); return true; } bool TypeSystemParser::parseAddConversion(const ConditionalStreamReader &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::TargetToNative) { - m_error = QLatin1String("Target to Native conversions can only be added inside 'target-to-native' tags."); + 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 (!readFileSnippet(attributes, &snip)) + if (!readCodeSnippet(attributes, &snip)) return false; - for (int i = attributes->size() - 1; i >= 0; --i) { + + 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 == QLatin1String("type")) + if (name == u"type") sourceTypeName = attributes->takeAt(i).value().toString(); - else if (name == QLatin1String("check")) + else if (name == u"check") typeCheck = attributes->takeAt(i).value().toString(); } + if (sourceTypeName.isEmpty()) { - m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); + 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; } - m_current->entry->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); - m_contextStack.top()->codeSnips.append(snip); + customConversion->addTargetToNativeConversion(sourceTypeName, typeCheck); return true; } @@ -2047,17 +2373,17 @@ static bool parseIndex(const QString &index, int *result, QString *errorMessage) bool ok = false; *result = index.toInt(&ok); if (!ok) - *errorMessage = QStringLiteral("Cannot convert '%1' to integer").arg(index); + *errorMessage = QString::fromLatin1("Cannot convert '%1' to integer").arg(index); return ok; } static bool parseArgumentIndex(const QString &index, int *result, QString *errorMessage) { - if (index == QLatin1String("return")) { + if (index == u"return") { *result = 0; return true; } - if (index == QLatin1String("this")) { + if (index == u"this") { *result = -1; return true; } @@ -2065,13 +2391,14 @@ static bool parseArgumentIndex(const QString &index, int *result, QString *error } bool TypeSystemParser::parseModifyArgument(const ConditionalStreamReader &, - const StackElement &topElement, QXmlStreamAttributes *attributes) -{ - if (topElement.type != StackElement::ModifyFunction - && topElement.type != StackElement::AddFunction) { - m_error = QString::fromLatin1("argument modification requires function" - " modification as parent, was %1") - .arg(topElement.type, 0, 16); + 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; } @@ -2079,22 +2406,22 @@ bool TypeSystemParser::parseModifyArgument(const ConditionalStreamReader &, QString renameTo; QString pyiType; bool resetAfterUse = false; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == indexAttribute()) { + if (name == indexAttribute) { index = attributes->takeAt(i).value().toString(); - } else if (name == invalidateAfterUseAttribute()) { + } else if (name == invalidateAfterUseAttribute) { resetAfterUse = convertBoolean(attributes->takeAt(i).value(), - invalidateAfterUseAttribute(), false); - } else if (name == renameAttribute()) { + invalidateAfterUseAttribute, false); + } else if (name == renameAttribute) { renameTo = attributes->takeAt(i).value().toString(); - } else if (name == pyiTypeAttribute()) { + } else if (name == pyiTypeAttribute) { pyiType = attributes->takeAt(i).value().toString(); } } if (index.isEmpty()) { - m_error = msgMissingAttribute(indexAttribute()); + m_error = msgMissingAttribute(indexAttribute); return false; } @@ -2111,10 +2438,10 @@ bool TypeSystemParser::parseModifyArgument(const ConditionalStreamReader &, } bool TypeSystemParser::parseNoNullPointer(const ConditionalStreamReader &reader, - const StackElement &topElement, QXmlStreamAttributes *attributes) + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("no-null-pointer requires argument modification as parent"); + if (topElement != StackElement::ModifyArgument) { + m_error = u"no-null-pointer requires argument modification as parent"_s; return false; } @@ -2132,19 +2459,19 @@ bool TypeSystemParser::parseNoNullPointer(const ConditionalStreamReader &reader, } bool TypeSystemParser::parseDefineOwnership(const ConditionalStreamReader &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("define-ownership requires argument modification as parent"); + 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 (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == classAttribute()) { + if (name == classAttribute) { const auto classAttribute = attributes->takeAt(i); const auto langOpt = languageFromAttribute(classAttribute.value()); if (!langOpt.has_value() || langOpt.value() == TypeSystem::ShellCode) { @@ -2152,7 +2479,7 @@ bool TypeSystemParser::parseDefineOwnership(const ConditionalStreamReader &, return false; } lang = langOpt.value(); - } else if (name == ownershipAttribute()) { + } else if (name == ownershipAttribute) { const auto attribute = attributes->takeAt(i); ownershipOpt = ownershipFromFromAttribute(attribute.value()); if (!ownershipOpt.has_value()) { @@ -2163,7 +2490,7 @@ bool TypeSystemParser::parseDefineOwnership(const ConditionalStreamReader &, } if (!ownershipOpt.has_value()) { - m_error = QStringLiteral("unspecified ownership"); + m_error = "unspecified ownership"_L1; return false; } auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last(); @@ -2182,17 +2509,17 @@ bool TypeSystemParser::parseDefineOwnership(const ConditionalStreamReader &, // ### fixme PySide7: remove (replaced by attribute). bool TypeSystemParser::parseRename(const ConditionalStreamReader &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Argument modification parent required"); + if (topElement != StackElement::ModifyArgument) { + m_error = u"Argument modification parent required"_s; return false; } - const int toIndex = indexOfAttribute(*attributes, toAttribute()); + const auto toIndex = indexOfAttribute(*attributes, toAttribute); if (toIndex == -1) { - m_error = msgMissingAttribute(toAttribute()); + m_error = msgMissingAttribute(toAttribute); return false; } const QString renamed_to = attributes->takeAt(toIndex).value().toString(); @@ -2204,19 +2531,22 @@ bool TypeSystemParser::parseModifyField(const ConditionalStreamReader &, QXmlStreamAttributes *attributes) { FieldModification fm; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == nameAttribute()) { + if (name == nameAttribute) { fm.setName(attributes->takeAt(i).value().toString()); - } else if (name == removeAttribute()) { + } else if (name == removeAttribute) { fm.setRemoved(convertRemovalAttribute(attributes->takeAt(i).value())); - } 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()) { + } 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()) { + } else if (name == snakeCaseAttribute) { const auto attribute = attributes->takeAt(i); const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value()); if (snakeCaseOpt.has_value()) { @@ -2228,7 +2558,7 @@ bool TypeSystemParser::parseModifyField(const ConditionalStreamReader &, } } if (fm.name().isEmpty()) { - m_error = msgMissingAttribute(nameAttribute()); + m_error = msgMissingAttribute(nameAttribute); return false; } m_contextStack.top()->fieldMods << fm; @@ -2248,70 +2578,85 @@ static bool parseOverloadNumber(const QXmlStreamAttribute &attribute, int *overl } bool TypeSystemParser::parseAddFunction(const ConditionalStreamReader &, - const StackElement &topElement, - StackElement::ElementType t, + StackElement topElement, + StackElement t, QXmlStreamAttributes *attributes) { - if (!(topElement.type - & (StackElement::ComplexTypeEntryMask | StackElement::Root | StackElement::ContainerTypeEntry))) { + 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(topElement.type, 0, 16); + ", 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; - int overloadNumber = TypeSystem::OverloadNumberUnset; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("signature")) { - originalSignature = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("return-type")) { + 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()) { + } else if (name == staticAttribute) { staticFunction = convertBoolean(attributes->takeAt(i).value(), - staticAttribute(), false); - } else if (name == classmethodAttribute()) { + staticAttribute, false); + } else if (name == classmethodAttribute) { classMethod = convertBoolean(attributes->takeAt(i).value(), - classmethodAttribute(), false); - } else if (name == accessAttribute()) { + classmethodAttribute, false); + } else if (name == accessAttribute) { access = attributes->takeAt(i).value().toString(); - } else if (name == overloadNumberAttribute()) { - if (!parseOverloadNumber(attributes->takeAt(i), &overloadNumber, &m_error)) - return false; + } else if (name == pythonOverrideAttribute) { + pythonOverride = convertBoolean(attributes->takeAt(i).value(), + pythonOverrideAttribute, false); } } - QString signature = TypeDatabase::normalizedSignature(originalSignature); + QString signature = TypeDatabase::normalizedAddedFunctionSignature(originalSignature); if (signature.isEmpty()) { - m_error = QLatin1String("No signature for the added function"); + m_error = u"No signature for the added function"_s; return false; } - QString errorString = checkSignatureError(signature, QLatin1String("add-function")); + 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.isNull()) { + if (!func) { m_error = errorString; return false; } func->setStatic(staticFunction); func->setClassMethod(classMethod); - if (!signature.contains(QLatin1Char('('))) - signature += QLatin1String("()"); + 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 = QString::fromLatin1("Bad access type '%1'").arg(access); + m_error = u"Bad access type '"_s + access + u'\''; return false; } func->setAccess(acessOpt.value()); @@ -2322,8 +2667,6 @@ bool TypeSystemParser::parseAddFunction(const ConditionalStreamReader &, m_contextStack.top()->addedFunctionModificationIndex = m_contextStack.top()->functionMods.size(); - FunctionModification mod; - mod.setOverloadNumber(overloadNumber); if (!mod.setSignature(m_currentSignature, &m_error)) return false; mod.setOriginalSignature(originalSignature); @@ -2331,156 +2674,217 @@ bool TypeSystemParser::parseAddFunction(const ConditionalStreamReader &, return true; } -bool TypeSystemParser::parseProperty(const ConditionalStreamReader &, const StackElement &topElement, +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 ((topElement.type & StackElement::ComplexTypeEntryMask) == 0) { + if (!isComplexTypeEntry(topElement)) { m_error = QString::fromLatin1("Add property requires a complex type as parent" - ", was=%1").arg(topElement.type, 0, 16); + ", was=%1").arg(tagFromElement(topElement)); return false; } TypeSystemProperty property; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == nameAttribute()) { + if (name == nameAttribute) { property.name = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("get")) { + } else if (name == u"get") { property.read = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("type")) { + } else if (name == u"type") { property.type = attributes->takeAt(i).value().toString(); - } else if (name == QLatin1String("set")) { + } else if (name == u"set") { property.write = attributes->takeAt(i).value().toString(); - } else if (name == generateGetSetDefAttribute()) { + } else if (name == generateGetSetDefAttribute) { property.generateGetSetDef = convertBoolean(attributes->takeAt(i).value(), - generateGetSetDefAttribute(), false); + generateGetSetDefAttribute, false); } } if (!property.isValid()) { - m_error = QLatin1String("<property> element is missing required attibutes (name/type/get)."); + m_error = u"<property> element is missing required attibutes (name/type/get)."_s; return false; } - static_cast<ComplexTypeEntry *>(topElement.entry)->addProperty(property); + std::static_pointer_cast<ComplexTypeEntry>(m_contextStack.top()->entry)->addProperty(property); return true; } -bool TypeSystemParser::parseModifyFunction(const ConditionalStreamReader &reader, - const StackElement &topElement, - QXmlStreamAttributes *attributes) +// 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) { - if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { - m_error = QString::fromLatin1("Modify function requires complex type as parent" - ", was=%1").arg(topElement.type, 0, 16); - return false; + 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; +} - QString originalSignature; - QString access; - bool removed = false; - QString rename; - bool deprecated = false; - bool isThread = false; - int overloadNumber = TypeSystem::OverloadNumberUnset; - TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; - TypeSystem::AllowThread allowThread = TypeSystem::AllowThread::Unspecified; - TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified; - for (int i = attributes->size() - 1; i >= 0; --i) { +// 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 == QLatin1String("signature")) { - originalSignature = attributes->takeAt(i).value().toString(); - } 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 == deprecatedAttribute()) { - deprecated = convertBoolean(attributes->takeAt(i).value(), - deprecatedAttribute(), false); - } else if (name == threadAttribute()) { - isThread = convertBoolean(attributes->takeAt(i).value(), - threadAttribute(), false); - } else if (name == allowThreadAttribute()) { + 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; } - allowThread = allowThreadOpt.value(); - } else if (name == exceptionHandlingAttribute()) { + mod->setAllowThread(allowThreadOpt.value()); + } 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 == overloadNumberAttribute()) { - if (!parseOverloadNumber(attributes->takeAt(i), &overloadNumber, &m_error)) + if (!exceptionOpt.has_value()) { + m_error = msgInvalidAttributeValue(attribute); return false; - } else if (name == snakeCaseAttribute()) { + } + mod->setExceptionHandling(exceptionOpt.value()); + } 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))); + if (!snakeCaseOpt.has_value()) { + m_error = msgInvalidAttributeValue(attribute); + return false; } - } else if (name == virtualSlotAttribute()) { + 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> - if (originalSignature.isEmpty() && topElement.entry->isFunction()) { - auto f = static_cast<const FunctionTypeEntry *>(topElement.entry); + 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 = QLatin1String("No signature for modified function"); + m_error = u"No signature for modified function"_s; return false; } - QString errorString = checkSignatureError(signature, QLatin1String("modify-function")); + QString errorString = checkSignatureError(signature, u"modify-function"_s); if (!errorString.isEmpty()) { m_error = errorString; return false; } - FunctionModification mod; if (!mod.setSignature(signature, &m_error)) return false; mod.setOriginalSignature(originalSignature); - mod.setExceptionHandling(exceptionHandling); - mod.setOverloadNumber(overloadNumber); - mod.setSnakeCase(snakeCase); m_currentSignature = signature; if (!access.isEmpty()) { const auto modifierFlagOpt = modifierFromAttribute(access); if (!modifierFlagOpt.has_value()) { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + m_error = u"Bad access type '"_s + access + u'\''; return false; } const FunctionModification::ModifierFlag m = modifierFlagOpt.value(); - if (m == FunctionModification::Final || m == FunctionModification::NonFinal) { + if (m == FunctionModification::NonFinal) { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedAttributeValueWarning(reader, - accessAttribute(), access))); + accessAttribute, access))); } mod.setModifierFlag(m); } - if (deprecated) - mod.setModifierFlag(FunctionModification::Deprecated); - mod.setRemoved(removed); if (!rename.isEmpty()) { @@ -2488,25 +2892,21 @@ bool TypeSystemParser::parseModifyFunction(const ConditionalStreamReader &reader mod.setModifierFlag(FunctionModification::Rename); } - mod.setIsThread(isThread); - if (allowThread != TypeSystem::AllowThread::Unspecified) - mod.setAllowThread(allowThread); - - m_contextStack.top()->functionMods << mod; + top->functionMods << mod; return true; } bool TypeSystemParser::parseReplaceDefaultExpression(const ConditionalStreamReader &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (!(topElement.type & StackElement::ModifyArgument)) { - m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); + if (!(topElement & StackElement::ModifyArgument)) { + m_error = u"Replace default expression only allowed as child of argument modification"_s; return false; } - const int withIndex = indexOfAttribute(*attributes, u"with"); + const auto withIndex = indexOfAttribute(*attributes, u"with"); if (withIndex == -1 || attributes->at(withIndex).value().isEmpty()) { - m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); + m_error = u"Default expression replaced with empty string. Use remove-default-expression instead."_s; return false; } @@ -2515,41 +2915,19 @@ bool TypeSystemParser::parseReplaceDefaultExpression(const ConditionalStreamRead return true; } -CustomFunction * - TypeSystemParser::parseCustomMetaConstructor(const ConditionalStreamReader &, - StackElement::ElementType type, - const StackElement &topElement, - QXmlStreamAttributes *attributes) -{ - QString functionName = topElement.entry->name().toLower() - + (type == StackElement::CustomMetaConstructor - ? QLatin1String("_create") : QLatin1String("_delete")); - QString paramName = QLatin1String("copy"); - for (int i = attributes->size() - 1; i >= 0; --i) { - const auto name = attributes->at(i).qualifiedName(); - if (name == nameAttribute()) - functionName = attributes->takeAt(i).value().toString(); - else if (name == QLatin1String("param-name")) - paramName = attributes->takeAt(i).value().toString(); - } - auto *func = new CustomFunction(functionName); - func->paramName = paramName; - return func; -} - bool TypeSystemParser::parseReferenceCount(const ConditionalStreamReader &reader, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("reference-count must be child of modify-argument"); + if (topElement != StackElement::ModifyArgument) { + m_error = u"reference-count must be child of modify-argument"_s; return false; } ReferenceCount rc; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == actionAttribute()) { + if (name == actionAttribute) { const QXmlStreamAttribute attribute = attributes->takeAt(i); const auto actionOpt = referenceCountFromAttribute(attribute.value()); if (!actionOpt.has_value()) { @@ -2566,7 +2944,7 @@ bool TypeSystemParser::parseReferenceCount(const ConditionalStreamReader &reader default: break; } - } else if (name == QLatin1String("variable-name")) { + } else if (name == u"variable-name") { rc.varName = attributes->takeAt(i).value().toString(); } } @@ -2576,21 +2954,21 @@ bool TypeSystemParser::parseReferenceCount(const ConditionalStreamReader &reader } bool TypeSystemParser::parseParentOwner(const ConditionalStreamReader &, - const StackElement &topElement, + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("parent-policy must be child of modify-argument"); + if (topElement != StackElement::ModifyArgument) { + m_error = u"parent-policy must be child of modify-argument"_s; return false; } ArgumentOwner ao; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == indexAttribute()) { + if (name == indexAttribute) { const QString index = attributes->takeAt(i).value().toString(); if (!parseArgumentIndex(index, &ao.index, &m_error)) return false; - } else if (name == actionAttribute()) { + } else if (name == actionAttribute) { const auto action = attributes->takeAt(i); const auto actionOpt = argumentOwnerActionFromAttribute(action.value()); if (!actionOpt.has_value()) { @@ -2604,46 +2982,62 @@ bool TypeSystemParser::parseParentOwner(const ConditionalStreamReader &, return true; } -bool TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip) +std::optional<TypeSystemParser::Snippet> + TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes) { - QString fileName; - QString snippetLabel; - for (int i = attributes->size() - 1; i >= 0; --i) { + Snippet result; + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("file")) { - fileName = attributes->takeAt(i).value().toString(); - } else if (name == snippetAttribute()) { - snippetLabel = attributes->takeAt(i).value().toString(); + if (name == fileAttribute) { + result.fileName = attributes->takeAt(i).value().toString(); + } else if (name == snippetAttribute) { + result.snippetLabel = attributes->takeAt(i).value().toString(); } } - if (fileName.isEmpty()) - return true; - const QString resolved = m_database->modifiedTypesystemFilepath(fileName, m_currentPath); + 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 = QLatin1String("File for inject code not exist: ") - + QDir::toNativeSeparators(fileName); - return false; + 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 false; + return std::nullopt; } - const auto codeOptional = extractSnippet(QString::fromUtf8(codeFile.readAll()), snippetLabel); + const auto contentOptional = extractSnippet(QString::fromUtf8(codeFile.readAll()), + result.snippetLabel); codeFile.close(); - if (!codeOptional.has_value()) { - m_error = msgCannotFindSnippet(resolved, snippetLabel); - return false; + if (!contentOptional.has_value()) { + m_error = msgCannotFindSnippet(resolved, result.snippetLabel); + return std::nullopt; } + result.content = contentOptional.value(); + return result; +} - QString source = fileName; - if (!snippetLabel.isEmpty()) - source += QLatin1String(" (") + snippetLabel + QLatin1Char(')'); +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" << codeOptional.value() + << source << "]\n" << snippet.content << "// END of custom code block [file: " << source << "]\n// ========================================================================\n"; snip->addCode(content); @@ -2651,25 +3045,25 @@ bool TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes, CodeSni } bool TypeSystemParser::parseInjectCode(const ConditionalStreamReader &, - const StackElement &topElement, - StackElement* element, QXmlStreamAttributes *attributes) -{ - if (!(topElement.type & StackElement::ComplexTypeEntryMask) - && (topElement.type != StackElement::AddFunction) - && (topElement.type != StackElement::ModifyFunction) - && (topElement.type != StackElement::Root)) { - m_error = QLatin1String("wrong parent type for code injection"); + 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 (!readFileSnippet(attributes, &snip)) + if (!readCodeSnippet(attributes, &snip)) return false; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == classAttribute()) { + if (name == classAttribute) { const auto attribute = attributes->takeAt(i); const auto langOpt = languageFromAttribute(attribute.value()); if (!langOpt.has_value()) { @@ -2677,7 +3071,7 @@ bool TypeSystemParser::parseInjectCode(const ConditionalStreamReader &, return false; } lang = langOpt.value(); - } else if (name == positionAttribute()) { + } else if (name == positionAttribute) { const auto attribute = attributes->takeAt(i); const auto positionOpt = codeSnipPositionFromAttribute(attribute.value()); if (!positionOpt.has_value()) { @@ -2691,32 +3085,36 @@ bool TypeSystemParser::parseInjectCode(const ConditionalStreamReader &, snip.position = position; snip.language = lang; - if (topElement.type == StackElement::ModifyFunction - || topElement.type == StackElement::AddFunction) { + 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); - element->type = StackElement::InjectCodeInFunction; - } else if (topElement.type == StackElement::Root) { - element->entry->addCodeSnip(snip); - } else if (topElement.type != StackElement::Root) { - m_contextStack.top()->codeSnips << snip; + } + 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 &, - const StackElement &topElement, - TypeEntry *entry, QXmlStreamAttributes *attributes) + StackElement topElement, + const TypeEntryPtr &entry, QXmlStreamAttributes *attributes) { QString fileName; Include::IncludeType location = Include::IncludePath; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == fileNameAttribute()) { + if (name == fileNameAttribute) { fileName = attributes->takeAt(i).value().toString(); - } else if (name == locationAttribute()) { + } else if (name == locationAttribute) { const auto attribute = attributes->takeAt(i); const auto locationOpt = locationFromAttribute(attribute.value()); if (!locationOpt.has_value()) { @@ -2728,13 +3126,16 @@ bool TypeSystemParser::parseInclude(const ConditionalStreamReader &, } Include inc(location, fileName); - if (topElement.type - & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { + if (isComplexTypeEntry(topElement) + || topElement == StackElement::PrimitiveTypeEntry + || topElement == StackElement::ContainerTypeEntry + || topElement == StackElement::SmartPointerTypeEntry + || topElement == StackElement::TypedefTypeEntry) { entry->setInclude(inc); - } else if (topElement.type == StackElement::ExtraIncludes) { + } else if (topElement == StackElement::ExtraIncludes) { entry->addExtraInclude(inc); } else { - m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); + m_error = u"Only supported parent tags are primitive-type, complex types or extra-includes"_s; return false; } return true; @@ -2743,57 +3144,79 @@ bool TypeSystemParser::parseInclude(const ConditionalStreamReader &, bool TypeSystemParser::parseSystemInclude(const ConditionalStreamReader &, QXmlStreamAttributes *attributes) { - const int index = indexOfAttribute(*attributes, fileNameAttribute()); + const auto index = indexOfAttribute(*attributes, fileNameAttribute); if (index == -1) { - m_error = msgMissingAttribute(fileNameAttribute()); + m_error = msgMissingAttribute(fileNameAttribute); return false; } - TypeDatabase::instance()->addSystemInclude(attributes->takeAt(index).value().toString()); + TypeDatabase::instance()->addForceProcessSystemInclude(attributes->takeAt(index).value().toString()); return true; } TemplateInstance * - TypeSystemParser::parseTemplateInstanceEnum(const ConditionalStreamReader &, - const StackElement &topElement, - QXmlStreamAttributes *attributes) + TypeSystemParser::parseInsertTemplate(const ConditionalStreamReader &, + StackElement topElement, + QXmlStreamAttributes *attributes) { - if (!(topElement.type & StackElement::CodeSnipMask) && - (topElement.type != StackElement::Template) && - (topElement.type != StackElement::CustomMetaConstructor) && - (topElement.type != StackElement::CustomMetaDestructor) && - (topElement.type != StackElement::NativeToTarget) && - (topElement.type != StackElement::AddConversion) && - (topElement.type != StackElement::ConversionRule)) { - m_error = QLatin1String("Can only insert templates into code snippets, templates, custom-constructors, "\ - "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); + 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 int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + const auto nameIndex = indexOfAttribute(*attributes, nameAttribute); if (nameIndex == -1) { - m_error = msgMissingAttribute(nameAttribute()); + m_error = msgMissingAttribute(nameAttribute); return nullptr; } return new TemplateInstance(attributes->takeAt(nameIndex).value().toString()); } bool TypeSystemParser::parseReplace(const ConditionalStreamReader &, - const StackElement &topElement, - StackElement *element, QXmlStreamAttributes *attributes) + StackElement topElement, QXmlStreamAttributes *attributes) { - if (topElement.type != StackElement::TemplateInstanceEnum) { - m_error = QLatin1String("Can only insert replace rules into insert-template."); + if (topElement != StackElement::InsertTemplate) { + m_error = u"Can only insert replace rules into insert-template."_s; return false; } QString from; QString to; - for (int i = attributes->size() - 1; i >= 0; --i) { + for (auto i = attributes->size() - 1; i >= 0; --i) { const auto name = attributes->at(i).qualifiedName(); - if (name == QLatin1String("from")) + if (name == u"from") from = attributes->takeAt(i).value().toString(); - else if (name == toAttribute()) + else if (name == toAttribute) to = attributes->takeAt(i).value().toString(); } - element->parent->value.templateInstance->addReplaceRule(from, to); + 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; } @@ -2808,7 +3231,7 @@ static bool parseVersion(const QString &versionSpec, const QString &package, return true; } -bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) +bool TypeSystemParser::startElement(const ConditionalStreamReader &reader, StackElement element) { if (m_ignoreDepth) { ++m_ignoreDepth; @@ -2819,14 +3242,14 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) QXmlStreamAttributes attributes = reader.attributes(); VersionRange versionRange; - for (int i = attributes.size() - 1; i >= 0; --i) { + for (auto i = attributes.size() - 1; i >= 0; --i) { const auto name = attributes.at(i).qualifiedName(); - if (name == sinceAttribute()) { + if (name == sinceAttribute) { if (!parseVersion(attributes.takeAt(i).value().toString(), m_defaultPackage, &versionRange.since, &m_error)) { return false; } - } else if (name == untilAttribute()) { + } else if (name == untilAttribute) { if (!parseVersion(attributes.takeAt(i).value().toString(), m_defaultPackage, &versionRange.until, &m_error)) { return false; @@ -2835,89 +3258,76 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) } if (!m_defaultPackage.isEmpty() && !versionRange.isNull()) { - TypeDatabase* td = TypeDatabase::instance(); + auto *td = TypeDatabase::instance(); if (!td->checkApiVersion(m_defaultPackage, versionRange)) { ++m_ignoreDepth; return true; } } - if (tagName.compare(QLatin1String("import-file"), Qt::CaseInsensitive) == 0) + if (element == StackElement::ImportFile) return importFileElement(attributes); - const auto elementTypeOpt = elementFromTag(tagName); - if (!elementTypeOpt.has_value()) { - m_error = QStringLiteral("Unknown tag name: '%1'").arg(tagName); - return false; - } - - if (m_currentDroppedEntry) { + if (m_currentDroppedEntryDepth) { ++m_currentDroppedEntryDepth; return true; } - std::unique_ptr<StackElement> element(new StackElement(m_current)); - element->type = elementTypeOpt.value(); - - if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateCode) + if (element == StackElement::Root && m_generate == TypeEntry::GenerateCode) customConversionsForReview.clear(); - if (element->type == StackElement::CustomMetaConstructor - || element->type == StackElement::CustomMetaDestructor) { + if (element == StackElement::Unimplemented) { qCWarning(lcShiboken, "%s", qPrintable(msgUnimplementedElementWarning(reader, tagName))); + return true; } - switch (element->type) { - case StackElement::Root: - case StackElement::NamespaceTypeEntry: - case StackElement::InterfaceTypeEntry: - case StackElement::ObjectTypeEntry: - case StackElement::ValueTypeEntry: - case StackElement::PrimitiveTypeEntry: - case StackElement::TypedefTypeEntry: - case StackElement::ContainerTypeEntry: - m_contextStack.push(new StackElementContext()); - break; - default: - break; + if (isTypeEntry(element) || element == StackElement::Root) + m_contextStack.push(std::make_shared<StackElementContext>()); + + if (m_contextStack.isEmpty()) { + m_error = msgNoRootTypeSystemEntry(); + return false; } - if (element->type & StackElement::TypeEntryMask) { + 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->type != StackElement::FunctionTypeEntry) { - const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (element != StackElement::FunctionTypeEntry) { + const auto nameIndex = indexOfAttribute(attributes, nameAttribute); if (nameIndex != -1) { name = attributes.takeAt(nameIndex).value().toString(); - } else if (element->type != StackElement::EnumTypeEntry) { // anonymous enum? - m_error = msgMissingAttribute(nameAttribute()); + } 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->type != StackElement::PrimitiveTypeEntry && name.contains(QLatin1Char(':')) - && !name.contains(QLatin1String("std::"))) { + if (element != StackElement::PrimitiveTypeEntry && name.contains(u':') + && !name.contains(u"std::")) { m_error = msgIncorrectlyNestedName(name); return false; } - if (m_database->hasDroppedTypeEntries()) { - const QString identifier = element->type == StackElement::FunctionTypeEntry - ? attributes.value(signatureAttribute()).toString() : name; - if (shouldDropTypeEntry(m_database, element.get(), identifier)) { - m_currentDroppedEntry = element.release(); + 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->type == StackElement::FunctionTypeEntry + if (element == StackElement::FunctionTypeEntry && !parseRenameFunction(reader, &name, &attributes)) { return false; } @@ -2925,144 +3335,134 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) // 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->type != StackElement::PrimitiveTypeEntry - && element->type != StackElement::FunctionTypeEntry) { - TypeEntry *tmp = m_database->findType(name); + if (element != StackElement::PrimitiveTypeEntry + && element != StackElement::FunctionTypeEntry) { + TypeEntryPtr tmp = m_context->db->findType(name); if (tmp && !tmp->isNamespace()) qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Duplicate type entry: '%1'").arg(name); + << "Duplicate type entry: '" << name << '\''; } - if (element->type == StackElement::EnumTypeEntry) { - const int enumIdentifiedByIndex = indexOfAttribute(attributes, enumIdentifiedByValueAttribute()); + 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 = QLatin1String("can't specify both 'name' and 'identified-by-value' attributes"); + m_error = u"can't specify both 'name' and 'identified-by-value' attributes"_s; return false; } } if (name.isEmpty()) { - m_error = QLatin1String("no 'name' attribute specified"); + m_error = u"no 'name' attribute specified"_s; return false; } - switch (element->type) { + switch (element) { case StackElement::CustomTypeEntry: - element->entry = - parseCustomTypeEntry(reader, name, versionRange.since, &attributes); - if (Q_UNLIKELY(!element->entry)) + top->entry = parseCustomTypeEntry(reader, name, versionRange.since, &attributes); + if (Q_UNLIKELY(!top->entry)) return false; break; case StackElement::PrimitiveTypeEntry: - element->entry = parsePrimitiveTypeEntry(reader, name, versionRange.since, &attributes); - if (Q_UNLIKELY(!element->entry)) + top->entry = parsePrimitiveTypeEntry(reader, name, versionRange.since, &attributes); + if (Q_UNLIKELY(!top->entry)) return false; break; case StackElement::ContainerTypeEntry: - if (ContainerTypeEntry *ce = parseContainerTypeEntry(reader, name, versionRange.since, &attributes)) { - applyComplexTypeAttributes(reader, ce, &attributes); - element->entry = ce; - } else { + top->entry = parseContainerTypeEntry(reader, name, versionRange.since, &attributes); + if (top->entry == nullptr) return false; - } break; case StackElement::SmartPointerTypeEntry: - if (SmartPointerTypeEntry *se = parseSmartPointerEntry(reader, name, versionRange.since, &attributes)) { - applyComplexTypeAttributes(reader, se, &attributes); - element->entry = se; - } else { + 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; - element->entry = m_currentEnum; + top->entry = m_currentEnum; break; case StackElement::ValueTypeEntry: - if (ValueTypeEntry *ve = parseValueTypeEntry(reader, name, versionRange.since, &attributes)) { - applyComplexTypeAttributes(reader, ve, &attributes); - element->entry = ve; - } else { + top->entry = parseValueTypeEntry(reader, name, versionRange.since, &attributes); + if (top->entry == nullptr) return false; - } break; case StackElement::NamespaceTypeEntry: - if (auto entry = parseNamespaceTypeEntry(reader, name, versionRange.since, &attributes)) - element->entry = entry; - else + top->entry = parseNamespaceTypeEntry(reader, name, versionRange.since, &attributes); + if (top->entry == nullptr) return false; break; case StackElement::ObjectTypeEntry: - case StackElement::InterfaceTypeEntry: + case StackElement::InterfaceTypeEntry: { if (!checkRootElement()) return false; - element->entry = new ObjectTypeEntry(name, versionRange.since, currentParentTypeEntry()); - applyCommonAttributes(reader, element->entry, &attributes); - applyComplexTypeAttributes(reader, static_cast<ComplexTypeEntry *>(element->entry), &attributes); + 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: - element->entry = parseFunctionTypeEntry(reader, name, versionRange.since, &attributes); - if (Q_UNLIKELY(!element->entry)) + top->entry = parseFunctionTypeEntry(reader, name, versionRange.since, &attributes); + if (Q_UNLIKELY(!top->entry)) return false; break; case StackElement::TypedefTypeEntry: - if (TypedefEntry *te = parseTypedefEntry(reader, name, versionRange.since, &attributes)) { - applyComplexTypeAttributes(reader, te, &attributes); - element->entry = te; - } else { + top->entry = parseTypedefEntry(reader, name, topElement, + versionRange.since, &attributes); + if (top->entry == nullptr) return false; - } break; default: Q_ASSERT(false); } - if (element->entry) { - if (!m_database->addType(element->entry, &m_error)) + 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() - << QStringLiteral("Type: %1 was rejected by typesystem").arg(name); + << u"Type: "_s + name + u" was rejected by typesystem"_s; } - } else if (element->type == StackElement::InjectDocumentation) { - if (!parseInjectDocumentation(reader, &attributes)) + } else if (element == StackElement::InjectDocumentation) { + if (!parseInjectDocumentation(reader, topElement, &attributes)) return false; - } else if (element->type == StackElement::ModifyDocumentation) { - if (!parseModifyDocumentation(reader, &attributes)) + } else if (element == StackElement::ModifyDocumentation) { + if (!parseModifyDocumentation(reader, topElement, &attributes)) return false; - } else if (element->type != StackElement::None) { - bool topLevel = element->type == StackElement::Root - || element->type == StackElement::SuppressedWarning - || element->type == StackElement::Rejection - || element->type == StackElement::LoadTypesystem - || element->type == StackElement::InjectCode - || element->type == StackElement::ExtraIncludes - || element->type == StackElement::SystemInclude - || element->type == StackElement::ConversionRule - || element->type == StackElement::AddFunction - || element->type == StackElement::Template; - - if (!topLevel && m_current->type == StackElement::Root) { - m_error = QStringLiteral("Tag requires parent: '%1'").arg(tagName); + } 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; } - StackElement topElement = !m_current ? StackElement(nullptr) : *m_current; - element->entry = topElement.entry; - - switch (element->type) { + switch (element) { case StackElement::Root: - element->entry = parseRootElement(reader, versionRange.since, &attributes); - element->type = StackElement::Root; + top->entry = parseRootElement(reader, versionRange.since, &attributes); break; case StackElement::LoadTypesystem: if (!loadTypesystem(reader, &attributes)) @@ -3085,15 +3485,24 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) return false; break; case StackElement::TargetToNative: { - if (topElement.type != StackElement::ConversionRule) { - m_error = QLatin1String("Target to Native conversions can only be specified for custom conversion rules."); + if (topElement != StackElement::ConversionRule) { + m_error = u"Target to Native conversions can only be specified for custom conversion rules."_s; return false; } - const int replaceIndex = indexOfAttribute(attributes, replaceAttribute()); - const bool replace = replaceIndex == -1 - || convertBoolean(attributes.takeAt(replaceIndex).value(), - replaceAttribute(), true); - m_current->entry->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); + + 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: @@ -3113,14 +3522,17 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) return false; break; case StackElement::SuppressedWarning: { - const int textIndex = indexOfAttribute(attributes, textAttribute()); + 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_database->addSuppressedWarning(suppressedWarning, &m_error)) + if (!m_context->db->addSuppressedWarning(suppressedWarning, + m_generate == TypeEntry::GenerateCode, + &m_error)) { return false; + } } } break; @@ -3129,12 +3541,12 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) return false; break; case StackElement::RemoveArgument: - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Removing argument requires argument modification as parent"); + if (topElement != StackElement::ModifyArgument) { + m_error = u"Removing argument requires argument modification as parent"_s; return false; } - m_contextStack.top()->functionMods.last().argument_mods().last().setRemoved(true); + top->functionMods.last().argument_mods().last().setRemoved(true); break; case StackElement::ModifyField: @@ -3143,7 +3555,11 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) break; case StackElement::DeclareFunction: case StackElement::AddFunction: - if (!parseAddFunction(reader, topElement, element->type, &attributes)) + if (!parseAddFunction(reader, topElement, element, &attributes)) + return false; + break; + case StackElement::AddPyMethodDef: + if (!parseAddPyMethodDef(reader, topElement, &attributes)) return false; break; case StackElement::Property: @@ -3159,12 +3575,7 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) return false; break; case StackElement::RemoveDefaultExpression: - m_contextStack.top()->functionMods.last().argument_mods().last().setRemovedDefaultExpression(true); - break; - case StackElement::CustomMetaConstructor: - case StackElement::CustomMetaDestructor: - element->value.customFunction = - parseCustomMetaConstructor(reader, element->type, topElement, &attributes); + top->functionMods.last().argument_mods().last().setRemovedDefaultExpression(true); break; case StackElement::ReferenceCount: if (!parseReferenceCount(reader, topElement, &attributes)) @@ -3175,46 +3586,52 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) return false; break; case StackElement::Array: - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("array must be child of modify-argument"); + if (topElement != StackElement::ModifyArgument) { + m_error = u"array must be child of modify-argument"_s; return false; } - m_contextStack.top()->functionMods.last().argument_mods().last().setArray(true); + top->functionMods.last().argument_mods().last().setArray(true); break; case StackElement::InjectCode: - if (!parseInjectCode(reader, topElement, element.get(), &attributes)) + if (!parseInjectCode(reader, topElement, &attributes)) return false; break; case StackElement::Include: - if (!parseInclude(reader, topElement, element->entry, &attributes)) + if (!parseInclude(reader, topElement, top->entry, &attributes)) return false; break; case StackElement::Rejection: - if (!addRejection(m_database, &attributes, &m_error)) + 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 int nameIndex = indexOfAttribute(attributes, nameAttribute()); + const auto nameIndex = indexOfAttribute(attributes, nameAttribute); if (nameIndex == -1) { - m_error = msgMissingAttribute(nameAttribute()); + m_error = msgMissingAttribute(nameAttribute); return false; } - element->value.templateEntry = - new TemplateEntry(attributes.takeAt(nameIndex).value().toString()); + m_templateEntry.reset(new TemplateEntry(attributes.takeAt(nameIndex).value().toString())); } break; - case StackElement::TemplateInstanceEnum: - element->value.templateInstance = - parseTemplateInstanceEnum(reader, topElement, &attributes); - if (!element->value.templateInstance) + case StackElement::InsertTemplate: + m_templateInstance.reset(parseInsertTemplate(reader, topElement, &attributes)); + if (!m_templateInstance) return false; break; case StackElement::Replace: - if (!parseReplace(reader, topElement, element.get(), &attributes)) + 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: @@ -3227,6 +3644,5 @@ bool TypeSystemParser::startElement(const ConditionalStreamReader &reader) qCWarning(lcShiboken, "%s", qPrintable(msgReaderWarning(reader, message))); } - m_current = element.release(); return true; } |