summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTopi Reinio <topi.reinio@qt.io>2020-01-15 11:08:27 +0100
committerTopi Reinio <topi.reinio@qt.io>2020-01-20 13:11:50 +0100
commit46f71fcce6d278e62ec5a766d769681400b330ed (patch)
treea5c6395ff780c1335958534b37b0b781b243d430
parent03f1c6e3ed01f8270463ab8aa4afb4f7c2321cdb (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.cpp9
-rw-r--r--src/qdoc/cppcodemarker.cpp21
-rw-r--r--src/qdoc/node.cpp17
-rw-r--r--src/qdoc/node.h7
-rw-r--r--src/qdoc/qdocindexfiles.cpp4
-rw-r--r--tests/auto/qdoc/generatedoutput/expected_output/scopedenum/testqdoc-test.html108
-rw-r--r--tests/auto/qdoc/generatedoutput/scopedenum.qdoc35
-rw-r--r--tests/auto/qdoc/generatedoutput/scopedenum.qdocconf7
-rw-r--r--tests/auto/qdoc/generatedoutput/testcpp.h8
-rw-r--r--tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp6
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 &lt;Test&gt;</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"