diff options
author | Luca Di Sera <luca.disera@qt.io> | 2022-12-27 12:48:37 +0100 |
---|---|---|
committer | Luca Di Sera <luca.disera@qt.io> | 2022-12-27 21:26:48 +0000 |
commit | 6b5ffb7e139c03778479a15e73e02133fa2790aa (patch) | |
tree | f2b3a923b5f04c4b80d6e72b0a4aafd92d22ec80 | |
parent | e055120820bf3a18387599d2d4e19939255d2a81 (diff) |
QDoc: Tag C++ elements as "explicit" when they are marked as such
C++'s constructors and conversion functions can be marked by
the explicit specifier, so that the element cannot participate in
implicit conversion and copy-initialization.
This information is important to the user of an API, as it may dictate
when and how they can use the given constructor or conversion function.
Nonetheless, up to now, QDoc would not treat the "explicit" 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 explicit or not, QDoc will now "tag" a
relevant documentable element as "explicit" when the "explicit"
specifiers 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_explicit`, trough the
a setter, `markExplicit`, which enables the flag, and a getter,
`isExplicit`, which allows to peek into the flag value.
The flag is initially defaulted to `false` and is set only when the
"explicit" 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 `explicit` 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 an "explicit" keyword is encountered, we consider
the given declaration to be marked "explicit".
To do so, a static function `get_specifiers` was introduced in
"clangcodeparser.cpp", which performs the above process.
The function returns a newly introduced structure, `CXXSpecifiers`, that
contains some minimum information with regards to the specifiers that we
are interested into that Libclang does not directly support, such as
"explicit".
`ClangCodeParser::processFunction` will now call this newly introduced
function and mark any relevant information in the processed
`FunctionNode` based on the returned `CXXSpecifiers` instance.
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 "explicit" information,
`CodeMarker::extraSynopsis`, which is the particular methods that
generated the relevant set of "tags" for an element, was modified to
take into account the "explicit" flag of a `FunctionNode`, and generate
an "explicit" "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 "explicit" 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 an "explicit" attribute in the XML-element
representing the `FunctionNode`.
The attribute is elided when the `FunctionNode` is nor marked as
"explicit", 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 "explicit" 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 "explicit",
to retain the information.
Fixes: QTBUG-109370
Pick-to: 6.5
Change-Id: Iab86082cc63d1191024c514bd197aade63e1d4f6
Reviewed-by: Luca Di Sera <luca.disera@qt.io>
-rw-r--r-- | src/qdoc/clangcodeparser.cpp | 30 | ||||
-rw-r--r-- | src/qdoc/codemarker.cpp | 2 | ||||
-rw-r--r-- | src/qdoc/functionnode.cpp | 2 | ||||
-rw-r--r-- | src/qdoc/functionnode.h | 5 | ||||
-rw-r--r-- | src/qdoc/qdocindexfiles.cpp | 7 |
5 files changed, 46 insertions, 0 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index 2caa39ede..d5acfd1cd 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -896,6 +896,32 @@ void ClangVisitor::readParameterNamesAndAttributes(FunctionNode *fn, CXCursor cu }); } +struct CXXSpecifiers { + bool is_explicit = false; +}; + +static CXXSpecifiers get_specifiers(CXCursor cursor) { + CXXSpecifiers specifiers{}; + + CXTranslationUnit tu{clang_Cursor_getTranslationUnit(cursor)}; + + CXToken* tokens = nullptr; + unsigned token_count{0}; + + clang_tokenize(tu, clang_getCursorExtent(cursor), &tokens, &token_count); + + 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; + } + } + + clang_disposeTokens(tu, tokens, token_count); + + return specifiers; +} + void ClangVisitor::processFunction(FunctionNode *fn, CXCursor cursor) { CXCursorKind kind = clang_getCursorKind(cursor); @@ -919,6 +945,10 @@ void ClangVisitor::processFunction(FunctionNode *fn, CXCursor cursor) : clang_CXXMethod_isPureVirtual(cursor) ? FunctionNode::PureVirtual : FunctionNode::NormalVirtual); + + CXXSpecifiers specifiers{get_specifiers(cursor)}; + if (specifiers.is_explicit) fn->markExplicit(); + CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType); if (refQualKind == CXRefQualifier_LValue) fn->setRef(true); diff --git a/src/qdoc/codemarker.cpp b/src/qdoc/codemarker.cpp index 4f83c6ee2..1ab0df106 100644 --- a/src/qdoc/codemarker.cpp +++ b/src/qdoc/codemarker.cpp @@ -149,6 +149,8 @@ QString CodeMarker::extraSynopsis(const Node *node, Section::Style style) extra << "virtual"; } + if (func->isExplicit()) extra << "explicit"; + if (func->access() == Access::Protected) extra << "protected"; else if (func->access() == Access::Private) diff --git a/src/qdoc/functionnode.cpp b/src/qdoc/functionnode.cpp index 1d8f6b3a4..4f29b7e9d 100644 --- a/src/qdoc/functionnode.cpp +++ b/src/qdoc/functionnode.cpp @@ -60,6 +60,7 @@ FunctionNode::FunctionNode(Aggregate *parent, const QString &name) m_isRef(false), m_isRefRef(false), m_isInvokable(false), + m_explicit{false}, m_metaness(Plain), m_virtualness(NonVirtual), m_overloadNumber(0), @@ -91,6 +92,7 @@ FunctionNode::FunctionNode(Metaness kind, Aggregate *parent, const QString &name m_isRef(false), m_isRefRef(false), m_isInvokable(false), + m_explicit{false}, m_metaness(kind), m_virtualness(NonVirtual), m_overloadNumber(0), diff --git a/src/qdoc/functionnode.h b/src/qdoc/functionnode.h index e62b347de..93dbab99e 100644 --- a/src/qdoc/functionnode.h +++ b/src/qdoc/functionnode.h @@ -75,6 +75,9 @@ public: } [[nodiscard]] bool isDeprecated() const override; + void markExplicit() { m_explicit = true; } + bool isExplicit() const { return m_explicit; } + [[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); } @@ -178,6 +181,8 @@ private: bool m_isRef : 1; bool m_isRefRef : 1; bool m_isInvokable : 1; + bool m_explicit; + Metaness m_metaness {}; Virtualness m_virtualness {}; signed short m_overloadNumber {}; diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp index 5a35e7bbf..e40e9b41f 100644 --- a/src/qdoc/qdocindexfiles.cpp +++ b/src/qdoc/qdocindexfiles.cpp @@ -423,6 +423,10 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader &reader, Node *current, fn->setStatic(attributes.value(QLatin1String("static")) == QLatin1String("true")); fn->setFinal(attributes.value(QLatin1String("final")) == QLatin1String("true")); fn->setOverride(attributes.value(QLatin1String("override")) == QLatin1String("true")); + + if (attributes.value(QLatin1String("explicit")) == QLatin1String("true")) + fn->markExplicit(); + qsizetype refness = attributes.value(QLatin1String("refness")).toUInt(); if (refness == 1) fn->setRef(true); @@ -1195,6 +1199,9 @@ void QDocIndexFiles::generateFunctionSection(QXmlStreamWriter &writer, FunctionN writer.writeAttribute("static", fn->isStatic() ? "true" : "false"); writer.writeAttribute("final", fn->isFinal() ? "true" : "false"); writer.writeAttribute("override", fn->isOverride() ? "true" : "false"); + + if (fn->isExplicit()) writer.writeAttribute("explicit", "true"); + /* This ensures that for functions that have overloads, the first function written is the one that is not an |