diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp | 1296 |
1 files changed, 1296 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp new file mode 100644 index 000000000..31e7efb05 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp @@ -0,0 +1,1296 @@ +// 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> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStack> +#include <QtCore/QList> + +#include <cstring> +#include <ctype.h> + +using namespace Qt::StringLiterals; + +namespace clang { + +static inline bool isClassCursor(const CXCursor &c) +{ + return c.kind == CXCursor_ClassDecl || c.kind == CXCursor_StructDecl + || c.kind == CXCursor_ClassTemplate + || 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)); +} + +static QString fixTypeName(QString t) +{ + // Fix "Foo &" -> "Foo&", similarly "Bar **" -> "Bar**" + 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; +} + +// Insert template parameter to class name: "Foo<>" -> "Foo<T1>" -> "Foo<T1,T2>" +// This needs to be done immediately when template parameters are encountered since +// 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(u'>'))) + return false; + const bool needsComma = name->at(name->size() - 2) != u'<'; + const auto insertionPos = name->size() - 1; + name->insert(insertionPos, parmName); + if (needsComma) + name->insert(insertionPos, u','); + return true; +} + +static inline bool insertTemplateParameterIntoClassName(const QString &parmName, + const ClassModelItem &item) +{ + QString name = item->name(); + const bool result = insertTemplateParameterIntoClassName(parmName, &name); + item->setName(name); + return result; +} + +static inline Access accessPolicy(CX_CXXAccessSpecifier access) +{ + Access result = Access::Public; + switch (access) { + case CX_CXXProtected: + result = Access::Protected; + break; + case CX_CXXPrivate: + result = Access::Private; + break; + default: + break; + } + return result; +} + +static bool isSigned(CXTypeKind kind) +{ + switch (kind) { + case CXType_UChar: + case CXType_Char16: + case CXType_Char32: + case CXType_UShort: + case CXType_UInt: + case CXType_ULong: + case CXType_ULongLong: + case CXType_UInt128: + return false; + default: + break; + } + return true; +} + +class BuilderPrivate { +public: + Q_DISABLE_COPY_MOVE(BuilderPrivate) + + enum class SpecialSystemHeader { + None, + Types, + OpenGL, + WhiteListed, + WhiteListedPath + }; + + using CursorClassHash = QHash<CXCursor, ClassModelItem>; + 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 + // appears in the translation unit while the scope is the outer class. + void updateScope() + { + if (m_scopeStack.size() <= 1) + m_scope.clear(); + else + m_scope = m_scopeStack.back()->scope() << m_scopeStack.back()->name(); + } + + void pushScope(const ScopeModelItem &i) + { + m_scopeStack.push(i); + updateScope(); + } + + 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, + bool isTemplateCode = false); + FunctionModelItem createMemberFunction(const CXCursor &cursor, + bool isTemplateCode = false); + void qualifyConstructor(const CXCursor &cursor); + TypeInfo createTypeInfoUncached(const CXType &type, + bool *cacheable = nullptr) const; + TypeInfo createTypeInfo(const CXType &type) const; + TypeInfo createTypeInfo(const CXCursor &cursor) const + { return createTypeInfo(clang_getCursorType(cursor)); } + void addTemplateInstantiations(const CXType &type, + QString *typeName, + TypeInfo *t) const; + bool addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const; + + void addTypeDef(const CXCursor &cursor, const CXType &cxType); + ClassModelItem currentTemplateClass() const; + void startTemplateTypeAlias(const CXCursor &cursor); + void endTemplateTypeAlias(const CXCursor &typeAliasCursor); + + TemplateParameterModelItem createTemplateParameter(const CXCursor &cursor) const; + TemplateParameterModelItem createNonTypeTemplateParameter(const CXCursor &cursor) const; + void addField(const CXCursor &cursor); + + static QString cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor); + std::pair<QString, ClassModelItem> getBaseClass(CXType type) const; + void addBaseClass(const CXCursor &cursor); + + 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); + + BaseVisitor *m_baseVisitor; + CodeModel *m_model; + + QStack<ScopeModelItem> m_scopeStack; + QStringList m_scope; + // Store all classes by cursor so that base classes can be found and inner + // classes can be correctly parented in case of forward-declared inner classes + // (QMetaObject::Connection) + CursorClassHash m_cursorClassHash; + + 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; + 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.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: + // class QMetaObject { class Connection; } + // class QMetaObject::Connection {} + const CXCursor semPar = clang_getCursorSemanticParent(cursor); + if (isClassCursor(semPar)) { + const CursorClassHash::const_iterator it = m_cursorClassHash.constFind(semPar); + if (it == m_cursorClassHash.constEnd()) { + QString message; + QTextStream(&message) << "Unable to find containing class \"" + << getCursorSpelling(semPar) << "\" of inner class \"" + << className << "\"."; + // PYSIDE-1501: Has been observed to fail for inner class of + // template with separated implementation where a forward + // declaration of the outer template is reported (Boost). + const auto severity = semPar.kind == CXCursor_ClassTemplate + ? CXDiagnostic_Warning : CXDiagnostic_Error; + const Diagnostic d(message, cursor, severity); + qWarning() << d; + m_baseVisitor->appendDiagnostic(d); + return false; + } + const ClassModelItem &containingClass = it.value(); + containingClass->addClass(m_currentClass); + m_currentClass->setScope(containingClass->scope() << containingClass->name()); + } else { + m_currentClass->setScope(m_scope); + m_scopeStack.back()->addClass(m_currentClass); + } + pushScope(m_currentClass); + m_cursorClassHash.insert(cursor, m_currentClass); + return true; +} + +static QString msgCannotDetermineException(const std::string_view &snippetV) +{ + const auto newLine = snippetV.find('\n'); // Multiline noexcept specifications have been found in Qt + const bool truncate = newLine != std::string::npos; + const qsizetype length = qsizetype(truncate ? newLine : snippetV.size()); + QString snippet = QString::fromUtf8(snippetV.data(), length); + if (truncate) + snippet += "..."_L1; + + return u"Cannot determine exception specification: \""_s + snippet + u'"'; +} + +// Return whether noexcept(<value>) throws. noexcept() takes a constexpr value. +// Try to determine the simple cases (true|false) via code snippet. +static ExceptionSpecification computedExceptionSpecificationFromClang(BaseVisitor *bv, + const CXCursor &cursor, + bool isTemplateCode) +{ + const std::string_view snippet = bv->getCodeSnippet(cursor); + if (snippet.empty()) + return ExceptionSpecification::Unknown; // Macro expansion, cannot tell + if (snippet.find("noexcept(false)") != std::string::npos) + return ExceptionSpecification::Throws; + if (snippet.find("noexcept(true)") != std::string::npos) + return ExceptionSpecification::NoExcept; + // Warn about it unless it is some form of template code where it is common + // to have complicated code, which is of no concern to shiboken, like: + // "QList::emplace(T) noexcept(is_pod<T>)". + if (!isTemplateCode && ReportHandler::isDebug(ReportHandler::FullDebug)) { + const Diagnostic d(msgCannotDetermineException(snippet), cursor, CXDiagnostic_Warning); + qWarning() << d; + bv->appendDiagnostic(d); + } + return ExceptionSpecification::Unknown; +} + +static ExceptionSpecification exceptionSpecificationFromClang(BaseVisitor *bv, + const CXCursor &cursor, + bool isTemplateCode) +{ + const auto ce = clang_getCursorExceptionSpecificationType(cursor); + switch (ce) { + case CXCursor_ExceptionSpecificationKind_ComputedNoexcept: + return computedExceptionSpecificationFromClang(bv, cursor, isTemplateCode); + case CXCursor_ExceptionSpecificationKind_BasicNoexcept: + case CXCursor_ExceptionSpecificationKind_DynamicNone: // throw() + case CXCursor_ExceptionSpecificationKind_NoThrow: + return ExceptionSpecification::NoExcept; + case CXCursor_ExceptionSpecificationKind_Dynamic: // throw(t1..) + case CXCursor_ExceptionSpecificationKind_MSAny: // throw(...) + return ExceptionSpecification::Throws; + default: + // CXCursor_ExceptionSpecificationKind_None, + // CXCursor_ExceptionSpecificationKind_Unevaluated, + // CXCursor_ExceptionSpecificationKind_Uninstantiated + break; + } + return ExceptionSpecification::Unknown; +} + +FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor, + CodeModel::FunctionType t, + bool isTemplateCode) +{ + QString name = getCursorSpelling(cursor); + // Apply type fixes to "operator X &" -> "operator X&" + if (name.startsWith(u"operator ")) + name = fixTypeName(name); + 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); + result->setExceptionSpecification(exceptionSpecificationFromClang(m_baseVisitor, cursor, isTemplateCode)); + switch (clang_getCursorAvailability(cursor)) { + case CXAvailability_Available: + break; + case CXAvailability_Deprecated: + result->setAttribute(FunctionAttribute::Deprecated); + break; + case CXAvailability_NotAvailable: // "Foo(const Foo&) = delete;" + result->setDeleted(true); + break; + case CXAvailability_NotAccessible: + break; + } + return result; +} + +static inline CodeModel::FunctionType functionTypeFromCursor(const CXCursor &cursor) +{ + CodeModel::FunctionType result = CodeModel::Normal; + switch (cursor.kind) { + case CXCursor_Constructor: + if (clang_CXXConstructor_isCopyConstructor(cursor) != 0) + result = CodeModel::CopyConstructor; + else if (clang_CXXConstructor_isMoveConstructor(cursor) != 0) + result = CodeModel::MoveConstructor; + else + result = CodeModel::Constructor; + break; + case CXCursor_Destructor: + result = CodeModel::Destructor; + break; + default: + break; + } + return result; +} + +FunctionModelItem BuilderPrivate::createMemberFunction(const CXCursor &cursor, + bool isTemplateCode) +{ + const CodeModel::FunctionType functionType = + m_currentFunctionType == CodeModel::Signal || m_currentFunctionType == CodeModel::Slot + ? m_currentFunctionType // by annotation + : functionTypeFromCursor(cursor); + 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->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; +} + +// For CXCursor_Constructor, on endToken(). +void BuilderPrivate::qualifyConstructor(const CXCursor &cursor) +{ + // Clang does not tell us whether a constructor is explicit, preventing it + // from being used for implicit conversions. Try to guess whether a + // constructor is explicit in the C++99 sense (1 parameter) by checking for + // isConvertingConstructor() == 0. Fixme: The notion of "isConvertingConstructor" + // should be used in the code model instead of "explicit" + if (clang_CXXConstructor_isDefaultConstructor(cursor) == 0 + && m_currentFunction->arguments().size() == 1 + && clang_CXXConstructor_isCopyConstructor(cursor) == 0 + && clang_CXXConstructor_isMoveConstructor(cursor) == 0) { + m_currentFunction->setAttribute(FunctionAttribute::Explicit, + clang_CXXConstructor_isConvertingConstructor(cursor) == 0); + } +} + +TemplateParameterModelItem BuilderPrivate::createTemplateParameter(const CXCursor &cursor) const +{ + return std::make_shared<_TemplateParameterModelItem>(m_model, getCursorSpelling(cursor)); +} + +TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const CXCursor &cursor) const +{ + TemplateParameterModelItem result = createTemplateParameter(cursor); + result->setType(createTypeInfo(clang_getCursorType(cursor))); + return result; +} + +// CXCursor_VarDecl, CXCursor_FieldDecl cursors +void BuilderPrivate::addField(const CXCursor &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); +} + +// Create qualified name "std::list<std::string>" -> ("std", "list<std::string>") +static QStringList qualifiedName(const QString &t) +{ + QStringList result; + int end = t.indexOf(u'<'); + if (end == -1) + end = t.indexOf(u'('); + if (end == -1) + end = t.size(); + int lastPos = 0; + while (true) { + const int nextPos = t.indexOf(u"::"_s, lastPos); + if (nextPos < 0 || nextPos >= end) + break; + result.append(t.mid(lastPos, nextPos - lastPos)); + lastPos = nextPos + 2; + } + result.append(t.right(t.size() - lastPos)); + return result; +} + +static bool isArrayType(CXTypeKind k) +{ + return k == CXType_ConstantArray || k == CXType_IncompleteArray + || k == CXType_VariableArray || k == CXType_DependentSizedArray; +} + +static bool isPointerType(CXTypeKind k) +{ + return k == CXType_Pointer || k == CXType_LValueReference || k == CXType_RValueReference; +} + +bool BuilderPrivate::addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const +{ + // Template arguments + switch (type.kind) { + case CXType_Elaborated: + case CXType_Record: + case CXType_Unexposed: + if (const int numTemplateArguments = qMax(0, clang_Type_getNumTemplateArguments(type))) { + for (unsigned tpl = 0; tpl < unsigned(numTemplateArguments); ++tpl) { + const CXType argType = clang_Type_getTemplateArgumentAsType(type, tpl); + // CXType_Invalid is returned when hitting on a specialization + // of a non-type template (template <int v>). + if (argType.kind == CXType_Invalid) + return false; + t->addInstantiation(createTypeInfoUncached(argType)); + } + } + break; + default: + break; + } + return true; +} + +static void dummyTemplateArgumentHandler(int, QStringView) {} + +void BuilderPrivate::addTemplateInstantiations(const CXType &type, + QString *typeName, + TypeInfo *t) const +{ + // In most cases, for templates like "Vector<A>", Clang will give us the + // arguments by recursing down the type. However this will fail for example + // within template classes (for functions like the copy constructor): + // template <class T> + // class Vector { + // Vector(const Vector&); + // }; + // In that case, have TypeInfo parse the list from the spelling. + // Finally, remove the list "<>" from the type name. + const bool parsed = addTemplateInstantiationsRecursion(type, t) + && !t->instantiations().isEmpty(); + if (!parsed) + t->setInstantiations({}); + const auto pos = parsed + ? parseTemplateArgumentList(*typeName, dummyTemplateArgumentHandler) + : t->parseTemplateArgumentList(*typeName); + if (pos.first != -1 && pos.second != -1 && pos.second > pos.first) + typeName->remove(pos.first, pos.second - pos.first); +} + +TypeInfo BuilderPrivate::createTypeInfoUncached(const CXType &type, + bool *cacheable) const +{ + if (type.kind == CXType_Pointer) { // Check for function pointers, first. + const CXType pointeeType = clang_getPointeeType(type); + const int argCount = clang_getNumArgTypes(pointeeType); + if (argCount >= 0) { + TypeInfo result = createTypeInfoUncached(clang_getResultType(pointeeType), + cacheable); + result.setFunctionPointer(true); + for (int a = 0; a < argCount; ++a) + result.addArgument(createTypeInfoUncached(clang_getArgType(pointeeType, unsigned(a)), + cacheable)); + return result; + } + } + + TypeInfo typeInfo; + + CXType nestedType = type; + for (; isArrayType(nestedType.kind); nestedType = clang_getArrayElementType(nestedType)) { + const long long size = clang_getArraySize(nestedType); + typeInfo.addArrayElement(size >= 0 ? QString::number(size) : QString()); + } + + TypeInfo::Indirections indirections; + for (; isPointerType(nestedType.kind); nestedType = clang_getPointeeType(nestedType)) { + switch (nestedType.kind) { + case CXType_Pointer: + indirections.prepend(clang_isConstQualifiedType(nestedType) != 0 + ? Indirection::ConstPointer : Indirection::Pointer); + break; + case CXType_LValueReference: + typeInfo.setReferenceType(LValueReference); + break; + case CXType_RValueReference: + typeInfo.setReferenceType(RValueReference); + break; + default: + break; + } + } + typeInfo.setIndirectionsV(indirections); + + typeInfo.setConstant(clang_isConstQualifiedType(nestedType) != 0); + typeInfo.setVolatile(clang_isVolatileQualifiedType(nestedType) != 0); + + QString typeName = getResolvedTypeName(nestedType); + while (TypeInfo::stripLeadingConst(&typeName) + || TypeInfo::stripLeadingVolatile(&typeName)) { + } + + // For typedefs within templates or nested classes within templates (iterators): + // "template <class T> class QList { using Value=T; .." + // 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 && 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 && 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(u'<')) + addTemplateInstantiations(nestedType, &typeName, &typeInfo); + + typeInfo.setQualifiedName(qualifiedName(typeName)); + // 3320:CINDEX_LINKAGE int clang_getNumArgTypes(CXType T); function ptr types? + typeInfo.simplifyStdType(); + return typeInfo; +} + +TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const +{ + const auto it = m_typeInfoHash.constFind(type); + if (it != m_typeInfoHash.constEnd()) + return it.value(); + bool cacheable = true; + TypeInfo result = createTypeInfoUncached(type, &cacheable); + if (cacheable) + m_typeInfoHash.insert(type, result); + return result; +} + +void BuilderPrivate::addTypeDef(const CXCursor &cursor, const CXType &cxType) +{ + const QString target = getCursorSpelling(cursor); + 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); +} + +ClassModelItem BuilderPrivate::currentTemplateClass() const +{ + 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 {}; +} + +void BuilderPrivate::startTemplateTypeAlias(const CXCursor &cursor) +{ + const QString target = getCursorSpelling(cursor); + m_currentTemplateTypeAlias.reset(new _TemplateTypeAliasModelItem(m_model, target)); + setFileName(cursor, m_currentTemplateTypeAlias.get()); + 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 std::string_view snippet = bv->getCodeSnippet(cursor); + auto equalSign = snippet.find('='); + if (equalSign == std::string::npos) + return QString(); + ++equalSign; + 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 +{ + CXType type; + CXCursor declaration; +}; + +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); + 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". +std::pair<QString, ClassModelItem> BuilderPrivate::getBaseClass(CXType type) const +{ + const auto decl = resolveBaseClassType(type); + // Note: spelling has "struct baseClass", use 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, {}}; + + // Completely qualify the class name by looking it up and taking its scope + // plus the actual baseClass stripped off any scopes. Consider: + // namespace std { + // template <class T> class vector {}; + // namespace n { + // class Foo : public vector<int> {}; + // } + // } + // should have "std::vector<int>" as base class (whereas the type of the base class is + // "std::vector<T>"). + const QStringList &baseScope = it.value()->scope(); + if (!baseScope.isEmpty()) { + const int lastSep = baseClassName.lastIndexOf(u"::"_s); + if (lastSep >= 0) + baseClassName.remove(0, lastSep + u"::"_s.size()); + baseClassName.prepend(u"::"_s); + baseClassName.prepend(baseScope.join(u"::"_s)); + } + return {baseClassName, it.value()}; +} + +// Add a base class to the current class from CXCursor_CXXBaseSpecifier +void BuilderPrivate::addBaseClass(const CXCursor &cursor) +{ + Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_CXXBaseSpecifier); + const auto access = accessPolicy(clang_getCXXAccessSpecifier(cursor)); + const auto baseClass = getBaseClass(clang_getCursorType(cursor)); + m_currentClass->addBaseClass({baseClass.first, baseClass.second, access}); +} + +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); +} + +Builder::~Builder() +{ + delete d; +} + +static QString baseName(QString path) +{ + qsizetype lastSlash = path.lastIndexOf(u'/'); +#ifdef Q_OS_WIN + if (lastSlash < 0) + lastSlash = path.lastIndexOf(u'\\'); +#endif + if (lastSlash > 0) + path.remove(0, lastSlash + 1); + return path; +} + +const char * BuilderPrivate::specialSystemHeaderReason(BuilderPrivate::SpecialSystemHeader sh) +{ + 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, ""); +} + +bool BuilderPrivate::visitHeader(const QString &fileName) const +{ + 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; +} + +BuilderPrivate::SpecialSystemHeader + BuilderPrivate::specialSystemHeader(const QString &fileName) const +{ + // Resolve OpenGL typedefs although the header is considered a system header. + 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; + } + + 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; + } + + // 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 SpecialSystemHeader::None; +} + +bool Builder::visitLocation(const QString &fileName, LocationType locationType) const +{ + return locationType != LocationType::System || d->visitHeader(fileName); +} + +void Builder::setForceProcessSystemIncludes(const QStringList &systemIncludes) +{ + for (const auto &i : systemIncludes) { + QFileInfo fi(i); + if (fi.exists() && fi.isDir()) + d->m_forceProcessSystemIncludePaths.append(i); + else + d->m_forceProcessSystemIncludes.append(i); + } +} + +FileModelItem Builder::dom() const +{ + Q_ASSERT(!d->m_scopeStack.isEmpty()); + auto rootScope = d->m_scopeStack.constFirst(); + rootScope->purgeClassDeclarations(); + return std::dynamic_pointer_cast<_FileModelItem>(rootScope); +} + +static QString msgOutOfOrder(const CXCursor &cursor, const char *expectedScope) +{ + return getCursorKindName(cursor.kind) + u' ' + + getCursorSpelling(cursor) + u" encountered outside "_s + + QLatin1StringView(expectedScope) + u'.'; +} + +static CodeModel::ClassType codeModelClassTypeFromCursor(CXCursorKind kind) +{ + CodeModel::ClassType result = CodeModel::Class; + if (kind == CXCursor_UnionDecl) + result = CodeModel::Union; + else if (kind == CXCursor_StructDecl) + result = CodeModel::Struct; + 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.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(u"(unnamed") // Clang 12.0.1 + || name.contains(u"(anonymous")) { // earlier + name.clear(); + } + } + return name; +} + +BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) +{ + switch (cursor.kind) { + case CXCursor_CXXAccessSpecifier: + d->m_currentFunctionType = CodeModel::Normal; + break; + case CXCursor_AnnotateAttr: { + const QString annotation = getCursorSpelling(cursor); + if (annotation == u"qt_slot") + d->m_currentFunctionType = CodeModel::Slot; + 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) { + const Diagnostic d(msgOutOfOrder(cursor, "class"), cursor, CXDiagnostic_Error); + qWarning() << d; + appendDiagnostic(d); + return Error; + } + d->addBaseClass(cursor); + break; + case CXCursor_ClassDecl: + case CXCursor_UnionDecl: + case CXCursor_StructDecl: + if (d->m_withinFriendDecl || clang_isCursorDefinition(cursor) == 0 + || !d->addClass(cursor, codeModelClassTypeFromCursor(cursor.kind))) { + return Skip; + } + break; + case CXCursor_ClassTemplate: + case CXCursor_ClassTemplatePartialSpecialization: + if (d->m_withinFriendDecl || clang_isCursorDefinition(cursor) == 0 + || !d->addClass(cursor, CodeModel::Class)) { + return Skip; + } + 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 = "enum_"_L1 + QString::number(++d->m_anonymousEnumCount); +#if !CLANG_NO_ENUMDECL_ISSCOPED + } else if (clang_EnumDecl_isScoped(cursor) != 0) { +#else + } else if (clang_EnumDecl_isScoped4(this, cursor) != 0) { +#endif + kind = EnumClass; + } + d->m_currentEnum.reset(new _EnumModelItem(d->m_model, name)); + d->setFileName(cursor, d->m_currentEnum.get()); + d->m_currentEnum->setScope(d->m_scope); + d->m_currentEnum->setEnumKind(kind); + 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) { + const Diagnostic d(msgOutOfOrder(cursor, "enum"), cursor, CXDiagnostic_Error); + qWarning() << d; + appendDiagnostic(d); + return Error; + } + EnumValue enumValue; + if (d->m_currentEnum->isSigned()) + enumValue.setValue(clang_getEnumConstantDeclValue(cursor)); + else + enumValue.setUnsignedValue(clang_getEnumConstantDeclUnsignedValue(cursor)); + 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; + case CXCursor_VarDecl: + // static class members are seen as CXCursor_VarDecl + if (isClassOrNamespaceCursor(clang_getCursorSemanticParent(cursor))) { + d->addField(cursor); + d->m_currentField->setStatic(true); + } + break; + case CXCursor_FieldDecl: + d->addField(cursor); + break; + case CXCursor_FriendDecl: + d->m_withinFriendDecl = true; + break; + case CXCursor_CompoundStmt: // Function bodies + return Skip; + case CXCursor_Constructor: + case CXCursor_Destructor: // Note: Also use clang_CXXConstructor_is..Constructor? + case CXCursor_CXXMethod: + case CXCursor_ConversionFunction: + // Member functions of other classes can be declared to be friends. + // Skip inline member functions outside class, only go by declarations inside class + if (d->m_withinFriendDecl || !withinClassDeclaration(cursor)) + return Skip; + d->m_currentFunction = d->createMemberFunction(cursor, false); + d->m_scopeStack.back()->addFunction(d->m_currentFunction); + break; + // Not fully supported, currently, seen as normal function + // Note: May appear inside class (member template) or outside (free template). + case CXCursor_FunctionTemplate: { + const CXCursor semParent = clang_getCursorSemanticParent(cursor); + if (isClassCursor(semParent)) { + if (semParent == clang_getCursorLexicalParent(cursor)) { + d->m_currentFunction = d->createMemberFunction(cursor, true); + d->m_scopeStack.back()->addFunction(d->m_currentFunction); + break; + } + return Skip; // inline member functions outside class + } + } + 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: + // Free functions or functions completely defined within "friend" (class + // operators). Note: CXTranslationUnit_SkipFunctionBodies must be off for + // clang_isCursorDefinition() to work here. + if (!d->m_withinFriendDecl || clang_isCursorDefinition(cursor) != 0) { + 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; + case CXCursor_Namespace: { + const auto type = namespaceType(cursor); + if (type == NamespaceType::Anonymous) + return Skip; + const QString name = getCursorSpelling(cursor); + const auto parentNamespaceItem = std::dynamic_pointer_cast<_NamespaceModelItem>(d->m_scopeStack.back()); + if (!parentNamespaceItem) { + const QString message = msgOutOfOrder(cursor, "namespace") + + u" (current scope: "_s + d->m_scopeStack.back()->name() + u')'; + const Diagnostic d(message, cursor, CXDiagnostic_Error); + qWarning() << d; + appendDiagnostic(d); + return Error; + } + // Treat namespaces separately to allow for extending namespaces + // in subsequent modules. + NamespaceModelItem namespaceItem = parentNamespaceItem->findNamespace(name); + namespaceItem.reset(new _NamespaceModelItem(d->m_model, name)); + d->setFileName(cursor, namespaceItem.get()); + namespaceItem->setScope(d->m_scope); + namespaceItem->setType(type); + parentNamespaceItem->addNamespace(namespaceItem); + d->pushScope(namespaceItem); + } + break; + 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 && d->m_currentFunction) { + const QString name = getCursorSpelling(cursor); + d->m_currentArgument.reset(new _ArgumentModelItem(d->m_model, name)); + 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()) { + d->m_currentArgument->setDefaultValueExpression(defaultValueExpression); + d->m_currentArgument->setDefaultValue(true); + } + } else { + return Skip; + } + break; + case CXCursor_TemplateTypeParameter: + case CXCursor_NonTypeTemplateParameter: { + const TemplateParameterModelItem tItem = cursor.kind == CXCursor_TemplateTemplateParameter + ? d->createTemplateParameter(cursor) : d->createNonTypeTemplateParameter(cursor); + // Apply to function/member template? + if (d->m_currentFunction) { + d->m_currentFunction->setTemplateParameters(d->m_currentFunction->templateParameters() << tItem); + } else if (d->m_currentTemplateTypeAlias) { + d->m_currentTemplateTypeAlias->addTemplateParameter(tItem); + } 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 = "Error inserting template parameter \""_L1 + tplParmName + + "\" into "_L1 + d->m_currentClass->name(); + const Diagnostic d(message, cursor, CXDiagnostic_Error); + qWarning() << d; + appendDiagnostic(d); + return Error; + } + d->m_currentClass->setTemplateParameters(d->m_currentClass->templateParameters() << tItem); + } + } + break; + case CXCursor_TypeAliasTemplateDecl: + d->startTemplateTypeAlias(cursor); + break; + case CXCursor_TypeAliasDecl: // May contain nested CXCursor_TemplateTypeParameter + if (!d->m_currentTemplateTypeAlias) { + 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; + } + break; + // Using declarations look as follows: + // 1) Normal, non-template case ("using QObject::parent"): UsingDeclaration, TypeRef + // 2) Simple template case ("using QList::append()"): UsingDeclaration, TypeRef "QList<T>" + // 3) Template case with parameters ("using QList<T>::append()"): + // UsingDeclaration, TemplateRef "QList", TypeRef "T" + case CXCursor_TemplateRef: + if (d->m_withinUsingDeclaration && d->m_usingTypeRef.isEmpty()) + d->m_usingTypeRef = getCursorSpelling(cursor); + break; + case CXCursor_TypeRef: + 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) + d->m_currentFunction->setAttribute(FunctionAttribute::Final); + else if (d->m_currentClass) + d->m_currentClass->setFinal(true); + break; + case CXCursor_CXXOverrideAttr: + 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) { + auto snippet = getCodeSnippet(cursor); + const auto length = snippet.size(); + if (length > 12 && *snippet.rbegin() == ')' + && snippet.compare(0, 11, "Q_PROPERTY(") == 0) { + const QString qProperty = QString::fromUtf8(snippet.data() + 11, length - 12); + d->m_currentClass->addPropertyDeclaration(qProperty); + } + } + break; + // UsingDeclaration: consists of a TypeRef (base) and OverloadedDeclRef (member name) + case CXCursor_UsingDeclaration: + if (d->m_currentClass) + d->m_withinUsingDeclaration = true; + break; + case CXCursor_OverloadedDeclRef: + if (d->m_withinUsingDeclaration && !d->m_usingTypeRef.isEmpty()) { + QString member = getCursorSpelling(cursor); + if (member == d->m_currentClass->name()) + member = d->m_usingTypeRef; // Overloaded member is Constructor, use base + const auto ap = accessPolicy(clang_getCXXAccessSpecifier(cursor)); + d->m_currentClass->addUsingMember(d->m_usingTypeRef, member, ap); + } + break; + default: + break; + } + return BaseVisitor::Recurse; +} + +bool Builder::endToken(const CXCursor &cursor) +{ + switch (cursor.kind) { + case CXCursor_UnionDecl: + case CXCursor_ClassDecl: + case CXCursor_StructDecl: + case CXCursor_ClassTemplate: + case CXCursor_ClassTemplatePartialSpecialization: + d->popScope(); + // Continue in outer class after leaving inner class? + if (auto lastClass = std::dynamic_pointer_cast<_ClassModelItem>(d->m_scopeStack.back())) + d->m_currentClass = lastClass; + else + d->m_currentClass.reset(); + d->m_currentFunctionType = CodeModel::Normal; + break; + case CXCursor_EnumDecl: + if (d->m_currentEnum) + d->m_scopeStack.back()->addEnum(d->m_currentEnum); + d->m_currentEnum.reset(); + break; + case CXCursor_FriendDecl: + d->m_withinFriendDecl = false; + break; + case CXCursor_VarDecl: + case CXCursor_FieldDecl: + d->m_currentField.reset(); + break; + case CXCursor_Constructor: + d->qualifyConstructor(cursor); + if (d->m_currentFunction) { + d->m_currentFunction->_determineType(); + d->m_currentFunction.reset(); + } + break; + case CXCursor_Destructor: + case CXCursor_CXXMethod: + case CXCursor_FunctionDecl: + case CXCursor_FunctionTemplate: + if (d->m_currentFunction) { + d->m_currentFunction->_determineType(); + d->m_currentFunction.reset(); + } + break; + case CXCursor_ConversionFunction: + if (d->m_currentFunction) { + d->m_currentFunction->setFunctionType(CodeModel::ConversionOperator); + d->m_currentFunction.reset(); + } + break; + case CXCursor_Namespace: + d->popScope(); + break; + case CXCursor_ParmDecl: + d->m_currentArgument.reset(); + break; + case CXCursor_TypeAliasTemplateDecl: + d->m_currentTemplateTypeAlias.reset(); + break; + case CXCursor_UsingDeclaration: + d->m_withinUsingDeclaration = false; + d->m_usingTypeRef.clear(); + break; + default: + break; + } + return true; +} + +} // namespace clang |