aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp')
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp311
1 files changed, 245 insertions, 66 deletions
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
index 8d1b4debf..73b1aca63 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
@@ -40,7 +40,7 @@
#include <QtCore/QStack>
#include <QtCore/QVector>
-#include <string.h>
+#include <cstring>
#include <ctype.h>
#if QT_VERSION < 0x050800
@@ -59,6 +59,11 @@ static inline bool isClassCursor(const CXCursor &c)
|| c.kind == CXCursor_ClassTemplatePartialSpecialization;
}
+static inline bool isClassOrNamespaceCursor(const CXCursor &c)
+{
+ return c.kind == CXCursor_Namespace || isClassCursor(c);
+}
+
static inline bool withinClassDeclaration(const CXCursor &cursor)
{
return isClassCursor(clang_getCursorLexicalParent(cursor));
@@ -114,16 +119,6 @@ static inline CodeModel::AccessPolicy accessPolicy(CX_CXXAccessSpecifier access)
return result;
}
-static void setFileName(const CXCursor &cursor, _CodeModelItem *item)
-{
- const SourceRange range = getCursorRange(cursor);
- if (!range.first.file.isEmpty()) { // Has been observed to be 0 for invalid locations
- item->setFileName(QDir::cleanPath(range.first.file));
- item->setStartPosition(int(range.first.line), int(range.first.column));
- item->setEndPosition(int(range.second.line), int(range.second.column));
- }
-}
-
static bool isSigned(CXTypeKind kind)
{
switch (kind) {
@@ -172,14 +167,15 @@ public:
void popScope()
{
+ m_scopeStack.back()->purgeClassDeclarations();
m_scopeStack.pop();
updateScope();
}
bool addClass(const CXCursor &cursor, CodeModel::ClassType t);
FunctionModelItem createFunction(const CXCursor &cursor,
- CodeModel::FunctionType t = CodeModel::Normal) const;
- FunctionModelItem createMemberFunction(const CXCursor &cursor) const;
+ CodeModel::FunctionType t = CodeModel::Normal);
+ FunctionModelItem createMemberFunction(const CXCursor &cursor);
void qualifyConstructor(const CXCursor &cursor);
TypeInfo createTypeInfoHelper(const CXType &type) const; // uncashed
TypeInfo createTypeInfo(const CXType &type) const;
@@ -190,7 +186,9 @@ public:
TypeInfo *t) const;
bool addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const;
- void addTypeDef(const CXCursor &cursor, const TypeInfo &ti);
+ void addTypeDef(const CXCursor &cursor, const CXType &cxType);
+ void startTemplateTypeAlias(const CXCursor &cursor);
+ void endTemplateTypeAlias(const CXCursor &typeAliasCursor);
TemplateParameterModelItem createTemplateParameter(const CXCursor &cursor) const;
TemplateParameterModelItem createNonTypeTemplateParameter(const CXCursor &cursor) const;
@@ -202,6 +200,10 @@ public:
template <class Item>
void qualifyTypeDef(const CXCursor &typeRefCursor, const QSharedPointer<Item> &item) const;
+ bool visitHeader(const char *cFileName) const;
+
+ void setFileName(const CXCursor &cursor, _CodeModelItem *item);
+
BaseVisitor *m_baseVisitor;
CodeModel *m_model;
@@ -214,12 +216,16 @@ public:
CursorTypedefHash m_cursorTypedefHash;
mutable TypeInfoHash m_typeInfoHash; // Cache type information
+ mutable QHash<QString, TemplateTypeAliasModelItem> m_templateTypeAliases;
ClassModelItem m_currentClass;
EnumModelItem m_currentEnum;
FunctionModelItem m_currentFunction;
ArgumentModelItem m_currentArgument;
VariableModelItem m_currentField;
+ TemplateTypeAliasModelItem m_currentTemplateTypeAlias;
+ QByteArrayList m_systemIncludes; // files, like "memory"
+ QByteArrayList m_systemIncludePaths; // paths, like "/usr/include/Qt/"
int m_anonymousEnumCount = 0;
CodeModel::FunctionType m_currentFunctionType = CodeModel::Normal;
@@ -277,7 +283,7 @@ static inline ExceptionSpecification exceptionSpecificationFromClang(int ce)
}
FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor,
- CodeModel::FunctionType t) const
+ CodeModel::FunctionType t)
{
QString name = getCursorSpelling(cursor);
// Apply type fixes to "operator X &" -> "operator X&"
@@ -326,7 +332,7 @@ static inline CodeModel::FunctionType functionTypeFromCursor(const CXCursor &cur
return result;
}
-FunctionModelItem BuilderPrivate::createMemberFunction(const CXCursor &cursor) const
+FunctionModelItem BuilderPrivate::createMemberFunction(const CXCursor &cursor)
{
const CodeModel::FunctionType functionType =
m_currentFunctionType == CodeModel::Signal || m_currentFunctionType == CodeModel::Slot
@@ -462,6 +468,8 @@ void BuilderPrivate::addTemplateInstantiations(const CXType &type,
// Finally, remove the list "<>" from the type name.
const bool parsed = addTemplateInstantiationsRecursion(type, t)
&& !t->instantiations().isEmpty();
+ if (!parsed)
+ t->setInstantiations({});
const QPair<int, int> pos = parsed
? parseTemplateArgumentList(*typeName, dummyTemplateArgumentHandler)
: t->parseTemplateArgumentList(*typeName);
@@ -537,16 +545,37 @@ TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const
return it.value();
}
-void BuilderPrivate::addTypeDef(const CXCursor &cursor, const TypeInfo &ti)
+void BuilderPrivate::addTypeDef(const CXCursor &cursor, const CXType &cxType)
{
- TypeDefModelItem item(new _TypeDefModelItem(m_model, getCursorSpelling(cursor)));
+ const QString target = getCursorSpelling(cursor);
+ TypeDefModelItem item(new _TypeDefModelItem(m_model, target));
setFileName(cursor, item.data());
- item->setType(ti);
+ item->setType(createTypeInfo(cxType));
item->setScope(m_scope);
m_scopeStack.back()->addTypeDef(item);
m_cursorTypedefHash.insert(cursor, item);
}
+void BuilderPrivate::startTemplateTypeAlias(const CXCursor &cursor)
+{
+ const QString target = getCursorSpelling(cursor);
+ m_currentTemplateTypeAlias.reset(new _TemplateTypeAliasModelItem(m_model, target));
+ setFileName(cursor, m_currentTemplateTypeAlias.data());
+ m_currentTemplateTypeAlias->setScope(m_scope);
+}
+
+void BuilderPrivate::endTemplateTypeAlias(const CXCursor &typeAliasCursor)
+{
+ CXType type = clang_getTypedefDeclUnderlyingType(typeAliasCursor);
+ // Usually "<elaborated>std::list<T>" or "<unexposed>Container1<T>",
+ // as obtained with parser of PYSIDE-323
+ if (type.kind == CXType_Unexposed || type.kind == CXType_Elaborated) {
+ m_currentTemplateTypeAlias->setType(createTypeInfo(type));
+ m_scopeStack.back()->addTemplateTypeAlias(m_currentTemplateTypeAlias);
+ }
+ m_currentTemplateTypeAlias.reset();
+}
+
// extract an expression from the cursor via source
// CXCursor_EnumConstantDecl, ParmDecl (a = Flag1 | Flag2)
QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor) const
@@ -589,13 +618,52 @@ long clang_EnumDecl_isScoped4(BaseVisitor *bv, const CXCursor &cursor)
}
#endif // CLANG_NO_ENUMDECL_ISSCOPED
+// Resolve declaration and type of a base class
+
+struct TypeDeclaration
+{
+ CXType type;
+ CXCursor declaration;
+};
+
+static TypeDeclaration resolveBaseSpecifier(const CXCursor &cursor)
+{
+ Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier);
+ CXType inheritedType = clang_getCursorType(cursor);
+ CXCursor decl = clang_getTypeDeclaration(inheritedType);
+ if (inheritedType.kind != CXType_Unexposed) {
+ while (true) {
+ auto kind = clang_getCursorKind(decl);
+ if (kind != CXCursor_TypeAliasDecl && kind != CXCursor_TypedefDecl)
+ break;
+ inheritedType = clang_getTypedefDeclUnderlyingType(decl);
+ decl = clang_getTypeDeclaration(inheritedType);
+ }
+ }
+ return {inheritedType, decl};
+}
+
// Add a base class to the current class from CXCursor_CXXBaseSpecifier
void BuilderPrivate::addBaseClass(const CXCursor &cursor)
{
- const CXType inheritedType = clang_getCursorType(cursor); // Note spelling has "struct baseClass",
- QString baseClassName = getTypeName(inheritedType); // use type.
- const CXCursor declCursor = clang_getTypeDeclaration(inheritedType);
- const CursorClassHash::const_iterator it = m_cursorClassHash.constFind(declCursor);
+ Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier);
+ // Note: spelling has "struct baseClass", use type
+ QString baseClassName;
+ const auto decl = resolveBaseSpecifier(cursor);
+ 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);
+
+ auto it = m_cursorClassHash.constFind(decl.declaration);
const CodeModel::AccessPolicy access = accessPolicy(clang_getCXXAccessSpecifier(cursor));
if (it == m_cursorClassHash.constEnd()) {
// Set unqualified name. This happens in cases like "class X : public std::list<...>"
@@ -655,6 +723,17 @@ void BuilderPrivate::qualifyTypeDef(const CXCursor &typeRefCursor, const QShared
}
}
+void BuilderPrivate::setFileName(const CXCursor &cursor, _CodeModelItem *item)
+{
+ const SourceRange range = getCursorRange(cursor);
+ QString file = m_baseVisitor->getFileName(range.first.file);
+ if (!file.isEmpty()) { // Has been observed to be 0 for invalid locations
+ item->setFileName(QDir::cleanPath(file));
+ item->setStartPosition(int(range.first.line), int(range.first.column));
+ item->setEndPosition(int(range.second.line), int(range.second.column));
+ }
+}
+
Builder::Builder()
{
d = new BuilderPrivate(this);
@@ -665,30 +744,76 @@ Builder::~Builder()
delete d;
}
-static inline bool compareHeaderName(const char *haystack, const char *needle)
+static const char *cBaseName(const char *fileName)
{
- const char *lastSlash = strrchr(haystack, '/');
+ const char *lastSlash = std::strrchr(fileName, '/');
#ifdef Q_OS_WIN
if (lastSlash == nullptr)
- lastSlash = strrchr(haystack, '\\');
+ lastSlash = std::strrchr(fileName, '\\');
#endif
- if (lastSlash == nullptr)
- lastSlash = haystack;
- else
- ++lastSlash;
+ return lastSlash != nullptr ? (lastSlash + 1) : fileName;
+}
+
+static inline bool cCompareFileName(const char *f1, const char *f2)
+{
#ifdef Q_OS_WIN
- return _stricmp(lastSlash, needle) == 0;
+ return _stricmp(f1, f2) == 0;
#else
- return strcmp(lastSlash, needle) == 0;
+ return std::strcmp(f1, f2) == 0;
#endif
}
#ifdef Q_OS_UNIX
-static bool cStringStartsWith(const char *prefix, const char *str)
+template<size_t N>
+static bool cStringStartsWith(const char *str, const char (&prefix)[N])
+{
+ return std::strncmp(prefix, str, N - 1) == 0;
+}
+#endif
+
+static bool cStringStartsWith(const char *str, const QByteArray &prefix)
{
- return strncmp(prefix, str, strlen(prefix)) == 0;
+ return std::strncmp(prefix.constData(), str, int(prefix.size())) == 0;
}
+
+bool BuilderPrivate::visitHeader(const char *cFileName) 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;
+ }
+#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;
+ }
+ }
+ for (const auto &systemIncludePath : m_systemIncludePaths) {
+ if (cStringStartsWith(cFileName, systemIncludePath))
+ return true;
+ }
+ return false;
+}
bool Builder::visitLocation(const CXSourceLocation &location) const
{
@@ -701,34 +826,30 @@ bool Builder::visitLocation(const CXSourceLocation &location) const
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)) {
- // Resolve OpenGL typedefs although the header is considered a system header.
- const bool visitHeader = compareHeaderName(cFileName, "gl.h")
-#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
- || cStringStartsWith("/usr/include/stdint.h", cFileName)
-#endif
-#if defined(Q_OS_LINUX)
- || cStringStartsWith("/usr/include/stdlib.h", cFileName)
- || cStringStartsWith("/usr/include/sys/types.h", cFileName)
-#elif defined(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.
- || compareHeaderName(cFileName, "gltypes.h")
- || cStringStartsWith("/usr/include/_types", cFileName)
- || cStringStartsWith("/usr/include/sys/_types", cFileName)
-#endif
- ;
+ result = d->visitHeader(cFileName);
clang_disposeString(cxFileName);
- if (visitHeader)
- return true;
}
- return false;
+ return result;
+}
+
+void Builder::setSystemIncludes(const QByteArrayList &systemIncludes)
+{
+ for (const auto &i : systemIncludes) {
+ if (i.endsWith('/'))
+ d->m_systemIncludePaths.append(i);
+ else
+ d->m_systemIncludes.append(i);
+ }
}
FileModelItem Builder::dom() const
{
Q_ASSERT(!d->m_scopeStack.isEmpty());
- return qSharedPointerDynamicCast<_FileModelItem>(d->m_scopeStack.constFirst());
+ auto rootScope = d->m_scopeStack.constFirst();
+ rootScope->purgeClassDeclarations();
+ return qSharedPointerDynamicCast<_FileModelItem>(rootScope);
}
static QString msgOutOfOrder(const CXCursor &cursor, const char *expectedScope)
@@ -748,6 +869,30 @@ static CodeModel::ClassType codeModelClassTypeFromCursor(CXCursorKind kind)
return result;
}
+static NamespaceType namespaceType(const CXCursor &cursor)
+{
+ if (clang_Cursor_isAnonymous(cursor))
+ return NamespaceType::Anonymous;
+#if CINDEX_VERSION_MAJOR > 0 || CINDEX_VERSION_MINOR >= 59
+ if (clang_Cursor_isInlineNamespace(cursor))
+ return NamespaceType::Inline;
+#endif
+ return NamespaceType::Default;
+}
+
+static QString enumType(const CXCursor &cursor)
+{
+ QString name = getCursorSpelling(cursor); // "enum Foo { v1, v2 };"
+ 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")))
+ name.clear();
+ }
+ return name;
+}
+
BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
{
switch (cursor.kind) {
@@ -790,7 +935,7 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
d->m_scope.back() += templateBrackets();
break;
case CXCursor_EnumDecl: {
- QString name = getCursorSpelling(cursor);
+ QString name = enumType(cursor);
EnumKind kind = CEnum;
if (name.isEmpty()) {
kind = AnonymousEnum;
@@ -803,7 +948,7 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
kind = EnumClass;
}
d->m_currentEnum.reset(new _EnumModelItem(d->m_model, name));
- setFileName(cursor, d->m_currentEnum.data());
+ d->setFileName(cursor, d->m_currentEnum.data());
d->m_currentEnum->setScope(d->m_scope);
d->m_currentEnum->setEnumKind(kind);
d->m_currentEnum->setSigned(isSigned(clang_getEnumDeclIntegerType(cursor).kind));
@@ -832,7 +977,7 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
break;
case CXCursor_VarDecl:
// static class members are seen as CXCursor_VarDecl
- if (!d->m_currentClass.isNull() && isClassCursor(clang_getCursorSemanticParent(cursor))) {
+ if (isClassOrNamespaceCursor(clang_getCursorSemanticParent(cursor))) {
d->addField(cursor);
d->m_currentField->setStatic(true);
}
@@ -873,6 +1018,9 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
d->m_scopeStack.back()->addFunction(d->m_currentFunction);
break;
case CXCursor_Namespace: {
+ const auto type = namespaceType(cursor);
+ if (type == NamespaceType::Anonymous)
+ return Skip;
const QString name = getCursorSpelling(cursor);
const NamespaceModelItem parentNamespaceItem = qSharedPointerDynamicCast<_NamespaceModelItem>(d->m_scopeStack.back());
if (parentNamespaceItem.isNull()) {
@@ -887,8 +1035,9 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
// in subsequent modules.
NamespaceModelItem namespaceItem = parentNamespaceItem->findNamespace(name);
namespaceItem.reset(new _NamespaceModelItem(d->m_model, name));
- setFileName(cursor, namespaceItem.data());
+ d->setFileName(cursor, namespaceItem.data());
namespaceItem->setScope(d->m_scope);
+ namespaceItem->setType(type);
parentNamespaceItem->addNamespace(namespaceItem);
d->pushScope(namespaceItem);
}
@@ -917,6 +1066,8 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
// Apply to function/member template?
if (!d->m_currentFunction.isNull()) {
d->m_currentFunction->setTemplateParameters(d->m_currentFunction->templateParameters() << tItem);
+ } else if (!d->m_currentTemplateTypeAlias.isNull()) {
+ d->m_currentTemplateTypeAlias->addTemplateParameter(tItem);
} else if (!d->m_currentClass.isNull()) { // Apply to class
const QString &tplParmName = tItem->name();
if (Q_UNLIKELY(!insertTemplateParameterIntoClassName(tplParmName, d->m_currentClass)
@@ -932,15 +1083,27 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
}
}
break;
- case CXCursor_TypeAliasDecl:
- case CXCursor_TypeAliasTemplateDecl: { // May contain nested CXCursor_TemplateTypeParameter
- const CXType type = clang_getCanonicalType(clang_getCursorType(cursor));
- if (type.kind > CXType_Unexposed)
- d->addTypeDef(cursor, d->createTypeInfo(type));
+ case CXCursor_TypeAliasTemplateDecl:
+ d->startTemplateTypeAlias(cursor);
+ break;
+ case CXCursor_TypeAliasDecl: // May contain nested CXCursor_TemplateTypeParameter
+ if (d->m_currentTemplateTypeAlias.isNull()) {
+ const CXType type = clang_getCanonicalType(clang_getCursorType(cursor));
+ if (type.kind > CXType_Unexposed)
+ d->addTypeDef(cursor, type);
+ return Skip;
+ } else {
+ d->endTemplateTypeAlias(cursor);
+ }
+ break;
+ case CXCursor_TypedefDecl: {
+ auto underlyingType = clang_getTypedefDeclUnderlyingType(cursor);
+ d->addTypeDef(cursor, underlyingType);
+ // For "typedef enum/struct {} Foo;", skip the enum/struct
+ // definition nested into the typedef (PYSIDE-1228).
+ if (underlyingType.kind == CXType_Elaborated)
+ return Skip;
}
- return Skip;
- case CXCursor_TypedefDecl:
- d->addTypeDef(cursor, d->createTypeInfo(clang_getTypedefDeclUnderlyingType(cursor)));
break;
case CXCursor_TypeRef:
if (!d->m_currentFunction.isNull()) {
@@ -962,6 +1125,19 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
if (!d->m_currentFunction.isNull())
d->m_currentFunction->setOverride(true);
break;
+ case CXCursor_StaticAssert:
+ // Check for Q_PROPERTY() (see PySide2/global.h.in for an explanation
+ // how it is defined, and qdoc).
+ if (clang_isDeclaration(cursor.kind) && !d->m_currentClass.isNull()) {
+ auto snippet = getCodeSnippet(cursor);
+ const auto length = snippet.second - snippet.first;
+ if (length > 12 && *(snippet.second - 1) == ')'
+ && std::strncmp(snippet.first, "Q_PROPERTY(", 11) == 0) {
+ const QString qProperty = QString::fromUtf8(snippet.first + 11, length - 12);
+ d->m_currentClass->addPropertyDeclaration(qProperty);
+ }
+ }
+ break;
default:
break;
}
@@ -1011,6 +1187,9 @@ bool Builder::endToken(const CXCursor &cursor)
case CXCursor_ParmDecl:
d->m_currentArgument.clear();
break;
+ case CXCursor_TypeAliasTemplateDecl:
+ d->m_currentTemplateTypeAlias.reset();
+ break;
default:
break;
}