From ea29dae17dd4238c259a05667ebd8ce7ca54b4fd Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Sun, 16 Feb 2020 16:33:22 +0100 Subject: qdoc: Store and use the current namespace scope for \fn comments Writing \fn commands for member functions of classes declared under a namespace was error-prone as QDoc required the fully qualified paths for the function signature, even though the documentation comment was located under the correct namespace scope. Store the scope by backtracking the Clang AST until we reach the level of the translation unit, and store each encountered namespace declaration into a list. When generating the temporary source file for an \fn command, we can then recreate the correct namespace hierarchy. This does not break the existing \fn command usage as it's perfectly valid, even if unnecessary, to provide fully qualified paths in the function signature. [ChangeLog][qdoc] QDoc is now aware of the namespace scope of an \fn command without requiring fully qualified paths. Fixes: QTBUG-82190 Change-Id: Ia446c19d130b2ef48b16b67e4dfcbdaab1f9d4f5 Reviewed-by: Paul Wicking --- src/qdoc/clangcodeparser.cpp | 30 +++++++++++++++++++++++------ src/qdoc/clangcodeparser.h | 1 + tests/auto/qdoc/generatedoutput/testcpp.cpp | 3 ++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index 89ef4f067..2f167eaad 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -593,6 +593,8 @@ CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocat bool &ignoreSignature) { switch (clang_getCursorKind(cursor)) { + case CXCursor_Namespace: + return CXChildVisit_Recurse; case CXCursor_FunctionDecl: case CXCursor_FunctionTemplate: case CXCursor_CXXMethod: @@ -1503,14 +1505,14 @@ void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QStri 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 &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) @@ -1519,7 +1521,8 @@ void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QStri 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); @@ -1536,7 +1539,6 @@ void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QStri 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. @@ -1568,6 +1570,17 @@ void ClangCodeParser::parseSourceFile(const Location & /*location*/, const QStri } } } 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); @@ -1654,9 +1667,14 @@ 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(fn.size()) }; diff --git a/src/qdoc/clangcodeparser.h b/src/qdoc/clangcodeparser.h index 078d307c2..9af292e67 100644 --- a/src/qdoc/clangcodeparser.h +++ b/src/qdoc/clangcodeparser.h @@ -78,6 +78,7 @@ private: QVector defines_; std::vector args_; QVector moreArgs_; + QStringList namespaceScope_; }; QT_END_NAMESPACE diff --git a/tests/auto/qdoc/generatedoutput/testcpp.cpp b/tests/auto/qdoc/generatedoutput/testcpp.cpp index b703a844b..5d3055ac2 100644 --- a/tests/auto/qdoc/generatedoutput/testcpp.cpp +++ b/tests/auto/qdoc/generatedoutput/testcpp.cpp @@ -156,7 +156,8 @@ void TestDerived::virtualFun() /*! \fn TestQDoc::Test::overload() - \fn TestQDoc::Test::overload(bool b) + \fn Test::overload(bool b) + //! The second overload should match even without the fully qualified path Overloads that share a documentation comment, optionally taking a parameter \a b. -- cgit v1.2.3