diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/typedatabase.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/typedatabase.cpp | 1294 |
1 files changed, 1032 insertions, 262 deletions
diff --git a/sources/shiboken6/ApiExtractor/typedatabase.cpp b/sources/shiboken6/ApiExtractor/typedatabase.cpp index 0613da82a..749c4baa3 100644 --- a/sources/shiboken6/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken6/ApiExtractor/typedatabase.cpp @@ -1,70 +1,317 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 "typedatabase.h" -#include "typesystem.h" -#include "typesystemparser.h" - +#include "abstractmetatype.h" +#include "addedfunction.h" +#include "messages.h" +#include "typesystemparser_p.h" +#include "complextypeentry.h" +#include "constantvaluetypeentry.h" +#include "containertypeentry.h" +#include "customtypenentry.h" +#include "debughelpers_p.h" +#include "exception.h" +#include "flagstypeentry.h" +#include "functiontypeentry.h" +#include "namespacetypeentry.h" +#include "objecttypeentry.h" +#include "primitivetypeentry.h" +#include "optionsparser.h" +#include "pythontypeentry.h" +#include "smartpointertypeentry.h" +#include "typedefentry.h" +#include "typesystemtypeentry.h" +#include "varargstypeentry.h" +#include "voidtypeentry.h" +#include "conditionalstreamreader.h" +#include "predefined_templates.h" +#include "clangparser/compilersupport.h" +#include "modifications.h" + +#include "qtcompat.h" + +#include <QtCore/QBuffer> #include <QtCore/QFile> #include <QtCore/QDebug> #include <QtCore/QDir> -#include <QtCore/QPair> #include <QtCore/QList> #include <QtCore/QRegularExpression> #include <QtCore/QVersionNumber> #include <QtCore/QXmlStreamReader> #include "reporthandler.h" -// #include <tr1/tuple> + #include <algorithm> +#include <utility> + +using namespace Qt::StringLiterals; + +using TypeDatabaseParserContextPtr = std::shared_ptr<TypeDatabaseParserContext>; // package -> api-version static QString wildcardToRegExp(QString w) { - w.replace(QLatin1Char('?'), QLatin1Char('.')); - w.replace(QLatin1Char('*'), QStringLiteral(".*")); + w.replace(u'?', u'.'); + w.replace(u'*', ".*"_L1); return w; } -using ApiVersion =QPair<QRegularExpression, QVersionNumber>; +using ApiVersion = std::pair<QRegularExpression, QVersionNumber>; using ApiVersions = QList<ApiVersion>; Q_GLOBAL_STATIC(ApiVersions, apiVersions) -TypeDatabase::TypeDatabase() +struct PythonType +{ + QString name; + QString checkFunction; + TypeSystem::CPythonType type; +}; + +using PythonTypes = QList<PythonType>; + +static const PythonTypes &builtinPythonTypes() +{ + static const PythonTypes result{ + // "Traditional" custom types + // numpy + {u"PyArrayObject"_s, u"PyArray_Check"_s, TypeSystem::CPythonType::Other}, + {u"PyBuffer"_s, u"Shiboken::Buffer::checkType"_s, TypeSystem::CPythonType::Other}, + {u"PyByteArray"_s, u"PyByteArray_Check"_s, TypeSystem::CPythonType::Other}, + {u"PyBytes"_s, u"PyBytes_Check"_s, TypeSystem::CPythonType::Other}, + {u"PyCallable"_s, u"PyCallable_Check"_s, TypeSystem::CPythonType::Other}, + {u"PyDate"_s, u"PyDate_Check"_s, TypeSystem::CPythonType::Other}, + {u"PyDateTime"_s, u"PyDateTime_Check_Check"_s, TypeSystem::CPythonType::Other}, + {u"PyDict"_s, u"PyDict_Check"_s, TypeSystem::CPythonType::Other}, + // Convenience macro in sbkconverter.h + {u"PyObject"_s, u"true"_s, TypeSystem::CPythonType::Other}, + // shiboken-specific + {u"PyPathLike"_s, u"Shiboken::String::checkPath"_s, TypeSystem::CPythonType::Other}, + {u"PySequence"_s, u"Shiboken::String::checkIterable"_s, TypeSystem::CPythonType::Other}, + {u"PyUnicode"_s, u"PyUnicode_Check"_s, TypeSystem::CPythonType::String}, + {u"PyTypeObject"_s, u"PyType_Check"_s, TypeSystem::CPythonType::Other}, + {u"str"_s, u"Shiboken::String::check"_s, TypeSystem::CPythonType::String}, + // Types used as target lang API types for primitive types + {u"PyBool"_s, u"PyBool_Check"_s, TypeSystem::CPythonType::Bool}, + {u"PyComplex"_s, u"PyComplex_Check"_s, TypeSystem::CPythonType::Other}, + {u"PyLong"_s, u"PyLong_Check"_s, TypeSystem::CPythonType::Integer}, + {u"PyFloat"_s, u"PyFloat_Check"_s, TypeSystem::CPythonType::Float}, + // Single character strings to match C++ char types + {u"SbkChar"_s, u"SbkChar_Check"_s, TypeSystem::CPythonType::String} + }; + return result; +} + +struct SuppressedWarning +{ + QRegularExpression pattern; + QString rawText; + bool generate; // Current type system + mutable bool matched = false; +}; + +QList<OptionDescription> TypeDatabase::options() +{ + return { + {u"api-version=<\"package mask\">,<\"version\">"_s, + u"Specify the supported api version used to generate the bindings"_s}, + {u"drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\""_s, + u"Semicolon separated list of type system entries (classes, namespaces,\n" + "global functions and enums) to be dropped from generation."_s}, + {u"-T<path>"_s, {} }, + {u"typesystem-paths="_s + OptionsParser::pathSyntax(), + u"Paths used when searching for typesystems"_s}, + {u"force-process-system-include-paths="_s + OptionsParser::pathSyntax(), + u"Include paths that are considered as system headers by the C++ parser, but should still " + "be processed to extract types (e.g. Qt include paths in a yocto sysroot)"_s}, + {u"keywords=keyword1[,keyword2,...]"_s, + u"A comma-separated list of keywords for conditional typesystem parsing"_s}, + }; +} + +struct TypeDatabaseOptions +{ + QStringList m_dropTypeEntries; + QStringList m_forceProcessSystemIncludes; + QStringList m_typesystemKeywords; + QStringList m_typesystemPaths; + bool m_suppressWarnings = true; +}; + +class TypeDatabaseOptionsParser : public OptionsParser +{ +public: + explicit TypeDatabaseOptionsParser(TypeDatabaseOptions *o) : m_options(o) {} + + bool handleBoolOption(const QString &key, OptionSource source) override; + bool handleOption(const QString &key, const QString &value, OptionSource source) override; + +private: + TypeDatabaseOptions *m_options; +}; + +bool TypeDatabaseOptionsParser::handleBoolOption(const QString &key, OptionSource source) +{ + switch (source) { + case OptionSource::CommandLine: + case OptionSource::ProjectFile: + if (key == u"no-suppress-warnings") { + m_options->m_suppressWarnings = false; + return true; + } + break; + case OptionSource::CommandLineSingleDash: + if (key.startsWith(u'T')) { // "-T/path" ends up a bool option + m_options->m_typesystemPaths += key.sliced(1).split(QDir::listSeparator(), + Qt::SkipEmptyParts); + return true; + } + break; + } + return false; +} + +bool TypeDatabaseOptionsParser::handleOption(const QString &key, const QString &value, + OptionSource source) +{ + if (source == OptionSource::CommandLineSingleDash) + return false; + if (key == u"api-version") { + const auto fullVersions = QStringView{value}.split(u'|'); + for (const auto &fullVersion : fullVersions) { + const auto parts = fullVersion.split(u','); + const QString package = parts.size() == 1 + ? u"*"_s : parts.constFirst().toString(); + const QString version = parts.constLast().toString(); + if (!TypeDatabase::setApiVersion(package, version)) + throw Exception(msgInvalidVersion(package, version)); + } + return true; + } + + if (key == u"drop-type-entries") { + m_options->m_dropTypeEntries = value.split(u';'); + m_options->m_dropTypeEntries.sort(); + return true; + } + + if (key == u"keywords") { + m_options->m_typesystemKeywords = value.split(u','); + return true; + } + + if (key == u"typesystem-paths") { + m_options->m_typesystemPaths += value.split(QDir::listSeparator(), + Qt::SkipEmptyParts); + return true; + } + + if (key == u"force-process-system-include-paths") { + m_options->m_forceProcessSystemIncludes += value.split(QDir::listSeparator(), + Qt::SkipEmptyParts); + return true; + } + + if (source == OptionSource::ProjectFile) { + if (key == u"typesystem-path") { + m_options->m_typesystemPaths += value; + return true; + } + } + + return false; +} + +struct TypeDatabasePrivate : public TypeDatabaseOptions +{ + TypeSystemTypeEntryCPtr defaultTypeSystemType() const; + TypeEntryPtr findType(const QString &name) const; + TypeEntryCList findCppTypes(const QString &name) const; + bool addType(TypeEntryPtr e, QString *errorMessage = nullptr); + bool parseFile(QIODevice *device, TypeDatabase *db, bool generate = true); + static bool parseFile(const TypeDatabaseParserContextPtr &context, + QIODevice *device, bool generate = true); + bool parseFile(const TypeDatabaseParserContextPtr &context, + const QString &filename, const QString ¤tPath, bool generate); + bool prepareParsing(QFile &file, const QString &origFileName, + const QString ¤tPath = {}); + + QString modifiedTypesystemFilepath(const QString& tsFile, + const QString ¤tPath) const; + void addBuiltInType(const TypeEntryPtr &e); + PrimitiveTypeEntryPtr addBuiltInPrimitiveType(const QString &name, + const TypeSystemTypeEntryCPtr &root, + const QString &rootPackage, + const CustomTypeEntryPtr &targetLang); + void addBuiltInCppStringPrimitiveType(const QString &name, + const QString &viewName, + const TypeSystemTypeEntryCPtr &root, + const QString &rootPackage, + const CustomTypeEntryPtr &targetLang); + void addBuiltInPrimitiveTypes(); + void addBuiltInContainerTypes(const TypeDatabaseParserContextPtr &context); + bool addOpaqueContainers(const TypeDatabaseParserContextPtr &context); + TypeEntryMultiMapConstIteratorRange findTypeRange(const QString &name) const; + template <class Predicate> + TypeEntryCList findTypesHelper(const QString &name, Predicate pred) const; + template <class Type, class Predicate> + QList<std::shared_ptr<const Type> > findTypesByTypeHelper(Predicate pred) const; + TypeEntryPtr resolveTypeDefEntry(const TypedefEntryPtr &typedefEntry, QString *errorMessage); + template <class String> + bool isSuppressedWarningHelper(const String &s) const; + bool resolveSmartPointerInstantiations(const TypeDatabaseParserContextPtr &context); + void formatDebug(QDebug &d) const; + void formatBuiltinTypes(QDebug &d) const; + + TypeEntryMultiMap m_entries; // Contains duplicate entries (cf addInlineNamespaceLookups). + TypeEntryMap m_flagsEntries; + TypedefEntryMap m_typedefEntries; + TemplateEntryMap m_templates; + QList<SuppressedWarning> m_suppressedWarnings; + QList<TypeSystemTypeEntryCPtr > m_typeSystemEntries; // maintain order, default is first. + + AddedFunctionList m_globalUserFunctions; + FunctionModificationList m_functionMods; + + QStringList m_requiredTargetImports; + + QHash<QString, bool> m_parsedTypesystemFiles; + + QList<TypeRejection> m_rejections; +}; + +static const char ENV_TYPESYSTEMPATH[] = "TYPESYSTEMPATH"; + +TypeDatabase::TypeDatabase() : d(new TypeDatabasePrivate) +{ + // Environment TYPESYSTEMPATH + if (qEnvironmentVariableIsSet(ENV_TYPESYSTEMPATH)) { + d->m_typesystemPaths + += qEnvironmentVariable(ENV_TYPESYSTEMPATH).split(QDir::listSeparator(), + Qt::SkipEmptyParts); + } + + d->addBuiltInType(TypeEntryPtr(new VoidTypeEntry())); + d->addBuiltInType(TypeEntryPtr(new VarargsTypeEntry())); + for (const auto &pt : builtinPythonTypes()) + d->addBuiltInType(TypeEntryPtr(new PythonTypeEntry(pt.name, pt.checkFunction, pt.type))); + + for (const auto &p : predefinedTemplates()) + addTemplate(p.name, p.content); +} + +TypeDatabase::~TypeDatabase() { - addType(new VoidTypeEntry()); - addType(new VarargsTypeEntry()); + delete d; } -TypeDatabase::~TypeDatabase() = default; +std::shared_ptr<OptionsParser> TypeDatabase::createOptionsParser() +{ + return std::make_shared<TypeDatabaseOptionsParser>(d); +} -TypeDatabase* TypeDatabase::instance(bool newInstance) +TypeDatabase *TypeDatabase::instance(bool newInstance) { static TypeDatabase *db = nullptr; if (!db || newInstance) { @@ -90,12 +337,11 @@ static const IntTypeNormalizationEntries &intTypeNormalizationEntries() static bool firstTime = true; if (firstTime) { firstTime = false; - for (auto t : {"char", "short", "int", "long"}) { - const QString intType = QLatin1String(t); - if (!TypeDatabase::instance()->findType(QLatin1Char('u') + intType)) { + for (const auto &intType : {"char"_L1, "short"_L1, "int"_L1, "long"_L1}) { + if (!TypeDatabase::instance()->findType(u'u' + intType)) { IntTypeNormalizationEntry entry; - entry.replacement = QStringLiteral("unsigned ") + intType; - entry.regex.setPattern(QStringLiteral("\\bu") + intType + QStringLiteral("\\b")); + entry.replacement = "unsigned "_L1 + intType; + entry.regex.setPattern("\\bu"_L1 + intType + "\\b"_L1); Q_ASSERT(entry.regex.isValid()); result.append(entry); } @@ -104,11 +350,64 @@ static const IntTypeNormalizationEntries &intTypeNormalizationEntries() return result; } +// Normalization helpers +enum CharCategory { Space, Identifier, Other }; + +static CharCategory charCategory(QChar c) +{ + if (c.isSpace()) + return Space; + if (c.isLetterOrNumber() || c == u'_') + return Identifier; + return Other; +} + +// Normalize a C++ function signature: +// Drop space except between identifiers ("unsigned int", "const int") +static QString normalizeCppFunctionSignature(const QString &signatureIn) +{ + const QString signature = signatureIn.simplified(); + QString result; + result.reserve(signature.size()); + + CharCategory lastNonSpaceCategory = Other; + bool pendingSpace = false; + for (QChar c : signature) { + if (c.isSpace()) { + pendingSpace = true; + } else { + const auto category = charCategory(c); + if (pendingSpace) { + if (lastNonSpaceCategory == Identifier && category == Identifier) + result.append(u' '); + pendingSpace = false; + } + lastNonSpaceCategory = category; + result.append(c); + } + } + return result; +} + +// Normalize a signature for <add-function> by removing spaces +QString TypeDatabase::normalizedAddedFunctionSignature(const QString &signature) +{ + return normalizeCppFunctionSignature(signature); +} + +// Normalize a signature for matching by <modify-function>/<function> +// by removing spaces and changing const-ref to value. +// FIXME: PYSIDE7: Check whether the above simple normalization can be used +// here as well. Note though that const-ref would then have to be spelled out +// in typeystem XML. QString TypeDatabase::normalizedSignature(const QString &signature) { - QString normalized = QLatin1String(QMetaObject::normalizedSignature(signature.toUtf8().constData())); + // QMetaObject::normalizedSignature() changes const-ref to value and + // changes "unsigned int" to "uint" which is undone by the below code + QByteArray normalizedB = QMetaObject::normalizedSignature(signature.toUtf8().constData()); + QString normalized = QLatin1StringView(normalizedB); - if (instance() && signature.contains(QLatin1String("unsigned"))) { + if (instance() && signature.contains(u"unsigned")) { const IntTypeNormalizationEntries &entries = intTypeNormalizationEntries(); for (const auto &entry : entries) normalized.replace(entry.regex, entry.replacement); @@ -119,133 +418,185 @@ QString TypeDatabase::normalizedSignature(const QString &signature) QStringList TypeDatabase::requiredTargetImports() const { - return m_requiredTargetImports; + return d->m_requiredTargetImports; } void TypeDatabase::addRequiredTargetImport(const QString& moduleName) { - if (!m_requiredTargetImports.contains(moduleName)) - m_requiredTargetImports << moduleName; + if (!d->m_requiredTargetImports.contains(moduleName)) + d->m_requiredTargetImports << moduleName; } -void TypeDatabase::addTypesystemPath(const QString& typesystem_paths) +QStringList TypeDatabase::typesystemKeywords() const { - #if defined(Q_OS_WIN32) - const char path_splitter = ';'; - #else - const char path_splitter = ':'; - #endif - m_typesystemPaths += typesystem_paths.split(QLatin1Char(path_splitter)); + QStringList result = d->m_typesystemKeywords; + for (const auto &d : d->m_dropTypeEntries) + result.append("no_"_L1 + d); + + switch (clang::emulatedCompilerLanguageLevel()) { + case LanguageLevel::Cpp11: + result.append(u"c++11"_s); + break; + case LanguageLevel::Cpp14: + result.append(u"c++14"_s); + break; + case LanguageLevel::Cpp17: + result.append(u"c++17"_s); + break; + case LanguageLevel::Cpp20: + result.append(u"c++20"_s); + break; + default: + break; + } + return result; } IncludeList TypeDatabase::extraIncludes(const QString& className) const { - ComplexTypeEntry* typeEntry = findComplexType(className); - return typeEntry ? typeEntry->extraIncludes() : IncludeList(); + auto typeEntry = findComplexType(className); + return typeEntry ? typeEntry->extraIncludes() : IncludeList(); } -void TypeDatabase::addSystemInclude(const QString &name) +const QStringList &TypeDatabase::forceProcessSystemIncludes() const { - m_systemIncludes.append(name.toUtf8()); + return d->m_forceProcessSystemIncludes; +} + +void TypeDatabase::addForceProcessSystemInclude(const QString &name) +{ + d->m_forceProcessSystemIncludes.append(name); } // Add a lookup for the short name excluding inline namespaces // so that "std::shared_ptr" finds "std::__1::shared_ptr" as well. -// Note: This inserts duplicate TypeEntry * into m_entries. -void TypeDatabase::addInlineNamespaceLookups(const NamespaceTypeEntry *n) +// Note: This inserts duplicate TypeEntryPtr into m_entries. +void TypeDatabase::addInlineNamespaceLookups(const NamespaceTypeEntryCPtr &n) { TypeEntryList additionalEntries; // Store before modifying the hash - for (TypeEntry *entry : qAsConst(m_entries)) { + for (const auto &entry : std::as_const(d->m_entries)) { if (entry->isChildOf(n)) additionalEntries.append(entry); } - for (const auto &ae : qAsConst(additionalEntries)) - m_entries.insert(ae->shortName(), ae); + for (const auto &ae : std::as_const(additionalEntries)) + d->m_entries.insert(ae->shortName(), ae); } -ContainerTypeEntry* TypeDatabase::findContainerType(const QString &name) const +ContainerTypeEntryPtr TypeDatabase::findContainerType(const QString &name) const { QString template_name = name; - int pos = name.indexOf(QLatin1Char('<')); + const auto pos = name.indexOf(u'<'); if (pos > 0) template_name = name.left(pos); - TypeEntry* type_entry = findType(template_name); + auto type_entry = findType(template_name); if (type_entry && type_entry->isContainer()) - return static_cast<ContainerTypeEntry*>(type_entry); - return nullptr; + return std::static_pointer_cast<ContainerTypeEntry>(type_entry); + return {}; } -static bool inline useType(const TypeEntry *t) +static bool inline useType(const TypeEntryCPtr &t) { return !t->isPrimitive() - || static_cast<const PrimitiveTypeEntry *>(t)->preferredTargetLangType(); + || std::static_pointer_cast<const PrimitiveTypeEntry>(t)->preferredTargetLangType(); } -FunctionTypeEntry* TypeDatabase::findFunctionType(const QString& name) const +FunctionTypeEntryPtr TypeDatabase::findFunctionType(const QString &name) const { - const auto entries = findTypeRange(name); - for (TypeEntry *entry : entries) { + const auto entries = d->findTypeRange(name); + for (const TypeEntryPtr &entry : entries) { if (entry->type() == TypeEntry::FunctionType && useType(entry)) - return static_cast<FunctionTypeEntry*>(entry); + return std::static_pointer_cast<FunctionTypeEntry>(entry); } - return nullptr; + return {}; } -void TypeDatabase::addTypeSystemType(const TypeSystemTypeEntry *e) +void TypeDatabase::addTypeSystemType(const TypeSystemTypeEntryCPtr &e) { - m_typeSystemEntries.append(e); + d->m_typeSystemEntries.append(e); } -const TypeSystemTypeEntry *TypeDatabase::findTypeSystemType(const QString &name) const +TypeSystemTypeEntryCPtr TypeDatabase::findTypeSystemType(const QString &name) const { - for (auto entry : m_typeSystemEntries) { + for (auto entry : d->m_typeSystemEntries) { if (entry->name() == name) return entry; } - return nullptr; + return {}; +} + +TypeSystemTypeEntryCPtr TypeDatabase::defaultTypeSystemType() const +{ + return d->defaultTypeSystemType(); } -const TypeSystemTypeEntry *TypeDatabase::defaultTypeSystemType() const +QString TypeDatabase::loadedTypeSystemNames() const +{ + QString result; + for (const auto &entry : d->m_typeSystemEntries) { + if (!result.isEmpty()) + result += u", "_s; + result += entry->name(); + } + return result; +} + +TypeSystemTypeEntryCPtr TypeDatabasePrivate::defaultTypeSystemType() const { return m_typeSystemEntries.value(0, nullptr); } QString TypeDatabase::defaultPackageName() const { - Q_ASSERT(!m_typeSystemEntries.isEmpty()); - return m_typeSystemEntries.constFirst()->name(); + Q_ASSERT(!d->m_typeSystemEntries.isEmpty()); + return d->m_typeSystemEntries.constFirst()->name(); } -TypeEntry* TypeDatabase::findType(const QString& name) const +TypeEntryPtr TypeDatabase::findType(const QString& name) const +{ + return d->findType(name); +} + +TypeEntryPtr TypeDatabasePrivate::findType(const QString& name) const { const auto entries = findTypeRange(name); - for (TypeEntry *entry : entries) { + for (const auto &entry : entries) { if (useType(entry)) return entry; } - return nullptr; + return {}; } template <class Predicate> -TypeEntries TypeDatabase::findTypesHelper(const QString &name, Predicate pred) const +TypeEntryCList TypeDatabasePrivate::findTypesHelper(const QString &name, Predicate pred) const { - TypeEntries result; + TypeEntryCList result; const auto entries = findTypeRange(name); - for (TypeEntry *entry : entries) { + for (const auto &entry : entries) { if (pred(entry)) result.append(entry); } return result; } -TypeEntries TypeDatabase::findTypes(const QString &name) const +template<class Type, class Predicate> +QList<std::shared_ptr<const Type> > TypeDatabasePrivate::findTypesByTypeHelper(Predicate pred) const +{ + QList<std::shared_ptr<const Type> > result; + for (const auto &entry : m_entries) { + if (pred(entry)) + result.append(std::static_pointer_cast<const Type>(entry)); + } + return result; +} + +TypeEntryCList TypeDatabase::findTypes(const QString &name) const { - return findTypesHelper(name, useType); + return d->findTypesHelper(name, useType); } -static bool useCppType(const TypeEntry *t) +static bool useCppType(const TypeEntryCPtr &t) { bool result = false; switch (t->type()) { @@ -269,37 +620,48 @@ static bool useCppType(const TypeEntry *t) return result; } -TypeEntries TypeDatabase::findCppTypes(const QString &name) const +TypeEntryCList TypeDatabase::findCppTypes(const QString &name) const +{ + return d->findCppTypes(name); +} + +TypeEntryCList TypeDatabasePrivate::findCppTypes(const QString &name) const { return findTypesHelper(name, useCppType); } -TypeEntryMultiMapConstIteratorRange TypeDatabase::findTypeRange(const QString &name) const +const TypeEntryMultiMap &TypeDatabase::entries() const +{ + return d->m_entries; +} + +const TypedefEntryMap &TypeDatabase::typedefEntries() const +{ + return d->m_typedefEntries; +} + +TypeEntryMultiMapConstIteratorRange TypeDatabasePrivate::findTypeRange(const QString &name) const { const auto range = m_entries.equal_range(name); return {range.first, range.second}; } -PrimitiveTypeEntryList TypeDatabase::primitiveTypes() const +PrimitiveTypeEntryCList TypeDatabase::primitiveTypes() const { - PrimitiveTypeEntryList returned; - for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { - TypeEntry *typeEntry = it.value(); - if (typeEntry->isPrimitive()) - returned.append(static_cast<PrimitiveTypeEntry *>(typeEntry)); - } - return returned; + auto pred = [](const TypeEntryCPtr &t) { return t->isPrimitive(); }; + return d->findTypesByTypeHelper<PrimitiveTypeEntry>(pred); } -ContainerTypeEntryList TypeDatabase::containerTypes() const +ContainerTypeEntryCList TypeDatabase::containerTypes() const { - ContainerTypeEntryList returned; - for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { - TypeEntry *typeEntry = it.value(); - if (typeEntry->isContainer()) - returned.append(static_cast<ContainerTypeEntry *>(typeEntry)); - } - return returned; + auto pred = [](const TypeEntryCPtr &t) { return t->isContainer(); }; + return d->findTypesByTypeHelper<ContainerTypeEntry>(pred); +} + +SmartPointerTypeEntryList TypeDatabase::smartPointerTypes() const +{ + auto pred = [](const TypeEntryCPtr &t) { return t->isSmartPointer(); }; + return d->findTypesByTypeHelper<SmartPointerTypeEntry>(pred); } #ifndef QT_NO_DEBUG_STREAM @@ -316,38 +678,15 @@ QDebug operator<<(QDebug d, const TypeRejection &r) void TypeDatabase::addRejection(const TypeRejection &r) { - m_rejections << r; -} - -static inline QString msgRejectReason(const TypeRejection &r, const QString &needle = QString()) -{ - QString result; - QTextStream str(&result); - switch (r.matchType) { - case TypeRejection::ExcludeClass: - str << " matches class exclusion \"" << r.className.pattern() << '"'; - break; - case TypeRejection::Function: - case TypeRejection::Field: - case TypeRejection::Enum: - str << " matches class \"" << r.className.pattern() << "\" and \"" << r.pattern.pattern() << '"'; - break; - case TypeRejection::ArgumentType: - case TypeRejection::ReturnType: - str << " matches class \"" << r.className.pattern() << "\" and \"" << needle - << "\" matches \"" << r.pattern.pattern() << '"'; - break; - case TypeRejection::Invalid: - break; - } - return result; + d->m_rejections << r; } // Match class name only bool TypeDatabase::isClassRejected(const QString& className, QString *reason) const { - for (const TypeRejection& r : m_rejections) { + for (const TypeRejection& r : d->m_rejections) { if (r.matchType == TypeRejection::ExcludeClass && r.className.match(className).hasMatch()) { + r.matched = true; if (reason) *reason = msgRejectReason(r); return true; @@ -366,6 +705,7 @@ static bool findRejection(const QList<TypeRejection> &rejections, for (const TypeRejection& r : rejections) { if (r.matchType == matchType && r.pattern.match(name).hasMatch() && r.className.match(className).hasMatch()) { + r.matched = true; if (reason) *reason = msgRejectReason(r, name); return true; @@ -376,24 +716,24 @@ static bool findRejection(const QList<TypeRejection> &rejections, bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumName, QString *reason) const { - return findRejection(m_rejections, TypeRejection::Enum, className, enumName, reason); + return findRejection(d->m_rejections, TypeRejection::Enum, className, enumName, reason); } -TypeEntry *TypeDatabase::resolveTypeDefEntry(TypedefEntry *typedefEntry, +TypeEntryPtr TypeDatabasePrivate::resolveTypeDefEntry(const TypedefEntryPtr &typedefEntry, QString *errorMessage) { QString sourceName = typedefEntry->sourceType(); - const int lessThanPos = sourceName.indexOf(QLatin1Char('<')); + const auto lessThanPos = sourceName.indexOf(u'<'); if (lessThanPos != -1) sourceName.truncate(lessThanPos); - ComplexTypeEntry *source = nullptr; - for (TypeEntry *e : findTypeRange(sourceName)) { + ComplexTypeEntryPtr source; + for (const auto &e : findTypeRange(sourceName)) { switch (e->type()) { case TypeEntry::BasicValueType: case TypeEntry::ContainerType: case TypeEntry::ObjectType: case TypeEntry::SmartPointerType: - source = dynamic_cast<ComplexTypeEntry *>(e); + source = std::dynamic_pointer_cast<ComplexTypeEntry>(e); Q_ASSERT(source); break; default: @@ -402,23 +742,34 @@ TypeEntry *TypeDatabase::resolveTypeDefEntry(TypedefEntry *typedefEntry, } if (!source) { if (errorMessage) - *errorMessage = QLatin1String("Unable to resolve typedef \"") - + typedefEntry->sourceType() + QLatin1Char('"'); + *errorMessage = msgUnableToResolveTypedef(typedefEntry->sourceType(), sourceName); return nullptr; } - auto *result = static_cast<ComplexTypeEntry *>(source->clone()); + m_typedefEntries.insert(typedefEntry->qualifiedCppName(), typedefEntry); + return TypeDatabase::initializeTypeDefEntry(typedefEntry, source); +} + +ComplexTypeEntryPtr + TypeDatabase::initializeTypeDefEntry(const TypedefEntryPtr &typedefEntry, + const ComplexTypeEntryCPtr &source) +{ + ComplexTypeEntryPtr result(static_cast<ComplexTypeEntry *>(source->clone())); result->useAsTypedef(typedefEntry); typedefEntry->setSource(source); typedefEntry->setTarget(result); - m_typedefEntries.insert(typedefEntry->qualifiedCppName(), typedefEntry); return result; } -bool TypeDatabase::addType(TypeEntry *e, QString *errorMessage) +bool TypeDatabase::addType(const TypeEntryPtr &e, QString *errorMessage) +{ + return d->addType(e, errorMessage); +} + +bool TypeDatabasePrivate::addType(TypeEntryPtr e, QString *errorMessage) { if (e->type() == TypeEntry::TypedefType) { - e = resolveTypeDefEntry(static_cast<TypedefEntry *>(e), errorMessage); + e = resolveTypeDefEntry(std::static_pointer_cast<TypedefEntry>(e), errorMessage); if (Q_UNLIKELY(!e)) return false; } @@ -427,11 +778,11 @@ bool TypeDatabase::addType(TypeEntry *e, QString *errorMessage) } // Add a dummy value entry for non-type template parameters -ConstantValueTypeEntry * +ConstantValueTypeEntryPtr TypeDatabase::addConstantValueTypeEntry(const QString &value, - const TypeEntry *parent) + const TypeEntryCPtr &parent) { - auto result = new ConstantValueTypeEntry(value, parent); + auto result = std::make_shared<ConstantValueTypeEntry>(value, parent); result->setCodeGeneration(TypeEntry::GenerateNothing); addType(result); return result; @@ -440,35 +791,36 @@ ConstantValueTypeEntry * bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName, QString *reason) const { - return findRejection(m_rejections, TypeRejection::Function, className, functionName, reason); + return findRejection(d->m_rejections, TypeRejection::Function, className, functionName, reason); } bool TypeDatabase::isFieldRejected(const QString& className, const QString& fieldName, QString *reason) const { - return findRejection(m_rejections, TypeRejection::Field, className, fieldName, reason); + return findRejection(d->m_rejections, TypeRejection::Field, className, fieldName, reason); } bool TypeDatabase::isArgumentTypeRejected(const QString& className, const QString& typeName, QString *reason) const { - return findRejection(m_rejections, TypeRejection::ArgumentType, className, typeName, reason); + return findRejection(d->m_rejections, TypeRejection::ArgumentType, className, typeName, reason); } bool TypeDatabase::isReturnTypeRejected(const QString& className, const QString& typeName, QString *reason) const { - return findRejection(m_rejections, TypeRejection::ReturnType, className, typeName, reason); + return findRejection(d->m_rejections, TypeRejection::ReturnType, className, typeName, reason); } -FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const +FlagsTypeEntryPtr TypeDatabase::findFlagsType(const QString &name) const { - TypeEntry *fte = findType(name); + TypeEntryPtr fte = findType(name); if (!fte) { - fte = m_flagsEntries.value(name); + fte = d->m_flagsEntries.value(name); if (!fte) { //last hope, search for flag without scope inside of flags hash - for (auto it = m_flagsEntries.cbegin(), end = m_flagsEntries.cend(); it != end; ++it) { + const auto end = d->m_flagsEntries.cend(); + for (auto it = d->m_flagsEntries.cbegin(); it != end; ++it) { if (it.key().endsWith(name)) { fte = it.value(); break; @@ -476,28 +828,45 @@ FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const } } } - return static_cast<FlagsTypeEntry *>(fte); + return std::static_pointer_cast<FlagsTypeEntry>(fte); +} + +void TypeDatabase::addFlagsType(const FlagsTypeEntryPtr &fte) +{ + d->m_flagsEntries[fte->originalName()] = fte; } -void TypeDatabase::addFlagsType(FlagsTypeEntry *fte) +TemplateEntryPtr TypeDatabase::findTemplate(const QString &name) const { - m_flagsEntries[fte->originalName()] = fte; + return d->m_templates[name]; } -void TypeDatabase::addTemplate(TemplateEntry *t) +void TypeDatabase::addTemplate(const TemplateEntryPtr &t) { - m_templates[t->name()] = t; + d->m_templates[t->name()] = t; +} + +void TypeDatabase::addTemplate(const QString &name, const QString &code) +{ + auto te = std::make_shared<TemplateEntry>(name); + te->addCode(code); + addTemplate(te); +} + +AddedFunctionList TypeDatabase::globalUserFunctions() const +{ + return d->m_globalUserFunctions; } void TypeDatabase::addGlobalUserFunctions(const AddedFunctionList &functions) { - m_globalUserFunctions << functions; + d->m_globalUserFunctions << functions; } AddedFunctionList TypeDatabase::findGlobalUserFunctions(const QString& name) const { AddedFunctionList addedFunctions; - for (const AddedFunctionPtr &func : m_globalUserFunctions) { + for (const AddedFunctionPtr &func : d->m_globalUserFunctions) { if (func->name() == name) addedFunctions.append(func); } @@ -506,150 +875,384 @@ AddedFunctionList TypeDatabase::findGlobalUserFunctions(const QString& name) con void TypeDatabase::addGlobalUserFunctionModifications(const FunctionModificationList &functionModifications) { - m_functionMods << functionModifications; + d->m_functionMods << functionModifications; } -QString TypeDatabase::globalNamespaceClassName(const TypeEntry * /*entry*/) +QString TypeDatabase::globalNamespaceClassName(const TypeEntryCPtr & /*entry*/) { - return QLatin1String("Global"); + return u"Global"_s; } -FunctionModificationList TypeDatabase::functionModifications(const QString& signature) const +FunctionModificationList + TypeDatabase::globalFunctionModifications(const QStringList &signatures) const { FunctionModificationList lst; - for (int i = 0; i < m_functionMods.count(); ++i) { - const FunctionModification& mod = m_functionMods.at(i); - if (mod.matches(signature)) + for (const auto &mod : d->m_functionMods) { + if (mod.matches(signatures)) lst << mod; } return lst; } -bool TypeDatabase::addSuppressedWarning(const QString &warning, QString *errorMessage) +bool TypeDatabase::addSuppressedWarning(const QString &warning, bool generate, + QString *errorMessage) { QString pattern; - if (warning.startsWith(QLatin1Char('^')) && warning.endsWith(QLatin1Char('$'))) { + if (warning.startsWith(u'^') && warning.endsWith(u'$')) { pattern = warning; } else { // Legacy syntax: Use wildcards '*' (unless escaped by '\') - QList<int> asteriskPositions; - const int warningSize = warning.size(); - for (int i = 0; i < warningSize; ++i) { - if (warning.at(i) == QLatin1Char('\\')) + QList<qsizetype> asteriskPositions; + const auto warningSize = warning.size(); + for (qsizetype i = 0, warningSize = warning.size(); i < warningSize; ++i) { + if (warning.at(i) == u'\\') ++i; - else if (warning.at(i) == QLatin1Char('*')) + else if (warning.at(i) == u'*') asteriskPositions.append(i); } asteriskPositions.append(warningSize); - pattern.append(QLatin1Char('^')); - int lastPos = 0; - for (int a = 0, aSize = asteriskPositions.size(); a < aSize; ++a) { + pattern.append(u'^'); + qsizetype lastPos = 0; + for (qsizetype a = 0, aSize = asteriskPositions.size(); a < aSize; ++a) { if (a) - pattern.append(QStringLiteral(".*")); - const int nextPos = asteriskPositions.at(a); + pattern.append(".*"_L1); + const auto nextPos = asteriskPositions.at(a); if (nextPos > lastPos) pattern.append(QRegularExpression::escape(warning.mid(lastPos, nextPos - lastPos))); lastPos = nextPos + 1; } - pattern.append(QLatin1Char('$')); + pattern.append(u'$'); } QRegularExpression expression(pattern); if (!expression.isValid()) { - *errorMessage = QLatin1String("Invalid message pattern \"") + warning - + QLatin1String("\": ") + expression.errorString(); + *errorMessage = u"Invalid message pattern \""_s + warning + + u"\": "_s + expression.errorString(); return false; } expression.setPatternOptions(expression.patternOptions() | QRegularExpression::MultilineOption); - m_suppressedWarnings.append(expression); + d->m_suppressedWarnings.append({expression, warning, generate}); return true; } bool TypeDatabase::isSuppressedWarning(QStringView s) const { - if (!m_suppressWarnings) + if (!d->m_suppressWarnings) return false; - return std::any_of(m_suppressedWarnings.cbegin(), m_suppressedWarnings.end(), - [&s] (const QRegularExpression &e) { - return e.match(s).hasMatch(); - }); + auto wit = std::find_if(d->m_suppressedWarnings.cbegin(), d->m_suppressedWarnings.cend(), + [&s] (const SuppressedWarning &e) { + return e.pattern.matchView(s).hasMatch(); + }); + const bool found = wit != d->m_suppressedWarnings.cend(); + if (found) + wit->matched = true; + return found; } QString TypeDatabase::modifiedTypesystemFilepath(const QString& tsFile, const QString ¤tPath) const { + return d->modifiedTypesystemFilepath(tsFile, currentPath); +} + +void TypeDatabase::logUnmatched() const +{ + for (auto &sw : d->m_suppressedWarnings) { + if (sw.generate && !sw.matched) + qWarning("Unmatched suppressed warning: \"%s\"", qPrintable(sw.rawText)); + } + + for (auto &tr : d->m_rejections) { + if (tr.generate && !tr.matched) { + QDebug d = qWarning(); + d.noquote(); + d.nospace(); + d << "Unmatched rejection: " << tr.matchType; + if (!tr.className.pattern().isEmpty()) + d << " class " << tr.className.pattern(); + if (!tr.pattern.pattern().isEmpty()) + d << " \"" << tr.pattern.pattern() << '"'; + } + } +} + +QString TypeDatabasePrivate::modifiedTypesystemFilepath(const QString& tsFile, + const QString ¤tPath) const +{ const QFileInfo tsFi(tsFile); if (tsFi.isAbsolute()) // No point in further lookups return tsFi.absoluteFilePath(); if (tsFi.isFile()) // Make path absolute return tsFi.absoluteFilePath(); if (!currentPath.isEmpty()) { - const QFileInfo fi(currentPath + QLatin1Char('/') + tsFile); + const QFileInfo fi(currentPath + u'/' + tsFile); if (fi.isFile()) return fi.absoluteFilePath(); } for (const QString &path : m_typesystemPaths) { - const QFileInfo fi(path + QLatin1Char('/') + tsFile); + const QFileInfo fi(path + u'/' + tsFile); if (fi.isFile()) return fi.absoluteFilePath(); } return tsFile; } -bool TypeDatabase::parseFile(const QString &filename, bool generate) -{ - return parseFile(filename, QString(), generate); +void TypeDatabasePrivate::addBuiltInContainerTypes(const TypeDatabaseParserContextPtr &context) +{ + // Unless the user has added the standard containers (potentially with + // some opaque types), add them by default. + const bool hasStdArray = findType(u"std::array"_s) != nullptr; + const bool hasStdPair = findType(u"std::pair"_s) != nullptr; + const bool hasStdList = findType(u"std::list"_s) != nullptr; + const bool hasStdVector = findType(u"std::vector"_s) != nullptr; + const bool hasStdMap = findType(u"std::map"_s) != nullptr; + const bool hasStdUnorderedMap = findType(u"std::unordered_map"_s) != nullptr; + const bool hasStdSpan = findType(u"std::span"_s) != nullptr; + + if (hasStdPair && hasStdList && hasStdVector && hasStdMap && hasStdUnorderedMap) + return; + + QByteArray ts = R"(<?xml version="1.0" encoding="UTF-8"?><typesystem>)"; + if (!hasStdArray) { + ts += containerTypeSystemSnippet( + "std::array", "list", "array", + "shiboken_conversion_cppsequence_to_pylist", + "PySequence", + "shiboken_conversion_pyiterable_to_cpparray"); + } + if (!hasStdPair) { + ts += containerTypeSystemSnippet( + "std::pair", "pair", "utility", + "shiboken_conversion_cpppair_to_pytuple", + "PySequence", "shiboken_conversion_pysequence_to_cpppair"); + } + if (!hasStdList) { + ts += containerTypeSystemSnippet( + "std::list", "list", "list", + "shiboken_conversion_cppsequence_to_pylist", + "PySequence", + "shiboken_conversion_pyiterable_to_cppsequentialcontainer"); + } + if (!hasStdVector) { + ts += containerTypeSystemSnippet( + "std::vector", "list", "vector", + "shiboken_conversion_cppsequence_to_pylist", + "PySequence", + "shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve"); + } + if (!hasStdMap) { + ts += containerTypeSystemSnippet( + "std::map", "map", "map", + "shiboken_conversion_stdmap_to_pydict", + "PyDict", "shiboken_conversion_pydict_to_stdmap"); + } + if (!hasStdUnorderedMap) { + ts += containerTypeSystemSnippet( + "std::unordered_map", "map", "unordered_map", + "shiboken_conversion_stdmap_to_pydict", + "PyDict", "shiboken_conversion_pydict_to_stdmap"); + } + if (!hasStdSpan + && clang::emulatedCompilerLanguageLevel() >= LanguageLevel::Cpp20) { + auto spanSnip = containerTypeSystemSnippet( + "std::span", "span", "span", + "shiboken_conversion_cppsequence_to_pylist"); + auto pos = spanSnip.indexOf('>'); + spanSnip.insert(pos, R"( view-on="std::vector")"); + ts += spanSnip; + } + + ts += "</typesystem>"; + QBuffer buffer(&ts); + buffer.open(QIODevice::ReadOnly); + const bool ok = parseFile(context, &buffer, true); + Q_ASSERT(ok); } -bool TypeDatabase::parseFile(const QString &filename, const QString ¤tPath, bool generate) +bool TypeDatabasePrivate::addOpaqueContainers(const TypeDatabaseParserContextPtr &context) { + const auto &och = context->opaqueContainerHash; + for (auto it = och.cbegin(), end = och.cend(); it != end; ++it) { + const QString &name = it.key(); + auto te = findType(name); + if (!te || !te->isContainer()) { + qCWarning(lcShiboken, "No container \"%s\" found.", qPrintable(name)); + return false; + } + auto cte = std::static_pointer_cast<ContainerTypeEntry>(te); + cte->appendOpaqueContainers(it.value()); + } + return true; +} - QString filepath = modifiedTypesystemFilepath(filename, currentPath); - if (m_parsedTypesystemFiles.contains(filepath)) - return m_parsedTypesystemFiles[filepath]; +bool TypeDatabase::parseFile(const QString &filename, bool generate) +{ + QString filepath = modifiedTypesystemFilepath(filename, {}); + QFile file(filepath); + return d->prepareParsing(file, filename) && d->parseFile(&file, this, generate); +} - m_parsedTypesystemFiles[filepath] = true; // Prevent recursion when including self. +bool TypeDatabase::parseFile(const TypeDatabaseParserContextPtr &context, + const QString &filename, const QString ¤tPath, + bool generate) +{ + return d->parseFile(context, filename, currentPath, generate); +} - QFile file(filepath); +bool TypeDatabasePrivate::prepareParsing(QFile &file, const QString &origFileName, + const QString ¤tPath) +{ + const QString &filepath = file.fileName(); if (!file.exists()) { m_parsedTypesystemFiles[filepath] = false; - QString message = QLatin1String("Can't find ") + filename; + QString message = u"Can't find "_s + origFileName; if (!currentPath.isEmpty()) - message += QLatin1String(", current path: ") + currentPath; - message += QLatin1String(", typesystem paths: ") + m_typesystemPaths.join(QLatin1String(", ")); - qCWarning(lcShiboken).noquote().nospace() << message; + message += u", current path: "_s + currentPath; + message += u", typesystem paths: "_s + m_typesystemPaths.join(u", "_s); + qCWarning(lcShiboken, "%s", qPrintable(message)); return false; } if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { m_parsedTypesystemFiles[filepath] = false; - qCWarning(lcShiboken).noquote().nospace() - << "Can't open " << QDir::toNativeSeparators(filename) << ": " << file.errorString(); + qCWarning(lcShiboken, "%s", qPrintable(msgCannotOpenForReading(file))); return false; } - bool ok = parseFile(&file, generate); + m_parsedTypesystemFiles[filepath] = true; + return true; +} + +bool TypeDatabasePrivate::parseFile(const TypeDatabaseParserContextPtr &context, + const QString &filename, const QString ¤tPath, + bool generate) +{ + // Prevent recursion when including self. + QString filepath = modifiedTypesystemFilepath(filename, currentPath); + const auto it = m_parsedTypesystemFiles.constFind(filepath); + if (it != m_parsedTypesystemFiles.cend()) + return it.value(); + + QFile file(filepath); + if (!prepareParsing(file, filename, currentPath)) + return false; + + const bool ok = parseFile(context, &file, generate); m_parsedTypesystemFiles[filepath] = ok; return ok; } -bool TypeDatabase::parseFile(QIODevice* device, bool generate) +bool TypeDatabase::parseFile(QIODevice *device, bool generate) +{ + return d->parseFile(device, this, generate); +} + +bool TypeDatabasePrivate::parseFile(QIODevice *device, TypeDatabase *db, bool generate) +{ + const auto context = std::make_shared<TypeDatabaseParserContext>(); + context->db = db; + + if (!parseFile(context, device, generate)) + return false; + + addBuiltInPrimitiveTypes(); + addBuiltInContainerTypes(context); + return addOpaqueContainers(context) + && resolveSmartPointerInstantiations(context); +} + +bool TypeDatabase::parseFile(const TypeDatabaseParserContextPtr &context, + QIODevice *device, bool generate) { - QXmlStreamReader reader(device); - TypeSystemParser handler(this, generate); + return d->parseFile(context, device, generate); +} + +bool TypeDatabasePrivate::parseFile(const TypeDatabaseParserContextPtr &context, + QIODevice *device, bool generate) +{ + ConditionalStreamReader reader(device); + reader.setConditions(context->db->typesystemKeywords()); + TypeSystemParser handler(context, generate); const bool result = handler.parse(reader); - if (!result) + if (!result) { qCWarning(lcShiboken, "%s", qPrintable(handler.errorString())); + return false; + } return result; } -PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const +// Split a type list potentially with template types +// "A<B,C>,D" -> ("A<B,C>", "D") +static QStringList splitTypeList(const QString &s) { - const auto entries = findTypeRange(name); - for (TypeEntry *entry : entries) { + QStringList result; + int templateDepth = 0; + qsizetype lastPos = 0; + const auto size = s.size(); + for (qsizetype 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 TypeDatabasePrivate::resolveSmartPointerInstantiations(const TypeDatabaseParserContextPtr &context) +{ + const auto &instantiations = context->smartPointerInstantiations; + for (auto it = instantiations.cbegin(), end = instantiations.cend(); it != end; ++it) { + auto smartPointerEntry = it.key(); + const auto instantiationNames = splitTypeList(it.value()); + SmartPointerTypeEntry::Instantiations instantiations; + instantiations.reserve(instantiationNames.size()); + for (const auto &instantiation : instantiationNames) { + QString name; + QString type = instantiation; + const auto equalsPos = instantiation.indexOf(u'='); + if (equalsPos != -1) { + type.truncate(equalsPos); + name = instantiation.mid(equalsPos + 1); + } + + const auto typeEntries = findCppTypes(type); + if (typeEntries.isEmpty()) { + const QString m = msgCannotFindTypeEntryForSmartPointer(type, + smartPointerEntry->name()); + qCWarning(lcShiboken, "%s", qPrintable(m)); + return false; + } + if (typeEntries.size() > 1) { + const QString m = msgAmbiguousTypesFound(type, typeEntries); + qCWarning(lcShiboken, "%s", qPrintable(m)); + return false; + } + instantiations.append({name, typeEntries.constFirst()}); + } + smartPointerEntry->setInstantiations(instantiations); + } + return true; +} + +PrimitiveTypeEntryPtr TypeDatabase::findPrimitiveType(const QString& name) const +{ + const auto entries = d->findTypeRange(name); + for (const auto &entry : entries) { if (entry->isPrimitive()) { - auto *pe = static_cast<PrimitiveTypeEntry *>(entry); + auto pe = std::static_pointer_cast<PrimitiveTypeEntry>(entry); if (pe->preferredTargetLangType()) return pe; } @@ -658,22 +1261,22 @@ PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const return nullptr; } -ComplexTypeEntry* TypeDatabase::findComplexType(const QString& name) const +ComplexTypeEntryPtr TypeDatabase::findComplexType(const QString& name) const { - const auto entries = findTypeRange(name); - for (TypeEntry *entry : entries) { + const auto entries = d->findTypeRange(name); + for (const auto &entry : entries) { if (entry->isComplex() && useType(entry)) - return static_cast<ComplexTypeEntry*>(entry); + return std::static_pointer_cast<ComplexTypeEntry>(entry); } return nullptr; } -ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const +ObjectTypeEntryPtr TypeDatabase::findObjectType(const QString& name) const { - const auto entries = findTypeRange(name); - for (TypeEntry *entry : entries) { + const auto entries = d->findTypeRange(name); + for (const auto &entry : entries) { if (entry && entry->isObject() && useType(entry)) - return static_cast<ObjectTypeEntry*>(entry); + return std::static_pointer_cast<ObjectTypeEntry>(entry); } return nullptr; } @@ -681,26 +1284,26 @@ ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const NamespaceTypeEntryList TypeDatabase::findNamespaceTypes(const QString& name) const { NamespaceTypeEntryList result; - const auto entries = findTypeRange(name); - for (TypeEntry *entry : entries) { + const auto entries = d->findTypeRange(name); + for (const auto &entry : entries) { if (entry->isNamespace()) - result.append(static_cast<NamespaceTypeEntry*>(entry)); + result.append(std::static_pointer_cast<NamespaceTypeEntry>(entry)); } return result; } -NamespaceTypeEntry *TypeDatabase::findNamespaceType(const QString& name, +NamespaceTypeEntryPtr TypeDatabase::findNamespaceType(const QString& name, const QString &fileName) const { const auto entries = findNamespaceTypes(name); // Preferably check on matching file name first, if a pattern was given. if (!fileName.isEmpty()) { - for (NamespaceTypeEntry *entry : entries) { + for (const auto &entry : entries) { if (entry->hasPattern() && entry->matchesFile(fileName)) return entry; } } - for (NamespaceTypeEntry *entry : entries) { + for (const auto &entry : entries) { if (!entry->hasPattern()) return entry; } @@ -709,19 +1312,19 @@ NamespaceTypeEntry *TypeDatabase::findNamespaceType(const QString& name, bool TypeDatabase::shouldDropTypeEntry(const QString& fullTypeName) const { - return m_dropTypeEntries.contains(fullTypeName); + return d->m_dropTypeEntries.contains(fullTypeName); } -void TypeDatabase::setDropTypeEntries(QStringList dropTypeEntries) +void TypeDatabase::setDropTypeEntries(const QStringList &dropTypeEntries) { - m_dropTypeEntries = dropTypeEntries; - m_dropTypeEntries.sort(); + d->m_dropTypeEntries = dropTypeEntries; + d->m_dropTypeEntries.sort(); } static bool computeTypeIndexes = true; static int maxTypeIndex; -static bool typeEntryLessThan(const TypeEntry* t1, const TypeEntry* t2) +static bool typeEntryLessThan(const TypeEntryCPtr &t1, const TypeEntryCPtr &t2) { if (t1->revision() < t2->revision()) return true; @@ -731,7 +1334,7 @@ static bool typeEntryLessThan(const TypeEntry* t1, const TypeEntry* t2) static void _computeTypeIndexes() { - TypeDatabase* tdb = TypeDatabase::instance(); + auto *tdb = TypeDatabase::instance(); TypeEntryList list; @@ -739,7 +1342,7 @@ static void _computeTypeIndexes() const auto &allEntries = tdb->entries(); list.reserve(allEntries.size()); for (auto tit = allEntries.cbegin(), end = allEntries.cend(); tit != end; ++tit) { - TypeEntry *entry = tit.value(); + const TypeEntryPtr &entry = tit.value(); if (entry->isPrimitive() || entry->isContainer() || entry->isFunction() @@ -758,7 +1361,7 @@ static void _computeTypeIndexes() std::sort(list.begin(), list.end(), typeEntryLessThan); maxTypeIndex = 0; - for (TypeEntry *e : qAsConst(list)) + for (const TypeEntryPtr &e : std::as_const(list)) e->setSbkIndex(maxTypeIndex++); computeTypeIndexes = false; } @@ -795,7 +1398,7 @@ bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QS if (versionNumber.isNull()) return false; ApiVersions &versions = *apiVersions(); - for (int i = 0, size = versions.size(); i < size; ++i) { + for (qsizetype i = 0, size = versions.size(); i < size; ++i) { if (versions.at(i).first.pattern() == packagePattern) { versions[i].second = versionNumber; return true; @@ -804,7 +1407,7 @@ bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QS const QRegularExpression packageRegex(packagePattern); if (!packageRegex.isValid()) return false; - versions.append(qMakePair(packageRegex, versionNumber)); + versions.append(std::make_pair(packageRegex, versionNumber)); return true; } @@ -814,7 +1417,7 @@ bool TypeDatabase::checkApiVersion(const QString &package, const ApiVersions &versions = *apiVersions(); if (versions.isEmpty()) // Nothing specified: use latest. return true; - for (int i = 0, size = versions.size(); i < size; ++i) { + for (qsizetype i = 0, size = versions.size(); i < size; ++i) { if (versions.at(i).first.match(package).hasMatch()) return versions.at(i).second >= vr.since && versions.at(i).second <= vr.until; @@ -822,23 +1425,18 @@ bool TypeDatabase::checkApiVersion(const QString &package, return false; } -#ifndef QT_NO_DEBUG_STREAM +bool TypeDatabase::hasDroppedTypeEntries() const +{ + return !d->m_dropTypeEntries.isEmpty(); +} -template <class Container, class Separator> -static void formatList(QDebug &d, const char *name, const Container &c, Separator sep) +#ifndef QT_NO_DEBUG_STREAM +void TypeDatabase::formatDebug(QDebug &debug) const { - if (const int size = c.size()) { - d << ", " << name << '[' << size << "]=("; - for (int i = 0; i < size; ++i) { - if (i) - d << sep; - d << c.at(i); - } - d << ')'; - } + d->formatDebug(debug); } -void TypeDatabase::formatDebug(QDebug &d) const +void TypeDatabasePrivate::formatDebug(QDebug &d) const { d << "TypeDatabase(" << "entries[" << m_entries.size() << "]="; @@ -875,10 +1473,182 @@ void TypeDatabase::formatDebug(QDebug &d) const d << ")\n"; } d <<"\nglobalUserFunctions=" << m_globalUserFunctions << '\n'; - formatList(d, "globalFunctionMods", m_functionMods, '\n'); + formatList(d, "globalFunctionMods", m_functionMods, "\n"); d << ')'; } +// Helpers for dumping out primitive type info + +struct formatPrimitiveEntry +{ + explicit formatPrimitiveEntry(const PrimitiveTypeEntryCPtr &e) : m_pe(e) {} + + PrimitiveTypeEntryCPtr m_pe; +}; + +QDebug operator<<(QDebug debug, const formatPrimitiveEntry &fe) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + const QString &name = fe.m_pe->name(); + const QString &targetLangName = fe.m_pe->targetLangApiName(); + debug << '"' << name << '"'; + if (name != targetLangName) + debug << " (\"" << targetLangName << "\")"; + if (fe.m_pe->isBuiltIn()) + debug << " [builtin]"; + if (isExtendedCppPrimitive(fe.m_pe)) { + debug << " ["; + if (!isCppPrimitive(fe.m_pe)) + debug << "extended "; + debug << "C++]"; + } + return debug; +} + +// Sort primitive types for displaying; base type and typedef'ed types +struct PrimitiveFormatListEntry +{ + PrimitiveTypeEntryCPtr baseType; + PrimitiveTypeEntryCList typedefs; +}; + +static bool operator<(const PrimitiveFormatListEntry &e1, const PrimitiveFormatListEntry &e2) +{ + return e1.baseType->name() < e2.baseType->name(); +} + +using PrimitiveFormatListEntries = QList<PrimitiveFormatListEntry>; + +static qsizetype indexOf(const PrimitiveFormatListEntries &e, const PrimitiveTypeEntryCPtr &needle) +{ + for (qsizetype i = 0, size = e.size(); i < size; ++i) { + if (e.at(i).baseType == needle) + return i; + } + return -1; +} + +void TypeDatabase::formatBuiltinTypes(QDebug debug) const +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + + // Determine base types and their typedef'ed types + QList<PrimitiveFormatListEntry> primitiveEntries; + for (const auto &e : std::as_const(d->m_entries)) { + if (e->isPrimitive()) { + auto pe = std::static_pointer_cast<const PrimitiveTypeEntry>(e); + auto basic = basicReferencedTypeEntry(pe); + if (basic != pe) { + const auto idx = indexOf(primitiveEntries, basic); + if (idx != -1) + primitiveEntries[idx].typedefs.append(pe); + else + primitiveEntries.append(PrimitiveFormatListEntry{basic, {pe}}); + } else { + primitiveEntries.append(PrimitiveFormatListEntry{pe, {}}); + } + } + } + + std::sort(primitiveEntries.begin(), primitiveEntries.end()); + + for (const auto &e : std::as_const(primitiveEntries)) { + debug << "Primitive: " << formatPrimitiveEntry(e.baseType) << '\n'; + for (const auto &pe : e.typedefs) + debug << " " << formatPrimitiveEntry(pe) << '\n'; + } +} + +void TypeDatabasePrivate::addBuiltInType(const TypeEntryPtr &e) +{ + e->setBuiltIn(true); + addType(e); +} + +PrimitiveTypeEntryPtr + TypeDatabasePrivate::addBuiltInPrimitiveType(const QString &name, + const TypeSystemTypeEntryCPtr &root, + const QString &rootPackage, + const CustomTypeEntryPtr &targetLang) +{ + auto result = std::make_shared<PrimitiveTypeEntry>(name, QVersionNumber{}, root); + result->setTargetLangApiType(targetLang); + result->setTargetLangPackage(rootPackage); + addBuiltInType(result); + return result; +} + +void TypeDatabasePrivate::addBuiltInCppStringPrimitiveType(const QString &name, + const QString &viewName, + const TypeSystemTypeEntryCPtr &root, + const QString &rootPackage, + const CustomTypeEntryPtr &targetLang) + +{ + auto stringType = addBuiltInPrimitiveType(name, root, rootPackage, + targetLang); + auto viewType = addBuiltInPrimitiveType(viewName, root, rootPackage, + nullptr); + viewType->setViewOn(stringType); +} + +void TypeDatabasePrivate::addBuiltInPrimitiveTypes() +{ + auto root = defaultTypeSystemType(); + const QString &rootPackage = root->name(); + + // C++ primitive types + auto pyLongEntry = findType(u"PyLong"_s); + Q_ASSERT(pyLongEntry && pyLongEntry->isCustom()); + auto pyLongCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyLongEntry); + auto pyBoolEntry = findType(u"PyBool"_s); + Q_ASSERT(pyBoolEntry && pyBoolEntry->isCustom()); + auto sbkCharEntry = findType(u"SbkChar"_s); + Q_ASSERT(sbkCharEntry && sbkCharEntry->isCustom()); + auto sbkCharCustomEntry = std::static_pointer_cast<CustomTypeEntry>(sbkCharEntry); + + auto pyBoolCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyBoolEntry); + for (const auto &t : AbstractMetaType::cppIntegralTypes()) { + if (!m_entries.contains(t)) { + CustomTypeEntryPtr targetLangApi = pyLongCustomEntry; + if (t == u"bool") + targetLangApi = pyBoolCustomEntry; + else if (AbstractMetaType::cppCharTypes().contains(t)) + targetLangApi = sbkCharCustomEntry; + addBuiltInPrimitiveType(t, root, rootPackage, targetLangApi); + } + } + + auto pyFloatEntry = findType(u"PyFloat"_s); + Q_ASSERT(pyFloatEntry && pyFloatEntry->isCustom()); + auto pyFloatCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyFloatEntry); + for (const auto &t : AbstractMetaType::cppFloatTypes()) { + if (!m_entries.contains(t)) + addBuiltInPrimitiveType(t, root, rootPackage, pyFloatCustomEntry); + } + + auto pyUnicodeEntry = findType(u"PyUnicode"_s); + Q_ASSERT(pyUnicodeEntry && pyUnicodeEntry->isCustom()); + auto pyUnicodeCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyUnicodeEntry); + + constexpr auto stdString = "std::string"_L1; + if (!m_entries.contains(stdString)) { + addBuiltInCppStringPrimitiveType(stdString, u"std::string_view"_s, + root, rootPackage, + pyUnicodeCustomEntry); + } + constexpr auto stdWString = "std::wstring"_L1; + if (!m_entries.contains(stdWString)) { + addBuiltInCppStringPrimitiveType(stdWString, u"std::wstring_view"_s, + root, rootPackage, + pyUnicodeCustomEntry); + } +} + QDebug operator<<(QDebug d, const TypeDatabase &db) { QDebugStateSaver saver(d); |