summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuca Di Sera <luca.disera@qt.io>2023-11-27 14:08:25 +0100
committerLuca Di Sera <luca.disera@qt.io>2023-12-05 15:24:29 +0100
commit2891c622c8ba02115e29d4486561d7fa9aba5b00 (patch)
treef0be100d3bc3727244ed717321f97ba184db8f05
parent436affa3ce95df8c5c042de91c4ddf522e0cae20 (diff)
QDoc: Use a more granular representation for template declarations
When QDoc parses a project, it parses the source code to extract the user-provided documentation and perform sanity checkings based on the code itself on it. When QDoc parses an "\fn" command as part of this process, it tries to understand, based on its intermediate representation built on the information extracted from the code-base, which "documentable element" the "\fn" refers to. When QDoc performs this "matching" process, it takes into consideration only a certain amount of information. For example, no checking is performed over the template declaration of a callable. Due to QDoc ignoring template declaration when "matching", certain elements are not documentable, as they are indistinguishable. For example, two callables that are overloaded on their specialization will be treated as a single documentable element by QDoc, so that they cannot both be referred to when documenting the code-base. Currently, QDoc extracts template declarations from templated elements directly from Clang, and stores them whole as a stringified representation. This representation allows QDoc to only take into consideration a whole template declaration, as the representation lacks granularity. Due to this representation, QDoc does not have, for example, easy access to the information about single template parameters in a declaration. This lack of granularity, in turn, prevents QDoc from performing more complex operations on a template declaration. For example, the representation would not easily allow QDoc to understand if a documentation block mentions or not the name of a template parameter, similar to what is done for callables parameters by the "\a" command. Similarly, it would only allow QDoc to perform "matching" on the whole declaration, which might be different under the current retrieval process when extracted from the code (where it is extracted from a declaration) or from an "\fn" command (where it is extracted from a mock out-of-line definition). To simplify the implementation of features related to template declarations, such as taking them into consideration when matching or allowing the user to document template parameters in a sanity-checked way, the more granular, albeit very simplified, representation that was recently added in "template_declaration.h" will now be used for storing the extracted template declarations. Previously, QDoc would extract template declarations, where necessary, by the usage of the mutually recursive free functions `templateDecl` and `getTemplateParameters` in "clangcodeparser.cpp". The two functions worked together to extract a string representation of the whole declaration, which was later stored in a `Node`, the intermediate representation that QDoc uses for documentable elements. Those two functions are now removed and replaced by the `get_template_declaration` free function, in "clangcodeparser.cpp", which performs a similar process but extracts a template declaration as a `RelaxedTemplateDeclaration`, a more granular representation for the same concept. To support the implementation of `get_template_declaration` and reduce some code duplication, a series of free functions where added to "clangcodeparser.cpp". `get_expression_as_string` now takes care of retrieving a C++ expression from the original code-base of the project. This is generally necessary when working with certain elements that can have a default value. The `get_default_value_initializer_as_string` overload set takes care of extracting a stringified version of the default argument of a parameter by dispatching to the correct concrete type some of the element of Clang's C++ AST, which is inheritance based. The parts of the code that previously depended on `templateDecl` and `getTemplateParameters` are now modified to depend on usages of `getTemplateDeclaration`. The stored representation of a template declaration as a string in `Node` was substituted with an optional `RelaxedTemplateDeclaration`, to be able to store the newly used representations, keeping the general interface for working with the stored declaration as intact as possible. Parts of the code-base that interacted with the template declaration that is stored in a `Node`, such as parts of the various generators that produce the final output documentation where modified to use the new representation, generally converting it to its stringified form to preserve the old behavior. The new representation is generally equivalent to the previous one with regards to the way it is shown to the user, even if it allows for more granularity internally. Nonetheless, it provides for some small formatting changes. Previously, template declarations would generally, but not always, lack a space between the "template" keyword and the following "<" literal. With the new representation, a the two elements are always divided by a space. Furthermore, QDoc now correctly acquires and shows both default argument initializers for template parameters and information about the parameter being a pack, while the previous representation elided those information. Furthermore, previously QDoc would present a mixed usage of the "typename" and "class" keyword to present a template declaration. With the new representation the "typename" keyword will always be preferred. The regression files in "tst_generatedOutput" that are touched by the changes were updated to reflect the new state. Task-number: QTBUG-118080 Task-number: QTBUG-117881 Change-Id: I0265b5dba81c05f5b145e926be9896a97f7e5446 Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
-rw-r--r--src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp212
-rw-r--r--src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp12
-rw-r--r--src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp14
-rw-r--r--src/qdoc/qdoc/src/qdoc/functionnode.cpp4
-rw-r--r--src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp8
-rw-r--r--src/qdoc/qdoc/src/qdoc/node.h9
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage-docbook/stdpair-proxypage-proxy.xml2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage/stdpair-proxypage-proxy.html2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/template/baz.html2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/template/testqdoc-test.html2
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templated-callables-h.html14
-rw-r--r--src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templatedclass.html14
12 files changed, 197 insertions, 98 deletions
diff --git a/src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp b/src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp
index e8ed433b2..86d3f1dc8 100644
--- a/src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp
+++ b/src/qdoc/qdoc/src/qdoc/clangcodeparser.cpp
@@ -38,6 +38,7 @@
#include <llvm/Support/Casting.h>
#include "clang/AST/QualTypeNames.h"
+#include "template_declaration.h"
#include <cstdio>
@@ -131,6 +132,108 @@ static std::string get_fully_qualified_type_name(clang::QualType type, const cla
);
}
+/*
+ * Retrieves expression as written in the original source code.
+ *
+ * declaration_context should be the ASTContext of the declaration
+ * from which the expression was extracted from.
+ *
+ * If the expression contains a leading equal sign it will be removed.
+ *
+ * Leading and trailing spaces will be similarly removed from the expression.
+ */
+static std::string get_expression_as_string(const clang::Expr* expression, const clang::ASTContext& declaration_context) {
+ QString default_value = QString::fromStdString(clang::Lexer::getSourceText(
+ clang::CharSourceRange::getTokenRange(expression->getSourceRange()),
+ declaration_context.getSourceManager(),
+ declaration_context.getLangOpts()
+ ).str());
+
+ if (default_value.startsWith("="))
+ default_value.remove(0, 1);
+
+ default_value = default_value.trimmed();
+
+ return default_value.toStdString();
+}
+
+/*
+ * Retrieves the default value of the passed in type template parameter as a string.
+ *
+ * The default value of a type template parameter is always a type,
+ * and its stringified representation will be return as the fully
+ * qualified version of the type.
+ *
+ * If the parameter as no default value the empty string will be returned.
+ */
+static std::string get_default_value_initializer_as_string(const clang::TemplateTypeParmDecl* parameter) {
+ return (parameter && parameter->hasDefaultArgument()) ?
+ get_fully_qualified_type_name(parameter->getDefaultArgument(), parameter->getASTContext()) :
+ "";
+
+}
+
+/*
+ * Retrieves the default value of the passed in non-type template parameter as a string.
+ *
+ * The default value of a non-type template parameter is an expression
+ * and its stringified representation will be return as it was written
+ * in the original code.
+ *
+ * If the parameter as no default value the empty string will be returned.
+ */
+static std::string get_default_value_initializer_as_string(const clang::NonTypeTemplateParmDecl* parameter) {
+ return (parameter && parameter->hasDefaultArgument()) ?
+ get_expression_as_string(parameter->getDefaultArgument(), parameter->getASTContext()) : "";
+
+}
+
+/*
+ * Retrieves the default value of the passed in template template parameter as a string.
+ *
+ * The default value of a template template parameter is a template
+ * name and its stringified representation will be returned as a fully
+ * qualified version of that name.
+ *
+ * If the parameter as no default value the empty string will be returned.
+ */
+static std::string get_default_value_initializer_as_string(const clang::TemplateTemplateParmDecl* parameter) {
+ std::string default_value{};
+
+ if (parameter && parameter->hasDefaultArgument()) {
+ const clang::TemplateName template_name = parameter->getDefaultArgument().getArgument().getAsTemplate();
+
+ llvm::raw_string_ostream ss{default_value};
+ template_name.print(ss, parameter->getASTContext().getPrintingPolicy(), clang::TemplateName::Qualified::Fully);
+ }
+
+ return default_value;
+}
+
+/*
+ * Retrieves the default value of the passed in declaration, based on
+ * its concrete type, as a string.
+ *
+ * If the declaration is a nullptr or the concrete type of the
+ * declaration is not a supported one, the returned string will be the
+ * empty string.
+ */
+static std::string get_default_value_initializer_as_string(const clang::NamedDecl* declaration) {
+ if (!declaration) return "";
+
+ if (auto type_template_parameter = llvm::dyn_cast<clang::TemplateTypeParmDecl>(declaration))
+ return get_default_value_initializer_as_string(type_template_parameter);
+
+ if (auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(declaration))
+ return get_default_value_initializer_as_string(non_type_template_parameter);
+
+ if (auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(declaration)) {
+ return get_default_value_initializer_as_string(template_template_parameter);
+ }
+
+ return "";
+}
+
/*!
Call clang_visitChildren on the given cursor with the lambda as a callback
T can be any functor that is callable with a CXCursor parameter and returns a CXChildVisitResult
@@ -156,61 +259,44 @@ static QString fromCXString(CXString &&string)
return ret;
}
-static QString templateDecl(CXCursor cursor);
+/*
+ * Returns an intermediate representation that models the the given
+ * template declaration.
+ */
+static RelaxedTemplateDeclaration get_template_declaration(const clang::TemplateDecl* template_declaration) {
+ assert(template_declaration);
-/*!
- Returns a list of template parameters at \a cursor.
-*/
-static QStringList getTemplateParameters(CXCursor cursor)
-{
- QStringList parameters;
- visitChildrenLambda(cursor, [&parameters](CXCursor cur) {
- QString name = fromCXString(clang_getCursorSpelling(cur));
- QString type;
-
- switch (clang_getCursorKind(cur)) {
- case CXCursor_TemplateTypeParameter:
- type = QStringLiteral("typename");
- break;
- case CXCursor_NonTypeTemplateParameter: {
- const auto* non_type_template_declaration =
- llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(get_cursor_declaration(cur));
- assert(non_type_template_declaration);
-
- type = QString::fromStdString(get_fully_qualified_type_name(
- non_type_template_declaration->getType(),
- non_type_template_declaration->getASTContext()
- ));
-
- // 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;
- }
+ RelaxedTemplateDeclaration template_declaration_ir{};
- if (!name.isEmpty())
- name.prepend(QLatin1Char(' '));
+ auto template_parameters = template_declaration->getTemplateParameters();
+ for (auto template_parameter : template_parameters->asArray()) {
+ auto kind{RelaxedTemplateParameter::Kind::TypeTemplateParameter};
+ std::string type{};
- parameters << type + name;
- return CXChildVisit_Continue;
- });
+ if (auto non_type_template_parameter = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_parameter)) {
+ kind = RelaxedTemplateParameter::Kind::NonTypeTemplateParameter;
+ type = get_fully_qualified_type_name(non_type_template_parameter->getType(), non_type_template_parameter->getASTContext());
+ }
- return parameters;
-}
+ auto template_template_parameter = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(template_parameter);
+ if (template_template_parameter) kind = RelaxedTemplateParameter::Kind::TemplateTemplateParameter;
-/*!
- Gets the template declaration at specified \a cursor.
- */
-static QString templateDecl(CXCursor cursor)
-{
- QStringList params = getTemplateParameters(cursor);
- return QLatin1String("template <") + params.join(QLatin1String(", ")) + QLatin1Char('>');
+ template_declaration_ir.parameters.push_back({
+ kind,
+ template_parameter->isTemplateParameterPack(),
+ {
+ type,
+ template_parameter->getNameAsString(),
+ get_default_value_initializer_as_string(template_parameter)
+ },
+ (template_template_parameter ?
+ std::optional<TemplateDeclarationStorage>(TemplateDeclarationStorage{
+ get_template_declaration(template_template_parameter).parameters
+ }) : std::nullopt)
+ });
+ }
+
+ return template_declaration_ir;
}
/*!
@@ -689,7 +775,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_TypeAliasTemplateDecl:
case CXCursor_TypeAliasDecl: {
@@ -700,15 +786,17 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
const QLatin1String usingString("using ");
qsizetype usingPos = typeAlias[0].indexOf(usingString);
if (usingPos != -1) {
- if (kind == CXCursor_TypeAliasTemplateDecl)
- templateString = typeAlias[0].left(usingPos).trimmed();
typeAlias[0].remove(0, usingPos + usingString.size());
typeAlias[0] = typeAlias[0].split(QLatin1Char(' ')).first();
typeAlias[1] = typeAlias[1].trimmed();
auto *ta = new TypeAliasNode(parent_, typeAlias[0], typeAlias[1]);
ta->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
ta->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
- ta->setTemplateDecl(templateString);
+
+ if (kind == CXCursor_TypeAliasTemplateDecl) {
+ auto template_decl = llvm::dyn_cast<clang::TemplateDecl>(get_cursor_declaration(cursor));
+ ta->setTemplateDecl(get_template_declaration(template_decl));
+ }
}
}
return CXChildVisit_Continue;
@@ -719,7 +807,6 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
return CXChildVisit_Continue;
Q_FALLTHROUGH();
case CXCursor_ClassTemplate:
- templateString = templateDecl(cursor);
Q_FALLTHROUGH();
case CXCursor_ClassDecl: {
if (!clang_isCursorDefinition(cursor))
@@ -748,8 +835,10 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
classe->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
classe->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
- if (kind == CXCursor_ClassTemplate)
- classe->setTemplateDecl(templateString);
+ if (kind == CXCursor_ClassTemplate) {
+ auto template_declaration = llvm::dyn_cast<clang::TemplateDecl>(get_cursor_declaration(cursor));
+ classe->setTemplateDecl(get_template_declaration(template_declaration));
+ }
QScopedValueRollback<Aggregate *> setParent(parent_, classe);
return visitChildren(cursor);
@@ -787,7 +876,6 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
return visitChildren(cursor);
}
case CXCursor_FunctionTemplate:
- templateString = templateDecl(cursor);
Q_FALLTHROUGH();
case CXCursor_FunctionDecl:
case CXCursor_CXXMethod:
@@ -816,8 +904,14 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
}
}
}
+
processFunction(fn, cursor);
- fn->setTemplateDecl(templateString);
+
+ if (kind == CXCursor_FunctionTemplate) {
+ auto template_declaration = get_cursor_declaration(cursor)->getAsFunction()->getDescribedFunctionTemplate();
+ fn->setTemplateDecl(get_template_declaration(template_declaration));
+ }
+
return CXChildVisit_Continue;
}
#if CINDEX_VERSION >= 36
diff --git a/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp b/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
index 78cde1b0e..4ef03986b 100644
--- a/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
+++ b/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
@@ -96,9 +96,9 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
case Node::Function:
func = (const FunctionNode *)node;
if (style == Section::Details) {
- const QString &templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- synopsis = templateDecl + QLatin1Char(' ');
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ synopsis = (*templateDecl).to_qstring() + QLatin1Char(' ');
}
if (style != Section::AllMembers && !func->returnType().isEmpty())
synopsis += typified(func->returnType(), true);
@@ -184,9 +184,9 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relati
break;
case Node::TypeAlias:
if (style == Section::Details) {
- const QString &templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- synopsis += templateDecl + QLatin1Char(' ');
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ synopsis += (*templateDecl).to_qstring() + QLatin1Char(' ');
}
synopsis += name;
break;
diff --git a/src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp b/src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp
index b7d321d9b..c4d875554 100644
--- a/src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp
+++ b/src/qdoc/qdoc/src/qdoc/docbookgenerator.cpp
@@ -3003,9 +3003,11 @@ void DocBookGenerator::generateCppReferencePage(Node *node)
title = rawTitle + " Namespace";
} else if (aggregate->isClass()) {
rawTitle = aggregate->plainName();
- QString templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- fullTitle = QString("%1 %2 ").arg(templateDecl, aggregate->typeWord(false));
+
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ fullTitle = QString("%1 %2 ").arg((*templateDecl).to_qstring(), aggregate->typeWord(false));
+
fullTitle += aggregate->plainFullName();
title = rawTitle + QLatin1Char(' ') + aggregate->typeWord(true);
} else if (aggregate->isHeader()) {
@@ -3850,9 +3852,9 @@ void DocBookGenerator::generateSynopsis(const Node *node, const Node *relative,
} break;
case Node::TypeAlias: {
if (style == Section::Details) {
- const QString& templateDecl = node->templateDecl();
- if (!templateDecl.isEmpty())
- m_writer->writeCharacters(templateDecl + QLatin1Char(' '));
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ m_writer->writeCharacters((*templateDecl).to_qstring() + QLatin1Char(' '));
}
m_writer->writeCharacters(namePrefix);
generateSynopsisName(node, relative, generateNameLink);
diff --git a/src/qdoc/qdoc/src/qdoc/functionnode.cpp b/src/qdoc/qdoc/src/qdoc/functionnode.cpp
index 9f77f9f06..2e6a81edc 100644
--- a/src/qdoc/qdoc/src/qdoc/functionnode.cpp
+++ b/src/qdoc/qdoc/src/qdoc/functionnode.cpp
@@ -427,8 +427,8 @@ QString FunctionNode::signature(Node::SignatureOptions options) const
{
QStringList elements;
- if (options & Node::SignatureTemplateParams)
- elements << templateDecl();
+ if (options & Node::SignatureTemplateParams && templateDecl())
+ elements << (*templateDecl()).to_qstring();
if (options & Node::SignatureReturnType)
elements << m_returnType;
elements.removeAll(QString());
diff --git a/src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp b/src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp
index e6e1db957..76ac1271e 100644
--- a/src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp
+++ b/src/qdoc/qdoc/src/qdoc/htmlgenerator.cpp
@@ -1079,7 +1079,7 @@ void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *m
Sections sections(aggregate);
QString word = aggregate->typeWord(true);
- QString templateDecl = aggregate->templateDecl();
+ auto templateDecl = aggregate->templateDecl();
if (aggregate->isNamespace()) {
rawTitle = aggregate->plainName();
fullTitle = aggregate->plainFullName();
@@ -1100,10 +1100,10 @@ void HtmlGenerator::generateCppReferencePage(Aggregate *aggregate, CodeMarker *m
}
Text subtitleText;
- if (rawTitle != fullTitle || !templateDecl.isEmpty()) {
+ if (rawTitle != fullTitle || templateDecl) {
if (aggregate->isClassNode()) {
- if (!templateDecl.isEmpty())
- subtitleText << templateDecl + QLatin1Char(' ');
+ if (templateDecl)
+ subtitleText << (*templateDecl).to_qstring() + QLatin1Char(' ');
subtitleText << aggregate->typeWord(false) + QLatin1Char(' ');
const QStringList ancestors = fullTitle.split(QLatin1String("::"));
for (const auto &a : ancestors) {
diff --git a/src/qdoc/qdoc/src/qdoc/node.h b/src/qdoc/qdoc/src/qdoc/node.h
index 65b16c141..65701f64c 100644
--- a/src/qdoc/qdoc/src/qdoc/node.h
+++ b/src/qdoc/qdoc/src/qdoc/node.h
@@ -11,12 +11,15 @@
#include "importrec.h"
#include "parameters.h"
#include "relatedclass.h"
+#include "template_declaration.h"
#include <QtCore/qdir.h>
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
#include <QtCore/qstringlist.h>
+#include <optional>
+
QT_BEGIN_NAMESPACE
class Aggregate;
@@ -207,7 +210,7 @@ public:
void setSince(const QString &since);
void setPhysicalModuleName(const QString &name) { m_physicalModuleName = name; }
void setUrl(const QString &url) { m_url = url; }
- void setTemplateDecl(const QString &t) { m_templateDecl = t; }
+ void setTemplateDecl(std::optional<RelaxedTemplateDeclaration> t) { m_templateDecl = t; }
void setReconstitutedBrief(const QString &t) { m_reconstitutedBrief = t; }
void setParent(Aggregate *n) { m_parent = n; }
void setIndexNodeFlag(bool isIndexNode = true) { m_indexNodeFlag = isIndexNode; }
@@ -274,7 +277,7 @@ public:
[[nodiscard]] ThreadSafeness threadSafeness() const;
[[nodiscard]] ThreadSafeness inheritedThreadSafeness() const;
[[nodiscard]] QString since() const { return m_since; }
- [[nodiscard]] const QString &templateDecl() const { return m_templateDecl; }
+ [[nodiscard]] const std::optional<RelaxedTemplateDeclaration>& templateDecl() const { return m_templateDecl; }
[[nodiscard]] const QString &reconstitutedBrief() const { return m_reconstitutedBrief; }
[[nodiscard]] bool isSharingComment() const { return (m_sharedCommentNode != nullptr); }
@@ -331,7 +334,7 @@ private:
QString m_physicalModuleName {};
QString m_url {};
QString m_since {};
- QString m_templateDecl {};
+ std::optional<RelaxedTemplateDeclaration> m_templateDecl{std::nullopt};
QString m_reconstitutedBrief {};
QString m_outSubDir {};
QString m_deprecatedSince {};
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage-docbook/stdpair-proxypage-proxy.xml b/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage-docbook/stdpair-proxypage-proxy.xml
index 7dbbc54c1..5476d7a21 100644
--- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage-docbook/stdpair-proxypage-proxy.xml
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage-docbook/stdpair-proxypage-proxy.xml
@@ -10,7 +10,7 @@
<db:section xml:id="type-documentation">
<db:title>Type Documentation</db:title>
<db:section xml:id="StdPair-typedef">
-<db:title>[alias] template &lt;class T1, class T2&gt; StdPair</db:title>
+<db:title>[alias] template &lt;typename T1, typename T2&gt; StdPair</db:title>
</db:section>
</db:section>
</db:article>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage/stdpair-proxypage-proxy.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage/stdpair-proxypage-proxy.html
index e91f28721..25241ddcb 100644
--- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage/stdpair-proxypage-proxy.html
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/proxypage/stdpair-proxypage-proxy.html
@@ -13,7 +13,7 @@
<div class="types">
<h2>Type Documentation</h2>
<!-- $$$StdPair -->
-<h3 class="fn" translate="no" id="StdPair-typedef"><code class="details extra" translate="no">[alias]</code> template &lt;class T1, class T2&gt; <span class="name">StdPair</span></h3>
+<h3 class="fn" translate="no" id="StdPair-typedef"><code class="details extra" translate="no">[alias]</code> template &lt;typename T1, typename T2&gt; <span class="name">StdPair</span></h3>
<!-- @@@StdPair -->
</div>
</body>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/baz.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/baz.html
index 216a7c191..eb939cec5 100644
--- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/baz.html
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/baz.html
@@ -18,7 +18,7 @@
</div>
<div class="sidebar-content" id="sidebar-content"></div></div>
<h1 class="title" translate="no">Baz Struct</h1>
-<span class="small-subtitle" translate="no">template &lt;template &lt;typename&gt; class X, typename Y&gt; struct Baz</span>
+<span class="small-subtitle" translate="no">template &lt;template &lt;typename&gt; typename X, typename Y&gt; struct Baz</span>
<!-- $$$Baz-brief -->
<p>Class template template. <a href="#details">More...</a></p>
<!-- @@@Baz -->
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/testqdoc-test.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/testqdoc-test.html
index 1794b2d9a..ab6ee6977 100644
--- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/testqdoc-test.html
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/template/testqdoc-test.html
@@ -90,7 +90,7 @@ target_link_libraries(mytarget PRIVATE Qt6::QDocTest)</td></tr>
<p>A typedef.</p>
<!-- @@@SomeType -->
<!-- $$$Specialized -->
-<h3 class="fn" translate="no" id="Specialized-typedef"><code class="details extra" translate="no">[alias]</code> template&lt;typename T&gt; Test::<span class="name">Specialized</span></h3>
+<h3 class="fn" translate="no" id="Specialized-typedef"><code class="details extra" translate="no">[alias]</code> template &lt;typename T&gt; Test::<span class="name">Specialized</span></h3>
<!-- @@@Specialized -->
</div>
<div class="func">
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templated-callables-h.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templated-callables-h.html
index f07956b11..7eac6608b 100644
--- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templated-callables-h.html
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templated-callables-h.html
@@ -37,15 +37,15 @@
<div class="func">
<h2>Function Documentation</h2>
<!-- $$$templated_function_with_defaulted_non_type_template_parameter[overload1]$$$templated_function_with_defaulted_non_type_template_parameter -->
-<h3 class="fn" translate="no" id="templated_function_with_defaulted_non_type_template_parameter">template &lt;char Category&gt; <span class="type">void</span> <span class="name">templated_function_with_defaulted_non_type_template_parameter</span>()</h3>
+<h3 class="fn" translate="no" id="templated_function_with_defaulted_non_type_template_parameter">template &lt;char Category = 'A'&gt; <span class="type">void</span> <span class="name">templated_function_with_defaulted_non_type_template_parameter</span>()</h3>
<p>A templated function with a defaulted non type template parameter.</p>
<!-- @@@templated_function_with_defaulted_non_type_template_parameter -->
<!-- $$$templated_function_with_defaulted_template_template_parameter[overload1]$$$templated_function_with_defaulted_template_template_parameterContainer<T,size> -->
-<h3 class="fn" translate="no" id="templated_function_with_defaulted_template_template_parameter">template &lt;typename T, int size, template &lt;typename, int&gt; class Container> <span class="type">void</span> <span class="name">templated_function_with_defaulted_template_template_parameter</span>(<span class="type">Container</span>&lt;<span class="type">T</span>, <span class="type">size</span>&gt;)</h3>
+<h3 class="fn" translate="no" id="templated_function_with_defaulted_template_template_parameter">template &lt;typename T, int size, template &lt;typename, int &gt; typename Container = std::array> <span class="type">void</span> <span class="name">templated_function_with_defaulted_template_template_parameter</span>(<span class="type">Container</span>&lt;<span class="type">T</span>, <span class="type">size</span>&gt;)</h3>
<p>A templated function with a defaulted template template parameter.</p>
<!-- @@@templated_function_with_defaulted_template_template_parameter -->
<!-- $$$templated_function_with_defaulted_type_template_parameter[overload1]$$$templated_function_with_defaulted_type_template_parameterT -->
-<h3 class="fn" translate="no" id="templated_function_with_defaulted_type_template_parameter">template &lt;typename T&gt; <span class="type">void</span> <span class="name">templated_function_with_defaulted_type_template_parameter</span>(<span class="type">T</span>)</h3>
+<h3 class="fn" translate="no" id="templated_function_with_defaulted_type_template_parameter">template &lt;typename T = char&gt; <span class="type">void</span> <span class="name">templated_function_with_defaulted_type_template_parameter</span>(<span class="type">T</span>)</h3>
<p>A templated function with a defaulted type template parameter.</p>
<!-- @@@templated_function_with_defaulted_type_template_parameter -->
<!-- $$$templated_function_with_non_type_template_parameter[overload1]$$$templated_function_with_non_type_template_parameter -->
@@ -53,7 +53,7 @@
<p>A templated function with a non-defaulted non type template parameter.</p>
<!-- @@@templated_function_with_non_type_template_parameter -->
<!-- $$$templated_function_with_non_type_template_parameter_pack[overload1]$$$templated_function_with_non_type_template_parameter_pack -->
-<h3 class="fn" translate="no" id="templated_function_with_non_type_template_parameter_pack">template &lt;unsigned int Weights&gt; <span class="type">void</span> <span class="name">templated_function_with_non_type_template_parameter_pack</span>()</h3>
+<h3 class="fn" translate="no" id="templated_function_with_non_type_template_parameter_pack">template &lt;unsigned int... Weights&gt; <span class="type">void</span> <span class="name">templated_function_with_non_type_template_parameter_pack</span>()</h3>
<p>A templated function with a non type template parameter pack.</p>
<!-- @@@templated_function_with_non_type_template_parameter_pack -->
<!-- $$$templated_function_with_placeholder_non_type_template_parameter[overload1]$$$templated_function_with_placeholder_non_type_template_parameter -->
@@ -61,11 +61,11 @@
<p>A templated function with a placeholder non type template parameter.</p>
<!-- @@@templated_function_with_placeholder_non_type_template_parameter -->
<!-- $$$templated_function_with_template_template_parameter[overload1]$$$templated_function_with_template_template_parameterK<T> -->
-<h3 class="fn" translate="no" id="templated_function_with_template_template_parameter">template &lt;typename T, template &lt;typename&gt; class K> <span class="type">void</span> <span class="name">templated_function_with_template_template_parameter</span>(<span class="type">K</span>&lt;<span class="type">T</span>&gt;)</h3>
+<h3 class="fn" translate="no" id="templated_function_with_template_template_parameter">template &lt;typename T, template &lt;typename&gt; typename K> <span class="type">void</span> <span class="name">templated_function_with_template_template_parameter</span>(<span class="type">K</span>&lt;<span class="type">T</span>&gt;)</h3>
<p>A templated function with a non-defaulted template template parameter.</p>
<!-- @@@templated_function_with_template_template_parameter -->
<!-- $$$templated_function_with_template_template_parameter_pack[overload1]$$$templated_function_with_template_template_parameter_packContainer<T>... -->
-<h3 class="fn" translate="no" id="templated_function_with_template_template_parameter_pack">template &lt;typename T, template &lt;typename&gt; class Container> <span class="type">void</span> <span class="name">templated_function_with_template_template_parameter_pack</span>(<span class="type">Container</span>&lt;<span class="type">T</span>&gt;...)</h3>
+<h3 class="fn" translate="no" id="templated_function_with_template_template_parameter_pack">template &lt;typename T, template &lt;typename&gt; typename... Container> <span class="type">void</span> <span class="name">templated_function_with_template_template_parameter_pack</span>(<span class="type">Container</span>&lt;<span class="type">T</span>&gt;...)</h3>
<p>A templated function with a template template parameter pack.</p>
<!-- @@@templated_function_with_template_template_parameter_pack -->
<!-- $$$templated_function_with_type_template_parameter[overload1]$$$templated_function_with_type_template_parameterT -->
@@ -73,7 +73,7 @@
<p>A templated function with a non-defaulted type template parameter.</p>
<!-- @@@templated_function_with_type_template_parameter -->
<!-- $$$templated_function_with_type_template_parameter_pack[overload1]$$$templated_function_with_type_template_parameter_packTs... -->
-<h3 class="fn" translate="no" id="templated_function_with_type_template_parameter_pack">template &lt;typename Ts&gt; <span class="type">void</span> <span class="name">templated_function_with_type_template_parameter_pack</span>(<span class="type">Ts</span>...)</h3>
+<h3 class="fn" translate="no" id="templated_function_with_type_template_parameter_pack">template &lt;typename... Ts&gt; <span class="type">void</span> <span class="name">templated_function_with_type_template_parameter_pack</span>(<span class="type">Ts</span>...)</h3>
<p>A templated function with a type template parameter pack.</p>
<!-- @@@templated_function_with_type_template_parameter_pack -->
</div>
diff --git a/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templatedclass.html b/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templatedclass.html
index bf8e84012..fa50411a8 100644
--- a/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templatedclass.html
+++ b/src/qdoc/qdoc/tests/generatedoutput/expected_output/templatedcallables/templatedclass.html
@@ -46,15 +46,15 @@
<div class="func">
<h2>Member Function Documentation</h2>
<!-- $$$templated_method_with_defaulted_non_type_template_parameter[overload1]$$$templated_method_with_defaulted_non_type_template_parameter -->
-<h3 class="fn" translate="no" id="templated_method_with_defaulted_non_type_template_parameter">template &lt;int Size&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_non_type_template_parameter</span>()</h3>
+<h3 class="fn" translate="no" id="templated_method_with_defaulted_non_type_template_parameter">template &lt;int Size = 10&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_non_type_template_parameter</span>()</h3>
<p>A templated method under a templated class with a defaulted non type template parameter.</p>
<!-- @@@templated_method_with_defaulted_non_type_template_parameter -->
<!-- $$$templated_method_with_defaulted_template_template_parameter[overload1]$$$templated_method_with_defaulted_template_template_parameterContainer<U,size> -->
-<h3 class="fn" translate="no" id="templated_method_with_defaulted_template_template_parameter">template &lt;typename U, int size, template &lt;typename, int&gt; class Container> <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_template_template_parameter</span>(<span class="type">Container</span>&lt;<span class="type">U</span>, <span class="type">size</span>&gt;)</h3>
+<h3 class="fn" translate="no" id="templated_method_with_defaulted_template_template_parameter">template &lt;typename U, int size, template &lt;typename, int &gt; typename Container = std::array> <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_template_template_parameter</span>(<span class="type">Container</span>&lt;<span class="type">U</span>, <span class="type">size</span>&gt;)</h3>
<p>A templated method under a templated class with a defaulted template template parameter.</p>
<!-- @@@templated_method_with_defaulted_template_template_parameter -->
<!-- $$$templated_method_with_defaulted_type_template_parameter[overload1]$$$templated_method_with_defaulted_type_template_parameterU -->
-<h3 class="fn" translate="no" id="templated_method_with_defaulted_type_template_parameter">template &lt;typename U&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_type_template_parameter</span>(<span class="type">U</span>)</h3>
+<h3 class="fn" translate="no" id="templated_method_with_defaulted_type_template_parameter">template &lt;typename U = bool&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_defaulted_type_template_parameter</span>(<span class="type">U</span>)</h3>
<p>A templated method under a templated class with a defaulted type template parameter.</p>
<!-- @@@templated_method_with_defaulted_type_template_parameter -->
<!-- $$$templated_method_with_non_type_template_parameter[overload1]$$$templated_method_with_non_type_template_parameter -->
@@ -62,7 +62,7 @@
<p>A templated method under a templated class with a non-defaulted non type template parameter.</p>
<!-- @@@templated_method_with_non_type_template_parameter -->
<!-- $$$templated_method_with_non_type_template_parameter_pack[overload1]$$$templated_method_with_non_type_template_parameter_pack -->
-<h3 class="fn" translate="no" id="templated_method_with_non_type_template_parameter_pack">template &lt;int Dimensions&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_non_type_template_parameter_pack</span>()</h3>
+<h3 class="fn" translate="no" id="templated_method_with_non_type_template_parameter_pack">template &lt;int... Dimensions&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_non_type_template_parameter_pack</span>()</h3>
<p>A templated method under a templated class with a non type template parameter pack.</p>
<!-- @@@templated_method_with_non_type_template_parameter_pack -->
<!-- $$$templated_method_with_placeholder_non_type_template_parameter[overload1]$$$templated_method_with_placeholder_non_type_template_parameter -->
@@ -70,11 +70,11 @@
<p>A templated method under a templated class with a placeholder non type template parameter.</p>
<!-- @@@templated_method_with_placeholder_non_type_template_parameter -->
<!-- $$$templated_method_with_template_template_parameter[overload1]$$$templated_method_with_template_template_parameterX<U> -->
-<h3 class="fn" translate="no" id="templated_method_with_template_template_parameter">template &lt;typename U, template &lt;typename&gt; class X> <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_template_template_parameter</span>(<span class="type">X</span>&lt;<span class="type">U</span>&gt;)</h3>
+<h3 class="fn" translate="no" id="templated_method_with_template_template_parameter">template &lt;typename U, template &lt;typename&gt; typename X> <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_template_template_parameter</span>(<span class="type">X</span>&lt;<span class="type">U</span>&gt;)</h3>
<p>A templated method under a templated class with a non-defaulted template template parameter.</p>
<!-- @@@templated_method_with_template_template_parameter -->
<!-- $$$templated_method_with_template_template_parameter_pack[overload1]$$$templated_method_with_template_template_parameter_packContainer<U>... -->
-<h3 class="fn" translate="no" id="templated_method_with_template_template_parameter_pack">template &lt;typename U, template &lt;typename&gt; class Container> <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_template_template_parameter_pack</span>(<span class="type">Container</span>&lt;<span class="type">U</span>&gt;...)</h3>
+<h3 class="fn" translate="no" id="templated_method_with_template_template_parameter_pack">template &lt;typename U, template &lt;typename&gt; typename... Container> <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_template_template_parameter_pack</span>(<span class="type">Container</span>&lt;<span class="type">U</span>&gt;...)</h3>
<p>A templated method under a templated class with a template template parameter pack.</p>
<!-- @@@templated_method_with_template_template_parameter_pack -->
<!-- $$$templated_method_with_type_template_parameter[overload1]$$$templated_method_with_type_template_parameterU -->
@@ -82,7 +82,7 @@
<p>A templated method under a templated class with a non-defaulted type template parameter.</p>
<!-- @@@templated_method_with_type_template_parameter -->
<!-- $$$templated_method_with_type_template_parameter_pack[overload1]$$$templated_method_with_type_template_parameter_packTs... -->
-<h3 class="fn" translate="no" id="templated_method_with_type_template_parameter_pack">template &lt;typename Ts&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_type_template_parameter_pack</span>(<span class="type">Ts</span>...)</h3>
+<h3 class="fn" translate="no" id="templated_method_with_type_template_parameter_pack">template &lt;typename... Ts&gt; <span class="type">void</span> TemplatedClass::<span class="name">templated_method_with_type_template_parameter_pack</span>(<span class="type">Ts</span>...)</h3>
<p>A templated method under a templated class with a type template parameter pack.</p>
<!-- @@@templated_method_with_type_template_parameter_pack -->
</div>