diff options
Diffstat (limited to 'src/qdoc/clangcodeparser.cpp')
-rw-r--r-- | src/qdoc/clangcodeparser.cpp | 419 |
1 files changed, 267 insertions, 152 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index 5d95c9c7e..a3d39dbd2 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -45,7 +45,6 @@ #include "codechunk.h" #include "config.h" -#include "generator.h" #include "loggingcategory.h" #include "qdocdatabase.h" #include "utilities.h" @@ -67,7 +66,7 @@ static CXTranslationUnit_Flags flags_ = static_cast<CXTranslationUnit_Flags>(0); static CXIndex index_ = nullptr; #ifndef QT_NO_DEBUG_STREAM -template <class T> +template<class T> static QDebug operator<<(QDebug debug, const std::vector<T> &v) { QDebugStateSaver saver(debug); @@ -90,10 +89,13 @@ static QDebug operator<<(QDebug debug, const std::vector<T> &v) T can be any functor that is callable with a CXCursor parameter and returns a CXChildVisitResult (in other word compatible with function<CXChildVisitResult(CXCursor)> */ -template <typename T> bool visitChildrenLambda(CXCursor cursor, T &&lambda) +template<typename T> +bool visitChildrenLambda(CXCursor cursor, T &&lambda) { - CXCursorVisitor visitor = [](CXCursor c, CXCursor , CXClientData client_data) -> CXChildVisitResult - { return (*static_cast<T *>(client_data))(c); }; + CXCursorVisitor visitor = [](CXCursor c, CXCursor, + CXClientData client_data) -> CXChildVisitResult { + return (*static_cast<T *>(client_data))(c); + }; return clang_visitChildren(cursor, visitor, &lambda); } @@ -107,6 +109,53 @@ static QString fromCXString(CXString &&string) return ret; } +static QString templateDecl(CXCursor cursor); + +/*! + Returns a list of template parameters at \a cursor. +*/ +static QStringList getTemplateParameters(CXCursor cursor) +{ + QStringList parameters; + visitChildrenLambda(cursor, [¶meters](CXCursor cur) { + QString name = fromCXString(clang_getCursorSpelling(cur)); + QString type; + + switch (clang_getCursorKind(cur)) { + case CXCursor_TemplateTypeParameter: + type = QStringLiteral("typename"); + break; + case CXCursor_NonTypeTemplateParameter: + type = fromCXString(clang_getTypeSpelling(clang_getCursorType(cur))); + // Hack: Omit QtPrivate template parameters from public documentation + if (type.startsWith(QLatin1String("QtPrivate"))) + return CXChildVisit_Continue; + break; + case CXCursor_TemplateTemplateParameter: + type = templateDecl(cur) + QLatin1String(" class"); + break; + default: + return CXChildVisit_Continue; + } + + if (!name.isEmpty()) + name.prepend(QLatin1Char(' ')); + + parameters << type + name; + return CXChildVisit_Continue; + }); + + return parameters; +} + +/*! + Gets the template declaration at specified \a cursor. + */ +static QString templateDecl(CXCursor cursor) +{ + QStringList params = getTemplateParameters(cursor); + return QLatin1String("template <") + params.join(QLatin1String(", ")) + QLatin1Char('>'); +} /*! convert a CXSourceLocation to a qdoc Location @@ -125,12 +174,17 @@ static Location fromCXSourceLocation(CXSourceLocation location) /*! convert a CX_CXXAccessSpecifier to Node::Access */ -static Node::Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec) { +static Node::Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec) +{ switch (spec) { - case CX_CXXPrivate: return Node::Private; - case CX_CXXProtected: return Node::Protected; - case CX_CXXPublic: return Node::Public; - default: return Node::Public; + case CX_CXXPrivate: + return Node::Private; + case CX_CXXProtected: + return Node::Protected; + case CX_CXXPublic: + return Node::Public; + default: + return Node::Public; } } @@ -154,7 +208,6 @@ static QString getSpelling(CXSourceRange range) return QString::fromUtf8(file.read(offset2 - offset1)); } - /*! Returns the function name from a given cursor representing a function declaration. This is usually clang_getCursorSpelling, but @@ -163,9 +216,9 @@ static QString getSpelling(CXSourceRange range) QString functionName(CXCursor cursor) { if (clang_getCursorKind(cursor) == CXCursor_ConversionFunction) { - // For a CXCursor_ConversionFunction we don't want the spelling which would be something like - // "operator type-parameter-0-0" or "operator unsigned int". - // we want the actual name as spelled; + // For a CXCursor_ConversionFunction we don't want the spelling which would be something + // like "operator type-parameter-0-0" or "operator unsigned int". we want the actual name as + // spelled; QString type = fromCXString(clang_getTypeSpelling(clang_getCursorResultType(cursor))); if (type.isEmpty()) return fromCXString(clang_getCursorSpelling(cursor)); @@ -185,7 +238,8 @@ QString functionName(CXCursor cursor) Reconstruct the qualified path name of a function that is being overridden. */ -static QString reconstructQualifiedPathForCursor(CXCursor cur) { +static QString reconstructQualifiedPathForCursor(CXCursor cur) +{ QString path; auto kind = clang_getCursorKind(cur); while (!clang_isInvalid(kind) && kind != CXCursor_TranslationUnit) { @@ -219,7 +273,8 @@ static QString reconstructQualifiedPathForCursor(CXCursor cur) { Find the node from the QDocDatabase \a qdb that corrseponds to the declaration represented by the cursor \a cur, if it exists. */ -static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur) { +static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur) +{ auto kind = clang_getCursorKind(cur); if (clang_isInvalid(kind)) return nullptr; @@ -269,7 +324,7 @@ static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur) { if (isVariadic && parameters.last().type() != QLatin1String("...")) continue; bool different = false; - for (int i = 0; i < actualArg; i++) { + for (int i = 0; i < actualArg; ++i) { if (args.size() <= i) args.append(fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i)))); QString t1 = parameters.at(i).type(); @@ -308,7 +363,8 @@ static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur) { corrseponds to the declaration represented by the cursor \a cur, if it exists. */ -static Node *findFunctionNodeForCursor(QDocDatabase *qdb, CXCursor cur) { +static Node *findFunctionNodeForCursor(QDocDatabase *qdb, CXCursor cur) +{ auto kind = clang_getCursorKind(cur); if (clang_isInvalid(kind)) return nullptr; @@ -347,7 +403,7 @@ static Node *findFunctionNodeForCursor(QDocDatabase *qdb, CXCursor cur) { if (isVariadic && parameters.last().type() != QLatin1String("...")) continue; bool different = false; - for (int i = 0; i < numArg; i++) { + for (int i = 0; i < numArg; ++i) { if (args.size() <= i) args.append(fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i)))); QString t1 = parameters.at(i).type(); @@ -375,10 +431,13 @@ static Node *findFunctionNodeForCursor(QDocDatabase *qdb, CXCursor cur) { return nullptr; } -class ClangVisitor { +class ClangVisitor +{ public: ClangVisitor(QDocDatabase *qdb, const QHash<QString, QString> &allHeaders) - : qdb_(qdb), parent_(qdb->primaryTreeRoot()), allHeaders_(allHeaders) { } + : qdb_(qdb), parent_(qdb->primaryTreeRoot()), allHeaders_(allHeaders) + { + } QDocDatabase *qdocDB() { return qdb_; } @@ -425,6 +484,7 @@ public: } Node *nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc); + private: /*! SimpleLoc represents a simple location in the main source file, @@ -433,7 +493,8 @@ private: struct SimpleLoc { unsigned int line, column; - friend bool operator<(const SimpleLoc &a, const SimpleLoc &b) { + friend bool operator<(const SimpleLoc &a, const SimpleLoc &b) + { return a.line != b.line ? a.line < b.line : a.column < b.column; } }; @@ -452,7 +513,8 @@ private: /*! Returns true if the symbol should be ignored for the documentation. */ - bool ignoredSymbol(const QString &symbolName) { + bool ignoredSymbol(const QString &symbolName) + { if (symbolName == QLatin1String("QPrivateSignal")) return true; return false; @@ -464,7 +526,8 @@ private: example: 'QLinkedList::iterator' -> 'iterator' */ - QString adjustTypeName(const QString &typeName) { + QString adjustTypeName(const QString &typeName) + { auto parent = parent_->parent(); if (parent && parent->isClassNode()) { QStringRef typeNameConstRemoved(&typeName); @@ -473,7 +536,7 @@ private: auto parentName = parent->fullName(); if (typeNameConstRemoved.startsWith(parentName) - && typeNameConstRemoved.mid(parentName.size(), 2) == QLatin1String("::")) { + && typeNameConstRemoved.mid(parentName.size(), 2) == QLatin1String("::")) { QString result = typeName; result.remove(typeNameConstRemoved.position(), parentName.size() + 2); return result; @@ -484,7 +547,8 @@ private: CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc); CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc); - CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc, Node **fnNode, bool &ignoreSignature); + CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc, Node **fnNode, + bool &ignoreSignature); void parseProperty(const QString &spelling, const Location &loc); void readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cursor); Aggregate *getSemanticParent(CXCursor cursor); @@ -524,9 +588,12 @@ Aggregate *ClangVisitor::getSemanticParent(CXCursor cursor) return parent_; } -CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation , Node **fnNode, bool &ignoreSignature) +CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation, Node **fnNode, + bool &ignoreSignature) { switch (clang_getCursorKind(cursor)) { + case CXCursor_Namespace: + return CXChildVisit_Recurse; case CXCursor_FunctionDecl: case CXCursor_FunctionTemplate: case CXCursor_CXXMethod: @@ -555,6 +622,7 @@ CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocat CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc) { auto kind = clang_getCursorKind(cursor); + QString templateString; switch (kind) { case CXCursor_TypeAliasDecl: { QString spelling = getSpelling(clang_getCursorExtent(cursor)); @@ -577,12 +645,15 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l if (fromCXString(clang_getCursorSpelling(cursor)).isEmpty()) // anonymous struct or union return CXChildVisit_Continue; Q_FALLTHROUGH(); - case CXCursor_ClassDecl: - case CXCursor_ClassTemplate: { + case CXCursor_ClassTemplate: + templateString = templateDecl(cursor); + Q_FALLTHROUGH(); + case CXCursor_ClassDecl: { if (!clang_isCursorDefinition(cursor)) return CXChildVisit_Continue; - if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit + if (findNodeForCursor(qdb_, + cursor)) // Was already parsed, propably in another translation unit return CXChildVisit_Continue; QString className = fromCXString(clang_getCursorSpelling(cursor)); @@ -603,14 +674,11 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l classe->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor))); classe->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor))); - if (kind == CXCursor_ClassTemplate) { - QString displayName = fromCXString(clang_getCursorSpelling(cursor)); - classe->setTemplateStuff(displayName.mid(className.size())); - } + if (kind == CXCursor_ClassTemplate) + classe->setTemplateDecl(templateString); QScopedValueRollback<Aggregate *> setParent(parent_, classe); return visitChildren(cursor); - } case CXCursor_CXXBaseSpecifier: { if (!parent_->isClassNode()) @@ -621,8 +689,9 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l auto baseNode = findNodeForCursor(qdb_, baseCursor); auto classe = static_cast<ClassNode *>(parent_); if (baseNode == nullptr || !baseNode->isClassNode()) { - QString bcName = fromCXString(clang_getCursorSpelling(baseCursor)); - classe->addUnresolvedBaseClass(access, QStringList(bcName), bcName); + QString bcName = reconstructQualifiedPathForCursor(baseCursor); + classe->addUnresolvedBaseClass( + access, bcName.split(QLatin1String("::"), Qt::SkipEmptyParts), bcName); return CXChildVisit_Continue; } auto baseClasse = static_cast<ClassNode *>(baseNode); @@ -633,7 +702,8 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l QString namespaceName = fromCXString(clang_getCursorDisplayName(cursor)); NamespaceNode *ns = nullptr; if (parent_) - ns = static_cast<NamespaceNode *>(parent_->findNonfunctionChild(namespaceName, &Node::isNamespace)); + ns = static_cast<NamespaceNode *>( + parent_->findNonfunctionChild(namespaceName, &Node::isNamespace)); if (!ns) { ns = new NamespaceNode(parent_, namespaceName); ns->setAccess(Node::Public); @@ -642,13 +712,16 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l QScopedValueRollback<Aggregate *> setParent(parent_, ns); return visitChildren(cursor); } - case CXCursor_FunctionDecl: case CXCursor_FunctionTemplate: + templateString = templateDecl(cursor); + Q_FALLTHROUGH(); + case CXCursor_FunctionDecl: case CXCursor_CXXMethod: case CXCursor_Constructor: case CXCursor_Destructor: case CXCursor_ConversionFunction: { - if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit + if (findNodeForCursor(qdb_, + cursor)) // Was already parsed, propably in another translation unit return CXChildVisit_Continue; QString name = functionName(cursor); if (ignoredSymbol(name)) @@ -679,14 +752,16 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l else if (kind == CXCursor_Destructor) fn->setMetaness(FunctionNode::Dtor); else - fn->setReturnType(adjustTypeName(fromCXString( - clang_getTypeSpelling(clang_getResultType(funcType))))); + fn->setReturnType(adjustTypeName( + fromCXString(clang_getTypeSpelling(clang_getResultType(funcType))))); fn->setStatic(clang_CXXMethod_isStatic(cursor)); fn->setConst(clang_CXXMethod_isConst(cursor)); - fn->setVirtualness(!clang_CXXMethod_isVirtual(cursor) ? FunctionNode::NonVirtual - : clang_CXXMethod_isPureVirtual(cursor) ? FunctionNode::PureVirtual - : FunctionNode::NormalVirtual); + fn->setVirtualness(!clang_CXXMethod_isVirtual(cursor) + ? FunctionNode::NonVirtual + : clang_CXXMethod_isPureVirtual(cursor) + ? FunctionNode::PureVirtual + : FunctionNode::NormalVirtual); CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType); if (refQualKind == CXRefQualifier_LValue) fn->setRef(true); @@ -738,6 +813,7 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l if (clang_isFunctionTypeVariadic(funcType)) parameters.append(QStringLiteral("...")); readParameterNamesAndAttributes(fn, cursor); + fn->setTemplateDecl(templateString); return CXChildVisit_Continue; } #if CINDEX_VERSION >= 36 @@ -746,16 +822,16 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l Aggregate *ns = parent_; while (ns && ns->isClassNode()) ns = ns->parent(); - QScopedValueRollback<Aggregate *>setParent(parent_, ns); + QScopedValueRollback<Aggregate *> setParent(parent_, ns); // Visit the friend functions return visitChildren(cursor); } #endif case CXCursor_EnumDecl: { - if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another tu - return CXChildVisit_Continue; + EnumNode *en = static_cast<EnumNode *>(findNodeForCursor(qdb_, cursor)); + if (en && en->items().count()) + return CXChildVisit_Continue; // Was already parsed, probably in another TU QString enumTypeName = fromCXString(clang_getCursorSpelling(cursor)); - EnumNode *en = nullptr; if (enumTypeName.isEmpty()) { enumTypeName = "anonymous"; if (parent_ && (parent_->isClassNode() || parent_->isNamespace())) { @@ -765,16 +841,16 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l } } if (!en) { - en = new EnumNode(parent_, enumTypeName); + en = new EnumNode(parent_, enumTypeName, clang_EnumDecl_isScoped(cursor)); en->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor))); en->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor))); } + // Enum values visitChildrenLambda(cursor, [&](CXCursor cur) { if (clang_getCursorKind(cur) != CXCursor_EnumConstantDecl) return CXChildVisit_Continue; - QString value; visitChildrenLambda(cur, [&](CXCursor cur) { if (clang_isExpression(clang_getCursorKind(cur))) { @@ -799,7 +875,8 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l } case CXCursor_FieldDecl: case CXCursor_VarDecl: { - if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit + if (findNodeForCursor(qdb_, + cursor)) // Was already parsed, propably in another translation unit return CXChildVisit_Continue; auto access = fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)); auto var = new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor))); @@ -810,7 +887,8 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l return CXChildVisit_Continue; } case CXCursor_TypedefDecl: { - if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit + if (findNodeForCursor(qdb_, + cursor)) // Was already parsed, propably in another translation unit return CXChildVisit_Continue; TypedefNode *td = new TypedefNode(parent_, fromCXString(clang_getCursorSpelling(cursor))); td->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor))); @@ -818,13 +896,14 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l // Search to see if this is a Q_DECLARE_FLAGS (if the type is QFlags<ENUM>) visitChildrenLambda(cursor, [&](CXCursor cur) { if (clang_getCursorKind(cur) != CXCursor_TemplateRef - || fromCXString(clang_getCursorSpelling(cur)) != QLatin1String("QFlags")) + || fromCXString(clang_getCursorSpelling(cur)) != QLatin1String("QFlags")) return CXChildVisit_Continue; // Found QFlags<XXX> visitChildrenLambda(cursor, [&](CXCursor cur) { if (clang_getCursorKind(cur) != CXCursor_TypeRef) return CXChildVisit_Continue; - auto *en = findNodeForCursor(qdb_, clang_getTypeDeclaration(clang_getCursorType(cur))); + auto *en = + findNodeForCursor(qdb_, clang_getTypeDeclaration(clang_getCursorType(cur))); if (en && en->isEnumType()) static_cast<EnumNode *>(en)->setFlagsType(td); return CXChildVisit_Break; @@ -836,10 +915,10 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l default: if (clang_isDeclaration(kind) && parent_->isClassNode()) { // maybe a static_assert (which is not exposed from the clang API) - QString spelling = getSpelling(clang_getCursorExtent(cursor)); + QString spelling = getSpelling(clang_getCursorExtent(cursor)); if (spelling.startsWith(QLatin1String("Q_PROPERTY")) - || spelling.startsWith(QLatin1String("QDOC_PROPERTY")) - || spelling.startsWith(QLatin1String("Q_OVERRIDE"))) { + || spelling.startsWith(QLatin1String("QDOC_PROPERTY")) + || spelling.startsWith(QLatin1String("Q_OVERRIDE"))) { parseProperty(spelling, fromCXSourceLocation(loc)); } } @@ -884,7 +963,7 @@ void ClangVisitor::readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cu } return CXChildVisit_Continue; }); - i++; + ++i; } return CXChildVisit_Continue; }); @@ -907,7 +986,7 @@ void ClangVisitor::parseProperty(const QString &spelling, const Location &loc) QString name = part.at(1); if (name.at(0) == QChar('*')) { type.append(QChar('*')); - name.remove(0,1); + name.remove(0, 1); } auto *property = new PropertyNode(parent_, name); property->setAccess(Node::Public); @@ -923,7 +1002,7 @@ void ClangVisitor::parseProperty(const QString &spelling, const Location &loc) property->setFinal(); } if (i < part.size()) { - QString value = part.at(i++); + QString value = part.at(i++); if (key == "READ") { qdb_->addPropertyFunction(property, value, PropertyNode::Getter); } else if (key == "WRITE") { @@ -990,14 +1069,14 @@ Node *ClangVisitor::nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocat // make sure the previous decl was finished. if (decl_it != declMap_.begin()) { - CXSourceLocation prevDeclEnd = clang_getRangeEnd(clang_getCursorExtent(*(decl_it-1))); + CXSourceLocation prevDeclEnd = clang_getRangeEnd(clang_getCursorExtent(*(decl_it - 1))); unsigned int prevDeclLine; clang_getPresumedLocation(prevDeclEnd, nullptr, &prevDeclLine, nullptr); if (prevDeclLine >= docloc.line) { // The previous declaration was still going. This is only valid if the previous // declaration is a parent of the next declaration. auto parent = clang_getCursorLexicalParent(*decl_it); - if (!clang_equalCursors(parent, *(decl_it-1))) + if (!clang_equalCursors(parent, *(decl_it - 1))) return nullptr; } } @@ -1021,8 +1100,9 @@ ClangCodeParser::~ClangCodeParser() \a config. Call the initializeParser() in the base class. Get the defines list from the qdocconf database. */ -void ClangCodeParser::initializeParser(const Config &config) +void ClangCodeParser::initializeParser() { + Config &config = Config::instance(); printParsingErrors_ = 1; version_ = config.getString(CONFIG_VERSION); const auto args = config.getStringList(CONFIG_INCLUDEPATHS); @@ -1039,13 +1119,13 @@ void ClangCodeParser::initializeParser(const Config &config) includePaths_.resize(squeezedArgs.size()); std::transform(squeezedArgs.begin(), squeezedArgs.end(), includePaths_.begin(), [](const QString &s) { - QByteArray path(s.toUtf8()); - QFileInfo fi(QDir::current(), s); - if (fi.exists()) - path = fi.canonicalFilePath().toUtf8(); - return path.prepend("-I"); - }); - CppCodeParser::initializeParser(config); + QByteArray path(s.toUtf8()); + QFileInfo fi(QDir::current(), s); + if (fi.exists()) + path = fi.canonicalFilePath().toUtf8(); + return path.prepend("-I"); + }); + CppCodeParser::initializeParser(); pchFileDir_.reset(nullptr); allHeaders_.clear(); pchName_.clear(); @@ -1073,8 +1153,8 @@ void ClangCodeParser::initializeParser(const Config &config) } } } - qCDebug(lcQdoc).nospace() << __FUNCTION__ << " Clang v" << CINDEX_VERSION_MAJOR - << '.' << CINDEX_VERSION_MINOR; + qCDebug(lcQdoc).nospace() << __FUNCTION__ << " Clang v" << CINDEX_VERSION_MAJOR << '.' + << CINDEX_VERSION_MINOR; } /*! @@ -1096,7 +1176,12 @@ QString ClangCodeParser::language() */ QStringList ClangCodeParser::headerFileNameFilter() { - return QStringList() << "*.ch" << "*.h" << "*.h++" << "*.hh" << "*.hpp" << "*.hxx"; + return QStringList() << "*.ch" + << "*.h" + << "*.h++" + << "*.hh" + << "*.hpp" + << "*.hxx"; } /*! @@ -1105,7 +1190,11 @@ QStringList ClangCodeParser::headerFileNameFilter() */ QStringList ClangCodeParser::sourceFileNameFilter() { - return QStringList() << "*.c++" << "*.cc" << "*.cpp" << "*.cxx" << "*.mm"; + return QStringList() << "*.c++" + << "*.cc" + << "*.cpp" + << "*.cxx" + << "*.mm"; } /*! @@ -1120,7 +1209,7 @@ void ClangCodeParser::parseHeaderFile(const Location & /*location*/, const QStri } static const char *defaultArgs_[] = { - "-std=c++14", + "-std=c++17", #ifndef Q_OS_WIN "-fPIC", #else @@ -1156,10 +1245,10 @@ void ClangCodeParser::getDefaultArgs() static QVector<QByteArray> includePathsFromHeaders(const QHash<QString, QString> &allHeaders) { QVector<QByteArray> result; - for (auto it = allHeaders.cbegin(), end = allHeaders.cend(); it != end; ++it) { + for (auto it = allHeaders.cbegin(); it != allHeaders.cend(); ++it) { const QByteArray path = "-I" + it.value().toLatin1(); - const QByteArray parent = "-I" - + QDir::cleanPath(it.value() + QLatin1String("/../")).toLatin1(); + const QByteArray parent = + "-I" + QDir::cleanPath(it.value() + QLatin1String("/../")).toLatin1(); if (!result.contains(path)) result.append(path); if (!result.contains(parent)) @@ -1191,7 +1280,7 @@ bool ClangCodeParser::getMoreArgs() of reasonable places to look for include files and use that list instead. */ - Location::logToStdErrAlways("No include paths passed to qdoc; guessing reasonable include paths"); + qCWarning(lcQdoc) << "No include paths passed to qdoc; guessing reasonable include paths"; guessedIncludePaths = true; auto forest = qdb_->searchOrder(); @@ -1199,8 +1288,7 @@ bool ClangCodeParser::getMoreArgs() QString basicIncludeDir = QDir::cleanPath(QString(Config::installDir + "/../include")); moreArgs_ += "-I" + basicIncludeDir.toLatin1(); moreArgs_ += includePathsFromHeaders(allHeaders_); - } - else { + } else { moreArgs_ = includePaths_; } @@ -1217,22 +1305,28 @@ void ClangCodeParser::buildPCH() if (!pchFileDir_ && !moduleHeader().isEmpty()) { pchFileDir_.reset(new QTemporaryDir(QDir::tempPath() + QLatin1String("/qdoc_pch"))); if (pchFileDir_->isValid()) { - //const QByteArray module = qdb_->primaryTreeRoot()->tree()->camelCaseModuleName().toUtf8(); + // const QByteArray module = + // qdb_->primaryTreeRoot()->tree()->camelCaseModuleName().toUtf8(); const QByteArray module = moduleHeader().toUtf8(); QByteArray header; QByteArray privateHeaderDir; - Location::logToStdErrAlways("Build & visit PCH for " + moduleHeader()); + qCDebug(lcQdoc) << "Build and visit PCH for" << moduleHeader(); // A predicate for std::find_if() to locate a path to the module's header // (e.g. QtGui/QtGui) to be used as pre-compiled header - struct FindPredicate { + struct FindPredicate + { enum SearchType { Any, Module, Private }; QByteArray &candidate_; const QByteArray &module_; SearchType type_; - FindPredicate(QByteArray &candidate, const QByteArray &module, SearchType type = Any) - : candidate_(candidate), module_(module), type_(type) {} + FindPredicate(QByteArray &candidate, const QByteArray &module, + SearchType type = Any) + : candidate_(candidate), module_(module), type_(type) + { + } - bool operator()(const QByteArray &p) const { + bool operator()(const QByteArray &p) const + { if (type_ != Any && !p.endsWith(module_)) return false; candidate_ = p + "/"; @@ -1255,27 +1349,25 @@ void ClangCodeParser::buildPCH() // First, search for an include path that contains the module name, then any path QByteArray candidate; - auto it = std::find_if(includePaths_.begin(), - includePaths_.end(), + auto it = std::find_if(includePaths_.begin(), includePaths_.end(), FindPredicate(candidate, module, FindPredicate::Module)); if (it == includePaths_.end()) - it = std::find_if(includePaths_.begin(), - includePaths_.end(), + it = std::find_if(includePaths_.begin(), includePaths_.end(), FindPredicate(candidate, module, FindPredicate::Any)); if (it != includePaths_.end()) header = candidate; // Find the path to module's private headers - currently unused - it = std::find_if(includePaths_.begin(), - includePaths_.end(), + it = std::find_if(includePaths_.begin(), includePaths_.end(), FindPredicate(candidate, module, FindPredicate::Private)); if (it != includePaths_.end()) privateHeaderDir = candidate; if (header.isEmpty()) { qWarning() << "(qdoc) Could not find the module header in include paths for module" - << module << " (include paths: "<< includePaths_ << ")"; - qWarning() << " Artificial module header built from header dirs in qdocconf file"; + << module << " (include paths: " << includePaths_ << ")"; + qWarning() << " Artificial module header built from header dirs in qdocconf " + "file"; } args_.push_back("-xc++"); CXTranslationUnit tu; @@ -1284,13 +1376,12 @@ void ClangCodeParser::buildPCH() if (tmpHeaderFile.open(QIODevice::Text | QIODevice::WriteOnly)) { QTextStream out(&tmpHeaderFile); if (header.isEmpty()) { - QList<QString> keys = allHeaders_.keys(); - QList<QString> values = allHeaders_.values(); - for (int i = 0; i < keys.size(); i++) { - if (!keys.at(i).endsWith(QLatin1String("_p.h")) && - !keys.at(i).startsWith(QLatin1String("moc_"))) { - QString line = QLatin1String("#include \"") + values.at(i) + - QLatin1String("/") + keys.at(i) + QLatin1String("\""); + for (auto it = allHeaders_.constKeyValueBegin(); + it != allHeaders_.constKeyValueEnd(); ++it) { + if (!(*it).first.endsWith(QLatin1String("_p.h")) + && !(*it).first.startsWith(QLatin1String("moc_"))) { + QString line = QLatin1String("#include \"") + (*it).second + + QLatin1String("/") + (*it).first + QLatin1String("\""); out << line << "\n"; } } @@ -1307,34 +1398,35 @@ void ClangCodeParser::buildPCH() out << line << "\n"; } } - tmpHeaderFile.close(); + tmpHeaderFile.close(); } if (printParsingErrors_ == 0) - Location::logToStdErrAlways("clang not printing errors; include paths were guessed"); - CXErrorCode err = clang_parseTranslationUnit2(index_, - tmpHeader.toLatin1().data(), - args_.data(), static_cast<int>(args_.size()), nullptr, 0, - flags_ | CXTranslationUnit_ForSerialization, &tu); - qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" - << tmpHeader << args_ << ") returns" << err; + qCWarning(lcQdoc) << "clang not printing errors; include paths were guessed"; + CXErrorCode err = + clang_parseTranslationUnit2(index_, tmpHeader.toLatin1().data(), args_.data(), + static_cast<int>(args_.size()), nullptr, 0, + flags_ | CXTranslationUnit_ForSerialization, &tu); + qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << tmpHeader << args_ + << ") returns" << err; if (!err && tu) { pchName_ = pchFileDir_->path().toUtf8() + "/" + module + ".pch"; - auto error = clang_saveTranslationUnit(tu, pchName_.constData(), clang_defaultSaveOptions(tu)); + auto error = clang_saveTranslationUnit(tu, pchName_.constData(), + clang_defaultSaveOptions(tu)); if (error) { - Location::logToStdErrAlways("Could not save PCH file for " + moduleHeader()); + qCCritical(lcQdoc) << "Could not save PCH file for" << moduleHeader(); pchName_.clear(); - } - else { - // Visit the header now, as token from pre-compiled header won't be visited later + } else { + // Visit the header now, as token from pre-compiled header won't be visited + // later CXCursor cur = clang_getTranslationUnitCursor(tu); ClangVisitor visitor(qdb_, allHeaders_); visitor.visitChildren(cur); - Location::logToStdErrAlways("PCH built & visited for " + moduleHeader()); + qCDebug(lcQdoc) << "PCH built and visited for" << moduleHeader(); } clang_disposeTranslationUnit(tu); } else { pchFileDir_->remove(); - Location::logToStdErrAlways("Could not create PCH file for " + moduleHeader()); + qCCritical(lcQdoc) << "Could not create PCH file for " << moduleHeader(); } args_.pop_back(); // remove the "-xc++"; } @@ -1352,7 +1444,9 @@ void ClangCodeParser::precompileHeaders() for (const auto &p : qAsConst(moreArgs_)) args_.push_back(p.constData()); - flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing); + flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete + | CXTranslationUnit_SkipFunctionBodies + | CXTranslationUnit_KeepGoing); // 1 as 2nd parameter tells clang to report parser errors. index_ = clang_createIndex(1, printParsingErrors_); buildPCH(); @@ -1373,7 +1467,7 @@ static float getUnpatchedVersion(QString t) Call matchDocsAndStuff() to do all the parsing and tree building. */ -void ClangCodeParser::parseSourceFile(const Location &/*location*/, const QString &filePath) +void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QString &filePath) { /* The set of open namespaces is cleared before parsing @@ -1381,7 +1475,9 @@ void ClangCodeParser::parseSourceFile(const Location &/*location*/, const QStrin */ qdb_->clearOpenNamespaces(); currentFile_ = filePath; - flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing); + flags_ = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete + | CXTranslationUnit_SkipFunctionBodies + | CXTranslationUnit_KeepGoing); index_ = clang_createIndex(1, 0); getDefaultArgs(); @@ -1395,24 +1491,25 @@ void ClangCodeParser::parseSourceFile(const Location &/*location*/, const QStrin args_.push_back(p.constData()); CXTranslationUnit tu; - CXErrorCode err = clang_parseTranslationUnit2(index_, filePath.toLocal8Bit(), args_.data(), - static_cast<int>(args_.size()), nullptr, 0, flags_, &tu); - qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" - << filePath << args_ << ") returns" << err; + CXErrorCode err = + clang_parseTranslationUnit2(index_, filePath.toLocal8Bit(), args_.data(), + static_cast<int>(args_.size()), nullptr, 0, flags_, &tu); + qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << filePath << args_ + << ") returns" << err; if (err || !tu) { qWarning() << "(qdoc) Could not parse source file" << filePath << " error code:" << err; clang_disposeIndex(index_); return; } - CXCursor cur = clang_getTranslationUnitCursor(tu); + CXCursor tuCur = clang_getTranslationUnitCursor(tu); ClangVisitor visitor(qdb_, allHeaders_); - visitor.visitChildren(cur); + visitor.visitChildren(tuCur); CXToken *tokens; unsigned int numTokens = 0; const QSet<QString> &commands = topicCommands() + metaCommands(); - clang_tokenize(tu, clang_getCursorExtent(cur), &tokens, &numTokens); + clang_tokenize(tu, clang_getCursorExtent(tuCur), &tokens, &numTokens); for (unsigned int i = 0; i < numTokens; ++i) { if (clang_getTokenKind(tokens[i]) != CXToken_Comment) @@ -1421,9 +1518,10 @@ void ClangCodeParser::parseSourceFile(const Location &/*location*/, const QStrin if (!comment.startsWith("/*!")) continue; - auto loc = fromCXSourceLocation(clang_getTokenLocation(tu, tokens[i])); + auto commentLoc = clang_getTokenLocation(tu, tokens[i]); + auto loc = fromCXSourceLocation(commentLoc); auto end_loc = fromCXSourceLocation(clang_getRangeEnd(clang_getTokenExtent(tu, tokens[i]))); - Doc::trimCStyleComment(loc,comment); + Doc::trimCStyleComment(loc, comment); // Doc constructor parses the comment. Doc doc(loc, end_loc, comment, commands, topicCommands()); @@ -1438,14 +1536,13 @@ void ClangCodeParser::parseSourceFile(const Location &/*location*/, const QStrin topic = topics[0].topic; if (topic.isEmpty()) { - CXSourceLocation commentLoc = clang_getTokenLocation(tu, tokens[i]); Node *n = nullptr; if (i + 1 < numTokens) { // Try to find the next declaration. CXSourceLocation nextCommentLoc = commentLoc; - while (i + 2 < numTokens && clang_getTokenKind(tokens[i+1]) != CXToken_Comment) + while (i + 2 < numTokens && clang_getTokenKind(tokens[i + 1]) != CXToken_Comment) ++i; // already skip all the tokens that are not comments - nextCommentLoc = clang_getTokenLocation(tu, tokens[i+1]); + nextCommentLoc = clang_getTokenLocation(tu, tokens[i + 1]); n = visitor.nodeForCommentAtLocation(commentLoc, nextCommentLoc); } @@ -1465,10 +1562,22 @@ void ClangCodeParser::parseSourceFile(const Location &/*location*/, const QStrin "topic command (e.g., '\\%1', '\\%2') in the " "comment and no function definition following " "the comment.") - .arg(COMMAND_FN).arg(COMMAND_PAGE)); + .arg(COMMAND_FN) + .arg(COMMAND_PAGE)); } } } else { + // Store the namespace scope from lexical parents of the comment + namespaceScope_.clear(); + CXCursor cur = clang_getCursor(tu, commentLoc); + while (true) { + CXCursorKind kind = clang_getCursorKind(cur); + if (clang_isTranslationUnit(kind) || clang_isInvalid(kind)) + break; + if (kind == CXCursor_Namespace) + namespaceScope_ << fromCXString(clang_getCursorSpelling(cur)); + cur = clang_getCursorLexicalParent(cur); + } processTopicArgs(doc, topic, nodes, docs); } processMetaCommands(nodes, docs); @@ -1477,6 +1586,7 @@ void ClangCodeParser::parseSourceFile(const Location &/*location*/, const QStrin clang_disposeTokens(tu, tokens, numTokens); clang_disposeTranslationUnit(tu); clang_disposeIndex(index_); + namespaceScope_.clear(); } /*! @@ -1499,7 +1609,9 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnArg QString tag = fnArg.left(end + 1); fnNode = qdb_->findFunctionNodeForTag(tag); if (!fnNode) { - location.error(ClangCodeParser::tr("tag \\fn %1 not used in any include file in current module").arg(tag)); + location.error(ClangCodeParser::tr( + "tag \\fn %1 not used in any include file in current module") + .arg(tag)); } else { /* The function node was found. Use the formal @@ -1522,7 +1634,7 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnArg QString pName = blankSplit.last(); int j = 0; while (j < pName.length() && !pName.at(i).isLetter()) - j++; + ++j; if (j > 0) pName = pName.mid(j); if (!pName.isEmpty() && pName != parameters[i].name()) @@ -1537,9 +1649,9 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnArg } return fnNode; } - CXTranslationUnit_Flags flags = static_cast<CXTranslationUnit_Flags>(CXTranslationUnit_Incomplete | - CXTranslationUnit_SkipFunctionBodies | - CXTranslationUnit_KeepGoing); + CXTranslationUnit_Flags flags = static_cast<CXTranslationUnit_Flags>( + CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies + | CXTranslationUnit_KeepGoing); // Change 2nd parameter to 1 to make clang report errors. CXIndex index = clang_createIndex(1, Utilities::debugging() ? 1 : 0); @@ -1553,21 +1665,21 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnArg args.push_back(pchName_.constData()); } CXTranslationUnit tu; - QByteArray fn = fnArg.toUtf8(); + QByteArray fn; + for (const auto &ns : qAsConst(namespaceScope_)) + fn.prepend("namespace " + ns.toUtf8() + " {"); + fn += fnArg.toUtf8(); if (!fn.endsWith(";")) fn += "{ }"; + fn.append(namespaceScope_.size(), '}'); + const char *dummyFileName = "/fn_dummyfile.cpp"; CXUnsavedFile unsavedFile { dummyFileName, fn.constData(), static_cast<unsigned long>(fn.size()) }; - CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, - args.data(), - args.size(), - &unsavedFile, - 1, - flags, - &tu); - qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" - << dummyFileName << args << ") returns" << err; + CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName, args.data(), args.size(), + &unsavedFile, 1, flags, &tu); + qCDebug(lcQdoc) << __FUNCTION__ << "clang_parseTranslationUnit2(" << dummyFileName << args + << ") returns" << err; if (err || !tu) { location.error(ClangCodeParser::tr("clang could not parse \\fn %1").arg(fnArg)); clang_disposeTranslationUnit(tu); @@ -1591,7 +1703,8 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnArg */ if (fnNode == nullptr) { unsigned diagnosticCount = clang_getNumDiagnostics(tu); - if (diagnosticCount > 0 && (!Generator::preparing() || Generator::singleExec())) { + const auto &config = Config::instance(); + if (diagnosticCount > 0 && (!config.preparing() || config.singleExec())) { bool report = true; QStringList signature = fnArg.split(QChar('(')); if (signature.size() > 1) { @@ -1610,10 +1723,12 @@ Node *ClangCodeParser::parseFnArg(const Location &location, const QString &fnArg } } if (report) { - location.warning(ClangCodeParser::tr("clang found diagnostics parsing \\fn %1").arg(fnArg)); + location.warning(ClangCodeParser::tr("clang found diagnostics parsing \\fn %1") + .arg(fnArg)); for (unsigned i = 0; i < diagnosticCount; ++i) { CXDiagnostic diagnostic = clang_getDiagnostic(tu, i); - location.report(tr(" %1").arg(fromCXString(clang_formatDiagnostic(diagnostic, 0)))); + location.report(tr(" %1").arg( + fromCXString(clang_formatDiagnostic(diagnostic, 0)))); } } } |