summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTopi Reinio <topi.reinio@qt.io>2024-03-25 18:55:33 +0000
committerTopi Reinio <topi.reinio@qt.io>2024-04-19 14:46:13 +0000
commit7c928c80085be39cf2354e27d8cbcee50705fcc9 (patch)
treed4ecb3f08212cf4017c24c44cbf0f0017966684f
parent540ae68c6e2784a0825c39c9d28eb4d8dac2c53a (diff)
qdoc: Delay processing of \relates meta-command arguments
The \relates command is used for C++ entities in the global scope to associate them with a page-generating Aggregate (class, namespace, or a header file). The \relates command is a meta-command, processed after all the topic commands (in the same source file) are handled. However, if the \relates command referred an aggregate that does not exist (because its documentation is in another file, yet to be processed), we ran into problems: QDoc created a new ProxyNode to serve as the location to generate the the related node's documentation to. After the proxy node had been created, all subsequent associations were tied to that proxy, even after the real aggregate's node had been created and added to the tree. An output page was generated for the proxy, but it often ended up being orphaned. In practice this was only a problem for header file aggregates, as they are the only 'relatable' node type not constructed by ClangCodeParser during pre-compiled header generation, but later during the processing of documentation comments (\headerfile topic). Recent changes to the order of processing topic and meta-commands unmasked this issue with the current documentation sources, but the implementation was always fragile. To fix, add a new post-processing step that handles \relates arguments after all sources have been parsed. This must be done before calling Aggregate::normalizeOverloads() as the related non-member functions have an effect on overload ordering. Remove remnants of the undocumented ability to allow multiple \relates commands in the same topic - it was defunct and can be re-introduced with a proper implementation. Fixes: QTBUG-123622 Change-Id: I9b85d9c9447825d17d4a4dd345c811dfecfb2411 Reviewed-by: Paul Wicking <paul.wicking@qt.io>
-rw-r--r--src/qdoc/qdoc/src/qdoc/aggregate.cpp36
-rw-r--r--src/qdoc/qdoc/src/qdoc/aggregate.h1
-rw-r--r--src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp46
-rw-r--r--src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp1
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h5
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp12
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html35
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html22
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index11
-rw-r--r--src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf7
11 files changed, 152 insertions, 35 deletions
diff --git a/src/qdoc/qdoc/src/qdoc/aggregate.cpp b/src/qdoc/qdoc/src/qdoc/aggregate.cpp
index fc5a042c6..bf210ba17 100644
--- a/src/qdoc/qdoc/src/qdoc/aggregate.cpp
+++ b/src/qdoc/qdoc/src/qdoc/aggregate.cpp
@@ -12,6 +12,8 @@
#include "sharedcommentnode.h"
#include <vector>
+using namespace Qt::Literals::StringLiterals;
+
QT_BEGIN_NAMESPACE
/*!
@@ -216,6 +218,40 @@ void Aggregate::markUndocumentedChildrenInternal()
}
/*!
+ Adopts each non-aggregate C++ node (function/macro, typedef, enum, variable,
+ or a shared comment node with genus Node::CPP) in the global scope to the
+ aggregate specified in the node's documentation using the \\relates command.
+
+ If the target Aggregate is not found in the primary tree, creates a new
+ ProxyNode to use as the parent.
+*/
+void Aggregate::resolveRelates()
+{
+ Q_ASSERT(name().isEmpty()); // Must be called on the root namespace
+ auto *database = QDocDatabase::qdocDB();
+
+ for (auto *node : m_children) {
+ if (node->isRelatedNonmember() || node->isAggregate())
+ continue;
+ if (node->genus() != Node::CPP)
+ continue;
+
+ const auto &relates_args = node->doc().metaCommandArgs("relates"_L1);
+ if (relates_args.isEmpty())
+ continue;
+
+ auto *aggregate = database->findRelatesNode(relates_args[0].first.split("::"_L1));
+ if (!aggregate)
+ aggregate = new ProxyNode(this, relates_args[0].first);
+ else if (node->parent() == aggregate)
+ continue;
+
+ aggregate->adoptChild(node);
+ node->setRelatedNonmember(true);
+ }
+}
+
+/*!
Sorts the lists of overloads in the function map and assigns overload
numbers.
diff --git a/src/qdoc/qdoc/src/qdoc/aggregate.h b/src/qdoc/qdoc/src/qdoc/aggregate.h
index 39ab5ca29..a02633e04 100644
--- a/src/qdoc/qdoc/src/qdoc/aggregate.h
+++ b/src/qdoc/qdoc/src/qdoc/aggregate.h
@@ -30,6 +30,7 @@ public:
FunctionNode *findFunctionChild(const QString &name, const Parameters &parameters);
FunctionNode *findFunctionChild(const FunctionNode *clone);
+ void resolveRelates();
void normalizeOverloads();
void markUndocumentedChildrenInternal();
diff --git a/src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp b/src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp
index 777127873..82bc09064 100644
--- a/src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp
+++ b/src/qdoc/qdoc/src/qdoc/cppcodeparser.cpp
@@ -409,41 +409,17 @@ void CppCodeParser::processMetaCommand(const Doc &doc, const QString &command,
}
}
} else if (command == COMMAND_RELATES) {
- QStringList path = arg.split("::");
- Aggregate *aggregate = database->findRelatesNode(path);
- if (aggregate == nullptr)
- aggregate = new ProxyNode(node->root(), arg);
-
- if (node->parent() == aggregate) { // node is already a child of aggregate
- doc.location().warning(QStringLiteral("Invalid '\\%1' (already a member of '%2')")
- .arg(COMMAND_RELATES, arg));
- } else {
- if (node->isAggregate()) {
- doc.location().warning(QStringLiteral("Invalid '\\%1' not allowed in '\\%2'")
- .arg(COMMAND_RELATES, node->nodeTypeString()));
- } else if (!node->isRelatedNonmember() &&
- !node->parent()->isNamespace() && !node->parent()->isHeader()) {
- if (!doc.isInternal()) {
- doc.location().warning(QStringLiteral("Invalid '\\%1' ('%2' must be global)")
- .arg(COMMAND_RELATES, node->name()));
- }
- } else if (!node->isRelatedNonmember() && !node->parent()->isHeader()) {
- aggregate->adoptChild(node);
- node->setRelatedNonmember(true);
- } else {
- /*
- There are multiple \relates commands. This
- one is not the first, so clone the node as
- a child of aggregate.
- */
- Node *clone = node->clone(aggregate);
- if (clone == nullptr) {
- doc.location().warning(
- QStringLiteral("Invalid '\\%1' (multiple uses not allowed in '%2')")
- .arg(COMMAND_RELATES, node->nodeTypeString()));
- } else {
- clone->setRelatedNonmember(true);
- }
+ // REMARK: Generates warnings only; Node instances are
+ // adopted from the root namespace to other Aggregates
+ // in a post-processing step, Aggregate::resolveRelates(),
+ // after all topic commands are processed.
+ if (node->isAggregate()) {
+ doc.location().warning("Invalid '\\%1' not allowed in '\\%2'"_L1
+ .arg(COMMAND_RELATES, node->nodeTypeString()));
+ } else if (!node->isRelatedNonmember() && node->parent()->isClassNode()) {
+ if (!doc.isInternal()) {
+ doc.location().warning("Invalid '\\%1' ('%2' must be global)"_L1
+ .arg(COMMAND_RELATES, node->name()));
}
}
} else if (command == COMMAND_NEXTPAGE) {
diff --git a/src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp b/src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp
index 9d811a881..57e88fbde 100644
--- a/src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp
+++ b/src/qdoc/qdoc/src/qdoc/qdocdatabase.cpp
@@ -897,6 +897,7 @@ void QDocDatabase::resolveStuff()
// order matters
primaryTree()->resolveBaseClasses(primaryTreeRoot());
primaryTree()->resolvePropertyOverriddenFromPtrs(primaryTreeRoot());
+ primaryTreeRoot()->resolveRelates();
primaryTreeRoot()->normalizeOverloads();
primaryTree()->markDontDocumentNodes();
primaryTree()->removePrivateAndInternalBases(primaryTreeRoot());
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp
new file mode 100644
index 000000000..c2dd411d7
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.cpp
@@ -0,0 +1,11 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "a.h"
+
+/*!
+ \typealias foo
+ \relates <Bar>
+
+ Related to a header file whose source might be parsed later.
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h
new file mode 100644
index 000000000..40c4add88
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/a.h
@@ -0,0 +1,5 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+using foo = int;
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp
new file mode 100644
index 000000000..7e3df2f24
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/b.cpp
@@ -0,0 +1,12 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+/*!
+ \module Module
+*/
+
+/*!
+ \headerfile <Bar>
+ \title A header
+ \inmodule Module
+*/
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html
new file mode 100644
index 000000000..b8dc9412a
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/bar.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- b.cpp -->
+ <title>&lt;Bar&gt; - A header | RelatesOrdering</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title" translate="no">&lt;Bar&gt; - A header</h1>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Bar&gt;</span></td></tr>
+</table></div>
+<h2 id="types">Types</h2>
+<div class="table"><table class="alignedsummary" translate="no">
+<tr><td class="memItemLeft rightAlign topAlign"> </td><td class="memItemRight bottomAlign"><b><a href="bar.html#foo-typedef" translate="no">foo</a></b></td></tr>
+</table></div>
+<!-- $$$<Bar>-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@<Bar> -->
+<div class="types">
+<h2>Type Documentation</h2>
+<!-- $$$foo -->
+<h3 class="fn" translate="no" id="foo-typedef"><code class="details extra" translate="no">[alias]</code> <span class="name">foo</span></h3>
+<p>Related to a header file whose source might be parsed later.</p>
+<!-- @@@foo -->
+</div>
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html
new file mode 100644
index 000000000..3f123509c
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/module-module.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- b.cpp -->
+ <title>RelatesOrdering</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3 id="toc">Contents</h3>
+<ul>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<!-- $$$Module-description -->
+<div class="descr" id="details">
+</div>
+<!-- @@@Module -->
+</body>
+</html>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index
new file mode 100644
index 000000000..905c657aa
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/expected/relatesordering.index
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QDOCINDEX>
+<INDEX url="" title="RelatesOrdering Reference Documentation" version="" project="RelatesOrdering">
+ <namespace name="" status="active" access="public" module="relatesordering">
+ <header name="&lt;Bar&gt;" href="bar.html" status="active" documented="true" module="Module" title="A header" fulltitle="&lt;Bar&gt; - A header" subtitle="">
+ <typedef name="foo" href="bar.html#foo-typedef" status="active" access="public" location="a.h" related="0" documented="true" aliasedtype="int"/>
+ </header>
+ <typedef name="foo" href="bar.html#foo-typedef" status="active" access="public" location="a.h" related="0" documented="true" aliasedtype="int"/>
+ <module name="Module" href="module-module.html" status="active" documented="true" seen="true" title=""/>
+ </namespace>
+</INDEX>
diff --git a/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf
new file mode 100644
index 000000000..b943729a3
--- /dev/null
+++ b/src/qdoc/qdoc/tests/validateqdocoutputfiles/testdata/relatesordering/relatesordering.qdocconf
@@ -0,0 +1,7 @@
+project = RelatesOrdering
+
+{sourcedirs,headerdirs} = .
+
+locationinfo = false
+warninglimit = 0
+warninglimit.enabled = true