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.cpp236
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp2
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp49
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangutils.h14
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp12
5 files changed, 221 insertions, 92 deletions
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
index af7f96068..40f915028 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
@@ -146,6 +146,7 @@ class BuilderPrivate {
public:
typedef QHash<CXCursor, ClassModelItem> CursorClassHash;
typedef QHash<CXCursor, TypeDefModelItem> CursorTypedefHash;
+ typedef QHash<CXType, TypeInfo> TypeInfoHash;
explicit BuilderPrivate(BaseVisitor *bv) : m_baseVisitor(bv), m_model(new CodeModel)
{
@@ -180,9 +181,16 @@ public:
CodeModel::FunctionType t = CodeModel::Normal) const;
FunctionModelItem createMemberFunction(const CXCursor &cursor) const;
void qualifyConstructor(const CXCursor &cursor);
+ TypeInfo createTypeInfoHelper(const CXType &type) const; // uncashed
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 TypeInfo &ti);
TemplateParameterModelItem createTemplateParameter(const CXCursor &cursor) const;
TemplateParameterModelItem createNonTypeTemplateParameter(const CXCursor &cursor) const;
@@ -205,6 +213,8 @@ public:
CursorClassHash m_cursorClassHash;
CursorTypedefHash m_cursorTypedefHash;
+ mutable TypeInfoHash m_typeInfoHash; // Cache type information
+
ClassModelItem m_currentClass;
EnumModelItem m_currentEnum;
FunctionModelItem m_currentFunction;
@@ -247,6 +257,25 @@ bool BuilderPrivate::addClass(const CXCursor &cursor, CodeModel::ClassType t)
return true;
}
+static inline ExceptionSpecification exceptionSpecificationFromClang(int ce)
+{
+ switch (ce) {
+ case CXCursor_ExceptionSpecificationKind_BasicNoexcept:
+ case CXCursor_ExceptionSpecificationKind_ComputedNoexcept:
+ case CXCursor_ExceptionSpecificationKind_DynamicNone: // throw()
+ 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) const
{
@@ -256,10 +285,11 @@ FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor,
name = fixTypeName(name);
FunctionModelItem result(new _FunctionModelItem(m_model, name));
setFileName(cursor, result.data());
- result->setType(createTypeInfo(clang_getCursorResultType(cursor)));
+ result->setType(createTypeInfoHelper(clang_getCursorResultType(cursor)));
result->setFunctionType(t);
result->setScope(m_scope);
result->setStatic(clang_Cursor_getStorageClass(cursor) == CX_SC_Static);
+ result->setExceptionSpecification(exceptionSpecificationFromClang(clang_getCursorExceptionSpecificationType(cursor)));
switch (clang_getCursorAvailability(cursor)) {
case CXAvailability_Available:
break;
@@ -335,7 +365,7 @@ TemplateParameterModelItem BuilderPrivate::createTemplateParameter(const CXCurso
TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const CXCursor &cursor) const
{
TemplateParameterModelItem result = createTemplateParameter(cursor);
- result->setType(createTypeInfo(cursor));
+ result->setType(createTypeInfoHelper(clang_getCursorType(cursor)));
return result;
}
@@ -359,38 +389,6 @@ struct ArrayDimensionResult
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;
-}
-
// Create qualified name "std::list<std::string>" -> ("std", "list<std::string>")
static QStringList qualifiedName(const QString &t)
{
@@ -412,71 +410,143 @@ static QStringList qualifiedName(const QString &t)
return result;
}
-TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const
+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(createTypeInfoHelper(argType));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+static void dummyTemplateArgumentHandler(int, const QStringRef &) {}
+
+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();
+ const QPair<int, int> 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::createTypeInfoHelper(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));
+ TypeInfo result = createTypeInfoHelper(clang_getResultType(pointeeType));
result.setFunctionPointer(true);
for (int a = 0; a < argCount; ++a)
- result.addArgument(createTypeInfo(clang_getArgType(pointeeType, unsigned(a))));
+ result.addArgument(createTypeInfoHelper(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);
+ 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());
}
- // "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));
+ 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);
- 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);
+ typeInfo.setConstant(clang_isConstQualifiedType(nestedType) != 0);
+ typeInfo.setVolatile(clang_isVolatileQualifiedType(nestedType) != 0);
- // clang_isVolatileQualifiedType() returns true for "volatile int", but not for "volatile int *"
- if (typeName.startsWith(QLatin1String("volatile "))) {
- typeName.remove(0, 9);
- typeInfo.setVolatile(true);
+ QString typeName = getTypeName(nestedType);
+ while (TypeInfo::stripLeadingConst(&typeName)
+ || TypeInfo::stripLeadingVolatile(&typeName)) {
}
- typeName = typeName.trimmed();
+ // Obtain template instantiations if the name has '<' (thus excluding
+ // typedefs like "std::string".
+ if (typeName.contains(QLatin1Char('<')))
+ 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
+{
+ TypeInfoHash::iterator it = m_typeInfoHash.find(type);
+ if (it == m_typeInfoHash.end())
+ it = m_typeInfoHash.insert(type, createTypeInfoHelper(type));
+ return it.value();
+}
+
+void BuilderPrivate::addTypeDef(const CXCursor &cursor, const TypeInfo &ti)
+{
+ TypeDefModelItem item(new _TypeDefModelItem(m_model, getCursorSpelling(cursor)));
+ setFileName(cursor, item.data());
+ item->setType(ti);
+ item->setScope(m_scope);
+ m_scopeStack.back()->addTypeDef(item);
+ m_cursorTypedefHash.insert(cursor, item);
+}
+
// extract an expression from the cursor via source
// CXCursor_EnumConstantDecl, ParmDecl (a = Flag1 | Flag2)
QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor) const
@@ -795,9 +865,8 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
d->m_currentFunction = d->createMemberFunction(cursor);
d->m_scopeStack.back()->addFunction(d->m_currentFunction);
break;
- } else {
- return Skip; // inline member functions outside class
}
+ return Skip; // inline member functions outside class
}
}
Q_FALLTHROUGH(); // fall through to free template function.
@@ -868,17 +937,14 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
}
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);
+ case CXCursor_TypeAliasTemplateDecl: { // May contain nested CXCursor_TemplateTypeParameter
+ const CXType type = clang_getCanonicalType(clang_getCursorType(cursor));
+ if (type.kind > CXType_Unexposed)
+ d->addTypeDef(cursor, d->createTypeInfo(type));
}
+ return Skip;
+ case CXCursor_TypedefDecl:
+ d->addTypeDef(cursor, d->createTypeInfo(clang_getTypedefDeclUnderlyingType(cursor)));
break;
case CXCursor_TypeRef:
if (!d->m_currentFunction.isNull()) {
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp
index ce0b6554d..e116f8b83 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp
@@ -166,7 +166,7 @@ static inline const char **byteArrayListToFlatArgV(const QByteArrayList &bl)
return result;
}
-static QByteArray msgCreateTranslationUnit(const QByteArrayList clangArgs, unsigned flags)
+static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsigned flags)
{
QByteArray result = "clang_parseTranslationUnit2(0x";
result += QByteArray::number(flags, 16);
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp
index 2ff18b23b..8bee28cdf 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp
@@ -46,6 +46,18 @@ uint qHash(const CXCursor &c, uint seed)
^ qHash(c.data[1]) ^ qHash(c.data[2]) ^ seed;
}
+bool operator==(const CXType &t1, const CXType &t2)
+{
+ return t1.kind == t2.kind && t1.data[0] == t2.data[0]
+ && t1.data[1] == t2.data[1];
+}
+
+uint qHash(const CXType &ct, uint seed)
+{
+ return uint(ct.kind) ^ uint(0xFFFFFFFF & quintptr(ct.data[0]))
+ ^ uint(0xFFFFFFFF & quintptr(ct.data[1])) ^ seed;
+}
+
namespace clang {
SourceLocation getExpansionLocation(const CXSourceLocation &location)
@@ -160,6 +172,43 @@ QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu)
return result;
}
+QPair<int, int> parseTemplateArgumentList(const QString &l,
+ const TemplateArgumentHandler &handler,
+ int from)
+{
+ const int ltPos = l.indexOf(QLatin1Char('<'), from);
+ if (ltPos == - 1)
+ return qMakePair(-1, -1);
+ int startPos = ltPos + 1;
+ int level = 1;
+ for (int p = startPos, end = l.size(); p < end; ) {
+ const char c = l.at(p).toLatin1();
+ switch (c) {
+ case ',':
+ case '>':
+ handler(level, l.midRef(startPos, p - startPos).trimmed());
+ ++p;
+ if (c == '>') {
+ if (--level == 0)
+ return qMakePair(ltPos, p);
+ // Skip over next ',': "a<b<c,d>,e>"
+ for (; p < end && (l.at(p).isSpace() || l.at(p) == QLatin1Char(',')); ++p) {}
+ }
+ startPos = p;
+ break;
+ case '<':
+ handler(level, l.midRef(startPos, p - startPos).trimmed());
+ ++level;
+ startPos = ++p;
+ break;
+ default:
+ ++p;
+ break;
+ }
+ }
+ return qMakePair(-1, -1);
+}
+
CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds)
{
CXDiagnosticSeverity result = CXDiagnostic_Ignored;
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.h b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h
index 98d0c9752..db2db6267 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangutils.h
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h
@@ -32,13 +32,19 @@
#include <clang-c/Index.h>
#include <QtCore/QPair>
#include <QtCore/QString>
+#include <QtCore/QStringList>
#include <QtCore/QVector>
+#include <functional>
+
QT_FORWARD_DECLARE_CLASS(QDebug)
bool operator==(const CXCursor &c1, const CXCursor &c2);
uint qHash(const CXCursor &c, uint seed = 0);
+bool operator==(const CXType &t1, const CXType &t2);
+uint qHash(const CXType &ct, uint seed);
+
namespace clang {
QString getCursorKindName(CXCursorKind cursorKind);
@@ -92,6 +98,14 @@ struct Diagnostic {
QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu);
CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds);
+// Parse a template argument list "a<b<c,d>,e>" and invoke a handler
+// with each match (level and string). Return begin and end of the list.
+typedef std::function<void(int /*level*/, const QStringRef &)> TemplateArgumentHandler;
+
+QPair<int, int> parseTemplateArgumentList(const QString &l,
+ const TemplateArgumentHandler &handler,
+ int from = 0);
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug, const SourceLocation &);
QDebug operator<<(QDebug, const Diagnostic &);
diff --git a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
index 74cad05ae..d3d5c8da8 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/compilersupport.cpp
@@ -239,11 +239,11 @@ static QByteArray noStandardIncludeOption() { return QByteArrayLiteral("-nostdin
#endif
#if NEED_CLANG_BUILTIN_INCLUDES
-static QString findClang()
+static QString findClangLibDir()
{
for (const char *envVar : {"LLVM_INSTALL_DIR", "CLANG_INSTALL_DIR"}) {
if (qEnvironmentVariableIsSet(envVar)) {
- const QString path = QFile::decodeName(qgetenv(envVar));
+ const QString path = QFile::decodeName(qgetenv(envVar)) + QLatin1String("/lib");
if (QFileInfo::exists(path))
return path;
}
@@ -252,7 +252,7 @@ static QString findClang()
QStandardPaths::findExecutable(QLatin1String("llvm-config"));
if (!llvmConfig.isEmpty()) {
QByteArray stdOut;
- if (runProcess(llvmConfig, QStringList{QLatin1String("--prefix")}, &stdOut)) {
+ if (runProcess(llvmConfig, QStringList{QLatin1String("--libdir")}, &stdOut)) {
const QString path = QFile::decodeName(stdOut.trimmed());
if (QFileInfo::exists(path))
return path;
@@ -264,11 +264,11 @@ static QString findClang()
static QString findClangBuiltInIncludesDir()
{
// Find the include directory of the highest version.
- const QString clangPath = findClang();
- if (!clangPath.isEmpty()) {
+ const QString clangPathLibDir = findClangLibDir();
+ if (!clangPathLibDir.isEmpty()) {
QString candidate;
QVersionNumber lastVersionNumber(1, 0, 0);
- QDir clangDir(clangPath + QLatin1String("/lib/clang"));
+ QDir clangDir(clangPathLibDir + QLatin1String("/clang"));
const QFileInfoList versionDirs =
clangDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &fi : versionDirs) {