aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/ApiExtractor/clangparser
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken2/ApiExtractor/clangparser')
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp858
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangbuilder.h58
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.cpp150
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.h48
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp272
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangparser.h93
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp227
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangutils.h101
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp202
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/compilersupport.h40
10 files changed, 2049 insertions, 0 deletions
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
new file mode 100644
index 000000000..1ed054d91
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
@@ -0,0 +1,858 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "clangbuilder.h"
+#include "compilersupport.h"
+#include "clangutils.h"
+
+#include <codemodel.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QStack>
+#include <QtCore/QVector>
+
+#include <string.h>
+
+#if QT_VERSION < 0x050800
+# define Q_FALLTHROUGH() (void)0
+#endif
+
+namespace clang {
+
+static inline QString colonColon() { return QStringLiteral("::"); }
+static inline QString templateBrackets() { return QStringLiteral("<>"); }
+
+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 withinClassDeclaration(const CXCursor &cursor)
+{
+ return isClassCursor(clang_getCursorLexicalParent(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(' '))
+ 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(QLatin1Char('>'))))
+ return false;
+ const bool needsComma = name->at(name->size() - 2) != QLatin1Char('<');
+ const int insertionPos = name->size() - 1;
+ name->insert(insertionPos, parmName);
+ if (needsComma)
+ name->insert(insertionPos, QLatin1Char(','));
+ 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 CodeModel::AccessPolicy accessPolicy(CX_CXXAccessSpecifier access)
+{
+ CodeModel::AccessPolicy result = CodeModel::Public;
+ switch (access) {
+ case CX_CXXProtected:
+ result = CodeModel::Protected;
+ break;
+ case CX_CXXPrivate:
+ result = CodeModel::Private;
+ break;
+ default:
+ break;
+ }
+ 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));
+ }
+}
+
+class BuilderPrivate {
+public:
+ typedef QHash<CXCursor, ClassModelItem> CursorClassHash;
+ typedef QHash<CXCursor, TypeDefModelItem> CursorTypedefHash;
+
+ explicit BuilderPrivate(BaseVisitor *bv) : m_baseVisitor(bv), m_model(new CodeModel)
+ {
+ m_scopeStack.push(NamespaceModelItem(new _FileModelItem(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.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;
+ void qualifyConstructor(const CXCursor &cursor);
+ TypeInfo createTypeInfo(const CXType &type) const;
+ TypeInfo createTypeInfo(const CXCursor &cursor) const
+ { return createTypeInfo(clang_getCursorType(cursor)); }
+
+ TemplateParameterModelItem createTemplateParameter(const CXCursor &cursor) const;
+ TemplateParameterModelItem createNonTypeTemplateParameter(const CXCursor &cursor) const;
+ void addField(const CXCursor &cursor);
+
+ QString cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor) const;
+ void addBaseClass(const CXCursor &cursor);
+
+ template <class Item>
+ void qualifyTypeDef(const CXCursor &typeRefCursor, const QSharedPointer<Item> &item) const;
+
+ 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;
+ CursorTypedefHash m_cursorTypedefHash;
+
+ ClassModelItem m_currentClass;
+ EnumModelItem m_currentEnum;
+ FunctionModelItem m_currentFunction;
+ ArgumentModelItem m_currentArgument;
+ VariableModelItem m_currentField;
+
+ int m_anonymousEnumCount = 0;
+ CodeModel::FunctionType m_currentFunctionType = CodeModel::Normal;
+};
+
+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());
+ 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()) {
+ const QString message = QStringLiteral("Unable to find parent of inner class ") + className;
+ const Diagnostic d(message, cursor, CXDiagnostic_Error);
+ 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;
+}
+
+FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor,
+ CodeModel::FunctionType t) const
+{
+ QString name = getCursorSpelling(cursor);
+ // Apply type fixes to "operator X &" -> "operator X&"
+ if (name.startsWith(QLatin1String("operator ")))
+ name = fixTypeName(name);
+ FunctionModelItem result(new _FunctionModelItem(m_model, name));
+ setFileName(cursor, result.data());
+ result->setType(createTypeInfo(clang_getCursorResultType(cursor)));
+ result->setFunctionType(t);
+ result->setScope(m_scope);
+ result->setStatic(clang_Cursor_getStorageClass(cursor) == CX_SC_Static);
+ 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) const
+{
+ const CodeModel::FunctionType functionType =
+ m_currentFunctionType == CodeModel::Signal || m_currentFunctionType == CodeModel::Slot
+ ? m_currentFunctionType // by annotation
+ : functionTypeFromCursor(cursor);
+ FunctionModelItem result = createFunction(cursor, functionType);
+ 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);
+ 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->setExplicit(clang_CXXConstructor_isConvertingConstructor(cursor) == 0);
+ }
+}
+
+TemplateParameterModelItem BuilderPrivate::createTemplateParameter(const CXCursor &cursor) const
+{
+ return TemplateParameterModelItem(new _TemplateParameterModelItem(m_model, getCursorSpelling(cursor)));
+}
+
+TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const CXCursor &cursor) const
+{
+ TemplateParameterModelItem result = createTemplateParameter(cursor);
+ result->setType(createTypeInfo(cursor));
+ return result;
+}
+
+// CXCursor_VarDecl, CXCursor_FieldDecl cursors
+void BuilderPrivate::addField(const CXCursor &cursor)
+{
+ VariableModelItem field(new _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);
+ m_currentField = field;
+ m_scopeStack.back()->addVariable(field);
+}
+
+// Array helpers: Parse "a[2][4]" into a list of dimensions
+
+struct ArrayDimensionResult
+{
+ QVector<QStringRef> dimensions;
+ int position;
+};
+
+static ArrayDimensionResult arrayDimensions(const QString &typeName)
+{
+ ArrayDimensionResult result;
+ result.position = typeName.indexOf(QLatin1Char('['));
+ for (int openingPos = result.position; openingPos != -1; ) {
+ const int closingPos = typeName.indexOf(QLatin1Char(']'), openingPos + 1);
+ if (closingPos == -1)
+ break;
+ result.dimensions.append(typeName.midRef(openingPos + 1, closingPos - openingPos - 1));
+ openingPos = typeName.indexOf(QLatin1Char('['), closingPos + 1);
+ }
+ return result;
+}
+
+// Array helpers: Parse "a[2][4]" into a list of dimensions or "" for none
+static QStringList parseArrayArgs(const CXType &type, QString *typeName)
+{
+ const ArrayDimensionResult dimensions = arrayDimensions(*typeName);
+ Q_ASSERT(!dimensions.dimensions.isEmpty());
+
+ QStringList result;
+ // get first dimension from clang, preferably.
+ // "a[]" is seen as pointer by Clang, set special indicator ""
+ const long long size = clang_getArraySize(type);
+ result.append(size >= 0 ? QString::number(size) : QString());
+ // Parse out remaining dimensions
+ for (int i = 1, count = dimensions.dimensions.size(); i < count; ++i)
+ result.append(dimensions.dimensions.at(i).toString());
+ typeName->truncate(dimensions.position);
+ return result;
+}
+
+TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) 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 = createTypeInfo(clang_getResultType(pointeeType));
+ result.setFunctionPointer(true);
+ for (int a = 0; a < argCount; ++a)
+ result.addArgument(createTypeInfo(clang_getArgType(pointeeType, unsigned(a))));
+ return result;
+ }
+ }
+
+ TypeInfo typeInfo;
+ QString typeName = fixTypeName(getTypeName(type));
+
+ int indirections = 0;
+ // "int **"
+ for ( ; typeName.endsWith(QLatin1Char('*')) ; ++indirections)
+ typeName.chop(1);
+ typeInfo.setIndirections(indirections);
+ // "int &&"
+ if (typeName.endsWith(QLatin1String("&&"))) {
+ typeName.chop(2);
+ typeInfo.setReferenceType(RValueReference);
+ } else if (typeName.endsWith(QLatin1Char('&'))) { // "int &"
+ typeName.chop(1);
+ typeInfo.setReferenceType(LValueReference);
+ }
+
+ // "int [3], int[]"
+ if (type.kind == CXType_ConstantArray || type.kind == CXType_IncompleteArray
+ || type.kind == CXType_VariableArray || type.kind == CXType_DependentSizedArray) {
+ typeInfo.setArrayElements(parseArrayArgs(type, &typeName));
+ }
+
+ bool isConstant = clang_isConstQualifiedType(type) != 0;
+ // A "char *const" parameter, is considered to be const-qualified by Clang, but
+ // not in the TypeInfo sense (corresponds to "char *" and not "const char *").
+ if (type.kind == CXType_Pointer && isConstant && typeName.endsWith(QLatin1String("const"))) {
+ typeName.chop(5);
+ typeName = typeName.trimmed();
+ isConstant = false;
+ }
+ // Clang has been observed to return false for "const int .."
+ if (!isConstant && typeName.startsWith(QLatin1String("const "))) {
+ typeName.remove(0, 6);
+ isConstant = true;
+ }
+ typeInfo.setConstant(isConstant);
+
+ // clang_isVolatileQualifiedType() returns true for "volatile int", but not for "volatile int *"
+ if (typeName.startsWith(QLatin1String("volatile "))) {
+ typeName.remove(0, 9);
+ typeInfo.setVolatile(true);
+ }
+
+ typeName = typeName.trimmed();
+
+ typeInfo.setQualifiedName(typeName.split(colonColon()));
+ // 3320:CINDEX_LINKAGE int clang_getNumArgTypes(CXType T); function ptr types?
+ return typeInfo;
+}
+
+// extract an expression from the cursor via source
+// CXCursor_EnumConstantDecl, ParmDecl (a = Flag1 | Flag2)
+QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor) const
+{
+ BaseVisitor::CodeSnippet snippet = bv->getCodeSnippet(cursor);
+ const char *equalSign = std::find(snippet.first, snippet.second, '=');
+ if (equalSign == snippet.second)
+ return QString();
+ ++equalSign;
+ return QString::fromLocal8Bit(equalSign, int(snippet.second - equalSign)).trimmed();
+}
+
+// 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);
+ 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<...>"
+ // "template<class T> class Foo : public T" and standard types like true_type, false_type.
+ m_currentClass->addBaseClass(baseClassName, access);
+ return;
+ }
+ // 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(colonColon());
+ if (lastSep >= 0)
+ baseClassName.remove(0, lastSep + colonColon().size());
+ baseClassName.prepend(colonColon());
+ baseClassName.prepend(baseScope.join(colonColon()));
+ }
+ 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
+{
+ typedef typename CursorTypedefHash::const_iterator ConstIt;
+
+ TypeInfo type = item->type();
+ if (type.qualifiedName().size() == 1) { // item's type is unqualified.
+ const ConstIt 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);
+ }
+ }
+}
+
+Builder::Builder()
+{
+ d = new BuilderPrivate(this);
+}
+
+Builder::~Builder()
+{
+ delete d;
+}
+
+static inline bool compareHeaderName(const char *haystack, const char *needle)
+{
+ const char *lastSlash = strrchr(haystack, '/');
+#ifdef Q_OS_WIN
+ if (lastSlash == nullptr)
+ lastSlash = strrchr(haystack, '\\');
+#endif
+ if (lastSlash == nullptr)
+ lastSlash = haystack;
+ else
+ ++lastSlash;
+#ifdef Q_OS_WIN
+ return _stricmp(lastSlash, needle) == 0;
+#else
+ return strcmp(lastSlash, needle) == 0;
+#endif
+}
+
+#ifdef Q_OS_UNIX
+static bool cStringStartsWith(const char *prefix, const char *str)
+{
+ return strncmp(prefix, str, strlen(prefix)) == 0;
+}
+#endif
+
+bool Builder::visitLocation(const CXSourceLocation &location) 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
+ 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
+ ;
+ clang_disposeString(cxFileName);
+ if (visitHeader)
+ return true;
+ }
+ return false;
+}
+
+FileModelItem Builder::dom() const
+{
+ Q_ASSERT(!d->m_scopeStack.isEmpty());
+ return qSharedPointerDynamicCast<_FileModelItem>(d->m_scopeStack.constFirst());
+}
+
+static QString msgOutOfOrder(const CXCursor &cursor, const char *expectedScope)
+{
+ return getCursorKindName(cursor.kind) + QLatin1Char(' ')
+ + getCursorSpelling(cursor) + QLatin1String(" encountered outside ")
+ + QLatin1String(expectedScope) + QLatin1Char('.');
+}
+
+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;
+}
+
+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 == QLatin1String("qt_slot"))
+ d->m_currentFunctionType = CodeModel::Slot;
+ else if (annotation == QLatin1String("qt_signal"))
+ d->m_currentFunctionType = CodeModel::Signal;
+ else
+ d->m_currentFunctionType = CodeModel::Normal;
+ }
+ break;
+ case CXCursor_CXXBaseSpecifier:
+ if (d->m_currentClass.isNull()) {
+ 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 (clang_isCursorDefinition(cursor) == 0)
+ return Skip;
+ if (!d->addClass(cursor, codeModelClassTypeFromCursor(cursor.kind)))
+ return Error;
+ break;
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ if (clang_isCursorDefinition(cursor) == 0)
+ return Skip;
+ d->addClass(cursor, CodeModel::Class);
+ d->m_currentClass->setName(d->m_currentClass->name() + templateBrackets());
+ d->m_scope.back() += templateBrackets();
+ break;
+ case CXCursor_EnumDecl: {
+ QString name = getCursorSpelling(cursor);
+ const bool anonymous = name.isEmpty();
+ if (anonymous)
+ name = QStringLiteral("enum_") + QString::number(++d->m_anonymousEnumCount);
+ d->m_currentEnum.reset(new _EnumModelItem(d->m_model, name));
+ setFileName(cursor, d->m_currentEnum.data());
+ d->m_currentEnum->setScope(d->m_scope);
+ d->m_currentEnum->setAnonymous(anonymous);
+ if (!qSharedPointerDynamicCast<_ClassModelItem>(d->m_scopeStack.back()).isNull())
+ d->m_currentEnum->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor)));
+ d->m_scopeStack.back()->addEnum(d->m_currentEnum);
+ }
+ break;
+ case CXCursor_EnumConstantDecl: {
+ const QString name = getCursorSpelling(cursor);
+ if (d->m_currentEnum.isNull()) {
+ const Diagnostic d(msgOutOfOrder(cursor, "enum"), cursor, CXDiagnostic_Error);
+ qWarning() << d;
+ appendDiagnostic(d);
+ return Error;
+ }
+ EnumeratorModelItem enumConstant(new _EnumeratorModelItem(d->m_model, name));
+ enumConstant->setValue(d->cursorValueExpression(this, cursor));
+ d->m_currentEnum->addEnumerator(enumConstant);
+ }
+ break;
+ case CXCursor_VarDecl:
+ // static class members are seen as CXCursor_VarDecl
+ if (!d->m_currentClass.isNull() && isClassCursor(clang_getCursorSemanticParent(cursor))) {
+ d->addField(cursor);
+ d->m_currentField->setStatic(true);
+ }
+ break;
+ case CXCursor_FieldDecl:
+ d->addField(cursor);
+ break;
+#if CINDEX_VERSION_MAJOR > 0 || CINDEX_VERSION_MINOR >= 37 // Clang 4.0
+ case CXCursor_FriendDecl:
+ return Skip;
+#endif
+ case CXCursor_Constructor:
+ case CXCursor_Destructor: // Note: Also use clang_CXXConstructor_is..Constructor?
+ case CXCursor_CXXMethod:
+ case CXCursor_ConversionFunction:
+ // Skip inline member functions outside class, only go by declarations inside class
+ if (!withinClassDeclaration(cursor))
+ return Skip;
+ d->m_currentFunction = d->createMemberFunction(cursor);
+ 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);
+ d->m_scopeStack.back()->addFunction(d->m_currentFunction);
+ break;
+ } else {
+ return Skip; // inline member functions outside class
+ }
+ }
+ }
+ Q_FALLTHROUGH(); // fall through to free template function.
+ case CXCursor_FunctionDecl:
+ d->m_currentFunction = d->createFunction(cursor);
+ d->m_scopeStack.back()->addFunction(d->m_currentFunction);
+ break;
+ case CXCursor_Namespace: {
+ const QString name = getCursorSpelling(cursor);
+ const NamespaceModelItem parentNamespaceItem = qSharedPointerDynamicCast<_NamespaceModelItem>(d->m_scopeStack.back());
+ if (parentNamespaceItem.isNull()) {
+ const QString message = msgOutOfOrder(cursor, "namespace")
+ + QLatin1String(" (current scope: ") + d->m_scopeStack.back()->name() + QLatin1Char(')');
+ const Diagnostic d(message, cursor, CXDiagnostic_Error);
+ qWarning() << d;
+ appendDiagnostic(d);
+ return Error;
+ }
+ // If possible, continue existing namespace (as otherwise, all headers
+ // where a namespace is continued show up in the type database).
+ NamespaceModelItem namespaceItem = parentNamespaceItem->findNamespace(name);
+ if (namespaceItem.isNull()) {
+ namespaceItem.reset(new _NamespaceModelItem(d->m_model, name));
+ setFileName(cursor, namespaceItem.data());
+ namespaceItem->setScope(d->m_scope);
+ 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.isNull() && !d->m_currentFunction.isNull()) {
+ const QString name = getCursorSpelling(cursor);
+ d->m_currentArgument.reset(new _ArgumentModelItem(d->m_model, name));
+ d->m_currentArgument->setType(d->createTypeInfo(cursor));
+ 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.isNull()) {
+ d->m_currentFunction->setTemplateParameters(d->m_currentFunction->templateParameters() << tItem);
+ } else if (!d->m_currentClass.isNull()) { // 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 Diagnostic d(message, cursor, CXDiagnostic_Error);
+ qWarning() << d;
+ appendDiagnostic(d);
+ return Error;
+ }
+ d->m_currentClass->setTemplateParameters(d->m_currentClass->templateParameters() << tItem);
+ }
+ }
+ break;
+ case CXCursor_TypeAliasDecl:
+ case CXCursor_TypeAliasTemplateDecl: // May contain nested CXCursor_TemplateTypeParameter
+ return Skip;
+ case CXCursor_TypedefDecl: {
+ const QString name = getCursorSpelling(cursor);
+ TypeDefModelItem item(new _TypeDefModelItem(d->m_model, name));
+ setFileName(cursor, item.data());
+ item->setType(d->createTypeInfo(clang_getTypedefDeclUnderlyingType(cursor)));
+ item->setScope(d->m_scope);
+ d->m_scopeStack.back()->addTypeDef(item);
+ d->m_cursorTypedefHash.insert(cursor, item);
+ }
+ 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);
+ }
+ break;
+ case CXCursor_CXXFinalAttr:
+ if (!d->m_currentFunction.isNull())
+ d->m_currentFunction->setFinal(true);
+ else if (!d->m_currentClass.isNull())
+ d->m_currentClass->setFinal(true);
+ break;
+ case CXCursor_CXXOverrideAttr:
+ if (!d->m_currentFunction.isNull())
+ d->m_currentFunction->setOverride(true);
+ 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 (ClassModelItem lastClass = qSharedPointerDynamicCast<_ClassModelItem>(d->m_scopeStack.back()))
+ d->m_currentClass = lastClass;
+ else
+ d->m_currentClass.clear();
+ d->m_currentFunctionType = CodeModel::Normal;
+ break;
+ case CXCursor_EnumDecl:
+ d->m_currentEnum.clear();
+ break;
+ case CXCursor_VarDecl:
+ case CXCursor_FieldDecl:
+ d->m_currentField.clear();
+ break;
+ case CXCursor_Constructor:
+ d->qualifyConstructor(cursor);
+ d->m_currentFunction.clear();
+ break;
+ case CXCursor_Destructor:
+ case CXCursor_CXXMethod:
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ d->m_currentFunction.clear();
+ break;
+ case CXCursor_Namespace:
+ d->popScope();
+ break;
+ case CXCursor_ParmDecl:
+ d->m_currentArgument.clear();
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+} // namespace clang
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.h b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.h
new file mode 100644
index 000000000..c97b7d2b7
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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$
+**
+****************************************************************************/
+
+#ifndef CLANGBUILDER_H
+#define CLANGBUILDER_H
+
+#include "clangparser.h"
+
+#include <codemodel_fwd.h>
+
+namespace clang {
+
+class BuilderPrivate;
+
+class Builder : public BaseVisitor {
+public:
+ Builder();
+ ~Builder();
+
+ bool visitLocation(const CXSourceLocation &location) const override;
+
+ StartTokenResult startToken(const CXCursor &cursor) override;
+ bool endToken(const CXCursor &cursor) override;
+
+ FileModelItem dom() const;
+
+private:
+ BuilderPrivate *d;
+};
+
+} // namespace clang
+
+#endif // CLANGBUILDER_H
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.cpp
new file mode 100644
index 000000000..8d0fd098a
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "clangdebugutils.h"
+#include "clangutils.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+#include <string.h>
+
+#ifndef QT_NO_DEBUG_STREAM
+
+#ifdef Q_OS_WIN
+const char pathSep = '\\';
+#else
+const char pathSep = '/';
+#endif
+
+static const char *baseName(const char *fileName)
+{
+ const char *b = strrchr(fileName, pathSep);
+ return b ? b + 1 : fileName;
+}
+
+QDebug operator<<(QDebug s, const CXString &cs)
+{
+ s << clang_getCString(cs);
+ return s;
+}
+
+QDebug operator<<(QDebug s, CXCursorKind cursorKind) // Enum
+{
+ const CXString kindName = clang_getCursorKindSpelling(cursorKind);
+ s << kindName;
+ clang_disposeString(kindName);
+ return s;
+}
+
+static const char *accessSpecsStrings[]
+{
+ // CX_CXXInvalidAccessSpecifier, CX_CXXPublic, CX_CXXProtected, CX_CXXPrivate
+ "invalid", "public", "protected", "private"
+};
+
+QDebug operator<<(QDebug s, CX_CXXAccessSpecifier ac)
+{
+ s << accessSpecsStrings[ac];
+ return s;
+}
+
+QDebug operator<<(QDebug s, const CXType &t)
+{
+ CXString typeSpelling = clang_getTypeSpelling(t);
+ s << typeSpelling;
+ clang_disposeString(typeSpelling);
+ return s;
+}
+
+QDebug operator<<(QDebug s, const CXCursor &cursor)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ s.noquote();
+ const CXCursorKind kind = clang_getCursorKind(cursor);
+ s << kind;
+ if (kind >= CXCursor_FirstInvalid && kind <= CXCursor_LastInvalid)
+ return s;
+ const CXType type = clang_getCursorType(cursor);
+ switch (kind) {
+ case CXCursor_CXXAccessSpecifier:
+ s << ' ' << clang_getCXXAccessSpecifier(cursor);
+ break;
+ case CXCursor_CXXBaseSpecifier:
+ s << ", inherits=\"" << clang::getCursorSpelling(clang_getTypeDeclaration(type)) << '"';
+ break;
+ case CXCursor_CXXMethod:
+ case CXCursor_FunctionDecl:
+ case CXCursor_ConversionFunction:
+ s << ", result type=\"" << clang_getCursorResultType(cursor) << '"';
+ break;
+ case CXCursor_TypedefDecl:
+ s << ", underlyingType=\"" << clang_getTypedefDeclUnderlyingType(cursor) << '"';
+ break;
+ default:
+ break;
+ }
+
+ if (type.kind != CXType_Invalid)
+ s << ", type=\"" << type << '"';
+ if (clang_Cursor_hasAttrs(cursor))
+ s << ", [attrs]";
+
+ const QString cursorSpelling = clang::getCursorSpelling(cursor);
+ if (!cursorSpelling.isEmpty())
+ s << ", spelling=\"" << cursorSpelling << '"';
+ CXString cursorDisplay = clang_getCursorDisplayName(cursor);
+ if (const char *dpy = clang_getCString(cursorDisplay)) {
+ const QString display = QString::fromUtf8(dpy);
+ if (display != cursorSpelling)
+ s << ", display=\"" << dpy << '"';
+ }
+ clang_disposeString(cursorDisplay);
+ return s;
+}
+
+QDebug operator<<(QDebug s, const CXSourceLocation &location)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ 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
+ if (const char *cFileName = clang_getCString(cxFileName))
+ s << baseName(cFileName) << ':';
+ s << line << ':' << column;
+ clang_disposeString(cxFileName);
+ return s;
+}
+
+#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.h b/sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.h
new file mode 100644
index 000000000..323efdd2a
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangdebugutils.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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$
+**
+****************************************************************************/
+
+#ifndef CLANGDEBUGUTILS_H
+#define CLANGDEBUGUTILS_H
+
+#include <QtCore/QtGlobal>
+
+#include <clang-c/Index.h>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+QT_FORWARD_DECLARE_CLASS(QString)
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug s, const CXString &cs);
+QDebug operator<<(QDebug s, CXCursorKind cursorKind);
+QDebug operator<<(QDebug s, CX_CXXAccessSpecifier ac);
+QDebug operator<<(QDebug s, const CXType &t);
+QDebug operator<<(QDebug s, const CXCursor &cursor);
+QDebug operator<<(QDebug s, const CXSourceLocation &location);
+#endif // !QT_NO_DEBUG_STREAM
+
+#endif // CLANGDEBUGUTILS_H
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp
new file mode 100644
index 000000000..16a7a3147
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp
@@ -0,0 +1,272 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "clangparser.h"
+#include "clangutils.h"
+#include "clangdebugutils.h"
+#include "compilersupport.h"
+
+#include <QtCore/QByteArrayList>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QScopedArrayPointer>
+#include <QtCore/QString>
+
+namespace clang {
+
+SourceFileCache::Snippet SourceFileCache::getCodeSnippet(const CXCursor &cursor)
+{
+ Snippet result(nullptr, nullptr);
+ const SourceRange range = getCursorRange(cursor);
+ if (range.first.file.isEmpty() || range.second.file != range.first.file)
+ return result;
+ FileBufferCache::Iterator it = m_fileBufferCache.find(range.first.file);
+ if (it == m_fileBufferCache.end()) {
+ QFile file(range.first.file);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning().noquote().nospace()
+ << "Can't open " << QDir::toNativeSeparators(range.first.file)
+ << ": " << file.errorString();
+ return result;
+ }
+ it = m_fileBufferCache.insert(range.first.file, file.readAll());
+ }
+
+ const unsigned pos = range.first.offset;
+ const unsigned end = range.second.offset;
+ const QByteArray &contents = it.value();
+ if (end >= unsigned(contents.size())) {
+ qWarning().noquote().nospace() << "Range end " << end << " is above size of "
+ << range.first.file << " (" << contents.size() << ')';
+ return result;
+ }
+ result.first = contents.constData() + pos;
+ result.second = contents.constData() + end;
+ return result;
+}
+
+BaseVisitor::BaseVisitor() = default;
+BaseVisitor::~BaseVisitor() = default;
+
+bool BaseVisitor::visitLocation(const CXSourceLocation &location) const
+{
+ return clang_Location_isFromMainFile(location) != 0;
+}
+
+BaseVisitor::StartTokenResult BaseVisitor::cbHandleStartToken(const CXCursor &cursor)
+{
+ switch (cursor.kind) {
+ default:
+ break;
+ }
+
+ return startToken(cursor);
+}
+
+bool BaseVisitor::cbHandleEndToken(const CXCursor &cursor, StartTokenResult startResult)
+{
+ const bool result = startResult != Recurse || endToken(cursor);
+ switch (cursor.kind) {
+ default:
+ break;
+ }
+
+ return result;
+}
+
+BaseVisitor::CodeSnippet BaseVisitor::getCodeSnippet(const CXCursor &cursor)
+{
+ CodeSnippet result = m_fileCache.getCodeSnippet(cursor);
+ if (result.first == nullptr)
+ appendDiagnostic(Diagnostic(QStringLiteral("Unable to retrieve code snippet."), cursor, CXDiagnostic_Error));
+ return result;
+}
+
+QString BaseVisitor::getCodeSnippetString(const CXCursor &cursor)
+{
+ CodeSnippet result = m_fileCache.getCodeSnippet(cursor);
+ return result.first != nullptr
+ ? QString::fromUtf8(result.first, int(result.second - result.first))
+ : QString();
+}
+
+static CXChildVisitResult
+ visitorCallback(CXCursor cursor, CXCursor /* parent */, CXClientData clientData)
+{
+ BaseVisitor *bv = reinterpret_cast<BaseVisitor *>(clientData);
+
+ const CXSourceLocation location = clang_getCursorLocation(cursor);
+ if (!bv->visitLocation(location))
+ return CXChildVisit_Continue;
+
+ const BaseVisitor::StartTokenResult startResult = bv->cbHandleStartToken(cursor);
+ switch (startResult) {
+ case clang::BaseVisitor::Error:
+ return CXChildVisit_Break;
+ case clang::BaseVisitor::Skip:
+ break;
+ case clang::BaseVisitor::Recurse:
+ clang_visitChildren(cursor, visitorCallback, clientData);
+ break;
+ }
+
+ if (!bv->cbHandleEndToken(cursor, startResult))
+ return CXChildVisit_Break;
+
+ return CXChildVisit_Continue;
+}
+
+BaseVisitor::Diagnostics BaseVisitor::diagnostics() const
+{
+ return m_diagnostics;
+}
+
+void BaseVisitor::setDiagnostics(const Diagnostics &d)
+{
+ m_diagnostics = d;
+}
+
+void BaseVisitor::appendDiagnostic(const Diagnostic &d)
+{
+ m_diagnostics.append(d);
+}
+
+static inline const char **byteArrayListToFlatArgV(const QByteArrayList &bl)
+{
+ const char **result = new const char *[bl.size() + 1];
+ result[bl.size()] = nullptr;
+ std::transform(bl.cbegin(), bl.cend(), result,
+ [] (const QByteArray &a) { return a.constData(); });
+ return result;
+}
+
+static QByteArray msgCreateTranslationUnit(const QByteArrayList clangArgs, unsigned flags)
+{
+ QByteArray result = "clang_parseTranslationUnit2(0x";
+ result += QByteArray::number(flags, 16);
+ const int count = clangArgs.size();
+ result += ", cmd[" + QByteArray::number(count) + "]=";
+ for (int i = 0; i < count; ++i) {
+ const QByteArray &arg = clangArgs.at(i);
+ if (i)
+ result += ' ';
+ const bool quote = arg.contains(' ') || arg.contains('(');
+ if (quote)
+ result += '"';
+ result += arg;
+ if (quote)
+ result += '"';
+ }
+ result += ')';
+ return result;
+}
+
+static CXTranslationUnit createTranslationUnit(CXIndex index,
+ const QByteArrayList &args,
+ unsigned flags = 0)
+{
+ // courtesy qdoc
+ const unsigned defaultFlags = CXTranslationUnit_SkipFunctionBodies
+ | CXTranslationUnit_Incomplete;
+
+ static const QByteArrayList defaultArgs = {
+#if defined(Q_CC_MSVC) && _MSC_VER > 1900
+ "-std=c++1z", // Fixes constexpr errors in MSVC2017 library headers with Clang 4.1
+#else
+ "-std=c++14", // ! otherwise, t.h is parsed as "C"
+#endif
+#ifndef Q_OS_WIN
+ "-fPIC",
+#endif
+ "-fno-exceptions", // Workaround for clang bug http://reviews.llvm.org/D17988
+#ifdef Q_OS_MACOS
+ "-Wno-expansion-to-defined", // Workaround for warnings in Darwin stdlib, see
+ // https://github.com/darlinghq/darling/issues/204
+#endif
+ "-Wno-constant-logical-operand"
+ };
+
+ const QByteArrayList clangArgs = emulatedCompilerOptions() + defaultArgs + args;
+ QScopedArrayPointer<const char *> argv(byteArrayListToFlatArgV(clangArgs));
+ qDebug().noquote().nospace() << msgCreateTranslationUnit(clangArgs, flags);
+
+ CXTranslationUnit tu;
+ CXErrorCode err = clang_parseTranslationUnit2(index, nullptr, argv.data(),
+ clangArgs.size(), nullptr, 0,
+ defaultFlags | flags, &tu);
+ if (err || !tu) {
+ qWarning().noquote().nospace() << "Could not parse "
+ << clangArgs.constLast().constData() << ", error code: " << err;
+ return nullptr;
+ }
+ return tu;
+}
+
+/* clangFlags are flags to clang_parseTranslationUnit2() such as
+ * CXTranslationUnit_KeepGoing (from CINDEX_VERSION_MAJOR/CINDEX_VERSION_MINOR 0.35)
+ */
+
+bool parse(const QByteArrayList &clangArgs, unsigned clangFlags, BaseVisitor &bv)
+{
+ CXIndex index = clang_createIndex(0 /* excludeDeclarationsFromPCH */,
+ 1 /* displayDiagnostics */);
+ if (!index) {
+ qWarning() << "clang_createIndex() failed!";
+ return false;
+ }
+
+ CXTranslationUnit translationUnit = createTranslationUnit(index, clangArgs, clangFlags);
+ if (!translationUnit)
+ return false;
+
+ CXCursor rootCursor = clang_getTranslationUnitCursor(translationUnit);
+
+ clang_visitChildren(rootCursor, visitorCallback, reinterpret_cast<CXClientData>(&bv));
+
+ QVector<Diagnostic> diagnostics = getDiagnostics(translationUnit);
+ diagnostics.append(bv.diagnostics());
+ bv.setDiagnostics(diagnostics);
+
+ const bool ok = maxSeverity(diagnostics) < CXDiagnostic_Error;
+ if (!ok) {
+ QDebug debug = qWarning();
+ debug.noquote();
+ debug.nospace();
+ debug << "Errors in "
+ << QDir::toNativeSeparators(QFile::decodeName(clangArgs.constLast())) << ":\n";
+ for (const Diagnostic &diagnostic : qAsConst(diagnostics))
+ debug << diagnostic << '\n';
+ }
+
+ clang_disposeTranslationUnit(translationUnit);
+ clang_disposeIndex(index);
+ return ok;
+}
+
+} // namespace clang
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangparser.h b/sources/shiboken2/ApiExtractor/clangparser/clangparser.h
new file mode 100644
index 000000000..ef1424f17
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangparser.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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$
+**
+****************************************************************************/
+
+#ifndef CLANGPARSER_H
+#define CLANGPARSER_H
+
+#include <clang-c/Index.h>
+
+#include <QtCore/QByteArrayList>
+#include <QtCore/QHash>
+#include <QtCore/QPair>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+
+namespace clang {
+
+struct Diagnostic;
+
+class SourceFileCache {
+public:
+ typedef QPair<const char *, const char *> Snippet;
+
+ Snippet getCodeSnippet(const CXCursor &cursor);
+
+private:
+ typedef QHash<QString, QByteArray> FileBufferCache;
+
+ FileBufferCache m_fileBufferCache;
+};
+
+class BaseVisitor {
+ Q_DISABLE_COPY(BaseVisitor)
+public:
+ typedef QVector<Diagnostic> Diagnostics;
+ typedef SourceFileCache::Snippet CodeSnippet;
+
+ enum StartTokenResult { Error, Skip, Recurse };
+
+ BaseVisitor();
+ virtual ~BaseVisitor();
+
+ // Whether location should be visited.
+ // defaults to clang_Location_isFromMainFile()
+ virtual bool visitLocation(const CXSourceLocation &location) const;
+
+ virtual StartTokenResult startToken(const CXCursor &cursor) = 0;
+ virtual bool endToken(const CXCursor &cursor) = 0;
+
+ StartTokenResult cbHandleStartToken(const CXCursor &cursor);
+ bool cbHandleEndToken(const CXCursor &cursor, StartTokenResult startResult);
+
+ CodeSnippet getCodeSnippet(const CXCursor &cursor);
+ QString getCodeSnippetString(const CXCursor &cursor);
+
+ Diagnostics diagnostics() const;
+ void setDiagnostics(const Diagnostics &d);
+ void appendDiagnostic(const Diagnostic &d);
+
+private:
+ SourceFileCache m_fileCache;
+ Diagnostics m_diagnostics;
+};
+
+bool parse(const QByteArrayList &clangArgs, unsigned clangFlags, BaseVisitor &ctx);
+
+} // namespace clang
+
+#endif // !CLANGPARSER_H
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp
new file mode 100644
index 000000000..f30a585bc
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "clangutils.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QHashFunctions>
+#include <QtCore/QProcess>
+
+bool operator==(const CXCursor &c1, const CXCursor &c2)
+{
+ return c1.kind == c2.kind
+ && c1.xdata == c2.xdata
+ && std::equal(c1.data, c1.data + sizeof(c1.data) / sizeof(c1.data[0]), c2.data);
+}
+
+uint qHash(const CXCursor &c, uint seed)
+{
+ return qHash(c.kind) ^ qHash(c.xdata) ^ qHash(c.data[0])
+ ^ qHash(c.data[1]) ^ qHash(c.data[2]) ^ seed;
+}
+
+namespace clang {
+
+SourceLocation getExpansionLocation(const CXSourceLocation &location)
+{
+ SourceLocation result;
+ CXFile file; // void *
+ clang_getExpansionLocation(location, &file, &result.line, &result.column, &result.offset);
+ const CXString cxFileName = clang_getFileName(file);
+ // Has been observed to be 0 for invalid locations
+ if (const char *cFileName = clang_getCString(cxFileName))
+ result.file = QString::fromUtf8(cFileName);
+ clang_disposeString(cxFileName);
+ return result;
+}
+
+SourceLocation getCursorLocation(const CXCursor &cursor)
+{
+ const CXSourceRange extent = clang_getCursorExtent(cursor);
+ return getExpansionLocation(clang_getRangeStart(extent));
+}
+
+CXString getFileNameFromLocation(const CXSourceLocation &location)
+{
+ CXFile file;
+ unsigned line;
+ unsigned column;
+ unsigned offset;
+ clang_getExpansionLocation(location, &file, &line, &column, &offset);
+ return clang_getFileName(file);
+}
+
+SourceRange getCursorRange(const CXCursor &cursor)
+{
+ const CXSourceRange extent = clang_getCursorExtent(cursor);
+ return qMakePair(getExpansionLocation(clang_getRangeStart(extent)),
+ getExpansionLocation(clang_getRangeEnd(extent)));
+}
+
+QString getCursorKindName(CXCursorKind cursorKind)
+{
+ CXString kindName = clang_getCursorKindSpelling(cursorKind);
+ const QString result = QString::fromUtf8(clang_getCString(kindName));
+ clang_disposeString(kindName);
+ return result;
+}
+
+QString getCursorSpelling(const CXCursor &cursor)
+{
+ CXString cursorSpelling = clang_getCursorSpelling(cursor);
+ const QString result = QString::fromUtf8(clang_getCString(cursorSpelling));
+ clang_disposeString(cursorSpelling);
+ return result;
+}
+
+QString getCursorDisplayName(const CXCursor &cursor)
+{
+ CXString displayName = clang_getCursorDisplayName(cursor);
+ const QString result = QString::fromUtf8(clang_getCString(displayName));
+ clang_disposeString(displayName);
+ return result;
+}
+
+QString getTypeName(const CXType &type)
+{
+ CXString typeSpelling = clang_getTypeSpelling(type);
+ const QString result = QString::fromUtf8(clang_getCString(typeSpelling));
+ clang_disposeString(typeSpelling);
+ return result;
+}
+
+Diagnostic::Diagnostic(const QString &m, const CXCursor &c, CXDiagnosticSeverity s)
+ : message(m), location(getCursorLocation(c)), source(Other), severity(s)
+{
+}
+
+Diagnostic Diagnostic::fromCXDiagnostic(CXDiagnostic cd)
+{
+ Diagnostic result;
+ result.source = Clang;
+ CXString spelling = clang_getDiagnosticSpelling(cd);
+ result.message = QString::fromUtf8(clang_getCString(spelling));
+ clang_disposeString(spelling);
+ result.severity = clang_getDiagnosticSeverity(cd);
+ result.location = getExpansionLocation(clang_getDiagnosticLocation(cd));
+
+ CXDiagnosticSet childDiagnostics = clang_getChildDiagnostics(cd);
+ if (const unsigned childCount = clang_getNumDiagnosticsInSet(childDiagnostics)) {
+ result.childMessages.reserve(int(childCount));
+ const unsigned format = clang_defaultDiagnosticDisplayOptions();
+ for (unsigned i = 0; i < childCount; ++i) {
+ CXDiagnostic childDiagnostic = clang_getDiagnosticInSet(childDiagnostics, i);
+ CXString cdm = clang_formatDiagnostic(childDiagnostic, format);
+ result.childMessages.append(QString::fromUtf8(clang_getCString(cdm)));
+ clang_disposeString(cdm);
+ clang_disposeDiagnostic(childDiagnostic);
+ }
+ }
+
+ return result;
+}
+
+QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu)
+{
+ QVector<Diagnostic> result;
+ const unsigned count = clang_getNumDiagnostics(tu);
+ result.reserve(int(count));
+ for (unsigned i = 0; i < count; ++i) {
+ const CXDiagnostic d = clang_getDiagnostic(tu, i);
+ result.append(Diagnostic::fromCXDiagnostic(d));
+ clang_disposeDiagnostic(d);
+ }
+ return result;
+}
+
+CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds)
+{
+ CXDiagnosticSeverity result = CXDiagnostic_Ignored;
+ for (const Diagnostic& d : ds) {
+ if (d.severity > result)
+ result = d.severity;
+ }
+ return result;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+QDebug operator<<(QDebug s, const SourceLocation &l)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ s.noquote();
+ s << QDir::toNativeSeparators(l.file) << ':' << l.line;
+ if (l.column)
+ s << ':' << l.column;
+ return s;
+}
+
+// Roughly follow g++ format:
+// file.cpp:214:37: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
+QDebug operator<<(QDebug s, const Diagnostic &d)
+{
+ QDebugStateSaver saver(s);
+ s.nospace();
+ s.noquote();
+ s << d.location << ": ";
+ switch (d.severity) {
+ case CXDiagnostic_Ignored:
+ s << "ignored";
+ break;
+ case CXDiagnostic_Note:
+ s << "note";
+ break;
+ case CXDiagnostic_Warning:
+ s << "warning";
+ break;
+ case CXDiagnostic_Error:
+ s << "error";
+ break;
+ case CXDiagnostic_Fatal:
+ s << "fatal";
+ break;
+ }
+ s << ": " << d.message;
+
+ if (d.source != Diagnostic::Clang)
+ s << " [other]";
+
+ if (const int childMessagesCount = d.childMessages.size()) {
+ s << '\n';
+ for (int i = 0; i < childMessagesCount; ++i)
+ s << " " << d.childMessages.at(i) << '\n';
+ }
+
+ return s;
+}
+
+#endif // QT_NO_DEBUG_STREAM
+
+} // namespace clang
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.h b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h
new file mode 100644
index 000000000..3437f51eb
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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$
+**
+****************************************************************************/
+
+#ifndef CLANGUTILS_H
+#define CLANGUTILS_H
+
+#include <clang-c/Index.h>
+#include <QtCore/QPair>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+bool operator==(const CXCursor &c1, const CXCursor &c2);
+uint qHash(const CXCursor &c, uint seed = 0);
+
+namespace clang {
+
+QString getCursorKindName(CXCursorKind cursorKind);
+QString getCursorSpelling(const CXCursor &cursor);
+QString getCursorDisplayName(const CXCursor &cursor);
+QString getTypeName(const CXType &type);
+inline QString getCursorTypeName(const CXCursor &cursor)
+ { return getTypeName(clang_getCursorType(cursor)); }
+inline QString getCursorResultTypeName(const CXCursor &cursor)
+ { return getTypeName(clang_getCursorResultType(cursor)); }
+
+inline bool isCursorValid(const CXCursor &c)
+{
+ return c.kind < CXCursor_FirstInvalid || c.kind > CXCursor_LastInvalid;
+}
+
+struct SourceLocation
+{
+ int compare(const SourceLocation &rhs) const;
+
+ QString file;
+ unsigned line = 0;
+ unsigned column = 0;
+ unsigned offset = 0;
+};
+
+SourceLocation getExpansionLocation(const CXSourceLocation &location);
+
+typedef QPair<SourceLocation, SourceLocation> SourceRange;
+
+SourceLocation getCursorLocation(const CXCursor &cursor);
+CXString getFileNameFromLocation(const CXSourceLocation &location);
+SourceRange getCursorRange(const CXCursor &cursor);
+
+struct Diagnostic {
+ enum Source { Clang, Other };
+
+ Diagnostic() : source(Clang) {}
+ // Clang
+ static Diagnostic fromCXDiagnostic(CXDiagnostic cd);
+ // Other
+ explicit Diagnostic(const QString &m, const CXCursor &c, CXDiagnosticSeverity s = CXDiagnostic_Warning);
+
+ QString message;
+ QStringList childMessages;
+ SourceLocation location;
+ Source source;
+ CXDiagnosticSeverity severity;
+};
+
+QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu);
+CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds);
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug, const SourceLocation &);
+QDebug operator<<(QDebug, const Diagnostic &);
+#endif // QT_NO_DEBUG_STREAM
+} // namespace clang
+
+#endif // CLANGUTILS_H
diff --git a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
new file mode 100644
index 000000000..e9af23bc5
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "compilersupport.h"
+#include "header_paths.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QProcess>
+#include <QtCore/QStringList>
+#include <QtCore/QVersionNumber>
+
+#include <string.h>
+#include <algorithm>
+#include <iterator>
+
+namespace clang {
+
+static bool runProcess(const QString &program, const QStringList &arguments,
+ QByteArray *stdOutIn = nullptr, QByteArray *stdErrIn = nullptr)
+{
+ QProcess process;
+ process.start(program, arguments, QProcess::ReadWrite);
+ if (!process.waitForStarted()) {
+ qWarning().noquote().nospace() << "Unable to start "
+ << process.program() << ": " << process.errorString();
+ return false;
+ }
+ process.closeWriteChannel();
+ const bool finished = process.waitForFinished();
+ const QByteArray stdErr = process.readAllStandardError();
+ if (stdErrIn)
+ *stdErrIn = stdErr;
+ if (stdOutIn)
+ *stdOutIn = process.readAllStandardOutput();
+
+ if (!finished) {
+ qWarning().noquote().nospace() << process.program() << " timed out: " << stdErr;
+ process.kill();
+ return false;
+ }
+
+ if (process.exitStatus() != QProcess::NormalExit) {
+ qWarning().noquote().nospace() << process.program() << " crashed: " << stdErr;
+ return false;
+ }
+
+ if (process.exitCode() != 0) {
+ qWarning().noquote().nospace() << process.program() << " exited "
+ << process.exitCode() << ": " << stdErr;
+ return false;
+ }
+
+ return true;
+}
+
+#if defined(Q_CC_GNU)
+
+static QByteArray frameworkPath() { return QByteArrayLiteral(" (framework directory)"); }
+
+// Determine g++'s internal include paths from the output of
+// g++ -E -x c++ - -v </dev/null
+// Output looks like:
+// #include <...> search starts here:
+// /usr/local/include
+// /System/Library/Frameworks (framework directory)
+// End of search list.
+static HeaderPaths gppInternalIncludePaths(const QString &compiler)
+{
+ HeaderPaths result;
+ QStringList arguments;
+ arguments << QStringLiteral("-E") << QStringLiteral("-x") << QStringLiteral("c++")
+ << QStringLiteral("-") << QStringLiteral("-v");
+ QByteArray stdOut;
+ QByteArray stdErr;
+ if (!runProcess(compiler, arguments, &stdOut, &stdErr))
+ return result;
+ const QByteArrayList stdErrLines = stdErr.split('\n');
+ bool isIncludeDir = false;
+ for (const QByteArray &line : stdErrLines) {
+ if (isIncludeDir) {
+ if (line.startsWith(QByteArrayLiteral("End of search list"))) {
+ isIncludeDir = false;
+ } else {
+ HeaderPath headerPath(line.trimmed());
+ if (headerPath.path.endsWith(frameworkPath())) {
+ headerPath.m_isFramework = true;
+ headerPath.path.truncate(headerPath.path.size() - frameworkPath().size());
+ }
+ result.append(headerPath);
+ }
+ } else if (line.startsWith(QByteArrayLiteral("#include <...> search starts here"))) {
+ isIncludeDir = true;
+ }
+ }
+ return result;
+}
+#endif // Q_CC_MSVC
+
+// Detect Vulkan as supported from Qt 5.10 by checking the environment variables.
+static void detectVulkan(HeaderPaths *headerPaths)
+{
+ static const char *vulkanVariables[] = {"VULKAN_SDK", "VK_SDK_PATH"};
+ for (const char *vulkanVariable : vulkanVariables) {
+ if (qEnvironmentVariableIsSet(vulkanVariable)) {
+ headerPaths->append(HeaderPath(qgetenv(vulkanVariable) + QByteArrayLiteral("/include")));
+ break;
+ }
+ }
+}
+
+#if defined(Q_CC_GNU)
+static inline bool isRedHat74()
+{
+ if (QSysInfo::productType() != QLatin1String("rhel"))
+ return false;
+ const QVersionNumber osVersion = QVersionNumber::fromString(QSysInfo::productVersion());
+ return osVersion.isNull() || osVersion >= QVersionNumber(7, 4);
+}
+#endif // Q_CC_GNU
+
+// For MSVC, we set the MS compatibility version and let Clang figure out its own
+// options and include paths.
+// For the others, we pass "-nostdinc" since libclang tries to add it's own system
+// include paths, which together with the clang compiler paths causes some clash
+// which causes std types not being found and construct -I/-F options from the
+// include paths of the host compiler.
+
+#ifdef Q_CC_CLANG
+static QByteArray noStandardIncludeOption() { return QByteArrayLiteral("-nostdinc"); }
+#endif
+
+// Returns clang options needed for emulating the host compiler
+QByteArrayList emulatedCompilerOptions()
+{
+ QByteArrayList result;
+#if defined(Q_CC_MSVC)
+ HeaderPaths headerPaths;
+ result.append(QByteArrayLiteral("-fms-compatibility-version=19"));
+ result.append(QByteArrayLiteral("-Wno-microsoft-enum-value"));
+#elif defined(Q_CC_CLANG)
+ HeaderPaths headerPaths = gppInternalIncludePaths(QStringLiteral("clang++"));
+ result.append(noStandardIncludeOption());
+#elif defined(Q_CC_GNU)
+ HeaderPaths headerPaths;
+
+ // The clang builtin includes directory is used to find the definitions for intrinsic functions
+ // and builtin types. It is necessary to use the clang includes to prevent redefinition errors.
+ // The default toolchain includes should be picked up automatically by clang without specifying
+ // them implicitly.
+ QByteArray clangBuiltinIncludesDir(CLANG_BUILTIN_INCLUDES_DIR);
+
+ if (!clangBuiltinIncludesDir.isEmpty()) {
+ result.append(QByteArrayLiteral("-isystem"));
+ result.append(clangBuiltinIncludesDir);
+ }
+ // Append the c++ include paths since Clang is unable to find <list> etc
+ // on RHEL 7.4 with g++ 6.3. A fix for this has been added to Clang 5.0,
+ // so, the code can be removed once Clang 5.0 is the minimum version.
+ if (isRedHat74()) {
+ const HeaderPaths gppPaths = gppInternalIncludePaths(QStringLiteral("g++"));
+ for (const HeaderPath &h : gppPaths) {
+ if (h.path.contains("c++"))
+ headerPaths.append(h);
+ }
+ }
+#else
+ HeaderPaths headerPaths;
+#endif
+ detectVulkan(&headerPaths);
+ std::transform(headerPaths.cbegin(), headerPaths.cend(),
+ std::back_inserter(result), [](const HeaderPath &p) {
+ return HeaderPath::includeOption(p, true);
+ });
+ return result;
+}
+
+} // namespace clang
diff --git a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.h b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.h
new file mode 100644
index 000000000..f556da551
--- /dev/null
+++ b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $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$
+**
+****************************************************************************/
+
+#ifndef COMPILERSUPPORT_H
+#define COMPILERSUPPORT_H
+
+#include <QtCore/QByteArrayList>
+
+namespace clang {
+
+QByteArrayList emulatedCompilerOptions();
+
+} // namespace clang
+
+#endif // COMPILERSUPPORT_H