aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-10-28 07:51:22 +0100
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2020-10-28 09:34:35 +0000
commit2a2a0827fa54b2eeb48e0e2090dfc503492ef33e (patch)
tree4311f7ad34ff98fabcbe4c34e59a970ae4ff4bcf /sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
parentfb1c0f204e09ba29fa3d360d72231d4ed468c1e4 (diff)
Rename shiboken2 to shiboken6
Adapt CMake files, build scripts, tests and examples. Task-number: PYSIDE-904 Change-Id: I4cb5ee4c8df539546014b08202a7b1e98ed3ff07 Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp3280
1 files changed, 3280 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
new file mode 100644
index 000000000..b6e68bfde
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
@@ -0,0 +1,3280 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "abstractmetabuilder_p.h"
+#include "messages.h"
+#include "propertyspec.h"
+#include "reporthandler.h"
+#include "typedatabase.h"
+
+#include <clangparser/clangbuilder.h>
+#include <clangparser/clangutils.h>
+#include <clangparser/compilersupport.h>
+
+#include "parser/codemodel.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QRegularExpression>
+#include <QTextStream>
+#include <QVariant>
+#include <QTime>
+#include <QQueue>
+#include <QDir>
+
+#include <cstdio>
+#include <algorithm>
+#include "graph.h"
+#include <QTemporaryFile>
+
+static inline QString colonColon() { return QStringLiteral("::"); }
+
+static QString stripTemplateArgs(const QString &name)
+{
+ int pos = name.indexOf(QLatin1Char('<'));
+ return pos < 0 ? name : name.left(pos);
+}
+
+static QStringList parseTemplateType(const QString &name) {
+ int n = name.indexOf(QLatin1Char('<'));
+ if (n <= 0) {
+ // If name starts with '<' or contains an unmatched (i.e. any) '>', we
+ // reject it
+ if (n == 0 || name.count(QLatin1Char('>')))
+ return QStringList();
+ // Doesn't look like a template instantiation; just return the name
+ return QStringList() << name;
+ }
+
+ // Split the type name into the template name and template arguments; the
+ // part before the opening '<' is the template name
+ //
+ // Example:
+ // "foo<A, bar<B, C>, D>" -> ( "foo", "A", "bar<B, C>", "D" )
+ QStringList result;
+ result << name.left(n).trimmed();
+
+ // Extract template arguments
+ int i, depth = 1;
+ const int l = name.length();
+ for (i = n + 1; i < l; ++i) {
+ // Consume balanced '<'/'>' within a single argument so that we won't
+ // split on ',' as part of a single argument which is itself a
+ // multi-argument template type
+ if (name[i] == QLatin1Char('<')) {
+ ++depth;
+ } else if (name[i] == QLatin1Char('>')) {
+ if (--depth == 0)
+ break;
+ } else if (name[i] == QLatin1Char(',') && depth == 1) {
+ // Encountered ',' in template argument list that is not within
+ // another template name; add current argument to result and start
+ // working on the next argument
+ result << name.mid(n + 1, i - n - 1).trimmed();
+ n = i;
+ }
+ }
+ if (i >= l) // arg list not closed
+ return QStringList();
+ if (i + 1 < l) // arg list closed before end of name
+ return QStringList();
+
+ // Add final argument and return result
+ result << name.mid(n + 1, i - n - 1).trimmed();
+ return result;
+}
+
+AbstractMetaBuilderPrivate::AbstractMetaBuilderPrivate() :
+ m_logDirectory(QLatin1String(".") + QDir::separator())
+{
+}
+
+AbstractMetaBuilderPrivate::~AbstractMetaBuilderPrivate()
+{
+ qDeleteAll(m_globalEnums);
+ qDeleteAll(m_globalFunctions);
+ qDeleteAll(m_templates);
+ qDeleteAll(m_smartPointers);
+ qDeleteAll(m_metaClasses);
+}
+
+AbstractMetaBuilder::AbstractMetaBuilder() : d(new AbstractMetaBuilderPrivate)
+{
+ d->q = this;
+}
+
+AbstractMetaBuilder::~AbstractMetaBuilder()
+{
+ delete d;
+}
+
+const AbstractMetaClassList &AbstractMetaBuilder::classes() const
+{
+ return d->m_metaClasses;
+}
+
+const AbstractMetaClassList &AbstractMetaBuilder::templates() const
+{
+ return d->m_templates;
+}
+
+const AbstractMetaClassList &AbstractMetaBuilder::smartPointers() const
+{
+ return d->m_smartPointers;
+}
+
+const AbstractMetaFunctionList &AbstractMetaBuilder::globalFunctions() const
+{
+ return d->m_globalFunctions;
+}
+
+const AbstractMetaEnumList &AbstractMetaBuilder::globalEnums() const
+{
+ return d->m_globalEnums;
+}
+
+AbstractMetaEnum *AbstractMetaBuilder::findEnum(const TypeEntry *typeEntry) const
+{
+ if (typeEntry && typeEntry->isFlags())
+ typeEntry = static_cast<const FlagsTypeEntry *>(typeEntry)->originator();
+ return d->m_enums.value(typeEntry);
+}
+
+void AbstractMetaBuilderPrivate::checkFunctionModifications()
+{
+ const auto &entries = TypeDatabase::instance()->entries();
+
+ for (auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) {
+ const TypeEntry *entry = it.value();
+ if (!entry)
+ continue;
+ if (!entry->isComplex() || !entry->generateCode())
+ continue;
+
+ auto centry = static_cast<const ComplexTypeEntry *>(entry);
+
+ if (!centry->generateCode())
+ continue;
+
+ FunctionModificationList modifications = centry->functionModifications();
+
+ for (const FunctionModification &modification : qAsConst(modifications)) {
+ QString signature = modification.signature();
+
+ QString name = signature.trimmed();
+ name.truncate(name.indexOf(QLatin1Char('(')));
+
+ AbstractMetaClass *clazz = AbstractMetaClass::findClass(m_metaClasses, centry);
+ if (!clazz)
+ continue;
+
+ const AbstractMetaFunctionList functions = clazz->functions();
+ bool found = false;
+ QStringList possibleSignatures;
+ for (AbstractMetaFunction *function : functions) {
+ if (function->implementingClass() == clazz
+ && modification.matches(function->minimalSignature())) {
+ found = true;
+ break;
+ }
+
+ if (function->originalName() == name) {
+ possibleSignatures.append(function->minimalSignature() + QLatin1String(" in ")
+ + function->implementingClass()->name());
+ }
+ }
+
+ if (!found) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << msgNoFunctionForModification(clazz, signature,
+ modification.originalSignature(),
+ possibleSignatures, functions);
+ }
+ }
+ }
+}
+
+AbstractMetaClass *AbstractMetaBuilderPrivate::argumentToClass(const ArgumentModelItem &argument,
+ AbstractMetaClass *currentClass)
+{
+ AbstractMetaClass *returned = nullptr;
+ AbstractMetaType type = translateType(argument->type(), currentClass);
+ if (type && type.typeEntry() && type.typeEntry()->isComplex()) {
+ const TypeEntry *entry = type.typeEntry();
+ returned = AbstractMetaClass::findClass(m_metaClasses, entry);
+ }
+ return returned;
+}
+
+/**
+ * Checks the argument of a hash function and flags the type if it is a complex type
+ */
+void AbstractMetaBuilderPrivate::registerHashFunction(const FunctionModelItem &function_item,
+ AbstractMetaClass *currentClass)
+{
+ ArgumentList arguments = function_item->arguments();
+ if (arguments.size() == 1) {
+ if (AbstractMetaClass *cls = argumentToClass(arguments.at(0), currentClass))
+ cls->setHasHashFunction(true);
+ }
+}
+
+void AbstractMetaBuilderPrivate::registerToStringCapabilityIn(const NamespaceModelItem &nsItem)
+{
+ const FunctionList &streamOps = nsItem->findFunctions(QLatin1String("operator<<"));
+ for (const FunctionModelItem &item : streamOps)
+ registerToStringCapability(item, nullptr);
+ for (const NamespaceModelItem &ni : nsItem->namespaces())
+ registerToStringCapabilityIn(ni);
+}
+
+/**
+ * Check if a class has a debug stream operator that can be used as toString
+ */
+
+void AbstractMetaBuilderPrivate::registerToStringCapability(const FunctionModelItem &function_item,
+ AbstractMetaClass *currentClass)
+{
+ ArgumentList arguments = function_item->arguments();
+ if (arguments.size() == 2) {
+ if (arguments.at(0)->type().toString() == QLatin1String("QDebug")) {
+ const ArgumentModelItem &arg = arguments.at(1);
+ if (AbstractMetaClass *cls = argumentToClass(arg, currentClass)) {
+ if (arg->type().indirections() < 2)
+ cls->setToStringCapability(true, int(arg->type().indirections()));
+ }
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelItem &item,
+ AbstractMetaClass *currentClass)
+{
+ if (item->accessPolicy() != CodeModel::Public)
+ return;
+
+ ArgumentList arguments = item->arguments();
+ bool firstArgumentIsSelf = true;
+ bool unaryOperator = false;
+
+ auto baseoperandClass = argumentToClass(arguments.at(0), currentClass);
+
+ if (arguments.size() == 1) {
+ unaryOperator = true;
+ } else if (!baseoperandClass
+ || !baseoperandClass->typeEntry()->generateCode()) {
+ baseoperandClass = argumentToClass(arguments.at(1), currentClass);
+ firstArgumentIsSelf = false;
+ } else {
+ AbstractMetaType type = translateType(item->type(), currentClass);
+ const TypeEntry *retType = type ? type.typeEntry() : nullptr;
+ AbstractMetaClass *otherArgClass = argumentToClass(arguments.at(1), currentClass);
+ if (otherArgClass && retType
+ && (retType->isValue() || retType->isObject())
+ && retType != baseoperandClass->typeEntry()
+ && retType == otherArgClass->typeEntry()) {
+ baseoperandClass = AbstractMetaClass::findClass(m_metaClasses, retType);
+ firstArgumentIsSelf = false;
+ }
+ }
+
+ if (baseoperandClass) {
+ AbstractMetaFunction *metaFunction = traverseFunction(item, baseoperandClass);
+ if (metaFunction) {
+ // Strip away first argument, since that is the containing object
+ AbstractMetaArgumentList arguments = metaFunction->arguments();
+ if (firstArgumentIsSelf || unaryOperator) {
+ AbstractMetaArgument first = arguments.takeFirst();
+ if (!unaryOperator && first.type().indirections())
+ metaFunction->setPointerOperator(true);
+ metaFunction->setArguments(arguments);
+ } else {
+ // If the operator method is not unary and the first operator is
+ // not of the same type of its owning class we suppose that it
+ // must be an reverse operator (e.g. CLASS::operator(TYPE, CLASS)).
+ // All operator overloads that operate over a class are already
+ // being added as member functions of that class by the API Extractor.
+ AbstractMetaArgument last = arguments.takeLast();
+ if (last.type().indirections())
+ metaFunction->setPointerOperator(true);
+
+ metaFunction->setArguments(arguments);
+ metaFunction->setReverseOperator(true);
+ }
+ metaFunction->setFunctionType(AbstractMetaFunction::NormalFunction);
+ metaFunction->setVisibility(AbstractMetaFunction::Public);
+ metaFunction->setOriginalAttributes(metaFunction->attributes());
+ setupFunctionDefaults(metaFunction, baseoperandClass);
+ baseoperandClass->addFunction(metaFunction);
+ Q_ASSERT(!metaFunction->wasPrivate());
+ } else {
+ delete metaFunction;
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem &item,
+ AbstractMetaClass *currentClass)
+{
+ ArgumentList arguments = item->arguments();
+ if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) {
+ AbstractMetaClass *streamClass = argumentToClass(arguments.at(0), currentClass);
+ AbstractMetaClass *streamedClass = argumentToClass(arguments.at(1), currentClass);
+
+ if (streamClass && streamedClass && (streamClass->isStream())) {
+ AbstractMetaFunction *streamFunction = traverseFunction(item, streamedClass);
+
+ if (streamFunction) {
+ streamFunction->setFunctionType(AbstractMetaFunction::GlobalScopeFunction);
+ // Strip first argument, since that is the containing object
+ AbstractMetaArgumentList arguments = streamFunction->arguments();
+ if (!streamClass->typeEntry()->generateCode())
+ arguments.takeLast();
+ else
+ arguments.takeFirst();
+
+ streamFunction->setArguments(arguments);
+
+ *streamFunction += AbstractMetaAttributes::FinalInTargetLang;
+ *streamFunction += AbstractMetaAttributes::Public;
+ streamFunction->setOriginalAttributes(streamFunction->attributes());
+
+// streamFunction->setType(0);
+
+ AbstractMetaClass *funcClass;
+
+ if (!streamClass->typeEntry()->generateCode()) {
+ AbstractMetaArgumentList reverseArgs = reverseList(streamFunction->arguments());
+ streamFunction->setArguments(reverseArgs);
+ streamFunction->setReverseOperator(true);
+ funcClass = streamedClass;
+ } else {
+ funcClass = streamClass;
+ }
+
+ setupFunctionDefaults(streamFunction, funcClass);
+ funcClass->addFunction(streamFunction);
+ if (funcClass == streamClass)
+ funcClass->typeEntry()->addExtraInclude(streamedClass->typeEntry()->include());
+ else
+ funcClass->typeEntry()->addExtraInclude(streamClass->typeEntry()->include());
+
+ } else {
+ delete streamFunction;
+ }
+
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::sortLists()
+{
+ for (AbstractMetaClass *cls : qAsConst(m_metaClasses))
+ cls->sortFunctions();
+}
+
+FileModelItem AbstractMetaBuilderPrivate::buildDom(QByteArrayList arguments,
+ LanguageLevel level,
+ unsigned clangFlags)
+{
+ clang::Builder builder;
+ builder.setSystemIncludes(TypeDatabase::instance()->systemIncludes());
+ if (level == LanguageLevel::Default)
+ level = clang::emulatedCompilerLanguageLevel();
+ arguments.prepend(QByteArrayLiteral("-std=")
+ + clang::languageLevelOption(level));
+ FileModelItem result = clang::parse(arguments, clangFlags, builder)
+ ? builder.dom() : FileModelItem();
+ const clang::BaseVisitor::Diagnostics &diagnostics = builder.diagnostics();
+ if (const int diagnosticsCount = diagnostics.size()) {
+ QDebug d = qWarning();
+ d.nospace();
+ d.noquote();
+ d << "Clang: " << diagnosticsCount << " diagnostic messages:\n";
+ for (int i = 0; i < diagnosticsCount; ++i)
+ d << " " << diagnostics.at(i) << '\n';
+ }
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom)
+{
+ const TypeDatabase *types = TypeDatabase::instance();
+
+ pushScope(dom);
+
+ // Start the generation...
+ const ClassList &typeValues = dom->classes();
+
+ ReportHandler::startProgress("Generating class model ("
+ + QByteArray::number(typeValues.size()) + ")...");
+ for (const ClassModelItem &item : typeValues) {
+ if (AbstractMetaClass *cls = traverseClass(dom, item, nullptr))
+ addAbstractMetaClass(cls, item.data());
+ }
+
+ // We need to know all global enums
+ const EnumList &enums = dom->enums();
+
+ ReportHandler::startProgress("Generating enum model ("
+ + QByteArray::number(enums.size()) + ")...");
+ for (const EnumModelItem &item : enums) {
+ AbstractMetaEnum *metaEnum = traverseEnum(item, nullptr, QSet<QString>());
+ if (metaEnum) {
+ if (metaEnum->typeEntry()->generateCode())
+ m_globalEnums << metaEnum;
+ }
+ }
+
+ const auto &namespaceTypeValues = dom->namespaces();
+ ReportHandler::startProgress("Generating namespace model ("
+ + QByteArray::number(namespaceTypeValues.size()) + ")...");
+ for (const NamespaceModelItem &item : namespaceTypeValues)
+ traverseNamespace(dom, item);
+
+ // Go through all typedefs to see if we have defined any
+ // specific typedefs to be used as classes.
+ const TypeDefList typeDefs = dom->typeDefs();
+ ReportHandler::startProgress("Resolving typedefs ("
+ + QByteArray::number(typeDefs.size()) + ")...");
+ for (const TypeDefModelItem &typeDef : typeDefs) {
+ if (AbstractMetaClass *cls = traverseTypeDef(dom, typeDef, nullptr))
+ addAbstractMetaClass(cls, typeDef.data());
+ }
+
+ traverseTypesystemTypedefs();
+
+ for (const ClassModelItem &item : typeValues)
+ traverseClassMembers(item);
+
+ for (const NamespaceModelItem &item : namespaceTypeValues)
+ traverseNamespaceMembers(item);
+
+ // Global functions
+ const FunctionList &functions = dom->functions();
+ for (const FunctionModelItem &func : functions) {
+ if (func->accessPolicy() != CodeModel::Public || func->name().startsWith(QLatin1String("operator")))
+ continue;
+
+ FunctionTypeEntry *funcEntry = types->findFunctionType(func->name());
+ if (!funcEntry || !funcEntry->generateCode())
+ continue;
+
+ AbstractMetaFunction *metaFunc = traverseFunction(func, nullptr);
+ if (!metaFunc)
+ continue;
+
+ if (!funcEntry->hasSignature(metaFunc->minimalSignature())) {
+ delete metaFunc;
+ continue;
+ }
+
+ applyFunctionModifications(metaFunc);
+
+ setInclude(funcEntry, func->fileName());
+ if (metaFunc->typeEntry())
+ delete metaFunc->typeEntry();
+
+ metaFunc->setTypeEntry(funcEntry);
+ m_globalFunctions << metaFunc;
+ }
+
+ ReportHandler::startProgress("Fixing class inheritance...");
+ for (AbstractMetaClass *cls : qAsConst(m_metaClasses)) {
+ if (!cls->isNamespace()) {
+ setupInheritance(cls);
+ if (cls->templateBaseClass())
+ inheritTemplateFunctions(cls);
+ if (!cls->hasVirtualDestructor() && cls->baseClass()
+ && cls->baseClass()->hasVirtualDestructor())
+ cls->setHasVirtualDestructor(true);
+ }
+ }
+
+ ReportHandler::startProgress("Detecting inconsistencies in class model...");
+ for (AbstractMetaClass *cls : qAsConst(m_metaClasses)) {
+ cls->fixFunctions();
+
+ if (!cls->typeEntry()) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("class '%1' does not have an entry in the type system")
+ .arg(cls->name());
+ } else {
+ const bool couldAddDefaultCtors = cls->isConstructible()
+ && !cls->isNamespace()
+ && (cls->attributes() & AbstractMetaAttributes::HasRejectedConstructor) == 0;
+ if (couldAddDefaultCtors) {
+ if (!cls->hasConstructors())
+ cls->addDefaultConstructor();
+ if (cls->typeEntry()->isValue() && !cls->isAbstract() && !cls->hasCopyConstructor())
+ cls->addDefaultCopyConstructor(ancestorHasPrivateCopyConstructor(cls));
+ }
+ }
+ }
+ const auto &allEntries = types->entries();
+
+ ReportHandler::startProgress("Detecting inconsistencies in typesystem ("
+ + QByteArray::number(allEntries.size()) + ")...");
+ for (auto it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) {
+ TypeEntry *entry = it.value();
+ if (!entry->isPrimitive()) {
+ if ((entry->isValue() || entry->isObject())
+ && !types->shouldDropTypeEntry(entry->qualifiedCppName())
+ && !entry->isContainer()
+ && !entry->isCustom()
+ && entry->generateCode()
+ && !AbstractMetaClass::findClass(m_metaClasses, entry)) {
+ qCWarning(lcShiboken, "%s", qPrintable(msgTypeNotDefined(entry)));
+ } else if (entry->generateCode() && entry->type() == TypeEntry::FunctionType) {
+ auto fte = static_cast<const FunctionTypeEntry *>(entry);
+ const QStringList &signatures = fte->signatures();
+ for (const QString &signature : signatures) {
+ bool ok = false;
+ for (AbstractMetaFunction *func : qAsConst(m_globalFunctions)) {
+ if (signature == func->minimalSignature()) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgGlobalFunctionNotDefined(fte, signature)));
+ }
+ }
+ } else if (entry->isEnum() && entry->generateCode()) {
+ auto enumEntry = static_cast<const EnumTypeEntry *>(entry);
+ const QString name = enumEntry->targetLangQualifier();
+ AbstractMetaClass *cls = AbstractMetaClass::findClass(m_metaClasses, name);
+
+ const bool enumFound = cls
+ ? cls->findEnum(entry->targetLangEntryName()) != nullptr
+ : m_enums.contains(entry);
+
+ if (!enumFound) {
+ entry->setCodeGeneration(TypeEntry::GenerateNothing);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgEnumNotDefined(enumEntry)));
+ }
+
+ }
+ }
+ }
+
+ {
+ const FunctionList &hashFunctions = dom->findFunctions(QLatin1String("qHash"));
+ for (const FunctionModelItem &item : hashFunctions)
+ registerHashFunction(item, nullptr);
+ }
+
+ registerToStringCapabilityIn(dom);
+
+ {
+ FunctionList binaryOperators = dom->findFunctions(QStringLiteral("operator=="));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator!=")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator<=")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator>=")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator<")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator+")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator/")));
+ // Filter binary operators, skipping for example
+ // class Iterator { ... Value *operator*() ... };
+ const FunctionList potentiallyBinaryOperators =
+ dom->findFunctions(QStringLiteral("operator*"))
+ + dom->findFunctions(QStringLiteral("operator&"));
+ for (const FunctionModelItem &item : potentiallyBinaryOperators) {
+ if (!item->arguments().isEmpty())
+ binaryOperators.append(item);
+ }
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator-")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator&")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator|")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator^")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator~")));
+ binaryOperators.append(dom->findFunctions(QStringLiteral("operator>")));
+
+ for (const FunctionModelItem &item : qAsConst(binaryOperators))
+ traverseOperatorFunction(item, nullptr);
+ }
+
+ {
+ const FunctionList streamOperators = dom->findFunctions(QLatin1String("operator<<"))
+ + dom->findFunctions(QLatin1String("operator>>"));
+ for (const FunctionModelItem &item : streamOperators)
+ traverseStreamOperator(item, nullptr);
+ }
+
+ ReportHandler::startProgress("Checking inconsistencies in function modifications...");
+
+ checkFunctionModifications();
+
+ ReportHandler::startProgress("Writing log files...");
+
+ // sort all classes topologically
+ m_metaClasses = classesTopologicalSorted(m_metaClasses);
+
+ for (AbstractMetaClass *cls : qAsConst(m_metaClasses)) {
+// setupEquals(cls);
+// setupComparable(cls);
+ setupClonable(cls);
+ setupExternalConversion(cls);
+
+ // sort all inner classes topologically
+ if (!cls->typeEntry()->codeGeneration() || cls->innerClasses().size() < 2)
+ continue;
+
+ cls->setInnerClasses(classesTopologicalSorted(cls->innerClasses()));
+ }
+
+ dumpLog();
+
+ sortLists();
+
+ // Functions added to the module on the type system.
+ const AddedFunctionList &globalUserFunctions = types->globalUserFunctions();
+ for (const AddedFunctionPtr &addedFunc : globalUserFunctions) {
+ AbstractMetaFunction *metaFunc = traverseFunction(addedFunc);
+ if (Q_UNLIKELY(!metaFunc)) {
+ qFatal("Unable to traverse added global function \"%s\".",
+ qPrintable(addedFunc->name()));
+ }
+ metaFunc->setFunctionType(AbstractMetaFunction::NormalFunction);
+ m_globalFunctions << metaFunc;
+ }
+
+ m_itemToClass.clear();
+ m_typeSystemTypeDefs.clear();
+
+ ReportHandler::endProgress();
+}
+
+static bool metaEnumLessThan(const AbstractMetaEnum *e1, const AbstractMetaEnum *e2)
+{ return e1->fullName() < e2->fullName(); }
+
+static bool metaClassLessThan(const AbstractMetaClass *c1, const AbstractMetaClass *c2)
+{ return c1->fullName() < c2->fullName(); }
+
+static bool metaFunctionLessThan(const AbstractMetaFunction *f1, const AbstractMetaFunction *f2)
+{ return f1->name() < f2->name(); }
+
+bool AbstractMetaBuilder::build(const QByteArrayList &arguments,
+ LanguageLevel level,
+ unsigned clangFlags)
+{
+ const FileModelItem dom = d->buildDom(arguments, level, clangFlags);
+ if (dom.isNull())
+ return false;
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug))
+ qCDebug(lcShiboken) << dom.data();
+ d->traverseDom(dom);
+
+ // Ensure that indexes are in alphabetical order, roughly
+ std::sort(d->m_globalEnums.begin(), d->m_globalEnums.end(), metaEnumLessThan);
+ std::sort(d->m_metaClasses.begin(), d->m_metaClasses.end(), metaClassLessThan);
+ std::sort(d->m_templates.begin(), d->m_templates.end(), metaClassLessThan);
+ std::sort(d->m_smartPointers.begin(), d->m_smartPointers.end(), metaClassLessThan);
+ std::sort(d->m_globalFunctions.begin(), d->m_globalFunctions.end(), metaFunctionLessThan);
+
+ return true;
+}
+
+void AbstractMetaBuilder::setLogDirectory(const QString &logDir)
+{
+ d->m_logDirectory = logDir;
+ if (!d->m_logDirectory.endsWith(QDir::separator()))
+ d->m_logDirectory.append(QDir::separator());
+}
+
+void AbstractMetaBuilderPrivate::addAbstractMetaClass(AbstractMetaClass *cls,
+ const _CodeModelItem *item)
+{
+ cls->setOriginalAttributes(cls->attributes());
+ m_itemToClass.insert(item, cls);
+ if (cls->typeEntry()->isContainer()) {
+ m_templates << cls;
+ } else if (cls->typeEntry()->isSmartPointer()) {
+ m_smartPointers << cls;
+ } else {
+ m_metaClasses << cls;
+ }
+}
+
+AbstractMetaClass *AbstractMetaBuilderPrivate::traverseNamespace(const FileModelItem &dom,
+ const NamespaceModelItem &namespaceItem)
+{
+ QString namespaceName = currentScope()->qualifiedName().join(colonColon());
+ if (!namespaceName.isEmpty())
+ namespaceName.append(colonColon());
+ namespaceName.append(namespaceItem->name());
+
+ if (TypeDatabase::instance()->isClassRejected(namespaceName)) {
+ m_rejectedClasses.insert(namespaceName, AbstractMetaBuilder::GenerationDisabled);
+ return nullptr;
+ }
+
+ auto type = TypeDatabase::instance()->findNamespaceType(namespaceName, namespaceItem->fileName());
+ if (!type) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgNamespaceNoTypeEntry(namespaceItem, namespaceName)));
+ return nullptr;
+ }
+
+ if (namespaceItem->type() == NamespaceType::Inline) {
+ type->setInlineNamespace(true);
+ TypeDatabase::instance()->addInlineNamespaceLookups(type);
+ }
+
+ // Continue populating namespace?
+ AbstractMetaClass *metaClass = AbstractMetaClass::findClass(m_metaClasses, type);
+ if (!metaClass) {
+ metaClass = new AbstractMetaClass;
+ metaClass->setTypeEntry(type);
+ *metaClass += AbstractMetaAttributes::Public;
+ addAbstractMetaClass(metaClass, namespaceItem.data());
+ if (auto extendsType = type->extends()) {
+ AbstractMetaClass *extended = AbstractMetaClass::findClass(m_metaClasses, extendsType);
+ if (!extended) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgNamespaceToBeExtendedNotFound(extendsType->name(), extendsType->targetLangPackage())));
+ return nullptr;
+ }
+ metaClass->setExtendedNamespace(extended);
+ }
+ } else {
+ m_itemToClass.insert(namespaceItem.data(), metaClass);
+ }
+
+ traverseEnums(namespaceItem, metaClass, namespaceItem->enumsDeclarations());
+
+ pushScope(namespaceItem);
+
+ const ClassList &classes = namespaceItem->classes();
+ for (const ClassModelItem &cls : classes) {
+ AbstractMetaClass *mjc = traverseClass(dom, cls, metaClass);
+ if (mjc) {
+ metaClass->addInnerClass(mjc);
+ mjc->setEnclosingClass(metaClass);
+ addAbstractMetaClass(mjc, cls.data());
+ }
+ }
+
+ // Go through all typedefs to see if we have defined any
+ // specific typedefs to be used as classes.
+ const TypeDefList typeDefs = namespaceItem->typeDefs();
+ for (const TypeDefModelItem &typeDef : typeDefs) {
+ AbstractMetaClass *cls = traverseTypeDef(dom, typeDef, metaClass);
+ if (cls) {
+ metaClass->addInnerClass(cls);
+ cls->setEnclosingClass(metaClass);
+ addAbstractMetaClass(cls, typeDef.data());
+ }
+ }
+
+ // Traverse namespaces recursively
+ for (const NamespaceModelItem &ni : namespaceItem->namespaces()) {
+ AbstractMetaClass *mjc = traverseNamespace(dom, ni);
+ if (mjc) {
+ metaClass->addInnerClass(mjc);
+ mjc->setEnclosingClass(metaClass);
+ }
+ }
+
+ popScope();
+
+ if (!type->include().isValid())
+ setInclude(type, namespaceItem->fileName());
+
+ return metaClass;
+}
+
+AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(const EnumModelItem &enumItem,
+ AbstractMetaClass *enclosing,
+ const QSet<QString> &enumsDeclarations)
+{
+ QString qualifiedName = enumItem->qualifiedName().join(colonColon());
+
+ TypeEntry *typeEntry = nullptr;
+ const TypeEntry *enclosingTypeEntry = enclosing ? enclosing->typeEntry() : nullptr;
+ if (enumItem->accessPolicy() == CodeModel::Private) {
+ typeEntry = new EnumTypeEntry(enumItem->qualifiedName().constLast(),
+ QVersionNumber(0, 0), enclosingTypeEntry);
+ TypeDatabase::instance()->addType(typeEntry);
+ } else if (enumItem->enumKind() != AnonymousEnum) {
+ typeEntry = TypeDatabase::instance()->findType(qualifiedName);
+ } else {
+ QStringList tmpQualifiedName = enumItem->qualifiedName();
+ const EnumeratorList &enums = enumItem->enumerators();
+ for (const EnumeratorModelItem &enumValue : enums) {
+ tmpQualifiedName.removeLast();
+ tmpQualifiedName << enumValue->name();
+ qualifiedName = tmpQualifiedName.join(colonColon());
+ typeEntry = TypeDatabase::instance()->findType(qualifiedName);
+ if (typeEntry)
+ break;
+ }
+ }
+
+ QString enumName = enumItem->name();
+
+ QString className;
+ if (enclosingTypeEntry)
+ className = enclosingTypeEntry->qualifiedCppName();
+
+ QString rejectReason;
+ if (TypeDatabase::instance()->isEnumRejected(className, enumName, &rejectReason)) {
+ if (typeEntry)
+ typeEntry->setCodeGeneration(TypeEntry::GenerateNothing);
+ m_rejectedEnums.insert(qualifiedName + rejectReason, AbstractMetaBuilder::GenerationDisabled);
+ return nullptr;
+ }
+
+ const bool rejectionWarning = !enclosing || enclosing->typeEntry()->generateCode();
+
+ if (!typeEntry) {
+ if (rejectionWarning)
+ qCWarning(lcShiboken, "%s", qPrintable(msgNoEnumTypeEntry(enumItem, className)));
+ m_rejectedEnums.insert(qualifiedName, AbstractMetaBuilder::NotInTypeSystem);
+ return nullptr;
+ }
+
+ if (!typeEntry->isEnum()) {
+ if (rejectionWarning) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgNoEnumTypeConflict(enumItem, className, typeEntry)));
+ }
+ m_rejectedEnums.insert(qualifiedName, AbstractMetaBuilder::NotInTypeSystem);
+ return nullptr;
+ }
+
+ auto *metaEnum = new AbstractMetaEnum;
+ metaEnum->setEnumKind(enumItem->enumKind());
+ metaEnum->setSigned(enumItem->isSigned());
+ if (enumsDeclarations.contains(qualifiedName)
+ || enumsDeclarations.contains(enumName)) {
+ metaEnum->setHasQEnumsDeclaration(true);
+ }
+
+ auto *enumTypeEntry = static_cast<EnumTypeEntry *>(typeEntry);
+ metaEnum->setTypeEntry(enumTypeEntry);
+ switch (enumItem->accessPolicy()) {
+ case CodeModel::Public:
+ *metaEnum += AbstractMetaAttributes::Public;
+ break;
+ case CodeModel::Protected:
+ *metaEnum += AbstractMetaAttributes::Protected;
+ break;
+ case CodeModel::Private:
+ *metaEnum += AbstractMetaAttributes::Private;
+ typeEntry->setCodeGeneration(TypeEntry::GenerateNothing);
+ break;
+ default:
+ break;
+ }
+
+ const EnumeratorList &enums = enumItem->enumerators();
+ for (const EnumeratorModelItem &value : enums) {
+
+ auto *metaEnumValue = new AbstractMetaEnumValue;
+ metaEnumValue->setName(value->name());
+ // Deciding the enum value...
+
+ metaEnumValue->setStringValue(value->stringValue());
+ metaEnumValue->setValue(value->value());
+ metaEnum->addEnumValue(metaEnumValue);
+ }
+
+ m_enums.insert(typeEntry, metaEnum);
+
+ if (!metaEnum->typeEntry()->include().isValid())
+ setInclude(metaEnum->typeEntry(), enumItem->fileName());
+
+ metaEnum->setOriginalAttributes(metaEnum->attributes());
+
+ // Register all enum values on Type database
+ const bool isScopedEnum = enumItem->enumKind() == EnumClass;
+ const EnumeratorList &enumerators = enumItem->enumerators();
+ for (const EnumeratorModelItem &e : enumerators) {
+ auto enumValue =
+ new EnumValueTypeEntry(e->name(), e->stringValue(),
+ enumTypeEntry, isScopedEnum,
+ enumTypeEntry->version());
+ TypeDatabase::instance()->addType(enumValue);
+ if (e->value().isNullValue())
+ enumTypeEntry->setNullValue(enumValue);
+ }
+
+ return metaEnum;
+}
+
+AbstractMetaClass *AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelItem &,
+ const TypeDefModelItem &typeDef,
+ AbstractMetaClass *currentClass)
+{
+ TypeDatabase *types = TypeDatabase::instance();
+ QString className = stripTemplateArgs(typeDef->name());
+
+ QString fullClassName = className;
+ // we have an inner class
+ if (currentClass) {
+ fullClassName = stripTemplateArgs(currentClass->typeEntry()->qualifiedCppName())
+ + colonColon() + fullClassName;
+ }
+
+ // If this is the alias for a primitive type
+ // we store the aliased type on the alias
+ // TypeEntry
+ PrimitiveTypeEntry *ptype = types->findPrimitiveType(className);
+ if (ptype) {
+ QString typeDefName = typeDef->type().qualifiedName()[0];
+ ptype->setReferencedTypeEntry(types->findPrimitiveType(typeDefName));
+ return nullptr;
+ }
+
+
+ // If we haven't specified anything for the typedef, then we don't care
+ ComplexTypeEntry *type = types->findComplexType(fullClassName);
+ if (!type)
+ return nullptr;
+
+ auto *metaClass = new AbstractMetaClass;
+ metaClass->setTypeDef(true);
+ metaClass->setTypeEntry(type);
+ metaClass->setBaseClassNames(QStringList(typeDef->type().toString()));
+ *metaClass += AbstractMetaAttributes::Public;
+
+ // Set the default include file name
+ if (!type->include().isValid())
+ setInclude(type, typeDef->fileName());
+
+ fillAddedFunctions(metaClass);
+
+ return metaClass;
+}
+
+// Add the typedef'ed classes
+void AbstractMetaBuilderPrivate::traverseTypesystemTypedefs()
+{
+ const auto &entries = TypeDatabase::instance()->typedefEntries();
+ for (auto it = entries.begin(), end = entries.end(); it != end; ++it) {
+ TypedefEntry *te = it.value();
+ auto *metaClass = new AbstractMetaClass;
+ metaClass->setTypeDef(true);
+ metaClass->setTypeEntry(te->target());
+ metaClass->setBaseClassNames(QStringList(te->sourceType()));
+ *metaClass += AbstractMetaAttributes::Public;
+ fillAddedFunctions(metaClass);
+ addAbstractMetaClass(metaClass, nullptr);
+ // Ensure base classes are set up when traversing functions for the
+ // type to be resolved.
+ if (setupInheritance(metaClass)) {
+ // Create an entry to look up up types obtained from parsing
+ // functions in reverse. As opposed to container specializations,
+ // which are generated into every instantiating module (indicated
+ // by ContainerTypeEntry::targetLangPackage() being empty), the
+ // correct index array of the module needs to be found by reverse
+ // mapping the instantiations to the typedef entry.
+ // Synthesize a AbstractMetaType which would be found by an
+ // instantiation.
+ AbstractMetaType sourceType;
+ sourceType.setTypeEntry(metaClass->templateBaseClass()->typeEntry());
+ sourceType.setInstantiations(metaClass->templateBaseClassInstantiations());
+ sourceType.decideUsagePattern();
+ m_typeSystemTypeDefs.append({sourceType, metaClass});
+ }
+ }
+}
+
+AbstractMetaClass *AbstractMetaBuilderPrivate::traverseClass(const FileModelItem &dom,
+ const ClassModelItem &classItem,
+ AbstractMetaClass *currentClass)
+{
+ QString className = stripTemplateArgs(classItem->name());
+ QString fullClassName = className;
+
+ // we have inner an class
+ if (currentClass) {
+ fullClassName = stripTemplateArgs(currentClass->typeEntry()->qualifiedCppName())
+ + colonColon() + fullClassName;
+ }
+
+ ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(fullClassName);
+ AbstractMetaBuilder::RejectReason reason = AbstractMetaBuilder::NoReason;
+
+ if (TypeDatabase::instance()->isClassRejected(fullClassName)) {
+ reason = AbstractMetaBuilder::GenerationDisabled;
+ } else if (!type) {
+ TypeEntry *te = TypeDatabase::instance()->findType(fullClassName);
+ if (te && !te->isComplex())
+ reason = AbstractMetaBuilder::RedefinedToNotClass;
+ else
+ reason = AbstractMetaBuilder::NotInTypeSystem;
+ } else if (type->codeGeneration() == TypeEntry::GenerateNothing) {
+ reason = AbstractMetaBuilder::GenerationDisabled;
+ }
+ if (reason != AbstractMetaBuilder::NoReason) {
+ if (fullClassName.isEmpty()) {
+ QTextStream(&fullClassName) << "anonymous struct at " << classItem->fileName()
+ << ':' << classItem->startLine();
+ }
+ m_rejectedClasses.insert(fullClassName, reason);
+ return nullptr;
+ }
+
+ auto *metaClass = new AbstractMetaClass;
+ metaClass->setSourceLocation(classItem->sourceLocation());
+ metaClass->setTypeEntry(type);
+
+ if (classItem->isFinal())
+ *metaClass += AbstractMetaAttributes::FinalCppClass;
+
+ QStringList baseClassNames;
+ const QVector<_ClassModelItem::BaseClass> &baseClasses = classItem->baseClasses();
+ for (const _ClassModelItem::BaseClass &baseClass : baseClasses) {
+ if (baseClass.accessPolicy == CodeModel::Public)
+ baseClassNames.append(baseClass.name);
+ }
+
+ metaClass->setBaseClassNames(baseClassNames);
+ *metaClass += AbstractMetaAttributes::Public;
+ if (type->stream())
+ metaClass->setStream(true);
+
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug)) {
+ const QString message = type->isContainer()
+ ? QStringLiteral("container: '%1'").arg(fullClassName)
+ : QStringLiteral("class: '%1'").arg(metaClass->fullName());
+ qCInfo(lcShiboken, "%s", qPrintable(message));
+ }
+
+ TemplateParameterList template_parameters = classItem->templateParameters();
+ QVector<TypeEntry *> template_args;
+ template_args.clear();
+ auto argumentParent = metaClass->typeEntry()->typeSystemTypeEntry();
+ for (int i = 0; i < template_parameters.size(); ++i) {
+ const TemplateParameterModelItem &param = template_parameters.at(i);
+ auto param_type = new TemplateArgumentEntry(param->name(), type->version(),
+ argumentParent);
+ param_type->setOrdinal(i);
+ template_args.append(param_type);
+ }
+ metaClass->setTemplateArguments(template_args);
+
+ parseQ_Properties(metaClass, classItem->propertyDeclarations());
+
+ traverseEnums(classItem, metaClass, classItem->enumsDeclarations());
+
+ // Inner classes
+ {
+ const ClassList &innerClasses = classItem->classes();
+ for (const ClassModelItem &ci : innerClasses) {
+ AbstractMetaClass *cl = traverseClass(dom, ci, metaClass);
+ if (cl) {
+ cl->setEnclosingClass(metaClass);
+ metaClass->addInnerClass(cl);
+ addAbstractMetaClass(cl, ci.data());
+ }
+ }
+
+ }
+
+ // Go through all typedefs to see if we have defined any
+ // specific typedefs to be used as classes.
+ const TypeDefList typeDefs = classItem->typeDefs();
+ for (const TypeDefModelItem &typeDef : typeDefs) {
+ AbstractMetaClass *cls = traverseTypeDef(dom, typeDef, metaClass);
+ if (cls) {
+ cls->setEnclosingClass(metaClass);
+ addAbstractMetaClass(cls, typeDef.data());
+ }
+ }
+
+ // Set the default include file name
+ if (!type->include().isValid())
+ setInclude(type, classItem->fileName());
+
+ return metaClass;
+}
+
+void AbstractMetaBuilderPrivate::traverseScopeMembers(const ScopeModelItem &item,
+ AbstractMetaClass *metaClass)
+{
+ // Classes/Namespace members
+ traverseFields(item, metaClass);
+ traverseFunctions(item, metaClass);
+
+ // Inner classes
+ const ClassList &innerClasses = item->classes();
+ for (const ClassModelItem &ci : innerClasses)
+ traverseClassMembers(ci);
+}
+
+void AbstractMetaBuilderPrivate::traverseClassMembers(const ClassModelItem &item)
+{
+ AbstractMetaClass *metaClass = m_itemToClass.value(item.data());
+ if (!metaClass)
+ return;
+
+ // Class members
+ traverseScopeMembers(item, metaClass);
+}
+
+void AbstractMetaBuilderPrivate::traverseNamespaceMembers(const NamespaceModelItem &item)
+{
+ AbstractMetaClass *metaClass = m_itemToClass.value(item.data());
+ if (!metaClass)
+ return;
+
+ // Namespace members
+ traverseScopeMembers(item, metaClass);
+
+ // Inner namespaces
+ for (const NamespaceModelItem &ni : item->namespaces())
+ traverseNamespaceMembers(ni);
+
+}
+
+static inline QString fieldSignatureWithType(const VariableModelItem &field)
+{
+ return field->name() + QStringLiteral(" -> ") + field->type().toString();
+}
+
+static inline QString qualifiedFieldSignatureWithType(const QString &className,
+ const VariableModelItem &field)
+{
+ return className + colonColon() + fieldSignatureWithType(field);
+}
+
+AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(const VariableModelItem &field,
+ AbstractMetaClass *cls)
+{
+ QString fieldName = field->name();
+ QString className = cls->typeEntry()->qualifiedCppName();
+
+ // Ignore friend decl.
+ if (field->isFriend())
+ return nullptr;
+
+ if (field->accessPolicy() == CodeModel::Private)
+ return nullptr;
+
+ QString rejectReason;
+ if (TypeDatabase::instance()->isFieldRejected(className, fieldName, &rejectReason)) {
+ m_rejectedFields.insert(qualifiedFieldSignatureWithType(className, field) + rejectReason,
+ AbstractMetaBuilder::GenerationDisabled);
+ return nullptr;
+ }
+
+
+ auto *metaField = new AbstractMetaField;
+ metaField->setName(fieldName);
+ metaField->setEnclosingClass(cls);
+
+ TypeInfo fieldType = field->type();
+ AbstractMetaType metaType = translateType(fieldType, cls);
+
+ if (!metaType) {
+ const QString type = TypeInfo::resolveType(fieldType, currentScope()).qualifiedName().join(colonColon());
+ if (cls->typeEntry()->generateCode()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgSkippingField(field, cls->name(), type)));
+ }
+ delete metaField;
+ return nullptr;
+ }
+
+ metaField->setType(metaType);
+
+ AbstractMetaAttributes::Attributes attr;
+ if (field->isStatic())
+ attr |= AbstractMetaAttributes::Static;
+
+ CodeModel::AccessPolicy policy = field->accessPolicy();
+ if (policy == CodeModel::Public)
+ attr |= AbstractMetaAttributes::Public;
+ else if (policy == CodeModel::Protected)
+ attr |= AbstractMetaAttributes::Protected;
+ else
+ attr |= AbstractMetaAttributes::Private;
+ metaField->setAttributes(attr);
+
+ return metaField;
+}
+
+void AbstractMetaBuilderPrivate::traverseFields(const ScopeModelItem &scope_item,
+ AbstractMetaClass *metaClass)
+{
+ const VariableList &variables = scope_item->variables();
+ for (const VariableModelItem &field : variables) {
+ AbstractMetaField *metaField = traverseField(field, metaClass);
+
+ if (metaField && !metaField->isModifiedRemoved()) {
+ metaField->setOriginalAttributes(metaField->attributes());
+ metaClass->addField(metaField);
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::setupFunctionDefaults(AbstractMetaFunction *metaFunction,
+ AbstractMetaClass *metaClass)
+{
+ // Set the default value of the declaring class. This may be changed
+ // in fixFunctions later on
+ metaFunction->setDeclaringClass(metaClass);
+
+ // Some of the queries below depend on the implementing class being set
+ // to function properly. Such as function modifications
+ metaFunction->setImplementingClass(metaClass);
+
+ if (metaFunction->name() == QLatin1String("operator_equal"))
+ metaClass->setHasEqualsOperator(true);
+}
+
+void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction)
+{
+ if (!metaFunction->isConversionOperator())
+ return;
+
+ TypeDatabase *types = TypeDatabase::instance();
+ static const QRegularExpression operatorRegExp(QStringLiteral("^operator "));
+ Q_ASSERT(operatorRegExp.isValid());
+ QString castTo = metaFunction->name().remove(operatorRegExp).trimmed();
+
+ if (castTo.endsWith(QLatin1Char('&')))
+ castTo.chop(1);
+ if (castTo.startsWith(QLatin1String("const ")))
+ castTo.remove(0, 6);
+
+ TypeEntry *retType = types->findType(castTo);
+ if (!retType)
+ return;
+
+ AbstractMetaType metaType(retType);
+ metaType.decideUsagePattern();
+ metaFunction->setType(metaType);
+}
+
+AbstractMetaFunctionList AbstractMetaBuilderPrivate::classFunctionList(const ScopeModelItem &scopeItem,
+ AbstractMetaClass::Attributes *constructorAttributes,
+ AbstractMetaClass *currentClass)
+{
+ *constructorAttributes = {};
+ AbstractMetaFunctionList result;
+ const FunctionList &scopeFunctionList = scopeItem->functions();
+ result.reserve(scopeFunctionList.size());
+ for (const FunctionModelItem &function : scopeFunctionList) {
+ if (AbstractMetaFunction *metaFunction = traverseFunction(function, currentClass)) {
+ result.append(metaFunction);
+ } else if (function->functionType() == CodeModel::Constructor) {
+ auto arguments = function->arguments();
+ *constructorAttributes |= AbstractMetaAttributes::HasRejectedConstructor;
+ if (arguments.isEmpty() || arguments.constFirst()->defaultValue())
+ *constructorAttributes |= AbstractMetaAttributes::HasRejectedDefaultConstructor;
+ }
+ }
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::traverseFunctions(ScopeModelItem scopeItem,
+ AbstractMetaClass *metaClass)
+{
+ AbstractMetaAttributes::Attributes constructorAttributes;
+ const AbstractMetaFunctionList functions =
+ classFunctionList(scopeItem, &constructorAttributes, metaClass);
+ metaClass->setAttributes(metaClass->attributes() | constructorAttributes);
+
+ for (AbstractMetaFunction *metaFunction : functions) {
+ metaFunction->setOriginalAttributes(metaFunction->attributes());
+ if (metaClass->isNamespace())
+ *metaFunction += AbstractMetaAttributes::Static;
+
+ QPropertySpec *read = nullptr;
+ if (!metaFunction->isSignal() && (read = metaClass->propertySpecForRead(metaFunction->name()))) {
+ // Property reader must be in the form "<type> name()"
+ if (read->typeEntry() == metaFunction->type().typeEntry()
+ && metaFunction->arguments().isEmpty()) {
+ *metaFunction += AbstractMetaAttributes::PropertyReader;
+ metaFunction->setPropertySpec(read);
+ }
+ } else if (QPropertySpec *write = metaClass->propertySpecForWrite(metaFunction->name())) {
+ // Property setter must be in the form "void name(<type>)"
+ // Make sure the function was created with all arguments; some argument can be
+ // missing during the parsing because of errors in the typesystem.
+ if (metaFunction->isVoid() && metaFunction->arguments().size() == 1
+ && (write->typeEntry() == metaFunction->arguments().at(0).type().typeEntry())) {
+ *metaFunction += AbstractMetaAttributes::PropertyWriter;
+ metaFunction->setPropertySpec(write);
+ }
+ } else if (QPropertySpec *reset = metaClass->propertySpecForReset(metaFunction->name())) {
+ // Property resetter must be in the form "void name()"
+ if (metaFunction->isVoid() && metaFunction->arguments().isEmpty()) {
+ *metaFunction += AbstractMetaAttributes::PropertyResetter;
+ metaFunction->setPropertySpec(reset);
+ }
+ }
+
+ const bool isInvalidDestructor = metaFunction->isDestructor() && metaFunction->isPrivate();
+ const bool isInvalidConstructor = metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction
+ && metaFunction->isPrivate();
+ if (isInvalidConstructor)
+ metaClass->setHasPrivateConstructor(true);
+ if ((isInvalidDestructor || isInvalidConstructor)
+ && !metaClass->hasNonPrivateConstructor()) {
+ *metaClass += AbstractMetaAttributes::FinalInTargetLang;
+ } else if (metaFunction->isConstructor() && !metaFunction->isPrivate()) {
+ *metaClass -= AbstractMetaAttributes::FinalInTargetLang;
+ metaClass->setHasNonPrivateConstructor(true);
+ }
+
+ if (!metaFunction->isDestructor()
+ && !(metaFunction->isPrivate() && metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction)) {
+
+ setupFunctionDefaults(metaFunction, metaClass);
+
+ if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction)) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("signal '%1' in class '%2' is overloaded.")
+ .arg(metaFunction->name(), metaClass->name());
+ }
+
+ if (metaFunction->isConversionOperator())
+ fixReturnTypeOfConversionOperator(metaFunction);
+
+ metaClass->addFunction(metaFunction);
+ applyFunctionModifications(metaFunction);
+ } else if (metaFunction->isDestructor()) {
+ metaClass->setHasPrivateDestructor(metaFunction->isPrivate());
+ metaClass->setHasProtectedDestructor(metaFunction->isProtected());
+ metaClass->setHasVirtualDestructor(metaFunction->isVirtual());
+ }
+ if (!metaFunction->ownerClass()) {
+ delete metaFunction;
+ metaFunction = nullptr;
+ }
+ }
+
+ fillAddedFunctions(metaClass);
+}
+
+void AbstractMetaBuilderPrivate::fillAddedFunctions(AbstractMetaClass *metaClass)
+{
+ // Add the functions added by the typesystem
+ const AddedFunctionList &addedFunctions = metaClass->typeEntry()->addedFunctions();
+ for (const AddedFunctionPtr &addedFunc : addedFunctions) {
+ if (!traverseFunction(addedFunc, metaClass)) {
+ qFatal("Unable to traverse function \"%s\" added to \"%s\".",
+ qPrintable(addedFunc->name()), qPrintable(metaClass->name()));
+ }
+ }
+}
+
+void AbstractMetaBuilderPrivate::applyFunctionModifications(AbstractMetaFunction *func)
+{
+ const FunctionModificationList &mods = func->modifications(func->implementingClass());
+ AbstractMetaFunction& funcRef = *func;
+ for (const FunctionModification &mod : mods) {
+ if (mod.isRenameModifier()) {
+ func->setOriginalName(func->name());
+ func->setName(mod.renamedTo());
+ } else if (mod.isAccessModifier()) {
+ funcRef -= AbstractMetaAttributes::Public;
+ funcRef -= AbstractMetaAttributes::Protected;
+ funcRef -= AbstractMetaAttributes::Private;
+ funcRef -= AbstractMetaAttributes::Friendly;
+
+ if (mod.isPublic())
+ funcRef += AbstractMetaAttributes::Public;
+ else if (mod.isProtected())
+ funcRef += AbstractMetaAttributes::Protected;
+ else if (mod.isPrivate())
+ funcRef += AbstractMetaAttributes::Private;
+ else if (mod.isFriendly())
+ funcRef += AbstractMetaAttributes::Friendly;
+ }
+
+ if (mod.isFinal())
+ funcRef += AbstractMetaAttributes::FinalInTargetLang;
+ else if (mod.isNonFinal())
+ funcRef -= AbstractMetaAttributes::FinalInTargetLang;
+ }
+}
+
+bool AbstractMetaBuilderPrivate::setupInheritance(AbstractMetaClass *metaClass)
+{
+ if (m_setupInheritanceDone.contains(metaClass))
+ return true;
+
+ m_setupInheritanceDone.insert(metaClass);
+
+ QStringList baseClasses = metaClass->baseClassNames();
+
+ // we only support our own containers and ONLY if there is only one baseclass
+ if (baseClasses.size() == 1 && baseClasses.constFirst().contains(QLatin1Char('<'))) {
+ TypeInfo info;
+ ComplexTypeEntry* baseContainerType;
+ AbstractMetaClass* templ = findTemplateClass(baseClasses.constFirst(), metaClass, &info, &baseContainerType);
+ if (templ) {
+ setupInheritance(templ);
+ inheritTemplate(metaClass, templ, info);
+ metaClass->typeEntry()->setBaseContainerType(templ->typeEntry());
+ return true;
+ }
+
+ if (baseContainerType) {
+ // Container types are not necessarily wrapped as 'real' classes,
+ // but there may still be classes derived from them. In such case,
+ // we still need to set the base container type in order to
+ // generate correct code for type conversion checking.
+ //
+ // Additionally, we consider this case as successfully setting up
+ // inheritance.
+ metaClass->typeEntry()->setBaseContainerType(baseContainerType);
+ return true;
+ }
+
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("template baseclass '%1' of '%2' is not known")
+ .arg(baseClasses.constFirst(), metaClass->name());
+ return false;
+ }
+
+ TypeDatabase* types = TypeDatabase::instance();
+
+ for (const auto &baseClassName : baseClasses) {
+ if (!types->isClassRejected(baseClassName)) {
+ if (!types->findType(baseClassName)) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgUnknownBase(metaClass, baseClassName)));
+ return false;
+ }
+ auto baseClass = AbstractMetaClass::findClass(m_metaClasses, baseClassName);
+ if (!baseClass) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("class not found for setup inheritance '%1'").arg(baseClassName);
+ return false;
+ }
+ metaClass->addBaseClass(baseClass);
+
+ setupInheritance(baseClass);
+ }
+ }
+
+ return true;
+}
+
+void AbstractMetaBuilderPrivate::traverseEnums(const ScopeModelItem &scopeItem,
+ AbstractMetaClass *metaClass,
+ const QStringList &enumsDeclarations)
+{
+ const EnumList &enums = scopeItem->enums();
+ const QSet<QString> enumsDeclarationSet(enumsDeclarations.cbegin(), enumsDeclarations.cend());
+ for (const EnumModelItem &enumItem : enums) {
+ AbstractMetaEnum* metaEnum = traverseEnum(enumItem, metaClass, enumsDeclarationSet);
+ if (metaEnum) {
+ metaClass->addEnum(metaEnum);
+ metaEnum->setEnclosingClass(metaClass);
+ }
+ }
+}
+
+static void applyDefaultExpressionModifications(const FunctionModificationList &functionMods,
+ int i, AbstractMetaArgument *metaArg)
+{
+ // use replace/remove-default-expression for set default value
+ for (const auto &modification : functionMods) {
+ for (const auto &argumentModification : modification.argument_mods) {
+ if (argumentModification.index == i + 1) {
+ if (argumentModification.removedDefaultExpression) {
+ metaArg->setDefaultValueExpression(QString());
+ break;
+ }
+ if (!argumentModification.replacedDefaultExpression.isEmpty()) {
+ metaArg->setDefaultValueExpression(argumentModification.replacedDefaultExpression);
+ break;
+ }
+ }
+ }
+ }
+}
+
+AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFunctionPtr &addedFunc)
+{
+ return traverseFunction(addedFunc, nullptr);
+}
+
+AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFunctionPtr &addedFunc,
+ AbstractMetaClass *metaClass)
+{
+ QString errorMessage;
+
+ AbstractMetaType returnType = translateType(addedFunc->returnType(), &errorMessage);
+ if (!returnType) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgAddedFunctionInvalidReturnType(addedFunc->name(),
+ addedFunc->returnType().name,
+ errorMessage,
+ metaClass)));
+ return nullptr;
+ }
+
+ auto metaFunction = new AbstractMetaFunction(addedFunc);
+ metaFunction->setType(returnType);
+
+ const auto &args = addedFunc->arguments();
+
+ for (int i = 0; i < args.count(); ++i) {
+ const AddedFunction::TypeInfo& typeInfo = args.at(i).typeInfo;
+ AbstractMetaType type = translateType(typeInfo, &errorMessage);
+ if (Q_UNLIKELY(!type)) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgAddedFunctionInvalidArgType(addedFunc->name(),
+ typeInfo.name, i + 1,
+ errorMessage,
+ metaClass)));
+ delete metaFunction;
+ return nullptr;
+ }
+ type.decideUsagePattern();
+
+ AbstractMetaArgument metaArg;
+ if (!args.at(i).name.isEmpty())
+ metaArg.setName(args.at(i).name);
+ metaArg.setType(type);
+ metaArg.setArgumentIndex(i);
+ metaArg.setDefaultValueExpression(typeInfo.defaultValue);
+ metaArg.setOriginalDefaultValueExpression(typeInfo.defaultValue);
+ metaFunction->addArgument(metaArg);
+ }
+
+ AbstractMetaArgumentList &metaArguments = metaFunction->arguments();
+
+ if (metaFunction->isOperatorOverload() && !metaFunction->isCallOperator()) {
+ if (metaArguments.size() > 2) {
+ qCWarning(lcShiboken) << "An operator overload need to have 0, 1 or 2 arguments if it's reverse.";
+ } else if (metaArguments.size() == 2) {
+ // Check if it's a reverse operator
+ if (metaArguments[1].type().typeEntry() == metaClass->typeEntry()) {
+ metaFunction->setReverseOperator(true);
+ // we need to call these two function to cache the old signature (with two args)
+ // we do this buggy behaviour to comply with the original apiextractor buggy behaviour.
+ metaFunction->signature();
+ metaFunction->minimalSignature();
+ metaArguments.removeLast();
+ metaFunction->setArguments(metaArguments);
+ } else {
+ qCWarning(lcShiboken) << "Operator overload can have two arguments only if it's a reverse operator!";
+ }
+ }
+ }
+
+
+ // Find the correct default values
+ const FunctionModificationList functionMods = metaFunction->modifications(metaClass);
+ for (int i = 0; i < metaArguments.size(); ++i) {
+ AbstractMetaArgument &metaArg = metaArguments[i];
+
+ // use replace-default-expression for set default value
+ applyDefaultExpressionModifications(functionMods, i, &metaArg);
+ metaArg.setOriginalDefaultValueExpression(metaArg.defaultValueExpression()); // appear unmodified
+ }
+
+ metaFunction->setOriginalAttributes(metaFunction->attributes());
+ if (!metaArguments.isEmpty())
+ fixArgumentNames(metaFunction, metaFunction->modifications(metaClass));
+
+ if (metaClass) {
+ const AbstractMetaArgumentList fargs = metaFunction->arguments();
+ if (metaClass->isNamespace())
+ *metaFunction += AbstractMetaFunction::Static;
+ if (metaFunction->name() == metaClass->name()) {
+ metaFunction->setFunctionType(AbstractMetaFunction::ConstructorFunction);
+ if (fargs.size() == 1) {
+ const TypeEntry *te = fargs.constFirst().type().typeEntry();
+ if (te->isCustom())
+ metaFunction->setExplicit(true);
+ if (te->name() == metaFunction->name())
+ metaFunction->setFunctionType(AbstractMetaFunction::CopyConstructorFunction);
+ }
+ } else {
+ auto type = AbstractMetaFunction::NormalFunction;
+ if (metaFunction->name() == QLatin1String("__getattro__"))
+ type = AbstractMetaFunction::GetAttroFunction;
+ else if (metaFunction->name() == QLatin1String("__setattro__"))
+ type = AbstractMetaFunction::SetAttroFunction;
+ metaFunction->setFunctionType(type);
+ }
+
+ metaFunction->setDeclaringClass(metaClass);
+ metaFunction->setImplementingClass(metaClass);
+ metaClass->addFunction(metaFunction);
+ metaClass->setHasNonPrivateConstructor(true);
+ }
+
+ return metaFunction;
+}
+
+void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods)
+{
+ AbstractMetaArgumentList &arguments = func->arguments();
+
+ for (const FunctionModification &mod : mods) {
+ for (const ArgumentModification &argMod : mod.argument_mods) {
+ if (!argMod.renamed_to.isEmpty())
+ arguments[argMod.index - 1].setName(argMod.renamed_to, false);
+ }
+ }
+
+ for (int i = 0, size = arguments.size(); i < size; ++i) {
+ if (arguments.at(i).name().isEmpty())
+ arguments[i].setName(QLatin1String("arg__") + QString::number(i + 1), false);
+ }
+}
+
+static QString functionSignature(const FunctionModelItem &functionItem)
+{
+ QStringList args;
+ const ArgumentList &arguments = functionItem->arguments();
+ for (const ArgumentModelItem &arg : arguments)
+ args << arg->type().toString();
+ return functionItem->name() + QLatin1Char('(') + args.join(QLatin1Char(',')) + QLatin1Char(')');
+}
+
+static inline QString qualifiedFunctionSignatureWithType(const FunctionModelItem &functionItem,
+ const QString &className = QString())
+{
+ QString result = functionItem->type().toString() + QLatin1Char(' ');
+ if (!className.isEmpty())
+ result += className + colonColon();
+ result += functionSignature(functionItem);
+ return result;
+}
+static inline AbstractMetaFunction::FunctionType functionTypeFromCodeModel(CodeModel::FunctionType ft)
+{
+ AbstractMetaFunction::FunctionType result = AbstractMetaFunction::NormalFunction;
+ switch (ft) {
+ case CodeModel::Constructor:
+ result = AbstractMetaFunction::ConstructorFunction;
+ break;
+ case CodeModel::CopyConstructor:
+ result = AbstractMetaFunction::CopyConstructorFunction;
+ break;
+ case CodeModel::MoveConstructor:
+ result = AbstractMetaFunction::MoveConstructorFunction;
+ break;
+ case CodeModel::Destructor:
+ result = AbstractMetaFunction::DestructorFunction;
+ break;
+ case CodeModel::Normal:
+ break;
+ case CodeModel::Signal:
+ result = AbstractMetaFunction::SignalFunction;
+ break;
+ case CodeModel::Slot:
+ result = AbstractMetaFunction::SlotFunction;
+ break;
+ }
+ return result;
+}
+
+// Apply the <array> modifications of the arguments
+static bool applyArrayArgumentModifications(const FunctionModificationList &functionMods,
+ AbstractMetaFunction *func,
+ QString *errorMessage)
+{
+ for (const FunctionModification &mod : functionMods) {
+ for (const ArgumentModification &argMod : mod.argument_mods) {
+ if (argMod.array) {
+ const int i = argMod.index - 1;
+ if (i < 0 || i >= func->arguments().size()) {
+ *errorMessage = msgCannotSetArrayUsage(func->minimalSignature(), i,
+ QLatin1String("Index out of range."));
+ return false;
+ }
+ auto t = func->arguments().at(i).type();
+ if (!t.applyArrayModification(errorMessage)) {
+ *errorMessage = msgCannotSetArrayUsage(func->minimalSignature(), i, *errorMessage);
+ return false;
+ }
+ func->arguments()[i].setType(t);
+ }
+ }
+ }
+ return true;
+}
+
+AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem,
+ AbstractMetaClass *currentClass)
+{
+ if (functionItem->isDeleted() || !functionItem->templateParameters().isEmpty())
+ return nullptr;
+ QString functionName = functionItem->name();
+ QString className;
+ if (currentClass) {
+ // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT
+ // and overridden metaObject(), QGADGET helpers
+ if (functionName == QLatin1String("qt_check_for_QGADGET_macro")
+ || functionName.startsWith(QLatin1String("qt_meta"))) {
+ return nullptr;
+ }
+ className = currentClass->typeEntry()->qualifiedCppName();
+ if (functionName == QLatin1String("metaObject") && className != QLatin1String("QObject"))
+ return nullptr;
+ }
+
+ // Store original signature with unresolved typedefs for message/log purposes
+ const QString originalQualifiedSignatureWithReturn =
+ qualifiedFunctionSignatureWithType(functionItem, className);
+
+ QString rejectReason;
+ if (TypeDatabase::instance()->isFunctionRejected(className, functionName, &rejectReason)) {
+ m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled);
+ return nullptr;
+ }
+ const QString &signature = functionSignature(functionItem);
+ const bool rejected =
+ TypeDatabase::instance()->isFunctionRejected(className, signature, &rejectReason);
+
+ if (rejected) {
+ if (ReportHandler::isDebug(ReportHandler::MediumDebug)) {
+ qCInfo(lcShiboken, "%s::%s was rejected by the type database (%s).",
+ qPrintable(className), qPrintable(signature), qPrintable(rejectReason));
+ }
+ return nullptr;
+ }
+
+ if (functionItem->isFriend())
+ return nullptr;
+
+ const bool deprecated = functionItem->isDeprecated();
+ if (deprecated && m_skipDeprecated) {
+ m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + QLatin1String(" is deprecated."),
+ AbstractMetaBuilder::GenerationDisabled);
+ return nullptr;
+ }
+
+ auto *metaFunction = new AbstractMetaFunction;
+ metaFunction->setSourceLocation(functionItem->sourceLocation());
+ if (deprecated)
+ *metaFunction += AbstractMetaAttributes::Deprecated;
+
+ // Additional check for assignment/move assignment down below
+ metaFunction->setFunctionType(functionTypeFromCodeModel(functionItem->functionType()));
+ metaFunction->setConstant(functionItem->isConstant());
+ metaFunction->setExceptionSpecification(functionItem->exceptionSpecification());
+
+ metaFunction->setName(functionName);
+ metaFunction->setOriginalName(functionItem->name());
+
+ if (functionItem->isAbstract())
+ *metaFunction += AbstractMetaAttributes::Abstract;
+
+ if (functionItem->isVirtual()) {
+ *metaFunction += AbstractMetaAttributes::VirtualCppMethod;
+ if (functionItem->isOverride())
+ *metaFunction += AbstractMetaAttributes::OverriddenCppMethod;
+ if (functionItem->isFinal())
+ *metaFunction += AbstractMetaAttributes::FinalCppMethod;
+ } else {
+ *metaFunction += AbstractMetaAttributes::FinalInTargetLang;
+ }
+
+ if (functionItem->isInvokable())
+ *metaFunction += AbstractMetaAttributes::Invokable;
+
+ if (functionItem->isStatic()) {
+ *metaFunction += AbstractMetaAttributes::Static;
+ *metaFunction += AbstractMetaAttributes::FinalInTargetLang;
+ }
+
+ // Access rights
+ if (functionItem->accessPolicy() == CodeModel::Public)
+ *metaFunction += AbstractMetaAttributes::Public;
+ else if (functionItem->accessPolicy() == CodeModel::Private)
+ *metaFunction += AbstractMetaAttributes::Private;
+ else
+ *metaFunction += AbstractMetaAttributes::Protected;
+
+ QString errorMessage;
+ switch (metaFunction->functionType()) {
+ case AbstractMetaFunction::DestructorFunction:
+ metaFunction->setType(AbstractMetaType::createVoid());
+ break;
+ case AbstractMetaFunction::ConstructorFunction:
+ metaFunction->setExplicit(functionItem->isExplicit());
+ metaFunction->setName(currentClass->name());
+ metaFunction->setType(AbstractMetaType::createVoid());
+ break;
+ default: {
+ TypeInfo returnType = functionItem->type();
+
+ if (TypeDatabase::instance()->isReturnTypeRejected(className, returnType.toString(), &rejectReason)) {
+ m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ AbstractMetaType type = translateType(returnType, currentClass, {}, &errorMessage);
+ if (!type) {
+ const QString reason = msgUnmatchedReturnType(functionItem, errorMessage);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason)));
+ m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::UnmatchedReturnType);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ metaFunction->setType(type);
+ }
+ break;
+ }
+
+ ArgumentList arguments = functionItem->arguments();
+
+ if (arguments.size() == 1) {
+ ArgumentModelItem arg = arguments.at(0);
+ TypeInfo type = arg->type();
+ if (type.qualifiedName().constFirst() == QLatin1String("void") && type.indirections() == 0)
+ arguments.pop_front();
+ }
+
+ for (int i = 0; i < arguments.size(); ++i) {
+ const ArgumentModelItem &arg = arguments.at(i);
+
+ if (TypeDatabase::instance()->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) {
+ m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ AbstractMetaType metaType = translateType(arg->type(), currentClass, {}, &errorMessage);
+ if (!metaType) {
+ // If an invalid argument has a default value, simply remove it
+ // unless the function is virtual (since the override in the
+ // wrapper can then not correctly be generated).
+ if (arg->defaultValue() && !functionItem->isVirtual()) {
+ if (!currentClass || currentClass->typeEntry()->generateCode()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgStrippingArgument(functionItem, i, originalQualifiedSignatureWithReturn, arg)));
+ }
+ break;
+ }
+ Q_ASSERT(!metaType);
+ const QString reason = msgUnmatchedParameterType(arg, i, errorMessage);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason)));
+ const QString rejectedFunctionSignature = originalQualifiedSignatureWithReturn
+ + QLatin1String(": ") + reason;
+ m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::UnmatchedArgumentType);
+ delete metaFunction;
+ return nullptr;
+ }
+
+ // Add view substitution for simple view types of function arguments
+ // std::string_view -> std::string for foo(std::string_view)
+ auto viewOnTypeEntry = metaType.typeEntry()->viewOn();
+ if (viewOnTypeEntry != nullptr && metaType.indirections() == 0
+ && metaType.arrayElementType() == nullptr
+ && !metaType.hasInstantiations()) {
+ auto viewOn = metaType;
+ viewOn.setTypeEntry(viewOnTypeEntry);
+ metaType.setViewOn(viewOn);
+ }
+
+ AbstractMetaArgument metaArgument;
+ metaArgument.setType(metaType);
+ metaArgument.setName(arg->name());
+ metaArgument.setArgumentIndex(i);
+ metaFunction->addArgument(metaArgument);
+ }
+
+ AbstractMetaArgumentList &metaArguments = metaFunction->arguments();
+
+ const FunctionModificationList functionMods = metaFunction->modifications(currentClass);
+
+ for (const FunctionModification &mod : functionMods) {
+ if (mod.exceptionHandling() != TypeSystem::ExceptionHandling::Unspecified)
+ metaFunction->setExceptionHandlingModification(mod.exceptionHandling());
+ else if (mod.allowThread() != TypeSystem::AllowThread::Unspecified)
+ metaFunction->setAllowThreadModification(mod.allowThread());
+ }
+
+ // Find the correct default values
+ for (int i = 0, size = metaArguments.size(); i < size; ++i) {
+ const ArgumentModelItem &arg = arguments.at(i);
+ AbstractMetaArgument &metaArg = metaArguments[i];
+
+ const QString originalDefaultExpression =
+ fixDefaultValue(arg, metaArg.type(), metaFunction, currentClass, i);
+
+ metaArg.setOriginalDefaultValueExpression(originalDefaultExpression);
+ metaArg.setDefaultValueExpression(originalDefaultExpression);
+
+ applyDefaultExpressionModifications(functionMods, i, &metaArg);
+
+ //Check for missing argument name
+ if (!metaArg.defaultValueExpression().isEmpty()
+ && !metaArg.hasName()
+ && !metaFunction->isOperatorOverload()
+ && !metaFunction->isSignal()
+ && metaFunction->argumentName(i + 1, false, currentClass).isEmpty()) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("Argument %1 on function '%2::%3' has default expression but does not have name.")
+ .arg(i+1).arg(className, metaFunction->minimalSignature());
+ }
+
+ }
+
+ if (!metaArguments.isEmpty()) {
+ fixArgumentNames(metaFunction, functionMods);
+ QString errorMessage;
+ if (!applyArrayArgumentModifications(functionMods, metaFunction, &errorMessage)) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgArrayModificationFailed(functionItem, className, errorMessage)));
+ }
+ }
+
+ // Determine class special functions
+ if (currentClass && metaFunction->arguments().size() == 1) {
+ const AbstractMetaType &argType = metaFunction->arguments().constFirst().type();
+ if (argType.typeEntry() == currentClass->typeEntry() && argType.indirections() == 0) {
+ if (metaFunction->name() == QLatin1String("operator=")) {
+ switch (argType.referenceType()) {
+ case NoReference:
+ metaFunction->setFunctionType(AbstractMetaFunction::AssignmentOperatorFunction);
+ break;
+ case LValueReference:
+ if (argType.isConstant())
+ metaFunction->setFunctionType(AbstractMetaFunction::AssignmentOperatorFunction);
+ break;
+ case RValueReference:
+ metaFunction->setFunctionType(AbstractMetaFunction::MoveAssignmentOperatorFunction);
+ break;
+ }
+ }
+ }
+ }
+ return metaFunction;
+}
+
+AbstractMetaType AbstractMetaBuilderPrivate::translateType(const AddedFunction::TypeInfo &typeInfo,
+ QString *errorMessage)
+{
+ Q_ASSERT(!typeInfo.name.isEmpty());
+ TypeDatabase* typeDb = TypeDatabase::instance();
+
+ QString typeName = typeInfo.name;
+
+ if (typeName == QLatin1String("void"))
+ return AbstractMetaType::createVoid();
+
+ TypeEntry *type = typeDb->findType(typeName);
+ if (!type)
+ type = typeDb->findFlagsType(typeName);
+
+ // test if the type is a template, like a container
+ bool isTemplate = false;
+ QStringList templateArgs;
+ if (!type && typeInfo.name.contains(QLatin1Char('<'))) {
+ QStringList parsedType = parseTemplateType(typeInfo.name);
+ if (parsedType.isEmpty()) {
+ *errorMessage = QStringLiteral("Template type parsing failed for '%1'").arg(typeInfo.name);
+ return {};
+ }
+ const QString name = parsedType.takeFirst();
+ templateArgs = parsedType;
+ type = typeDb->findContainerType(name);
+ if (!type) { // A template typedef?
+ if (auto candidate = typeDb->findType(name)) {
+ if (candidate->type() == TypeEntry::ObjectType || candidate->type() == TypeEntry::BasicValueType)
+ type = candidate;
+ }
+ }
+ isTemplate = type != nullptr;
+ }
+
+ if (!type) {
+ QStringList candidates;
+ const auto &entries = typeDb->entries();
+ for (auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) {
+ // Let's try to find the type in different scopes.
+ if (it.key().endsWith(colonColon() + typeName))
+ candidates.append(it.key());
+ }
+ QTextStream str(errorMessage);
+ str << "Type '" << typeName << "' wasn't found in the type database.\n";
+
+ if (candidates.isEmpty()) {
+ str << "Declare it in the type system using the proper <*-type> tag.";
+ } else {
+ str << "Remember to inform the full qualified name for the type you want to use.\nCandidates are:\n";
+ candidates.sort();
+ for (const QString& candidate : qAsConst(candidates))
+ str << " " << candidate << '\n';
+ }
+ return {};
+ }
+
+ // These are only implicit and should not appear in code...
+ AbstractMetaType metaType(type);
+ metaType.setIndirections(typeInfo.indirections);
+ if (typeInfo.isReference)
+ metaType.setReferenceType(LValueReference);
+ metaType.setConstant(typeInfo.isConstant);
+ if (isTemplate) {
+ for (const QString& templateArg : qAsConst(templateArgs)) {
+ AbstractMetaType metaArgType = translateType(AddedFunction::TypeInfo::fromSignature(templateArg), errorMessage);
+ if (!metaArgType)
+ return {};
+ metaType.addInstantiation(metaArgType);
+ }
+ metaType.setTypeUsagePattern(AbstractMetaType::ContainerPattern);
+ } else {
+ metaType.decideUsagePattern();
+ }
+
+ return metaType;
+}
+
+static const TypeEntry* findTypeEntryUsingContext(const AbstractMetaClass* metaClass, const QString& qualifiedName)
+{
+ const TypeEntry* type = nullptr;
+ QStringList context = metaClass->qualifiedCppName().split(colonColon());
+ while (!type && !context.isEmpty()) {
+ type = TypeDatabase::instance()->findType(context.join(colonColon()) + colonColon() + qualifiedName);
+ context.removeLast();
+ }
+ return type;
+}
+
+// Helper for translateTypeStatic()
+TypeEntries AbstractMetaBuilderPrivate::findTypeEntries(const QString &qualifiedName,
+ const QString &name,
+ AbstractMetaClass *currentClass,
+ AbstractMetaBuilderPrivate *d)
+{
+ // 5.1 - Try first using the current scope
+ if (currentClass) {
+ if (auto type = findTypeEntryUsingContext(currentClass, qualifiedName))
+ return {type};
+
+ // 5.1.1 - Try using the class parents' scopes
+ if (d && !currentClass->baseClassNames().isEmpty()) {
+ const AbstractMetaClassList &baseClasses = d->getBaseClasses(currentClass);
+ for (const AbstractMetaClass *cls : baseClasses) {
+ if (auto type = findTypeEntryUsingContext(cls, qualifiedName))
+ return {type};
+ }
+ }
+ }
+
+ // 5.2 - Try without scope
+ auto types = TypeDatabase::instance()->findCppTypes(qualifiedName);
+ if (!types.isEmpty())
+ return types;
+
+ // 6. No? Try looking it up as a flags type
+ if (auto type = TypeDatabase::instance()->findFlagsType(qualifiedName))
+ return {type};
+
+ // 7. No? Try looking it up as a container type
+ if (auto type = TypeDatabase::instance()->findContainerType(name))
+ return {type};
+
+ // 8. No? Check if the current class is a template and this type is one
+ // of the parameters.
+ if (currentClass) {
+ const QVector<TypeEntry *> &template_args = currentClass->templateArguments();
+ for (TypeEntry *te : template_args) {
+ if (te->name() == qualifiedName)
+ return {te};
+ }
+ }
+ return {};
+}
+
+// Reverse lookup of AbstractMetaType representing a template specialization
+// found during traversing function arguments to its type system typedef'ed
+// class.
+const AbstractMetaClass *AbstractMetaBuilderPrivate::resolveTypeSystemTypeDef(const AbstractMetaType &t) const
+{
+ if (t.hasInstantiations()) {
+ auto pred = [t](const TypeClassEntry &e) { return e.type.equals(t); };
+ auto it = std::find_if(m_typeSystemTypeDefs.cbegin(), m_typeSystemTypeDefs.cend(), pred);
+ if (it != m_typeSystemTypeDefs.cend())
+ return it->klass;
+ }
+ return nullptr;
+}
+
+AbstractMetaType AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typei,
+ AbstractMetaClass *currentClass,
+ TranslateTypeFlags flags,
+ QString *errorMessage)
+{
+ return translateTypeStatic(_typei, currentClass, this, flags, errorMessage);
+}
+
+static bool isNumber(const QString &s)
+{
+ return std::all_of(s.cbegin(), s.cend(),
+ [](QChar c) { return c.isDigit(); });
+}
+
+AbstractMetaType AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo &_typei,
+ AbstractMetaClass *currentClass,
+ AbstractMetaBuilderPrivate *d,
+ TranslateTypeFlags flags,
+ QString *errorMessageIn)
+{
+ if (_typei.isVoid())
+ return AbstractMetaType::createVoid();
+
+ // 1. Test the type info without resolving typedefs in case this is present in the
+ // type system
+ const bool resolveType = !flags.testFlag(AbstractMetaBuilder::DontResolveType);
+ if (resolveType) {
+ AbstractMetaType resolved =
+ translateTypeStatic(_typei, currentClass, d,
+ flags | AbstractMetaBuilder::DontResolveType,
+ errorMessageIn);
+ if (resolved)
+ return resolved;
+ }
+
+ TypeInfo typeInfo = _typei;
+ if (resolveType) {
+ // Go through all parts of the current scope (including global namespace)
+ // to resolve typedefs. The parser does not properly resolve typedefs in
+ // the global scope when they are referenced from inside a namespace.
+ // This is a work around to fix this bug since fixing it in resolveType
+ // seemed non-trivial
+ int i = d ? d->m_scopes.size() - 1 : -1;
+ while (i >= 0) {
+ typeInfo = TypeInfo::resolveType(_typei, d->m_scopes.at(i--));
+ if (typeInfo.qualifiedName().join(colonColon()) != _typei.qualifiedName().join(colonColon()))
+ break;
+ }
+
+ }
+
+ if (typeInfo.isFunctionPointer()) {
+ if (errorMessageIn)
+ *errorMessageIn = msgUnableToTranslateType(_typei, QLatin1String("Unsupported function pointer."));
+ return {};
+ }
+
+ QString errorMessage;
+
+ // 2. Handle arrays.
+ // 2.1 Handle char arrays with unspecified size (aka "const char[]") as "const char*" with
+ // NativePointerPattern usage.
+ bool oneDimensionalArrayOfUnspecifiedSize =
+ typeInfo.arrayElements().size() == 1
+ && typeInfo.arrayElements().at(0).isEmpty();
+
+ bool isConstCharStarCase =
+ oneDimensionalArrayOfUnspecifiedSize
+ && typeInfo.qualifiedName().size() == 1
+ && typeInfo.qualifiedName().at(0) == QStringLiteral("char")
+ && typeInfo.indirections() == 0
+ && typeInfo.isConstant()
+ && typeInfo.referenceType() == NoReference
+ && typeInfo.arguments().isEmpty();
+
+ if (isConstCharStarCase)
+ typeInfo.setIndirections(typeInfo.indirections() + typeInfo.arrayElements().size());
+
+ // 2.2 Handle regular arrays.
+ if (!typeInfo.arrayElements().isEmpty() && !isConstCharStarCase) {
+ TypeInfo newInfo;
+ //newInfo.setArguments(typeInfo.arguments());
+ newInfo.setIndirectionsV(typeInfo.indirectionsV());
+ newInfo.setConstant(typeInfo.isConstant());
+ newInfo.setVolatile(typeInfo.isVolatile());
+ newInfo.setFunctionPointer(typeInfo.isFunctionPointer());
+ newInfo.setQualifiedName(typeInfo.qualifiedName());
+ newInfo.setReferenceType(typeInfo.referenceType());
+ newInfo.setVolatile(typeInfo.isVolatile());
+
+ AbstractMetaType elementType = translateTypeStatic(newInfo, currentClass, d, flags, &errorMessage);
+ if (!elementType) {
+ if (errorMessageIn) {
+ errorMessage.prepend(QLatin1String("Unable to translate array element: "));
+ *errorMessageIn = msgUnableToTranslateType(_typei, errorMessage);
+ }
+ return {};
+ }
+
+ for (int i = typeInfo.arrayElements().size() - 1; i >= 0; --i) {
+ AbstractMetaType arrayType;
+ arrayType.setArrayElementType(elementType);
+ const QString &arrayElement = typeInfo.arrayElements().at(i);
+ if (!arrayElement.isEmpty()) {
+ bool _ok;
+ const qint64 elems = d
+ ? d->findOutValueFromString(arrayElement, _ok)
+ : arrayElement.toLongLong(&_ok, 0);
+ if (_ok)
+ arrayType.setArrayElementCount(int(elems));
+ }
+ auto elementTypeEntry = elementType.typeEntry();
+ arrayType.setTypeEntry(new ArrayTypeEntry(elementTypeEntry, elementTypeEntry->version(),
+ elementTypeEntry->parent()));
+ arrayType.decideUsagePattern();
+
+ elementType = arrayType;
+ }
+
+ return elementType;
+ }
+
+ QStringList qualifierList = typeInfo.qualifiedName();
+ if (qualifierList.isEmpty()) {
+ errorMessage = msgUnableToTranslateType(_typei, QLatin1String("horribly broken type"));
+ if (errorMessageIn)
+ *errorMessageIn = errorMessage;
+ else
+ qCWarning(lcShiboken,"%s", qPrintable(errorMessage));
+ return {};
+ }
+
+ QString qualifiedName = qualifierList.join(colonColon());
+ QString name = qualifierList.takeLast();
+
+ // 4. Special case QFlags (include instantiation in name)
+ if (qualifiedName == QLatin1String("QFlags")) {
+ qualifiedName = typeInfo.toString();
+ typeInfo.clearInstantiations();
+ }
+
+ const TypeEntries types = findTypeEntries(qualifiedName, name, currentClass, d);
+ if (types.isEmpty()) {
+ if (errorMessageIn) {
+ *errorMessageIn =
+ msgUnableToTranslateType(_typei, msgCannotFindTypeEntry(qualifiedName));
+ }
+ return {};
+ }
+
+ const TypeEntry *type = types.constFirst();
+ const TypeEntry::Type typeEntryType = type->type();
+
+ AbstractMetaType metaType;
+ metaType.setIndirectionsV(typeInfo.indirectionsV());
+ metaType.setReferenceType(typeInfo.referenceType());
+ metaType.setConstant(typeInfo.isConstant());
+ metaType.setVolatile(typeInfo.isVolatile());
+ metaType.setOriginalTypeDescription(_typei.toString());
+
+ const auto &templateArguments = typeInfo.instantiations();
+ for (int t = 0, size = templateArguments.size(); t < size; ++t) {
+ const TypeInfo &ti = templateArguments.at(t);
+ AbstractMetaType targType = translateTypeStatic(ti, currentClass, d, flags, &errorMessage);
+ // For non-type template parameters, create a dummy type entry on the fly
+ // as is done for classes.
+ if (!targType) {
+ const QString value = ti.qualifiedName().join(colonColon());
+ if (isNumber(value)) {
+ TypeDatabase::instance()->addConstantValueTypeEntry(value, type->typeSystemTypeEntry());
+ targType = translateTypeStatic(ti, currentClass, d, flags, &errorMessage);
+ }
+ }
+ if (!targType) {
+ if (errorMessageIn)
+ *errorMessageIn = msgCannotTranslateTemplateArgument(t, ti, errorMessage);
+ return {};
+ }
+
+ metaType.addInstantiation(targType);
+ }
+
+ if (types.size() > 1) {
+ const bool sameType = std::all_of(types.cbegin() + 1, types.cend(),
+ [typeEntryType](const TypeEntry *e) {
+ return e->type() == typeEntryType; });
+ if (!sameType) {
+ if (errorMessageIn)
+ *errorMessageIn = msgAmbiguousVaryingTypesFound(qualifiedName, types);
+ return {};
+ }
+ // Ambiguous primitive/smart pointer types are possible (when
+ // including type systems).
+ if (typeEntryType != TypeEntry::PrimitiveType
+ && typeEntryType != TypeEntry::SmartPointerType) {
+ if (errorMessageIn)
+ *errorMessageIn = msgAmbiguousTypesFound(qualifiedName, types);
+ return {};
+ }
+ }
+
+ if (typeEntryType == TypeEntry::SmartPointerType) {
+ // Find a matching instantiation
+ if (metaType.instantiations().size() != 1) {
+ if (errorMessageIn)
+ *errorMessageIn = msgInvalidSmartPointerType(_typei);
+ return {};
+ }
+ auto instantiationType = metaType.instantiations().constFirst().typeEntry();
+ if (instantiationType->type() == TypeEntry::TemplateArgumentType) {
+ // Member functions of the template itself, SharedPtr(const SharedPtr &)
+ type = instantiationType;
+ } else {
+ auto it = std::find_if(types.cbegin(), types.cend(),
+ [instantiationType](const TypeEntry *e) {
+ auto smartPtr = static_cast<const SmartPointerTypeEntry *>(e);
+ return smartPtr->matchesInstantiation(instantiationType);
+ });
+ if (it == types.cend()) {
+ if (errorMessageIn)
+ *errorMessageIn = msgCannotFindSmartPointerInstantion(_typei);
+ return {};
+ }
+ type =*it;
+ }
+ }
+
+ metaType.setTypeEntry(type);
+
+ // The usage pattern *must* be decided *after* the possible template
+ // instantiations have been determined, or else the absence of
+ // such instantiations will break the caching scheme of
+ // AbstractMetaType::cppSignature().
+ metaType.decideUsagePattern();
+
+ if (d) {
+ // Reverse lookup of type system typedefs. Replace by class.
+ if (auto klass = d->resolveTypeSystemTypeDef(metaType)) {
+ metaType = AbstractMetaType{};
+ metaType.setTypeEntry(klass->typeEntry());
+ metaType.decideUsagePattern();
+ }
+ }
+
+ Q_ASSERT(metaType.typeUsagePattern() != AbstractMetaType::InvalidPattern);
+ return metaType;
+}
+
+AbstractMetaType AbstractMetaBuilder::translateType(const TypeInfo &_typei,
+ AbstractMetaClass *currentClass,
+ TranslateTypeFlags flags,
+ QString *errorMessage)
+{
+ return AbstractMetaBuilderPrivate::translateTypeStatic(_typei, currentClass,
+ nullptr, flags,
+ errorMessage);
+}
+
+AbstractMetaType AbstractMetaBuilder::translateType(const QString &t,
+ AbstractMetaClass *currentClass,
+ TranslateTypeFlags flags,
+ QString *errorMessageIn)
+{
+ QString errorMessage;
+ TypeInfo typeInfo = TypeParser::parse(t, &errorMessage);
+ if (typeInfo.qualifiedName().isEmpty()) {
+ errorMessage = msgUnableToTranslateType(t, errorMessage);
+ if (errorMessageIn)
+ *errorMessageIn = errorMessage;
+ else
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
+ return {};
+ }
+ return translateType(typeInfo, currentClass, flags, errorMessageIn);
+}
+
+qint64 AbstractMetaBuilderPrivate::findOutValueFromString(const QString &stringValue, bool &ok)
+{
+ qint64 value = stringValue.toLongLong(&ok);
+ if (ok)
+ return value;
+
+ if (stringValue == QLatin1String("true") || stringValue == QLatin1String("false")) {
+ ok = true;
+ return (stringValue == QLatin1String("true"));
+ }
+
+ // This is a very lame way to handle expression evaluation,
+ // but it is not critical and will do for the time being.
+ static const QRegularExpression variableNameRegExp(QStringLiteral("^[a-zA-Z_][a-zA-Z0-9_]*$"));
+ Q_ASSERT(variableNameRegExp.isValid());
+ if (!variableNameRegExp.match(stringValue).hasMatch()) {
+ ok = true;
+ return 0;
+ }
+
+ AbstractMetaEnumValue *enumValue = AbstractMetaClass::findEnumValue(m_metaClasses, stringValue);
+ if (enumValue) {
+ ok = true;
+ return enumValue->value().value();
+ }
+
+ for (AbstractMetaEnum *metaEnum : qAsConst(m_globalEnums)) {
+ if (const AbstractMetaEnumValue *ev = metaEnum->findEnumValue(stringValue)) {
+ ok = true;
+ return ev->value().value();
+ }
+ }
+
+ ok = false;
+ return 0;
+}
+
+QString AbstractMetaBuilderPrivate::fixDefaultValue(const ArgumentModelItem &item,
+ const AbstractMetaType &type,
+ AbstractMetaFunction *fnc,
+ AbstractMetaClass *implementingClass,
+ int /* argumentIndex */)
+{
+ QString expr = item->defaultValueExpression();
+ if (expr.isEmpty())
+ return expr;
+
+ if (type) {
+ if (type.isPrimitive()) {
+ if (type.name() == QLatin1String("boolean")) {
+ if (expr != QLatin1String("false") && expr != QLatin1String("true")) {
+ bool ok = false;
+ int number = expr.toInt(&ok);
+ if (ok && number)
+ expr = QLatin1String("true");
+ else
+ expr = QLatin1String("false");
+ }
+ } else {
+ // This can be an enum or flag so I need to delay the
+ // translation untill all namespaces are completly
+ // processed. This is done in figureOutEnumValues()
+ }
+ } else if (type.isFlags() || type.isEnum()) {
+ bool isNumber;
+ expr.toInt(&isNumber);
+ if (!isNumber && expr.indexOf(colonColon()) < 0) {
+ // Add the enum/flag scope to default value, making it usable
+ // from other contexts beside its owner class hierarchy
+ static const QRegularExpression typeRegEx(QStringLiteral("[^<]*[<]([^:]*::).*"));
+ Q_ASSERT(typeRegEx.isValid());
+ const QRegularExpressionMatch match = typeRegEx.match(type.minimalSignature());
+ if (match.hasMatch())
+ expr.prepend(match.captured(1));
+ }
+ } else if (type.isContainer() && expr.contains(QLatin1Char('<'))) {
+ static const QRegularExpression typeRegEx(QStringLiteral("[^<]*<(.*)>"));
+ Q_ASSERT(typeRegEx.isValid());
+ const QRegularExpressionMatch typeMatch = typeRegEx.match(type.minimalSignature());
+ static const QRegularExpression defaultRegEx(QLatin1String("([^<]*<).*(>[^>]*)"));
+ Q_ASSERT(defaultRegEx.isValid());
+ const QRegularExpressionMatch defaultMatch = defaultRegEx.match(expr);
+ if (typeMatch.hasMatch() && defaultMatch.hasMatch())
+ expr = defaultMatch.captured(1) + typeMatch.captured(1) + defaultMatch.captured(2);
+ } else {
+ // Here the default value is supposed to be a constructor,
+ // a class field, or a constructor receiving a class field
+ static const QRegularExpression defaultRegEx(QStringLiteral("([^\\(]*\\(|)([^\\)]*)(\\)|)"));
+ Q_ASSERT(defaultRegEx.isValid());
+ const QRegularExpressionMatch defaultMatch = defaultRegEx.match(expr);
+ QString defaultValueCtorName = defaultMatch.hasMatch() ? defaultMatch.captured(1) : QString();
+ if (defaultValueCtorName.endsWith(QLatin1Char('(')))
+ defaultValueCtorName.chop(1);
+
+ // Fix the scope for constructor using the already
+ // resolved argument type as a reference.
+ // The following regular expression extracts any
+ // use of namespaces/scopes from the type string.
+ static const QRegularExpression typeRegEx(QLatin1String("^(?:const[\\s]+|)([\\w:]*::|)([A-Za-z_]\\w*)\\s*[&\\*]?$"));
+ Q_ASSERT(typeRegEx.isValid());
+ const QRegularExpressionMatch typeMatch = typeRegEx.match(type.minimalSignature());
+
+ QString typeNamespace = typeMatch.hasMatch() ? typeMatch.captured(1) : QString();
+ QString typeCtorName = typeMatch.hasMatch() ? typeMatch.captured(2) : QString();
+ if (!typeNamespace.isEmpty() && defaultValueCtorName == typeCtorName)
+ expr.prepend(typeNamespace);
+
+ // Fix scope if the parameter is a field of the current class
+ if (implementingClass) {
+ const AbstractMetaFieldList &fields = implementingClass->fields();
+ for (const AbstractMetaField *field : fields) {
+ if (defaultMatch.hasMatch() && defaultMatch.captured(2) == field->name()) {
+ expr = defaultMatch.captured(1) + implementingClass->name()
+ + colonColon() + defaultMatch.captured(2) + defaultMatch.captured(3);
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ const QString className = implementingClass ? implementingClass->qualifiedCppName() : QString();
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("undefined type for default value '%3' of argument in function '%1', class '%2'")
+ .arg(fnc->name(), className, item->defaultValueExpression());
+
+ expr.clear();
+ }
+
+ return expr;
+}
+
+bool AbstractMetaBuilderPrivate::isEnum(const FileModelItem &dom, const QStringList& qualified_name)
+{
+ CodeModelItem item = dom->model()->findItem(qualified_name, dom);
+ return item && item->kind() == _EnumModelItem::__node_kind;
+}
+
+AbstractMetaClass* AbstractMetaBuilderPrivate::findTemplateClass(const QString &name,
+ const AbstractMetaClass *context,
+ TypeInfo *info,
+ ComplexTypeEntry **baseContainerType) const
+{
+ TypeDatabase* types = TypeDatabase::instance();
+
+ QStringList scope = context->typeEntry()->qualifiedCppName().split(colonColon());
+ QString errorMessage;
+ scope.removeLast();
+ for (int i = scope.size(); i >= 0; --i) {
+ QString prefix = i > 0 ? QStringList(scope.mid(0, i)).join(colonColon()) + colonColon() : QString();
+ QString completeName = prefix + name;
+ const TypeInfo parsed = TypeParser::parse(completeName, &errorMessage);
+ QString qualifiedName = parsed.qualifiedName().join(colonColon());
+ if (qualifiedName.isEmpty()) {
+ qWarning().noquote().nospace() << "Unable to parse type \"" << completeName
+ << "\" while looking for template \"" << name << "\": " << errorMessage;
+ continue;
+ }
+ if (info)
+ *info = parsed;
+
+ AbstractMetaClass *templ = nullptr;
+ for (AbstractMetaClass *c : qAsConst(m_templates)) {
+ if (c->typeEntry()->name() == qualifiedName) {
+ templ = c;
+ break;
+ }
+ }
+
+ if (!templ)
+ templ = AbstractMetaClass::findClass(m_metaClasses, qualifiedName);
+
+ if (templ)
+ return templ;
+
+ if (baseContainerType)
+ *baseContainerType = types->findContainerType(qualifiedName);
+ }
+
+ return nullptr;
+}
+
+AbstractMetaClassList AbstractMetaBuilderPrivate::getBaseClasses(const AbstractMetaClass *metaClass) const
+{
+ AbstractMetaClassList baseClasses;
+ const QStringList &baseClassNames = metaClass->baseClassNames();
+ for (const QString& parent : baseClassNames) {
+ AbstractMetaClass *cls = nullptr;
+ if (parent.contains(QLatin1Char('<')))
+ cls = findTemplateClass(parent, metaClass);
+ else
+ cls = AbstractMetaClass::findClass(m_metaClasses, parent);
+
+ if (cls)
+ baseClasses << cls;
+ }
+ return baseClasses;
+}
+
+bool AbstractMetaBuilderPrivate::ancestorHasPrivateCopyConstructor(const AbstractMetaClass *metaClass) const
+{
+ if (metaClass->hasPrivateCopyConstructor())
+ return true;
+ const AbstractMetaClassList &baseClasses = getBaseClasses(metaClass);
+ for (const AbstractMetaClass *cls : baseClasses) {
+ if (ancestorHasPrivateCopyConstructor(cls))
+ return true;
+ }
+ return false;
+}
+
+AbstractMetaType AbstractMetaBuilderPrivate::inheritTemplateType(const AbstractMetaTypeList &templateTypes,
+ const AbstractMetaType &metaType)
+{
+ Q_ASSERT(metaType);
+
+ auto returned = metaType;
+
+ if (!metaType.typeEntry()->isTemplateArgument() && !metaType.hasInstantiations())
+ return returned;
+
+ returned.setOriginalTemplateType(metaType);
+
+ if (returned.typeEntry()->isTemplateArgument()) {
+ const auto *tae = static_cast<const TemplateArgumentEntry*>(returned.typeEntry());
+
+ // If the template is intantiated with void we special case this as rejecting the functions that use this
+ // parameter from the instantiation.
+ const AbstractMetaType &templateType = templateTypes.value(tae->ordinal());
+ if (!templateType || templateType.typeEntry()->isVoid())
+ return {};
+
+ AbstractMetaType t = returned;
+ t.setTypeEntry(templateType.typeEntry());
+ t.setIndirections(templateType.indirections() + t.indirections() ? 1 : 0);
+ t.decideUsagePattern();
+
+ return inheritTemplateType(templateTypes, t);
+ }
+
+ if (returned.hasInstantiations()) {
+ AbstractMetaTypeList instantiations = returned.instantiations();
+ for (int i = 0; i < instantiations.count(); ++i) {
+ instantiations[i] =
+ inheritTemplateType(templateTypes, instantiations.at(i));
+ if (!instantiations.at(i))
+ return {};
+ }
+ returned.setInstantiations(instantiations);
+ }
+
+ return returned;
+}
+
+bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass,
+ const AbstractMetaClass *templateClass,
+ const TypeInfo &info)
+{
+ QVector<TypeInfo> targs = info.instantiations();
+ AbstractMetaTypeList templateTypes;
+
+ for (const TypeInfo &i : qAsConst(targs)) {
+ QString typeName = i.qualifiedName().join(colonColon());
+ TypeDatabase *typeDb = TypeDatabase::instance();
+ TypeEntry *t = nullptr;
+ // Check for a non-type template integer parameter, that is, for a base
+ // "template <int R, int C> Matrix<R, C>" and subclass
+ // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of
+ // EnumValueTypeEntry for the integer values encountered on the fly.
+ if (isNumber(typeName)) {
+ t = typeDb->findType(typeName);
+ if (!t) {
+ auto parent = subclass->typeEntry()->typeSystemTypeEntry();
+ t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent);
+ }
+ } else {
+ QStringList possibleNames;
+ possibleNames << subclass->qualifiedCppName() + colonColon() + typeName;
+ possibleNames << templateClass->qualifiedCppName() + colonColon() + typeName;
+ if (subclass->enclosingClass())
+ possibleNames << subclass->enclosingClass()->qualifiedCppName() + colonColon() + typeName;
+ possibleNames << typeName;
+
+ for (const QString &possibleName : qAsConst(possibleNames)) {
+ t = typeDb->findType(possibleName);
+ if (t)
+ break;
+ }
+ }
+
+ if (t) {
+ AbstractMetaType temporaryType(t);
+ temporaryType.setConstant(i.isConstant());
+ temporaryType.setReferenceType(i.referenceType());
+ temporaryType.setIndirectionsV(i.indirectionsV());
+ temporaryType.decideUsagePattern();
+ templateTypes << temporaryType;
+ } else {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Ignoring template parameter " << typeName << " from "
+ << info.toString() << ". The corresponding type was not found in the typesystem.";
+ }
+ }
+
+ subclass->setTemplateBaseClass(templateClass);
+ subclass->setTemplateBaseClassInstantiations(templateTypes);
+ subclass->setBaseClass(templateClass->baseClass());
+ return true;
+}
+
+void AbstractMetaBuilderPrivate::inheritTemplateFunctions(AbstractMetaClass *subclass)
+{
+ QString errorMessage;
+ auto templateClass = subclass->templateBaseClass();
+
+ if (subclass->isTypeDef()) {
+ subclass->setHasCloneOperator(templateClass->hasCloneOperator());
+ subclass->setHasEqualsOperator(templateClass->hasEqualsOperator());
+ subclass->setHasHashFunction(templateClass->hasHashFunction());
+ subclass->setHasNonPrivateConstructor(templateClass->hasNonPrivateConstructor());
+ subclass->setHasPrivateDestructor(templateClass->hasPrivateDestructor());
+ subclass->setHasProtectedDestructor(templateClass->hasProtectedDestructor());
+ subclass->setHasVirtualDestructor(templateClass->hasVirtualDestructor());
+ }
+
+ const auto &templateTypes = subclass->templateBaseClassInstantiations();
+ const AbstractMetaFunctionList &subclassFuncs = subclass->functions();
+ const AbstractMetaFunctionList &templateClassFunctions = templateClass->functions();
+ for (const AbstractMetaFunction *function : templateClassFunctions) {
+ // If the function is modified or the instantiation has an equally named
+ // function we have shadowing, so we need to skip it.
+ if (function->isModifiedRemoved(TypeSystem::All)
+ || AbstractMetaFunction::find(subclassFuncs, function->name()) != nullptr) {
+ continue;
+ }
+
+ QScopedPointer<AbstractMetaFunction> f(function->copy());
+ f->setArguments(AbstractMetaArgumentList());
+
+ if (!function->isVoid()) {
+ AbstractMetaType returnType = inheritTemplateType(templateTypes, function->type());
+ if (!returnType)
+ continue;
+ f->setType(returnType);
+ }
+
+ const AbstractMetaArgumentList &arguments = function->arguments();
+ for (const AbstractMetaArgument &argument : arguments) {
+ AbstractMetaType argType = inheritTemplateType(templateTypes, argument.type());
+ if (!argType)
+ break;
+ AbstractMetaArgument arg = argument;
+ arg.setType(argType);
+ f->addArgument(arg);
+ }
+
+ if (f->arguments().size() < function->arguments().size())
+ continue;
+
+ // There is no base class in the target language to inherit from here, so
+ // the template instantiation is the class that implements the function.
+ f->setImplementingClass(subclass);
+
+ // We also set it as the declaring class, since the superclass is
+ // supposed to disappear. This allows us to make certain function modifications
+ // on the inherited functions.
+ f->setDeclaringClass(subclass);
+
+ if (f->isConstructor()) {
+ if (!subclass->isTypeDef())
+ continue;
+ f->setName(subclass->name());
+ f->setOriginalName(subclass->name());
+ }
+
+ ComplexTypeEntry* te = subclass->typeEntry();
+ FunctionModificationList mods = function->modifications(templateClass);
+ for (int i = 0; i < mods.size(); ++i) {
+ FunctionModification mod = mods.at(i);
+ mod.setSignature(f->minimalSignature());
+
+ // If we ever need it... Below is the code to do
+ // substitution of the template instantation type inside
+ // injected code..
+#if 0
+ if (mod.modifiers & Modification::CodeInjection) {
+ for (int j = 0; j < template_types.size(); ++j) {
+ CodeSnip &snip = mod.snips.last();
+ QString code = snip.code();
+ code.replace(QString::fromLatin1("$$QT_TEMPLATE_%1$$").arg(j),
+ template_types.at(j)->typeEntry()->qualifiedCppName());
+ snip.codeList.clear();
+ snip.addCode(code);
+ }
+ }
+#endif
+ te->addFunctionModification(mod);
+ }
+
+
+ if (!applyArrayArgumentModifications(f->modifications(subclass), f.data(),
+ &errorMessage)) {
+ qCWarning(lcShiboken, "While specializing %s (%s): %s",
+ qPrintable(subclass->name()), qPrintable(templateClass->name()),
+ qPrintable(errorMessage));
+ }
+ subclass->addFunction(f.take());
+ }
+
+ const AbstractMetaFieldList &subClassFields = subclass->fields();
+ const AbstractMetaFieldList &templateClassFields = templateClass->fields();
+ for (const AbstractMetaField *field : templateClassFields) {
+ // If the field is modified or the instantiation has a field named
+ // the same as an existing field we have shadowing, so we need to skip it.
+ if (field->isModifiedRemoved(TypeSystem::All)
+ || field->attributes().testFlag(AbstractMetaAttributes::Static)
+ || AbstractMetaField::find(subClassFields, field->name()) != nullptr) {
+ continue;
+ }
+
+ QScopedPointer<AbstractMetaField> f(field->copy());
+ f->setEnclosingClass(subclass);
+ AbstractMetaType fieldType = inheritTemplateType(templateTypes, field->type());
+ if (!fieldType)
+ continue;
+ f->setType(fieldType);
+ subclass->addField(f.take());
+ }
+}
+
+void AbstractMetaBuilderPrivate::parseQ_Properties(AbstractMetaClass *metaClass,
+ const QStringList &declarations)
+{
+ const QStringList scopes = currentScope()->qualifiedName();
+ QString errorMessage;
+ int i = 0;
+ for (; i < declarations.size(); ++i) {
+ if (auto spec = QPropertySpec::parseQ_Property(this, metaClass, declarations.at(i), scopes, &errorMessage)) {
+ spec->setIndex(i);
+ metaClass->addPropertySpec(spec);
+ } else {
+ QString message;
+ QTextStream str(&message);
+ str << metaClass->sourceLocation() << errorMessage;
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ }
+ }
+
+ // User-added properties
+ auto typeEntry = metaClass->typeEntry();
+ for (const TypeSystemProperty &tp : typeEntry->properties()) {
+ QPropertySpec *spec = nullptr;
+ if (metaClass->propertySpecByName(tp.name))
+ errorMessage = msgPropertyExists(metaClass->name(), tp.name);
+ else
+ spec = QPropertySpec::fromTypeSystemProperty(this, metaClass, tp, scopes, &errorMessage);
+
+ if (spec) {
+ spec->setIndex(i++);
+ metaClass->addPropertySpec(spec);
+ } else {
+ QString message;
+ QTextStream str(&message);
+ str << typeEntry->sourceLocation() << errorMessage;
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ }
+ }
+}
+
+static AbstractMetaFunction* findCopyCtor(AbstractMetaClass* cls)
+{
+
+ const auto &functions = cls->functions();
+
+ for (AbstractMetaFunction *f : qAsConst(functions)) {
+ const AbstractMetaFunction::FunctionType t = f->functionType();
+ if (t == AbstractMetaFunction::CopyConstructorFunction || t == AbstractMetaFunction::AssignmentOperatorFunction)
+ return f;
+ }
+ return nullptr;
+}
+
+void AbstractMetaBuilderPrivate::setupClonable(AbstractMetaClass *cls)
+{
+ bool result = true;
+
+ // find copy ctor for the current class
+ AbstractMetaFunction* copyCtor = findCopyCtor(cls);
+ if (copyCtor) { // if exists a copy ctor in this class
+ result = copyCtor->isPublic();
+ } else { // else... lets find one in the parent class
+ QQueue<AbstractMetaClass*> baseClasses;
+ if (cls->baseClass())
+ baseClasses.enqueue(cls->baseClass());
+
+ while (!baseClasses.isEmpty()) {
+ AbstractMetaClass* currentClass = baseClasses.dequeue();
+ if (currentClass->baseClass())
+ baseClasses.enqueue(currentClass->baseClass());
+
+ copyCtor = findCopyCtor(currentClass);
+ if (copyCtor) {
+ result = copyCtor->isPublic();
+ break;
+ }
+ }
+ }
+ cls->setHasCloneOperator(result);
+}
+
+void AbstractMetaBuilderPrivate::setupExternalConversion(AbstractMetaClass *cls)
+{
+ const AbstractMetaFunctionList &convOps = cls->operatorOverloads(AbstractMetaClass::ConversionOp);
+ for (AbstractMetaFunction *func : convOps) {
+ if (func->isModifiedRemoved())
+ continue;
+ AbstractMetaClass *metaClass = AbstractMetaClass::findClass(m_metaClasses, func->type().typeEntry());
+ if (!metaClass)
+ continue;
+ metaClass->addExternalConversionOperator(func);
+ }
+ const AbstractMetaClassList &innerClasses = cls->innerClasses();
+ for (AbstractMetaClass *innerClass : innerClasses)
+ setupExternalConversion(innerClass);
+}
+
+static void writeRejectLogFile(const QString &name,
+ const QMap<QString, AbstractMetaBuilder::RejectReason> &rejects)
+{
+ QFile f(name);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << QStringLiteral("failed to write log file: '%1'")
+ .arg(QDir::toNativeSeparators(f.fileName()));
+ return;
+ }
+
+ QTextStream s(&f);
+
+
+ for (int reason = 0; reason < AbstractMetaBuilder::NoReason; ++reason) {
+ s << QString(72, QLatin1Char('*')) << Qt::endl;
+ switch (reason) {
+ case AbstractMetaBuilder::NotInTypeSystem:
+ s << "Not in type system";
+ break;
+ case AbstractMetaBuilder::GenerationDisabled:
+ s << "Generation disabled by type system";
+ break;
+ case AbstractMetaBuilder::RedefinedToNotClass:
+ s << "Type redefined to not be a class";
+ break;
+
+ case AbstractMetaBuilder::UnmatchedReturnType:
+ s << "Unmatched return type";
+ break;
+
+ case AbstractMetaBuilder::UnmatchedArgumentType:
+ s << "Unmatched argument type";
+ break;
+
+ case AbstractMetaBuilder::ApiIncompatible:
+ s << "Incompatible API";
+ break;
+
+ case AbstractMetaBuilder::Deprecated:
+ s << "Deprecated";
+ break;
+
+ default:
+ s << "unknown reason";
+ break;
+ }
+
+ s << Qt::endl;
+
+ for (QMap<QString, AbstractMetaBuilder::RejectReason>::const_iterator it = rejects.constBegin();
+ it != rejects.constEnd(); ++it) {
+ if (it.value() != reason)
+ continue;
+ s << " - " << it.key() << Qt::endl;
+ }
+
+ s << QString(72, QLatin1Char('*')) << Qt::endl << Qt::endl;
+ }
+
+}
+
+void AbstractMetaBuilderPrivate::dumpLog() const
+{
+ writeRejectLogFile(m_logDirectory + QLatin1String("mjb_rejected_classes.log"), m_rejectedClasses);
+ writeRejectLogFile(m_logDirectory + QLatin1String("mjb_rejected_enums.log"), m_rejectedEnums);
+ writeRejectLogFile(m_logDirectory + QLatin1String("mjb_rejected_functions.log"), m_rejectedFunctions);
+ writeRejectLogFile(m_logDirectory + QLatin1String("mjb_rejected_fields.log"), m_rejectedFields);
+}
+
+using ClassIndexHash = QHash<AbstractMetaClass *, int>;
+
+static ClassIndexHash::ConstIterator findByTypeEntry(const ClassIndexHash &map,
+ const TypeEntry *typeEntry)
+{
+ auto it = map.cbegin();
+ for (auto end = map.cend(); it != end; ++it) {
+ if (it.key()->typeEntry() == typeEntry)
+ break;
+ }
+ return it;
+}
+
+// Add a dependency of the class associated with typeEntry on clazz
+static void addClassDependency(const TypeEntry *typeEntry,
+ const AbstractMetaClass *clazz,
+ int classIndex, const ClassIndexHash &map,
+ Graph *graph)
+{
+ if (typeEntry->isComplex() && typeEntry != clazz->typeEntry()) {
+ const auto it = findByTypeEntry(map, typeEntry);
+ if (it != map.cend() && it.key()->enclosingClass() != clazz)
+ graph->addEdge(it.value(), classIndex);
+ }
+}
+
+AbstractMetaClassList AbstractMetaBuilderPrivate::classesTopologicalSorted(const AbstractMetaClassList &classList,
+ const Dependencies &additionalDependencies) const
+{
+ ClassIndexHash map;
+ QHash<int, AbstractMetaClass *> reverseMap;
+
+ int i = 0;
+ for (AbstractMetaClass *clazz : classList) {
+ if (map.contains(clazz))
+ continue;
+ map.insert(clazz, i);
+ reverseMap.insert(i, clazz);
+ i++;
+ }
+
+ Graph graph(map.count());
+
+ for (const auto &dep : additionalDependencies) {
+ const int parentIndex = map.value(dep.parent, -1);
+ const int childIndex = map.value(dep.child, -1);
+ if (parentIndex >= 0 && childIndex >= 0) {
+ graph.addEdge(parentIndex, childIndex);
+ } else {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "AbstractMetaBuilder::classesTopologicalSorted(): Invalid additional dependency: "
+ << dep.child->name() << " -> " << dep.parent->name() << '.';
+ }
+ }
+
+ for (AbstractMetaClass *clazz : classList) {
+ const int classIndex = map.value(clazz);
+ if (auto enclosing = clazz->enclosingClass()) {
+ const auto enclosingIt = map.constFind(const_cast< AbstractMetaClass *>(enclosing));
+ if (enclosingIt!= map.cend())
+ graph.addEdge(enclosingIt.value(), classIndex);
+ }
+
+ const AbstractMetaClassList &bases = getBaseClasses(clazz);
+ for (AbstractMetaClass *baseClass : bases) {
+ const auto baseIt = map.constFind(baseClass);
+ if (baseIt!= map.cend())
+ graph.addEdge(baseIt.value(), classIndex);
+ }
+
+ const AbstractMetaFunctionList &functions = clazz->functions();
+ for (AbstractMetaFunction *func : functions) {
+ const AbstractMetaArgumentList &arguments = func->arguments();
+ for (const AbstractMetaArgument &arg : arguments) {
+ // Check methods with default args: If a class is instantiated by value,
+ // ("QString s = QString()"), add a dependency.
+ if (!arg.originalDefaultValueExpression().isEmpty()
+ && arg.type().isValue()) {
+ addClassDependency(arg.type().typeEntry(), clazz, classIndex,
+ map, &graph);
+ }
+ }
+ }
+ // Member fields need to be initialized
+ const AbstractMetaFieldList &fields = clazz->fields();
+ for (AbstractMetaField *field : fields) {
+ addClassDependency(field->type().typeEntry(), clazz, classIndex,
+ map, &graph);
+ }
+ }
+
+ AbstractMetaClassList result;
+ const auto unmappedResult = graph.topologicalSort();
+ if (!unmappedResult.isValid() && graph.nodeCount()) {
+ QTemporaryFile tempFile(QDir::tempPath() + QLatin1String("/cyclic_depXXXXXX.dot"));
+ tempFile.setAutoRemove(false);
+ tempFile.open();
+ QHash<int, QString> hash;
+ for (auto it = map.cbegin(), end = map.cend(); it != end; ++it)
+ hash.insert(it.value(), it.key()->qualifiedCppName());
+ graph.dumpDot(hash, tempFile.fileName());
+
+ QString message;
+ QTextStream str(&message);
+ str << "Cyclic dependency of classes found:";
+ for (int c : unmappedResult.cyclic)
+ str << ' ' << reverseMap.value(c)->name();
+ str << ". Graph can be found at \"" << QDir::toNativeSeparators(tempFile.fileName()) << '"';
+ qCWarning(lcShiboken, "%s", qPrintable(message));
+ } else {
+ for (int i : qAsConst(unmappedResult.result)) {
+ Q_ASSERT(reverseMap.contains(i));
+ result << reverseMap[i];
+ }
+ }
+
+ return result;
+}
+
+void AbstractMetaBuilderPrivate::pushScope(const NamespaceModelItem &item)
+{
+ // For purposes of type lookup, join all namespaces of the same name
+ // within the parent item.
+ QVector<NamespaceModelItem> candidates;
+ const QString name = item->name();
+ if (!m_scopes.isEmpty()) {
+ for (const auto &n : m_scopes.constLast()->namespaces()) {
+ if (n->name() == name)
+ candidates.append(n);
+ }
+ }
+ if (candidates.size() > 1) {
+ NamespaceModelItem joined(new _NamespaceModelItem(m_scopes.constLast()->model(),
+ name, _CodeModelItem::Kind_Namespace));
+ joined->setScope(item->scope());
+ for (const auto &n : candidates)
+ joined->appendNamespace(*n);
+ m_scopes << joined;
+ } else {
+ m_scopes << item;
+ }
+}
+
+AbstractMetaClassList AbstractMetaBuilder::classesTopologicalSorted(const AbstractMetaClassList &classList,
+ const Dependencies &additionalDependencies) const
+{
+ return d->classesTopologicalSorted(classList, additionalDependencies);
+}
+
+AbstractMetaArgumentList AbstractMetaBuilderPrivate::reverseList(const AbstractMetaArgumentList &list)
+{
+ AbstractMetaArgumentList ret;
+
+ int index = list.size();
+ for (const AbstractMetaArgument &a : list) {
+ AbstractMetaArgument arg = a;
+ arg.setArgumentIndex(index);
+ ret.prepend(arg);
+ index--;
+ }
+
+ return ret;
+}
+
+void AbstractMetaBuilder::setGlobalHeaders(const QFileInfoList &globalHeaders)
+{
+ d->m_globalHeaders = globalHeaders;
+}
+
+void AbstractMetaBuilder::setHeaderPaths(const HeaderPaths &hp)
+{
+ for (const auto & h: hp) {
+ if (h.type != HeaderType::Framework && h.type != HeaderType::FrameworkSystem)
+ d->m_headerPaths.append(QFile::decodeName(h.path));
+ }
+}
+
+void AbstractMetaBuilder::setSkipDeprecated(bool value)
+{
+ d->m_skipDeprecated = value;
+}
+
+// PYSIDE-975: When receiving an absolute path name from the code model, try
+// to resolve it against the include paths set on shiboken in order to recreate
+// relative paths like #include <foo/bar.h>.
+
+static inline bool isFileSystemSlash(QChar c)
+{
+ return c == QLatin1Char('/') || c == QLatin1Char('\\');
+}
+
+static bool matchHeader(const QString &headerPath, const QString &fileName)
+{
+#if defined(Q_OS_WIN) || defined(Q_OS_DARWIN)
+ static const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
+#else
+ static const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
+#endif
+ const int pathSize = headerPath.size();
+ return fileName.size() > pathSize
+ && isFileSystemSlash(fileName.at(pathSize))
+ && fileName.startsWith(headerPath, caseSensitivity);
+}
+
+void AbstractMetaBuilderPrivate::setInclude(TypeEntry *te, const QString &path) const
+{
+ auto it = m_resolveIncludeHash.find(path);
+ if (it == m_resolveIncludeHash.end()) {
+ QFileInfo info(path);
+ const QString fileName = info.fileName();
+ if (std::any_of(m_globalHeaders.cbegin(), m_globalHeaders.cend(),
+ [fileName] (const QFileInfo &fi) {
+ return fi.fileName() == fileName; })) {
+ return;
+ }
+
+ int bestMatchLength = 0;
+ for (const auto &headerPath : m_headerPaths) {
+ if (headerPath.size() > bestMatchLength && matchHeader(headerPath, path))
+ bestMatchLength = headerPath.size();
+ }
+ const QString include = bestMatchLength > 0
+ ? path.right(path.size() - bestMatchLength - 1) : fileName;
+ it = m_resolveIncludeHash.insert(path, {Include::IncludePath, include});
+ }
+ te->setInclude(it.value());
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+template <class Container>
+static void debugFormatSequence(QDebug &d, const char *key, const Container& c,
+ const char *separator = ", ")
+{
+ if (c.isEmpty())
+ return;
+ const auto begin = c.begin();
+ const auto end = c.end();
+ d << "\n " << key << '[' << c.size() << "]=(";
+ for (auto it = begin; it != end; ++it) {
+ if (it != begin)
+ d << separator;
+ d << *it;
+ }
+ d << ')';
+}
+
+void AbstractMetaBuilder::formatDebug(QDebug &debug) const
+{
+ debug << "m_globalHeader=" << d->m_globalHeaders;
+ debugFormatSequence(debug, "globalEnums", d->m_globalEnums, "\n");
+ debugFormatSequence(debug, "globalFunctions", d->m_globalFunctions, "\n");
+ if (const int scopeCount = d->m_scopes.size()) {
+ debug << "\n scopes[" << scopeCount << "]=(";
+ for (int i = 0; i < scopeCount; ++i) {
+ if (i)
+ debug << ", ";
+ _CodeModelItem::formatKind(debug, d->m_scopes.at(i)->kind());
+ debug << " \"" << d->m_scopes.at(i)->name() << '"';
+ }
+ debug << ')';
+ }
+ debugFormatSequence(debug, "classes", d->m_metaClasses, "\n");
+ debugFormatSequence(debug, "templates", d->m_templates, "\n");
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaBuilder &ab)
+{
+ QDebugStateSaver saver(d);
+ d.noquote();
+ d.nospace();
+ d << "AbstractMetaBuilder(";
+ ab.formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM