diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp | 524 |
1 files changed, 250 insertions, 274 deletions
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp index de0f2eb4f..31e7efb05 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp @@ -1,38 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "clangbuilder.h" #include "compilersupport.h" #include "clangutils.h" +#include "clangdebugutils.h" #include <codemodel.h> #include <reporthandler.h> +#include "qtcompat.h" + #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QHash> @@ -44,10 +22,9 @@ #include <cstring> #include <ctype.h> -namespace clang { +using namespace Qt::StringLiterals; -static inline QString colonColon() { return QStringLiteral("::"); } -static inline QString templateBrackets() { return QStringLiteral("<>"); } +namespace clang { static inline bool isClassCursor(const CXCursor &c) { @@ -69,9 +46,9 @@ static inline bool withinClassDeclaration(const CXCursor &cursor) static QString fixTypeName(QString t) { // Fix "Foo &" -> "Foo&", similarly "Bar **" -> "Bar**" - int pos = t.size() - 1; - for (; pos >= 0 && (t.at(pos) == QLatin1Char('&') || t.at(pos) == QLatin1Char('*')); --pos) {} - if (pos > 0 && t.at(pos) == QLatin1Char(' ')) + auto pos = t.size() - 1; + for (; pos >= 0 && (t.at(pos) == u'&' || t.at(pos) == u'*'); --pos) {} + if (pos > 0 && t.at(pos) == u' ') t.remove(pos, 1); return t; } @@ -81,13 +58,13 @@ static QString fixTypeName(QString t) // the class name "Foo<T1,T2>" is the scope for nested items. static bool insertTemplateParameterIntoClassName(const QString &parmName, QString *name) { - if (Q_UNLIKELY(!name->endsWith(QLatin1Char('>')))) + if (Q_UNLIKELY(!name->endsWith(u'>'))) return false; - const bool needsComma = name->at(name->size() - 2) != QLatin1Char('<'); - const int insertionPos = name->size() - 1; + const bool needsComma = name->at(name->size() - 2) != u'<'; + const auto insertionPos = name->size() - 1; name->insert(insertionPos, parmName); if (needsComma) - name->insert(insertionPos, QLatin1Char(',')); + name->insert(insertionPos, u','); return true; } @@ -136,14 +113,27 @@ static bool isSigned(CXTypeKind kind) class BuilderPrivate { public: + Q_DISABLE_COPY_MOVE(BuilderPrivate) + + enum class SpecialSystemHeader { + None, + Types, + OpenGL, + WhiteListed, + WhiteListedPath + }; + using CursorClassHash = QHash<CXCursor, ClassModelItem>; - using CursorTypedefHash = QHash<CXCursor, TypeDefModelItem>; using TypeInfoHash = QHash<CXType, TypeInfo>; explicit BuilderPrivate(BaseVisitor *bv) : m_baseVisitor(bv), m_model(new CodeModel) { m_scopeStack.push(NamespaceModelItem(new _FileModelItem(m_model))); } + ~BuilderPrivate() + { + delete m_model; + } // Determine scope from top item. Note that the scope list does not necessarily // match the scope stack in case of forward-declared inner classes whose definition @@ -196,13 +186,12 @@ public: void addField(const CXCursor &cursor); static QString cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor); - QString getBaseClassName(CXType type) const; + std::pair<QString, ClassModelItem> getBaseClass(CXType type) const; void addBaseClass(const CXCursor &cursor); - template <class Item> - void qualifyTypeDef(const CXCursor &typeRefCursor, const QSharedPointer<Item> &item) const; - - bool visitHeader(const char *cFileName) const; + SpecialSystemHeader specialSystemHeader(const QString &fileName) const; + bool visitHeader(const QString &fileName) const; + static const char *specialSystemHeaderReason(SpecialSystemHeader sh); void setFileName(const CXCursor &cursor, _CodeModelItem *item); @@ -215,7 +204,6 @@ public: // classes can be correctly parented in case of forward-declared inner classes // (QMetaObject::Connection) CursorClassHash m_cursorClassHash; - CursorTypedefHash m_cursorTypedefHash; mutable TypeInfoHash m_typeInfoHash; // Cache type information mutable QHash<QString, TemplateTypeAliasModelItem> m_templateTypeAliases; @@ -226,21 +214,22 @@ public: ArgumentModelItem m_currentArgument; VariableModelItem m_currentField; TemplateTypeAliasModelItem m_currentTemplateTypeAlias; - QByteArrayList m_systemIncludes; // files, like "memory" - QByteArrayList m_systemIncludePaths; // paths, like "/usr/include/Qt/" + QStringList m_forceProcessSystemIncludes; // files, like "memory" + QStringList m_forceProcessSystemIncludePaths; // paths, like "/usr/include/Qt/" QString m_usingTypeRef; // Base classes in "using Base::member;" bool m_withinUsingDeclaration = false; int m_anonymousEnumCount = 0; CodeModel::FunctionType m_currentFunctionType = CodeModel::Normal; bool m_withinFriendDecl = false; + mutable QHash<QString, SpecialSystemHeader> m_systemHeaders; }; bool BuilderPrivate::addClass(const CXCursor &cursor, CodeModel::ClassType t) { QString className = getCursorSpelling(cursor); m_currentClass.reset(new _ClassModelItem(m_model, className)); - setFileName(cursor, m_currentClass.data()); + setFileName(cursor, m_currentClass.get()); m_currentClass->setClassType(t); // Some inner class? Note that it does not need to be (lexically) contained in a // class since it is possible to forward declare an inner class: @@ -283,10 +272,9 @@ static QString msgCannotDetermineException(const std::string_view &snippetV) const qsizetype length = qsizetype(truncate ? newLine : snippetV.size()); QString snippet = QString::fromUtf8(snippetV.data(), length); if (truncate) - snippet += QStringLiteral("..."); + snippet += "..."_L1; - return QLatin1String("Cannot determine exception specification: \"") - + snippet + QLatin1Char('"'); + return u"Cannot determine exception specification: \""_s + snippet + u'"'; } // Return whether noexcept(<value>) throws. noexcept() takes a constexpr value. @@ -343,11 +331,13 @@ FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor, { QString name = getCursorSpelling(cursor); // Apply type fixes to "operator X &" -> "operator X&" - if (name.startsWith(QLatin1String("operator "))) + if (name.startsWith(u"operator ")) name = fixTypeName(name); - FunctionModelItem result(new _FunctionModelItem(m_model, name)); - setFileName(cursor, result.data()); - result->setType(createTypeInfo(clang_getCursorResultType(cursor))); + auto result = std::make_shared<_FunctionModelItem>(m_model, name); + setFileName(cursor, result.get()); + const auto type = clang_getCursorResultType(cursor); + result->setType(createTypeInfo(type)); + result->setScopeResolution(hasScopeResolution(type)); result->setFunctionType(t); result->setScope(m_scope); result->setStatic(clang_Cursor_getStorageClass(cursor) == CX_SC_Static); @@ -356,7 +346,7 @@ FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor, case CXAvailability_Available: break; case CXAvailability_Deprecated: - result->setDeprecated(true); + result->setAttribute(FunctionAttribute::Deprecated); break; case CXAvailability_NotAvailable: // "Foo(const Foo&) = delete;" result->setDeleted(true); @@ -395,13 +385,13 @@ FunctionModelItem BuilderPrivate::createMemberFunction(const CXCursor &cursor, m_currentFunctionType == CodeModel::Signal || m_currentFunctionType == CodeModel::Slot ? m_currentFunctionType // by annotation : functionTypeFromCursor(cursor); - isTemplateCode |= m_currentClass->name().endsWith(QLatin1Char('>')); + isTemplateCode |= m_currentClass->name().endsWith(u'>'); auto result = createFunction(cursor, functionType, isTemplateCode); result->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor))); result->setConstant(clang_CXXMethod_isConst(cursor) != 0); - result->setStatic(clang_CXXMethod_isStatic(cursor) != 0); - result->setVirtual(clang_CXXMethod_isVirtual(cursor) != 0); - result->setAbstract(clang_CXXMethod_isPureVirtual(cursor) != 0); + result->setAttribute(FunctionAttribute::Static, clang_CXXMethod_isStatic(cursor) != 0); + result->setAttribute(FunctionAttribute::Virtual, clang_CXXMethod_isVirtual(cursor) != 0); + result->setAttribute(FunctionAttribute::Abstract, clang_CXXMethod_isPureVirtual(cursor) != 0); return result; } @@ -417,13 +407,14 @@ void BuilderPrivate::qualifyConstructor(const CXCursor &cursor) && m_currentFunction->arguments().size() == 1 && clang_CXXConstructor_isCopyConstructor(cursor) == 0 && clang_CXXConstructor_isMoveConstructor(cursor) == 0) { - m_currentFunction->setExplicit(clang_CXXConstructor_isConvertingConstructor(cursor) == 0); + m_currentFunction->setAttribute(FunctionAttribute::Explicit, + clang_CXXConstructor_isConvertingConstructor(cursor) == 0); } } TemplateParameterModelItem BuilderPrivate::createTemplateParameter(const CXCursor &cursor) const { - return TemplateParameterModelItem(new _TemplateParameterModelItem(m_model, getCursorSpelling(cursor))); + return std::make_shared<_TemplateParameterModelItem>(m_model, getCursorSpelling(cursor)); } TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const CXCursor &cursor) const @@ -436,11 +427,12 @@ TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const // CXCursor_VarDecl, CXCursor_FieldDecl cursors void BuilderPrivate::addField(const CXCursor &cursor) { - VariableModelItem field(new _VariableModelItem(m_model, getCursorSpelling(cursor))); + auto field = std::make_shared<_VariableModelItem>(m_model, getCursorSpelling(cursor)); field->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor))); field->setScope(m_scope); field->setType(createTypeInfo(cursor)); field->setMutable(clang_CXXField_isMutable(cursor) != 0); + setFileName(cursor, field.get()); m_currentField = field; m_scopeStack.back()->addVariable(field); } @@ -449,14 +441,14 @@ void BuilderPrivate::addField(const CXCursor &cursor) static QStringList qualifiedName(const QString &t) { QStringList result; - int end = t.indexOf(QLatin1Char('<')); + int end = t.indexOf(u'<'); if (end == -1) - end = t.indexOf(QLatin1Char('(')); + end = t.indexOf(u'('); if (end == -1) end = t.size(); int lastPos = 0; while (true) { - const int nextPos = t.indexOf(colonColon(), lastPos); + const int nextPos = t.indexOf(u"::"_s, lastPos); if (nextPos < 0 || nextPos >= end) break; result.append(t.mid(lastPos, nextPos - lastPos)); @@ -520,7 +512,7 @@ void BuilderPrivate::addTemplateInstantiations(const CXType &type, && !t->instantiations().isEmpty(); if (!parsed) t->setInstantiations({}); - const QPair<int, int> pos = parsed + const auto pos = parsed ? parseTemplateArgumentList(*typeName, dummyTemplateArgumentHandler) : t->parseTemplateArgumentList(*typeName); if (pos.first != -1 && pos.second != -1 && pos.second > pos.first) @@ -574,7 +566,7 @@ TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type, typeInfo.setConstant(clang_isConstQualifiedType(nestedType) != 0); typeInfo.setVolatile(clang_isVolatileQualifiedType(nestedType) != 0); - QString typeName = getTypeName(nestedType); + QString typeName = getResolvedTypeName(nestedType); while (TypeInfo::stripLeadingConst(&typeName) || TypeInfo::stripLeadingVolatile(&typeName)) { } @@ -584,21 +576,21 @@ TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type, // the typedef source is named "type-parameter-0-0". Convert it back to the // template parameter name. The CXTypes are the same for all templates and // must not be cached. - if (!m_currentClass.isNull() && typeName.startsWith(QLatin1String("type-parameter-0-"))) { + if (m_currentClass && typeName.startsWith(u"type-parameter-0-")) { if (cacheable != nullptr) *cacheable = false; bool ok; const int n = QStringView{typeName}.mid(17).toInt(&ok); if (ok) { auto currentTemplate = currentTemplateClass(); - if (!currentTemplate.isNull() && n < currentTemplate->templateParameters().size()) + if (currentTemplate && n < currentTemplate->templateParameters().size()) typeName = currentTemplate->templateParameters().at(n)->name(); } } // Obtain template instantiations if the name has '<' (thus excluding // typedefs like "std::string". - if (typeName.contains(QLatin1Char('<'))) + if (typeName.contains(u'<')) addTemplateInstantiations(nestedType, &typeName, &typeInfo); typeInfo.setQualifiedName(qualifiedName(typeName)); @@ -622,19 +614,18 @@ TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const void BuilderPrivate::addTypeDef(const CXCursor &cursor, const CXType &cxType) { const QString target = getCursorSpelling(cursor); - TypeDefModelItem item(new _TypeDefModelItem(m_model, target)); - setFileName(cursor, item.data()); + auto item = std::make_shared<_TypeDefModelItem>(m_model, target); + setFileName(cursor, item.get()); item->setType(createTypeInfo(cxType)); item->setScope(m_scope); m_scopeStack.back()->addTypeDef(item); - m_cursorTypedefHash.insert(cursor, item); } ClassModelItem BuilderPrivate::currentTemplateClass() const { - for (int i = m_scopeStack.size() - 1; i >= 0; --i) { - auto klass = qSharedPointerDynamicCast<_ClassModelItem>(m_scopeStack.at(i)); - if (!klass.isNull() && klass->isTemplate()) + for (auto i = m_scopeStack.size() - 1; i >= 0; --i) { + auto klass = std::dynamic_pointer_cast<_ClassModelItem>(m_scopeStack.at(i)); + if (klass && klass->isTemplate()) return klass; } return {}; @@ -644,7 +635,7 @@ void BuilderPrivate::startTemplateTypeAlias(const CXCursor &cursor) { const QString target = getCursorSpelling(cursor); m_currentTemplateTypeAlias.reset(new _TemplateTypeAliasModelItem(m_model, target)); - setFileName(cursor, m_currentTemplateTypeAlias.data()); + setFileName(cursor, m_currentTemplateTypeAlias.get()); m_currentTemplateTypeAlias->setScope(m_scope); } @@ -669,11 +660,17 @@ QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &c if (equalSign == std::string::npos) return QString(); ++equalSign; - return QString::fromLocal8Bit(snippet.data() + equalSign, - qsizetype(snippet.size() - equalSign)).trimmed(); + QString result = QString::fromLocal8Bit(snippet.data() + equalSign, + qsizetype(snippet.size() - equalSign)); + // Fix a default expression as read from code. Simplify white space + result.remove(u'\r'); + return result.contains(u'"') ? result.trimmed() : result.simplified(); } // Resolve a type (loop over aliases/typedefs), for example for base classes +// Note: TypeAliasTemplateDecl ("using QVector<T>=QList<T>") is automatically +// resolved by clang_getTypeDeclaration(), but it stops at +// TypeAliasDecl / TypedefDecl. struct TypeDeclaration { @@ -681,47 +678,41 @@ struct TypeDeclaration CXCursor declaration; }; -static TypeDeclaration resolveType(CXType type) +static inline bool isTypeAliasDecl(const CXCursor &cursor) +{ + const auto kind = clang_getCursorKind(cursor); + return kind == CXCursor_TypeAliasDecl || kind == CXCursor_TypedefDecl; +} + +static TypeDeclaration resolveBaseClassType(CXType type) { CXCursor decl = clang_getTypeDeclaration(type); - if (type.kind != CXType_Unexposed) { - while (true) { - auto kind = clang_getCursorKind(decl); - if (kind != CXCursor_TypeAliasDecl && kind != CXCursor_TypedefDecl) - break; - type = clang_getTypedefDeclUnderlyingType(decl); - decl = clang_getTypeDeclaration(type); - } + auto resolvedType = clang_getCursorType(decl); + if (resolvedType.kind != CXType_Invalid && resolvedType.kind != type.kind) + type = resolvedType; + while (isTypeAliasDecl(decl)) { + type = clang_getTypedefDeclUnderlyingType(decl); + decl = clang_getTypeDeclaration(type); } return {type, decl}; } // Note: Return the baseclass for cursors like CXCursor_CXXBaseSpecifier, // where the cursor spelling has "struct baseClass". -QString BuilderPrivate::getBaseClassName(CXType type) const +std::pair<QString, ClassModelItem> BuilderPrivate::getBaseClass(CXType type) const { - const auto decl = resolveType(type); + const auto decl = resolveBaseClassType(type); // Note: spelling has "struct baseClass", use type - QString baseClassName; - if (decl.type.kind == CXType_Unexposed) { - // The type is unexposed when the base class is a template type alias: - // "class QItemSelection : public QList<X>" where QList is aliased to QVector. - // Try to resolve via code model. - TypeInfo info = createTypeInfo(decl.type); - auto parentScope = m_scopeStack.at(m_scopeStack.size() - 2); // Current is class. - auto resolved = TypeInfo::resolveType(info, parentScope); - if (resolved != info) - baseClassName = resolved.toString(); - } - if (baseClassName.isEmpty()) - baseClassName = getTypeName(decl.type); + QString baseClassName = getTypeName(decl.type); + if (baseClassName.startsWith(u"std::")) // Simplify "std::" types + baseClassName = createTypeInfo(decl.type).toString(); auto it = m_cursorClassHash.constFind(decl.declaration); // Not found: Set unqualified name. This happens in cases like // "class X : public std::list<...>", "template<class T> class Foo : public T" // and standard types like true_type, false_type. if (it == m_cursorClassHash.constEnd()) - return baseClassName; + return {baseClassName, {}}; // Completely qualify the class name by looking it up and taking its scope // plus the actual baseClass stripped off any scopes. Consider: @@ -735,13 +726,13 @@ QString BuilderPrivate::getBaseClassName(CXType type) const // "std::vector<T>"). const QStringList &baseScope = it.value()->scope(); if (!baseScope.isEmpty()) { - const int lastSep = baseClassName.lastIndexOf(colonColon()); + const int lastSep = baseClassName.lastIndexOf(u"::"_s); if (lastSep >= 0) - baseClassName.remove(0, lastSep + colonColon().size()); - baseClassName.prepend(colonColon()); - baseClassName.prepend(baseScope.join(colonColon())); + baseClassName.remove(0, lastSep + u"::"_s.size()); + baseClassName.prepend(u"::"_s); + baseClassName.prepend(baseScope.join(u"::"_s)); } - return baseClassName; + return {baseClassName, it.value()}; } // Add a base class to the current class from CXCursor_CXXBaseSpecifier @@ -749,39 +740,8 @@ void BuilderPrivate::addBaseClass(const CXCursor &cursor) { Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier); const auto access = accessPolicy(clang_getCXXAccessSpecifier(cursor)); - QString baseClassName = getBaseClassName(clang_getCursorType(cursor)); - m_currentClass->addBaseClass(baseClassName, access); -} - -static inline CXCursor definitionFromTypeRef(const CXCursor &typeRefCursor) -{ - Q_ASSERT(typeRefCursor.kind == CXCursor_TypeRef); - return clang_getTypeDeclaration(clang_getCursorType(typeRefCursor)); -} - -// Qualify function arguments or fields that are typedef'ed from another scope: -// enum ConversionFlag {}; -// typedef QFlags<ConversionFlag> ConversionFlags; -// class QTextCodec { -// enum ConversionFlag {}; -// typedef QFlags<ConversionFlag> ConversionFlags; -// struct ConverterState { -// explicit ConverterState(ConversionFlags); -// ^^ qualify to QTextCodec::ConversionFlags -// ConversionFlags m_flags; -// ^^ ditto - -template <class Item> // ArgumentModelItem, VariableModelItem -void BuilderPrivate::qualifyTypeDef(const CXCursor &typeRefCursor, const QSharedPointer<Item> &item) const -{ - TypeInfo type = item->type(); - if (type.qualifiedName().size() == 1) { // item's type is unqualified. - const auto it = m_cursorTypedefHash.constFind(definitionFromTypeRef(typeRefCursor)); - if (it != m_cursorTypedefHash.constEnd() && !it.value()->scope().isEmpty()) { - type.setQualifiedName(it.value()->scope() + type.qualifiedName()); - item->setType(type); - } - } + const auto baseClass = getBaseClass(clang_getCursorType(cursor)); + m_currentClass->addBaseClass({baseClass.first, baseClass.second, access}); } void BuilderPrivate::setFileName(const CXCursor &cursor, _CodeModelItem *item) @@ -805,103 +765,115 @@ Builder::~Builder() delete d; } -static const char *cBaseName(const char *fileName) +static QString baseName(QString path) { - const char *lastSlash = std::strrchr(fileName, '/'); + qsizetype lastSlash = path.lastIndexOf(u'/'); #ifdef Q_OS_WIN - if (lastSlash == nullptr) - lastSlash = std::strrchr(fileName, '\\'); + if (lastSlash < 0) + lastSlash = path.lastIndexOf(u'\\'); #endif - return lastSlash != nullptr ? (lastSlash + 1) : fileName; + if (lastSlash > 0) + path.remove(0, lastSlash + 1); + return path; } -static inline bool cCompareFileName(const char *f1, const char *f2) +const char * BuilderPrivate::specialSystemHeaderReason(BuilderPrivate::SpecialSystemHeader sh) { -#ifdef Q_OS_WIN - return _stricmp(f1, f2) == 0; -#else - return std::strcmp(f1, f2) == 0; -#endif -} - -#ifdef Q_OS_UNIX -template<size_t N> -static bool cStringStartsWith(const char *str, const char (&prefix)[N]) -{ - return std::strncmp(prefix, str, N - 1) == 0; + static const QHash<SpecialSystemHeader, const char *> mapping { + {SpecialSystemHeader::OpenGL, "OpenGL"}, + {SpecialSystemHeader::Types, "types"}, + {SpecialSystemHeader::WhiteListed, "white listed"}, + {SpecialSystemHeader::WhiteListedPath, "white listed path"} + }; + return mapping.value(sh, ""); } -#endif -static bool cStringStartsWith(const char *str, const QByteArray &prefix) +bool BuilderPrivate::visitHeader(const QString &fileName) const { - return std::strncmp(prefix.constData(), str, int(prefix.size())) == 0; + auto it = m_systemHeaders.find(fileName); + if (it == m_systemHeaders.end()) { + it = m_systemHeaders.insert(fileName, specialSystemHeader(fileName)); + if (ReportHandler::isDebug(ReportHandler::MediumDebug)) { + const QString &name = QDir::toNativeSeparators(fileName); + if (it.value() == SpecialSystemHeader::None) { + qCInfo(lcShiboken, "Skipping system header %s", qPrintable(name)); + } else { + qCInfo(lcShiboken, "Parsing system header %s (%s)", + qPrintable(name), specialSystemHeaderReason(it.value())); + } + } + } + return it.value() != SpecialSystemHeader::None; } -bool BuilderPrivate::visitHeader(const char *cFileName) const +BuilderPrivate::SpecialSystemHeader + BuilderPrivate::specialSystemHeader(const QString &fileName) const { // Resolve OpenGL typedefs although the header is considered a system header. - const char *baseName = cBaseName(cFileName); - if (cCompareFileName(baseName, "gl.h")) - return true; -#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - if (cStringStartsWith(cFileName, "/usr/include/stdint.h")) - return true; -#endif -#ifdef Q_OS_LINUX - if (cStringStartsWith(cFileName, "/usr/include/stdlib.h") - || cStringStartsWith(cFileName, "/usr/include/sys/types.h")) { - return true; + const QString baseName = clang::baseName(fileName); + if (baseName == u"gl.h" + || baseName == u"gl2.h" + || baseName == u"gl3.h" + || baseName == u"gl31.h" + || baseName == u"gl32.h" + || baseName == u"stdint.h" // Windows: int32_t, uint32_t + || baseName == u"stddef.h") { // size_t` + return SpecialSystemHeader::OpenGL; } -#endif // Q_OS_LINUX -#ifdef Q_OS_MACOS - // Parse the following system headers to get the correct typdefs for types like - // int32_t, which are used in the macOS implementation of OpenGL framework. - if (cCompareFileName(baseName, "gltypes.h") - || cStringStartsWith(cFileName, "/usr/include/_types") - || cStringStartsWith(cFileName, "/usr/include/_types") - || cStringStartsWith(cFileName, "/usr/include/sys/_types")) { - return true; - } -#endif // Q_OS_MACOS - if (baseName) { - for (const auto &systemInclude : m_systemIncludes) { - if (systemInclude == baseName) - return true; + + switch (clang::platform()) { + case Platform::Unix: + if (fileName == u"/usr/include/stdlib.h" + || baseName == u"types.h" + || baseName == u"stdint-intn.h" // int32_t + || baseName == u"stdint-uintn.h") { // uint32_t + return SpecialSystemHeader::Types; } + break; + case Platform::macOS: + // Parse the following system headers to get the correct typdefs for types like + // int32_t, which are used in the macOS implementation of OpenGL framework. + // They are installed under /Applications/Xcode.app/Contents/Developer/Platforms... + if (baseName == u"gltypes.h" + || fileName.contains(u"/usr/include/_types") + || fileName.contains(u"/usr/include/sys/_types")) { + return SpecialSystemHeader::Types; + } + break; + default: + break; } - for (const auto &systemIncludePath : m_systemIncludePaths) { - if (cStringStartsWith(cFileName, systemIncludePath)) - return true; + + // When building against system Qt (as it happens with yocto / Boot2Qt), the Qt headers are + // considered system headers by clang_Location_isInSystemHeader, and shiboken will not + // process them. We need to explicitly process them by checking against the list of + // include paths that were passed to shiboken's --force-process-system-include-paths option + // or specified via the <system-include> xml tag. + if (m_forceProcessSystemIncludes.contains(baseName)) + return SpecialSystemHeader::WhiteListed; + + if (std::any_of(m_forceProcessSystemIncludePaths.cbegin(), + m_forceProcessSystemIncludePaths.cend(), + [fileName](const QString &p) { return fileName.startsWith(p); })) { + return SpecialSystemHeader::WhiteListedPath; } - return false; + + return SpecialSystemHeader::None; } -bool Builder::visitLocation(const CXSourceLocation &location) const +bool Builder::visitLocation(const QString &fileName, LocationType locationType) const { - if (clang_Location_isInSystemHeader(location) == 0) - return true; - CXFile file; // void * - unsigned line; - unsigned column; - unsigned offset; - clang_getExpansionLocation(location, &file, &line, &column, &offset); - const CXString cxFileName = clang_getFileName(file); - // Has been observed to be 0 for invalid locations - bool result = false; - if (const char *cFileName = clang_getCString(cxFileName)) { - result = d->visitHeader(cFileName); - clang_disposeString(cxFileName); - } - return result; + return locationType != LocationType::System || d->visitHeader(fileName); } -void Builder::setSystemIncludes(const QByteArrayList &systemIncludes) +void Builder::setForceProcessSystemIncludes(const QStringList &systemIncludes) { for (const auto &i : systemIncludes) { - if (i.endsWith('/')) - d->m_systemIncludePaths.append(i); + QFileInfo fi(i); + if (fi.exists() && fi.isDir()) + d->m_forceProcessSystemIncludePaths.append(i); else - d->m_systemIncludes.append(i); + d->m_forceProcessSystemIncludes.append(i); } } @@ -910,14 +882,14 @@ FileModelItem Builder::dom() const Q_ASSERT(!d->m_scopeStack.isEmpty()); auto rootScope = d->m_scopeStack.constFirst(); rootScope->purgeClassDeclarations(); - return qSharedPointerDynamicCast<_FileModelItem>(rootScope); + return std::dynamic_pointer_cast<_FileModelItem>(rootScope); } static QString msgOutOfOrder(const CXCursor &cursor, const char *expectedScope) { - return getCursorKindName(cursor.kind) + QLatin1Char(' ') - + getCursorSpelling(cursor) + QLatin1String(" encountered outside ") - + QLatin1String(expectedScope) + QLatin1Char('.'); + return getCursorKindName(cursor.kind) + u' ' + + getCursorSpelling(cursor) + u" encountered outside "_s + + QLatin1StringView(expectedScope) + u'.'; } static CodeModel::ClassType codeModelClassTypeFromCursor(CXCursorKind kind) @@ -944,12 +916,16 @@ static NamespaceType namespaceType(const CXCursor &cursor) static QString enumType(const CXCursor &cursor) { QString name = getCursorSpelling(cursor); // "enum Foo { v1, v2 };" + if (name.contains(u"unnamed enum")) // Clang 16.0 + return {}; if (name.isEmpty()) { // PYSIDE-1228: For "typedef enum { v1, v2 } Foo;", type will return // "Foo" as expected. Care must be taken to exclude real anonymous enums. name = getTypeName(clang_getCursorType(cursor)); - if (name.contains(QLatin1String("(anonymous"))) + if (name.contains(u"(unnamed") // Clang 12.0.1 + || name.contains(u"(anonymous")) { // earlier name.clear(); + } } return name; } @@ -962,16 +938,16 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) break; case CXCursor_AnnotateAttr: { const QString annotation = getCursorSpelling(cursor); - if (annotation == QLatin1String("qt_slot")) + if (annotation == u"qt_slot") d->m_currentFunctionType = CodeModel::Slot; - else if (annotation == QLatin1String("qt_signal")) + else if (annotation == u"qt_signal") d->m_currentFunctionType = CodeModel::Signal; else d->m_currentFunctionType = CodeModel::Normal; } break; case CXCursor_CXXBaseSpecifier: - if (d->m_currentClass.isNull()) { + if (!d->m_currentClass) { const Diagnostic d(msgOutOfOrder(cursor, "class"), cursor, CXDiagnostic_Error); qWarning() << d; appendDiagnostic(d); @@ -993,15 +969,15 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) || !d->addClass(cursor, CodeModel::Class)) { return Skip; } - d->m_currentClass->setName(d->m_currentClass->name() + templateBrackets()); - d->m_scope.back() += templateBrackets(); + d->m_currentClass->setName(d->m_currentClass->name() + "<>"_L1); + d->m_scope.back() += "<>"_L1; break; case CXCursor_EnumDecl: { QString name = enumType(cursor); EnumKind kind = CEnum; if (name.isEmpty()) { kind = AnonymousEnum; - name = QStringLiteral("enum_") + QString::number(++d->m_anonymousEnumCount); + name = "enum_"_L1 + QString::number(++d->m_anonymousEnumCount); #if !CLANG_NO_ENUMDECL_ISSCOPED } else if (clang_EnumDecl_isScoped(cursor) != 0) { #else @@ -1010,17 +986,21 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) kind = EnumClass; } d->m_currentEnum.reset(new _EnumModelItem(d->m_model, name)); - d->setFileName(cursor, d->m_currentEnum.data()); + d->setFileName(cursor, d->m_currentEnum.get()); d->m_currentEnum->setScope(d->m_scope); d->m_currentEnum->setEnumKind(kind); - d->m_currentEnum->setSigned(isSigned(clang_getEnumDeclIntegerType(cursor).kind)); - if (!qSharedPointerDynamicCast<_ClassModelItem>(d->m_scopeStack.back()).isNull()) + if (clang_getCursorAvailability(cursor) == CXAvailability_Deprecated) + d->m_currentEnum->setDeprecated(true); + const auto enumType = fullyResolveType(clang_getEnumDeclIntegerType(cursor)); + d->m_currentEnum->setSigned(isSigned(enumType.kind)); + d->m_currentEnum->setUnderlyingType(getTypeName(enumType)); + if (std::dynamic_pointer_cast<_ClassModelItem>(d->m_scopeStack.back())) d->m_currentEnum->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor))); } break; case CXCursor_EnumConstantDecl: { const QString name = getCursorSpelling(cursor); - if (d->m_currentEnum.isNull()) { + if (!d->m_currentEnum) { const Diagnostic d(msgOutOfOrder(cursor, "enum"), cursor, CXDiagnostic_Error); qWarning() << d; appendDiagnostic(d); @@ -1031,9 +1011,11 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) enumValue.setValue(clang_getEnumConstantDeclValue(cursor)); else enumValue.setUnsignedValue(clang_getEnumConstantDeclUnsignedValue(cursor)); - EnumeratorModelItem enumConstant(new _EnumeratorModelItem(d->m_model, name)); + auto enumConstant = std::make_shared<_EnumeratorModelItem>(d->m_model, name); enumConstant->setStringValue(d->cursorValueExpression(this, cursor)); enumConstant->setValue(enumValue); + if (clang_getCursorAvailability(cursor) == CXAvailability_Deprecated) + enumConstant->setDeprecated(true); d->m_currentEnum->addEnumerator(enumConstant); } break; @@ -1077,6 +1059,7 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) } } d->m_currentFunction = d->createFunction(cursor, CodeModel::Normal, true); + d->setFileName(cursor, d->m_currentFunction.get()); d->m_scopeStack.back()->addFunction(d->m_currentFunction); break; case CXCursor_FunctionDecl: @@ -1084,13 +1067,14 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) // operators). Note: CXTranslationUnit_SkipFunctionBodies must be off for // clang_isCursorDefinition() to work here. if (!d->m_withinFriendDecl || clang_isCursorDefinition(cursor) != 0) { - int scope = d->m_scopeStack.size() - 1; // enclosing class + auto scope = d->m_scopeStack.size() - 1; // enclosing class if (d->m_withinFriendDecl) { // Friend declaration: go back to namespace or file scope. for (--scope; d->m_scopeStack.at(scope)->kind() == _CodeModelItem::Kind_Class; --scope) { } } d->m_currentFunction = d->createFunction(cursor, CodeModel::Normal, false); + d->m_currentFunction->setHiddenFriend(d->m_withinFriendDecl); d->m_scopeStack.at(scope)->addFunction(d->m_currentFunction); } break; @@ -1099,10 +1083,10 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) if (type == NamespaceType::Anonymous) return Skip; const QString name = getCursorSpelling(cursor); - const NamespaceModelItem parentNamespaceItem = qSharedPointerDynamicCast<_NamespaceModelItem>(d->m_scopeStack.back()); - if (parentNamespaceItem.isNull()) { + const auto parentNamespaceItem = std::dynamic_pointer_cast<_NamespaceModelItem>(d->m_scopeStack.back()); + if (!parentNamespaceItem) { const QString message = msgOutOfOrder(cursor, "namespace") - + QLatin1String(" (current scope: ") + d->m_scopeStack.back()->name() + QLatin1Char(')'); + + u" (current scope: "_s + d->m_scopeStack.back()->name() + u')'; const Diagnostic d(message, cursor, CXDiagnostic_Error); qWarning() << d; appendDiagnostic(d); @@ -1112,7 +1096,7 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) // in subsequent modules. NamespaceModelItem namespaceItem = parentNamespaceItem->findNamespace(name); namespaceItem.reset(new _NamespaceModelItem(d->m_model, name)); - d->setFileName(cursor, namespaceItem.data()); + d->setFileName(cursor, namespaceItem.get()); namespaceItem->setScope(d->m_scope); namespaceItem->setType(type); parentNamespaceItem->addNamespace(namespaceItem); @@ -1122,10 +1106,12 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) case CXCursor_ParmDecl: // Skip in case of nested CXCursor_ParmDecls in case one parameter is a function pointer // and function pointer typedefs. - if (d->m_currentArgument.isNull() && !d->m_currentFunction.isNull()) { + if (!d->m_currentArgument && d->m_currentFunction) { const QString name = getCursorSpelling(cursor); d->m_currentArgument.reset(new _ArgumentModelItem(d->m_model, name)); - d->m_currentArgument->setType(d->createTypeInfo(cursor)); + const auto type = clang_getCursorType(cursor); + d->m_currentArgument->setScopeResolution(hasScopeResolution(type)); + d->m_currentArgument->setType(d->createTypeInfo(type)); d->m_currentFunction->addArgument(d->m_currentArgument); QString defaultValueExpression = d->cursorValueExpression(this, cursor); if (!defaultValueExpression.isEmpty()) { @@ -1141,16 +1127,16 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) const TemplateParameterModelItem tItem = cursor.kind == CXCursor_TemplateTemplateParameter ? d->createTemplateParameter(cursor) : d->createNonTypeTemplateParameter(cursor); // Apply to function/member template? - if (!d->m_currentFunction.isNull()) { + if (d->m_currentFunction) { d->m_currentFunction->setTemplateParameters(d->m_currentFunction->templateParameters() << tItem); - } else if (!d->m_currentTemplateTypeAlias.isNull()) { + } else if (d->m_currentTemplateTypeAlias) { d->m_currentTemplateTypeAlias->addTemplateParameter(tItem); - } else if (!d->m_currentClass.isNull()) { // Apply to class + } else if (d->m_currentClass) { // Apply to class const QString &tplParmName = tItem->name(); if (Q_UNLIKELY(!insertTemplateParameterIntoClassName(tplParmName, d->m_currentClass) || !insertTemplateParameterIntoClassName(tplParmName, &d->m_scope.back()))) { - const QString message = QStringLiteral("Error inserting template parameter \"") + tplParmName - + QStringLiteral("\" into ") + d->m_currentClass->name(); + const QString message = "Error inserting template parameter \""_L1 + tplParmName + + "\" into "_L1 + d->m_currentClass->name(); const Diagnostic d(message, cursor, CXDiagnostic_Error); qWarning() << d; appendDiagnostic(d); @@ -1164,7 +1150,7 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) d->startTemplateTypeAlias(cursor); break; case CXCursor_TypeAliasDecl: // May contain nested CXCursor_TemplateTypeParameter - if (d->m_currentTemplateTypeAlias.isNull()) { + if (!d->m_currentTemplateTypeAlias) { const CXType type = clang_getCanonicalType(clang_getCursorType(cursor)); if (type.kind > CXType_Unexposed) d->addTypeDef(cursor, type); @@ -1192,31 +1178,23 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) d->m_usingTypeRef = getCursorSpelling(cursor); break; case CXCursor_TypeRef: - if (!d->m_currentFunction.isNull()) { - if (d->m_currentArgument.isNull()) - d->qualifyTypeDef(cursor, d->m_currentFunction); // return type - else - d->qualifyTypeDef(cursor, d->m_currentArgument); - } else if (!d->m_currentField.isNull()) { - d->qualifyTypeDef(cursor, d->m_currentField); - } else if (d->m_withinUsingDeclaration && d->m_usingTypeRef.isEmpty()) { - d->m_usingTypeRef = d->getBaseClassName(clang_getCursorType(cursor)); - } + if (d->m_withinUsingDeclaration && d->m_usingTypeRef.isEmpty()) + d->m_usingTypeRef = d->getBaseClass(clang_getCursorType(cursor)).first; break; case CXCursor_CXXFinalAttr: - if (!d->m_currentFunction.isNull()) - d->m_currentFunction->setFinal(true); - else if (!d->m_currentClass.isNull()) + if (d->m_currentFunction) + d->m_currentFunction->setAttribute(FunctionAttribute::Final); + else if (d->m_currentClass) d->m_currentClass->setFinal(true); break; case CXCursor_CXXOverrideAttr: - if (!d->m_currentFunction.isNull()) - d->m_currentFunction->setOverride(true); + if (d->m_currentFunction) + d->m_currentFunction->setAttribute(FunctionAttribute::Override); break; case CXCursor_StaticAssert: // Check for Q_PROPERTY() (see PySide6/global.h.in for an explanation // how it is defined, and qdoc). - if (clang_isDeclaration(cursor.kind) && !d->m_currentClass.isNull()) { + if (clang_isDeclaration(cursor.kind) && d->m_currentClass) { auto snippet = getCodeSnippet(cursor); const auto length = snippet.size(); if (length > 12 && *snippet.rbegin() == ')' @@ -1228,7 +1206,7 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) break; // UsingDeclaration: consists of a TypeRef (base) and OverloadedDeclRef (member name) case CXCursor_UsingDeclaration: - if (!d->m_currentClass.isNull()) + if (d->m_currentClass) d->m_withinUsingDeclaration = true; break; case CXCursor_OverloadedDeclRef: @@ -1256,53 +1234,51 @@ bool Builder::endToken(const CXCursor &cursor) case CXCursor_ClassTemplatePartialSpecialization: d->popScope(); // Continue in outer class after leaving inner class? - if (ClassModelItem lastClass = qSharedPointerDynamicCast<_ClassModelItem>(d->m_scopeStack.back())) + if (auto lastClass = std::dynamic_pointer_cast<_ClassModelItem>(d->m_scopeStack.back())) d->m_currentClass = lastClass; else - d->m_currentClass.clear(); + d->m_currentClass.reset(); d->m_currentFunctionType = CodeModel::Normal; break; case CXCursor_EnumDecl: - // Add enum only if values were encountered, otherwise assume it - // is a forward declaration of an enum class. - if (!d->m_currentEnum.isNull() && d->m_currentEnum->hasValues()) + if (d->m_currentEnum) d->m_scopeStack.back()->addEnum(d->m_currentEnum); - d->m_currentEnum.clear(); + d->m_currentEnum.reset(); break; case CXCursor_FriendDecl: d->m_withinFriendDecl = false; break; case CXCursor_VarDecl: case CXCursor_FieldDecl: - d->m_currentField.clear(); + d->m_currentField.reset(); break; case CXCursor_Constructor: d->qualifyConstructor(cursor); - if (!d->m_currentFunction.isNull()) { + if (d->m_currentFunction) { d->m_currentFunction->_determineType(); - d->m_currentFunction.clear(); + d->m_currentFunction.reset(); } break; case CXCursor_Destructor: case CXCursor_CXXMethod: case CXCursor_FunctionDecl: case CXCursor_FunctionTemplate: - if (!d->m_currentFunction.isNull()) { + if (d->m_currentFunction) { d->m_currentFunction->_determineType(); - d->m_currentFunction.clear(); + d->m_currentFunction.reset(); } break; case CXCursor_ConversionFunction: - if (!d->m_currentFunction.isNull()) { + if (d->m_currentFunction) { d->m_currentFunction->setFunctionType(CodeModel::ConversionOperator); - d->m_currentFunction.clear(); + d->m_currentFunction.reset(); } break; case CXCursor_Namespace: d->popScope(); break; case CXCursor_ParmDecl: - d->m_currentArgument.clear(); + d->m_currentArgument.reset(); break; case CXCursor_TypeAliasTemplateDecl: d->m_currentTemplateTypeAlias.reset(); |