summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuca Di Sera <luca.disera@qt.io>2022-12-27 12:48:37 +0100
committerLuca Di Sera <luca.disera@qt.io>2022-12-27 21:26:48 +0000
commit6b5ffb7e139c03778479a15e73e02133fa2790aa (patch)
treef2b3a923b5f04c4b80d6e72b0a4aafd92d22ec80
parente055120820bf3a18387599d2d4e19939255d2a81 (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.cpp30
-rw-r--r--src/qdoc/codemarker.cpp2
-rw-r--r--src/qdoc/functionnode.cpp2
-rw-r--r--src/qdoc/functionnode.h5
-rw-r--r--src/qdoc/qdocindexfiles.cpp7
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