aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp524
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();