diff options
author | Topi Reinio <topi.reinio@qt.io> | 2020-01-15 11:08:27 +0100 |
---|---|---|
committer | Topi Reinio <topi.reinio@qt.io> | 2020-01-20 13:11:50 +0100 |
commit | 46f71fcce6d278e62ec5a766d769681400b330ed (patch) | |
tree | a5c6395ff780c1335958534b37b0b781b243d430 | |
parent | 03f1c6e3ed01f8270463ab8aa4afb4f7c2321cdb (diff) |
qdoc: Parse enum classes correctly
Clang provides us sufficient information to detect a scoped enum (an
enum class). For such enums, include the enum type name into the fully
qualified name of the enum values. For enums related to \headerfile
nodes, drop the '<Header>::' from the qualified name of the enum values
as they are not valid. Ensure that this also applies to all node types
in Node::plainFullName(), as including file names into a qualified path is
always incorrect.
In some cases, enum classes were also forward declared. This was a
problem as QDoc skipped the full declaration after seeing the forward
one. Combat this by re-processing the enum declarations that do not
contain any values yet.
Add relevant test case.
[ChangeLog][qdoc] QDoc now generates correct documentation for enum
classes.
Fixes: QTBUG-66740
Change-Id: I36dcb3393500acb788e0a305fd0dfecc4b58ebc3
Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
-rw-r--r-- | src/qdoc/clangcodeparser.cpp | 9 | ||||
-rw-r--r-- | src/qdoc/cppcodemarker.cpp | 21 | ||||
-rw-r--r-- | src/qdoc/node.cpp | 17 | ||||
-rw-r--r-- | src/qdoc/node.h | 7 | ||||
-rw-r--r-- | src/qdoc/qdocindexfiles.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qdoc/generatedoutput/expected_output/scopedenum/testqdoc-test.html | 108 | ||||
-rw-r--r-- | tests/auto/qdoc/generatedoutput/scopedenum.qdoc | 35 | ||||
-rw-r--r-- | tests/auto/qdoc/generatedoutput/scopedenum.qdocconf | 7 | ||||
-rw-r--r-- | tests/auto/qdoc/generatedoutput/testcpp.h | 8 | ||||
-rw-r--r-- | tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp | 6 |
10 files changed, 198 insertions, 24 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index 848f2b828..ed5272b8a 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -827,10 +827,10 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l } #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())) { @@ -840,10 +840,11 @@ 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) diff --git a/src/qdoc/cppcodemarker.cpp b/src/qdoc/cppcodemarker.cpp index 25c2f630b..38dd8e79f 100644 --- a/src/qdoc/cppcodemarker.cpp +++ b/src/qdoc/cppcodemarker.cpp @@ -221,7 +221,10 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati break; case Node::Enum: enume = static_cast<const EnumNode *>(node); - synopsis = "enum " + name; + synopsis = "enum "; + if (enume->isScoped()) + synopsis += "class "; + synopsis += name; if (style == Section::Summary) { synopsis += " { "; @@ -382,18 +385,18 @@ QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, const Node *r return enumValue; const Node *node = relative->parent(); - QString fullName; - while (node->parent()) { - fullName.prepend(markedUpName(node)); + QStringList parts; + while (!node->isHeader() && node->parent()) { + parts.prepend(markedUpName(node)); if (node->parent() == relative || node->parent()->name().isEmpty()) break; - fullName.prepend("<@op>::</@op>"); node = node->parent(); } - if (!fullName.isEmpty()) - fullName.append("<@op>::</@op>"); - fullName.append(enumValue); - return fullName; + if (static_cast<const EnumNode *>(relative)->isScoped()) + parts.append(relative->name()); + + parts.append(enumValue); + return parts.join(QLatin1String("<@op>::</@op>")); } QString CppCodeMarker::markedUpIncludes(const QStringList &includes) diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp index cd89737d7..9d9b9941b 100644 --- a/src/qdoc/node.cpp +++ b/src/qdoc/node.cpp @@ -600,25 +600,24 @@ QString Node::plainName() const /*! Constructs and returns the node's fully qualified name by recursively ascending the parent links and prepending each - parent name + "::". Breaks out when the parent pointer is - \a relative. Almost all calls to this function pass 0 for - \a relative. + parent name + "::". Breaks out when reaching a HeaderNode, + or when the parent pointer is \a relative. Typically, calls + to this function pass \c nullptr for \a relative. */ QString Node::plainFullName(const Node *relative) const { if (name_.isEmpty()) return QLatin1String("global"); - QString fullName; + QStringList parts; const Node *node = this; - while (node) { - fullName.prepend(node->plainName()); + while (node && !node->isHeader()) { + parts.prepend(node->plainName()); if (node->parent() == relative || node->parent()->name().isEmpty()) - break; - fullName.prepend(QLatin1String("::")); + break; node = node->parent(); } - return fullName; + return parts.join(QLatin1String("::")); } /*! diff --git a/src/qdoc/node.h b/src/qdoc/node.h index ea075e6ed..f0243a8b0 100644 --- a/src/qdoc/node.h +++ b/src/qdoc/node.h @@ -864,13 +864,17 @@ private: class EnumNode : public Node { public: - EnumNode(Aggregate *parent, const QString &name) : Node(Enum, parent, name), flagsType_(nullptr) + EnumNode(Aggregate *parent, const QString &name, bool isScoped = false) + : Node(Enum, parent, name), + flagsType_(nullptr), + isScoped_(isScoped) { } void addItem(const EnumItem &item); void setFlagsType(TypedefNode *typedeff); bool hasItem(const QString &name) const { return names_.contains(name); } + bool isScoped() const { return isScoped_; } const QVector<EnumItem> &items() const { return items_; } Access itemAccess(const QString &name) const; @@ -882,6 +886,7 @@ private: QVector<EnumItem> items_; QSet<QString> names_; const TypedefNode *flagsType_; + bool isScoped_; }; class TypedefNode : public Node diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp index fafeb006c..c68d675ea 100644 --- a/src/qdoc/qdocindexfiles.cpp +++ b/src/qdoc/qdocindexfiles.cpp @@ -421,7 +421,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current, node = pn; } else if (elementName == QLatin1String("enum")) { - EnumNode *enumNode = new EnumNode(parent, name); + EnumNode *enumNode = new EnumNode(parent, name, attributes.hasAttribute("scoped")); if (!indexUrl.isEmpty()) location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html"); @@ -1175,6 +1175,8 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter &writer, Node *node, } break; case Node::Enum: { const EnumNode *enumNode = static_cast<const EnumNode *>(node); + if (enumNode->isScoped()) + writer.writeAttribute("scoped", "true"); if (enumNode->flagsType()) writer.writeAttribute("typedef", enumNode->flagsType()->fullDocumentName()); const auto &items = enumNode->items(); diff --git a/tests/auto/qdoc/generatedoutput/expected_output/scopedenum/testqdoc-test.html b/tests/auto/qdoc/generatedoutput/expected_output/scopedenum/testqdoc-test.html new file mode 100644 index 000000000..145fc724a --- /dev/null +++ b/tests/auto/qdoc/generatedoutput/expected_output/scopedenum/testqdoc-test.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> +<!-- testcpp.cpp --> + <title>Test Class | TestCPP</title> +</head> +<body> +<li>Test</li> +<div class="sidebar"> +<div class="toc"> +<h3><a name="toc">Contents</a></h3> +<ul> +<li class="level1"><a href="#public-types">Public Types</a></li> +<li class="level1"><a href="#public-functions">Public Functions</a></li> +<li class="level1"><a href="#protected-functions">Protected Functions</a></li> +<li class="level1"><a href="#macros">Macros</a></li> +<li class="level1"><a href="#details">Detailed Description</a></li> +</ul> +</div> +<div class="sidebar-content" id="sidebar-content"></div></div> +<h1 class="title">Test Class</h1> +<span class="small-subtitle">class <a href="testqdoc.html">TestQDoc</a>::Test</span> +<!-- $$$Test-brief --> +<p>A class in a namespace. <a href="#details">More...</a></p> +<!-- @@@Test --> +<div class="table"><table class="alignedsummary"> +<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include <Test></span> +</td></tr><tr><td class="memItemLeft rightAlign topAlign"> qmake:</td><td class="memItemRight bottomAlign"> QT += testcpp</td></tr><tr><td class="memItemLeft rightAlign topAlign"> Inherited By:</td><td class="memItemRight bottomAlign"> <p><a href="testqdoc-testderived.html">TestQDoc::TestDerived</a></p> +</td></tr></table></div><ul> +<li><a href="testqdoc-test-members.html">List of all members, including inherited members</a></li> +<li><a href="testqdoc-test-obsolete.html">Obsolete members</a></li> +</ul> +<a name="public-types"></a> +<h2 id="public-types">Public Types</h2> +<div class="table"><table class="alignedsummary"> +<tr><td class="memItemLeft rightAlign topAlign"> enum class </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#ScopedEnum-enum">ScopedEnum</a></b> { This, That, All }</td></tr> +</table></div> +<a name="public-functions"></a> +<h2 id="public-functions">Public Functions</h2> +<div class="table"><table class="alignedsummary"> +<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#inlineFunction">inlineFunction</a></b>()</td></tr> +<tr><td class="memItemLeft rightAlign topAlign"> int </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunction">someFunction</a></b>(int <i>v</i>)</td></tr> +<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#someFunctionDefaultArg">someFunctionDefaultArg</a></b>(int <i>i</i>, bool <i>b</i> = false)</td></tr> +<tr><td class="memItemLeft rightAlign topAlign"> virtual void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#virtualFun">virtualFun</a></b>()</td></tr> +</table></div> +<a name="protected-functions"></a> +<h2 id="protected-functions">Protected Functions</h2> +<div class="table"><table class="alignedsummary"> +<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload">overload</a></b>()</td></tr> +<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#overload-1">overload</a></b>(bool <i>b</i>)</td></tr> +</table></div> +<a name="macros"></a> +<h2 id="macros">Macros</h2> +<div class="table"><table class="alignedsummary"> +<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="testqdoc-test.html#QDOCTEST_MACRO2">QDOCTEST_MACRO2</a></b>(<i>x</i>)</td></tr> +</table></div> +<a name="details"></a> +<!-- $$$Test-description --> +<div class="descr"> +<h2 id="details">Detailed Description</h2> +</div> +<!-- @@@Test --> +<div class="types"> +<h2>Member Type Documentation</h2> +<!-- $$$ScopedEnum$$$This$$$That$$$All$$$OmittedValue --> +<h3 class="fn" id="ScopedEnum-enum"><a name="ScopedEnum-enum"></a>enum class Test::<span class="name">ScopedEnum</span></h3> +<div class="table"><table class="valuelist"><tr valign="top" class="odd"><th class="tblConst">Constant</th><th class="tblval">Value</th><th class="tbldscr">Description</th></tr> +<tr><td class="topAlign"><code>TestQDoc::Test::ScopedEnum::This</code></td><td class="topAlign tblval"><code>0x01</code></td><td class="topAlign">Something</td></tr> +<tr><td class="topAlign"><code>TestQDoc::Test::ScopedEnum::That</code></td><td class="topAlign tblval"><code>0x02</code></td><td class="topAlign">Something else</td></tr> +<tr><td class="topAlign"><code>TestQDoc::Test::ScopedEnum::All</code></td><td class="topAlign tblval"><code>This | That</code></td><td class="topAlign">Everything</td></tr> +</table></div> +<!-- @@@ScopedEnum --> +</div> +<div class="func"> +<h2>Member Function Documentation</h2> +<!-- $$$ --> +<div class="fngroup"> +<h3 class="fn fngroupitem" id="overload"><a name="overload"></a><code>[protected] </code><span class="type">void</span> Test::<span class="name">overload</span>()</h3><h3 class="fn fngroupitem" id="overload-1"><a name="overload-1"></a><code>[protected] </code><span class="type">void</span> Test::<span class="name">overload</span>(<span class="type">bool</span> <i>b</i>)</h3></div> +<p>Overloads that share a documentation comment, optionally taking a parameter <i>b</i>.</p> +<!-- @@@ --> +<!-- $$$inlineFunction[overload1]$$$inlineFunction --> +<h3 class="fn" id="inlineFunction"><a name="inlineFunction"></a><span class="type">void</span> Test::<span class="name">inlineFunction</span>()</h3> +<p>An inline function, documented using the \fn QDoc command.</p> +<!-- @@@inlineFunction --> +<!-- $$$someFunction[overload1]$$$someFunctionint --> +<h3 class="fn" id="someFunction"><a name="someFunction"></a><span class="type">int</span> Test::<span class="name">someFunction</span>(<span class="type">int</span> <i>v</i>)</h3> +<p>Function that takes a parameter <i>v</i>. Also returns the value of <i>v</i>.</p> +<!-- @@@someFunction --> +<!-- $$$someFunctionDefaultArg[overload1]$$$someFunctionDefaultArgintbool --> +<h3 class="fn" id="someFunctionDefaultArg"><a name="someFunctionDefaultArg"></a><span class="type">void</span> Test::<span class="name">someFunctionDefaultArg</span>(<span class="type">int</span> <i>i</i>, <span class="type">bool</span> <i>b</i> = false)</h3> +<p>Function that takes a parameter <i>i</i> and <i>b</i>.</p> +<!-- @@@someFunctionDefaultArg --> +<!-- $$$virtualFun[overload1]$$$virtualFun --> +<h3 class="fn" id="virtualFun"><a name="virtualFun"></a><code>[virtual] </code><span class="type">void</span> Test::<span class="name">virtualFun</span>()</h3> +<p>Function that must be reimplemented.</p> +<!-- @@@virtualFun --> +</div> +<div class="macros"> +<h2>Macro Documentation</h2> +<!-- $$$QDOCTEST_MACRO2[overload1]$$$QDOCTEST_MACRO2 --> +<h3 class="fn" id="QDOCTEST_MACRO2"><a name="QDOCTEST_MACRO2"></a><span class="name">QDOCTEST_MACRO2</span>(<i>x</i>)</h3> +<p>A macro with argument <i>x</i>.</p> +<p>This function was introduced in Test 1.1.</p> +<!-- @@@QDOCTEST_MACRO2 --> +</div> +</body> +</html> diff --git a/tests/auto/qdoc/generatedoutput/scopedenum.qdoc b/tests/auto/qdoc/generatedoutput/scopedenum.qdoc new file mode 100644 index 000000000..4262c1ce6 --- /dev/null +++ b/tests/auto/qdoc/generatedoutput/scopedenum.qdoc @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \enum TestQDoc::Test::ScopedEnum + + \value This Something + \value That Something else + \value All Everything + \omitvalue OmittedValue +*/ diff --git a/tests/auto/qdoc/generatedoutput/scopedenum.qdocconf b/tests/auto/qdoc/generatedoutput/scopedenum.qdocconf new file mode 100644 index 000000000..f967e266d --- /dev/null +++ b/tests/auto/qdoc/generatedoutput/scopedenum.qdocconf @@ -0,0 +1,7 @@ +include(testcpp.qdocconf) +defines += test_scopedenum + +sources += scopedenum.qdoc + +HTML.nosubdirs = true +HTML.outputsubdir = scopedenum diff --git a/tests/auto/qdoc/generatedoutput/testcpp.h b/tests/auto/qdoc/generatedoutput/testcpp.h index 5cb542dc4..4ae33a404 100644 --- a/tests/auto/qdoc/generatedoutput/testcpp.h +++ b/tests/auto/qdoc/generatedoutput/testcpp.h @@ -33,6 +33,14 @@ namespace TestQDoc { class Test { public: +#ifdef test_scopedenum + enum class ScopedEnum : unsigned char { + This = 0x01, + That = 0x02, + All = This | That, + OmittedValue = 99 + }; +#endif int someFunction(int v); void someFunctionDefaultArg(int i, bool b); void obsoleteMember(); diff --git a/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp b/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp index a47f4735f..932441b4e 100644 --- a/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp +++ b/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp @@ -58,6 +58,7 @@ private slots: void examplesManifestXml(); void ignoresinceVariable(); void templateParameters(); + void scopedEnum(); private: QScopedPointer<QTemporaryDir> m_outputDir; @@ -284,6 +285,11 @@ void tst_generatedOutput::templateParameters() "template/baz.html"); } +void tst_generatedOutput::scopedEnum() +{ + testAndCompare("scopedenum.qdocconf", "scopedenum/testqdoc-test.html"); +} + QTEST_APPLESS_MAIN(tst_generatedOutput) #include "tst_generatedoutput.moc" |