diff options
author | Luca Di Sera <luca.disera@qt.io> | 2022-12-27 14:12:32 +0100 |
---|---|---|
committer | Luca Di Sera <luca.disera@qt.io> | 2022-12-28 11:02:05 +0100 |
commit | feb4a767b2728f706996217fd90efc54b2c3b2f1 (patch) | |
tree | 48436ea58a7b68add5030da70b81b6e943e93ec5 | |
parent | 6b5ffb7e139c03778479a15e73e02133fa2790aa (diff) |
QDoc: Tag C++ elements as "constexpr" when they are marked as such
C++'s allows certain declarations to be marked by
the `constexpr` specifier, so as to signify that the element can be used
in constant expressions contexts.
This information is important to the user of an API, as it may dictate
when and how they can use a certain element.
Nonetheless, up to now, QDoc would not treat the "constexpr" specifier,
such that this information was lost between source-code and
documentation.
To avoid the issue and ensure that users are given a way to discern
whether something is marked "constexpr" or not, QDoc will now "tag" a
relevant documentable element that is a callable, such as a C++ method,
as "constexpr" when the `constexpr` specifier appears in its
declaration.
To store this information, `FunctionNode`, the internal representation
for "callable" documentable elements, such as a C++ constructor or
method, was modified to expose a boolean flag, `m_constexpr`, trough the
a setter, `markConstexpr`, which enables the flag, and a getter,
`isConstexpr`, which allows to peek into the flag value.
The flag is initially defaulted to `false` and is set only when the
"constexpr" specifier is encountered in the declaration of the relevant
element.
Generally, this kind of information is extracted from the codebase in
`ClangCodeParser::processFunction`, where an instance of a
`FunctionNode` is populated based on the Libclang-provided `CXCursor`
that generated it.
As Libclang does not have direct support for the `constexpr` specifier,
at the current point in time, we extract the information based on the
tokenized version of the relevant declaration.
That is, the source code represented by the relevant `CXCursor`, which
will generally represent a declaration, is
tokenized by Clang, all keywords tokens that are part of the declaration
are inspected and, if a "constexpr" keyword is encountered, we consider
the given declaration to be marked "constexpr".
To do so, we reuse the infrastructure that was introduced in a recent
commit to preserve the information for `explicit` specifiers.
The static function `get_specifiers` in
"clangcodeparser.cpp", performs the above process, and is now modified
to take into account the `constexpr` specifier.
The function returns a structure, `CXXSpecifiers`, that
contains some minimum information with regards to the specifiers that we
are interested into that Libclang does not directly support.
The structure is now updated to provide information about the presence,
or lack thereof, of a `constexpr` specifier.
Similarly, the body of `get_specifiers` was modified to correctly
populate this information based on the available tokens.
`ClangCodeParser::processFunction`, which calls `get_specifiers`, was
modified to take into account the new "constexpr" information in the
returned `CXXSpecifiers` value, marking the processed `FunctionNode` as
"constexpr" if a `constexpr` specifier was encountered.
Later on, during the "Generation Phase", where QDoc destructures the
provided documentable elements to produce the final output
documentation, QDoc will call a `CodeMarker` to generate the set of
"tags", small strings that appear near the detailed documentation of an
element, to enhance the final documentation with certain information
about the documented element.
To make use of the now stored "constexpr" information,
`CodeMarker::extraSynopsis`, which is the particular methods that
generates the relevant set of "tags" for an element, was modified to
take into account the "constexpr" flag of a `FunctionNode`, and generate
an "constexpr" "tag" if it is set to `true`.
When QDoc parses the source code for a project, it generates an
XML-based "index" file, containing certain information about the
extracted documentable elements that can be consumed by external tools
and is consumed by QDoc itself to enable cross-module linking.
To allow the newly added "constexpr" information to be retained between
modules and to enable the inspection of such information for external
consumers, `QDocIndexFiles::generateFunctionSection`, which writes the
relevant information about a `FunctionNode` in the index file, was
modified to write a "constexpr" attribute in the XML-element
representing the `FunctionNode`.
The attribute is elided when the `FunctionNode` is not marked as
"constexpr", the default state, to save space and avoid cluttering the
output.
Similarly, `QDocIndexFiles::readIndexSection`, which retrieves the
information stored in a certain index file and rebuilds the internal
representations that QDoc uses for the represented elements, was
modified to read the "constexpr" attribute that was added.
If the attribute is present and has a "true" value in a "function"
element, the reconstructed `FunctionNode` will be marked as "constexpr",
to retain the information.
Task-number: QTBUG-93439
Pick-to: 6.5
Change-Id: I63b18c34d7b9f3d72047ab2ba823d105f5a694e7
Reviewed-by: Luca Di Sera <luca.disera@qt.io>
-rw-r--r-- | src/qdoc/clangcodeparser.cpp | 4 | ||||
-rw-r--r-- | src/qdoc/codemarker.cpp | 1 | ||||
-rw-r--r-- | src/qdoc/functionnode.cpp | 2 | ||||
-rw-r--r-- | src/qdoc/functionnode.h | 4 | ||||
-rw-r--r-- | src/qdoc/qdocindexfiles.cpp | 4 |
5 files changed, 15 insertions, 0 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index d5acfd1cd..0d27e2f3f 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -898,6 +898,7 @@ void ClangVisitor::readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cu struct CXXSpecifiers { bool is_explicit = false; + bool is_constexpr = false; }; static CXXSpecifiers get_specifiers(CXCursor cursor) { @@ -913,7 +914,9 @@ static CXXSpecifiers get_specifiers(CXCursor cursor) { for (unsigned index = 0; index < token_count; ++index) { if (clang_getTokenKind(tokens[index]) == CXToken_Keyword) { QString token_spelling{fromCXString(clang_getTokenSpelling(tu, tokens[index]))}; + if (token_spelling == "explicit") specifiers.is_explicit = true; + else if (token_spelling == "constexpr") specifiers.is_constexpr = true; } } @@ -948,6 +951,7 @@ void ClangVisitor::processFunction(FunctionNode *fn, CXCursor cursor) CXXSpecifiers specifiers{get_specifiers(cursor)}; if (specifiers.is_explicit) fn->markExplicit(); + if (specifiers.is_constexpr) fn->markConstexpr(); CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType); if (refQualKind == CXRefQualifier_LValue) diff --git a/src/qdoc/codemarker.cpp b/src/qdoc/codemarker.cpp index 1ab0df106..55885daf6 100644 --- a/src/qdoc/codemarker.cpp +++ b/src/qdoc/codemarker.cpp @@ -150,6 +150,7 @@ QString CodeMarker::extraSynopsis(const Node *node, Section::Style style) } if (func->isExplicit()) extra << "explicit"; + if (func->isConstexpr()) extra << "constexpr"; if (func->access() == Access::Protected) extra << "protected"; diff --git a/src/qdoc/functionnode.cpp b/src/qdoc/functionnode.cpp index 4f29b7e9d..90cc73ec6 100644 --- a/src/qdoc/functionnode.cpp +++ b/src/qdoc/functionnode.cpp @@ -61,6 +61,7 @@ FunctionNode::FunctionNode(Aggregate *parent, const QString &name) m_isRefRef(false), m_isInvokable(false), m_explicit{false}, + m_constexpr{false}, m_metaness(Plain), m_virtualness(NonVirtual), m_overloadNumber(0), @@ -93,6 +94,7 @@ FunctionNode::FunctionNode(Metaness kind, Aggregate *parent, const QString &name m_isRefRef(false), m_isInvokable(false), m_explicit{false}, + m_constexpr{false}, m_metaness(kind), m_virtualness(NonVirtual), m_overloadNumber(0), diff --git a/src/qdoc/functionnode.h b/src/qdoc/functionnode.h index 93dbab99e..ec790b454 100644 --- a/src/qdoc/functionnode.h +++ b/src/qdoc/functionnode.h @@ -78,6 +78,9 @@ public: void markExplicit() { m_explicit = true; } bool isExplicit() const { return m_explicit; } + void markConstexpr() { m_constexpr = true; } + bool isConstexpr() const { return m_constexpr; } + [[nodiscard]] bool isCppFunction() const { return m_metaness == Plain; } // Is this correct? [[nodiscard]] bool isSignal() const { return (m_metaness == Signal); } [[nodiscard]] bool isSlot() const { return (m_metaness == Slot); } @@ -182,6 +185,7 @@ private: bool m_isRefRef : 1; bool m_isInvokable : 1; bool m_explicit; + bool m_constexpr; Metaness m_metaness {}; Virtualness m_virtualness {}; diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp index e40e9b41f..d8e6cf794 100644 --- a/src/qdoc/qdocindexfiles.cpp +++ b/src/qdoc/qdocindexfiles.cpp @@ -427,6 +427,9 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current, if (attributes.value(QLatin1String("explicit")) == QLatin1String("true")) fn->markExplicit(); + if (attributes.value(QLatin1String("constexpr")) == QLatin1String("true")) + fn->markConstexpr(); + qsizetype refness = attributes.value(QLatin1String("refness")).toUInt(); if (refness == 1) fn->setRef(true); @@ -1201,6 +1204,7 @@ void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionN writer.writeAttribute("override", fn->isOverride() ? "true" : "false"); if (fn->isExplicit()) writer.writeAttribute("explicit", "true"); + if (fn->isConstexpr()) writer.writeAttribute("constexpr", "true"); /* This ensures that for functions that have overloads, |