Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
sections.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "sections.h"
5
6#include "aggregate.h"
7#include "classnode.h"
8#include "config.h"
9#include "enumnode.h"
10#include "functionnode.h"
11#include "genustypes.h"
14#include "utilities.h"
15#include "namespacenode.h"
16#include "node.h"
18#include "qmltypenode.h"
20#include "typedefnode.h"
21#include "variablenode.h"
22
23#include <QtCore/qobjectdefs.h>
24#include <QtCore/qset.h>
25
27
28using namespace Qt::StringLiterals;
29
30namespace {
31
32SectionVector makeStdSummarySections()
33{
34 return {
35 { "Namespaces"_L1, "namespace"_L1, "namespaces"_L1, ""_L1, Section::Summary },
36 { "Classes"_L1, "class"_L1, "classes"_L1, ""_L1, Section::Summary },
37 { "Types"_L1, "type"_L1, "types"_L1, ""_L1, Section::Summary },
38 { "Variables"_L1, "variable"_L1, "variables"_L1, ""_L1, Section::Summary },
39 { "Static Variables"_L1, "static variable"_L1, "static variables"_L1, ""_L1, Section::Summary },
40 { "Functions"_L1, "function"_L1, "functions"_L1, ""_L1, Section::Summary },
41 { "Macros"_L1, "macro"_L1, "macros"_L1, ""_L1, Section::Summary },
42 };
43}
44
45SectionVector makeStdDetailsSections()
46{
47 return {
48 { "Namespaces"_L1, "namespace"_L1, "namespaces"_L1, "nmspace"_L1, Section::Details },
49 { "Classes"_L1, "class"_L1, "classes"_L1, "classes"_L1, Section::Details },
50 { "Type Documentation"_L1, "type"_L1, "types"_L1, "types"_L1, Section::Details },
51 { "Variable Documentation"_L1, "variable"_L1, "variables"_L1, "vars"_L1, Section::Details },
52 { "Static Variables"_L1, "static variable"_L1, "static variables"_L1, ""_L1, Section::Details },
53 { "Function Documentation"_L1, "function"_L1, "functions"_L1, "func"_L1, Section::Details },
54 { "Macro Documentation"_L1, "macro"_L1, "macros"_L1, "macros"_L1, Section::Details },
55 };
56}
57
58SectionVector makeCppClassSummarySections()
59{
60 return {
61 { "Public Types"_L1, "public type"_L1, "public types"_L1, ""_L1, Section::Summary },
62 { "Properties"_L1, "property"_L1, "properties"_L1, ""_L1, Section::Summary },
63 { "Public Functions"_L1, "public function"_L1, "public functions"_L1, ""_L1, Section::Summary },
64 { "Public Slots"_L1, "public slot"_L1, "public slots"_L1, ""_L1, Section::Summary },
65 { "Signals"_L1, "signal"_L1, "signals"_L1, ""_L1, Section::Summary },
66 { "Public Variables"_L1, "public variable"_L1, "public variables"_L1, ""_L1, Section::Summary },
67 { "Static Public Members"_L1, "static public member"_L1, "static public members"_L1, ""_L1, Section::Summary },
68 { "Protected Types"_L1, "protected type"_L1, "protected types"_L1, ""_L1, Section::Summary },
69 { "Protected Functions"_L1, "protected function"_L1, "protected functions"_L1, ""_L1, Section::Summary },
70 { "Protected Slots"_L1, "protected slot"_L1, "protected slots"_L1, ""_L1, Section::Summary },
71 { "Protected Variables"_L1, "protected type"_L1, "protected variables"_L1, ""_L1, Section::Summary },
72 { "Static Protected Members"_L1, "static protected member"_L1, "static protected members"_L1, ""_L1, Section::Summary },
73 { "Private Types"_L1, "private type"_L1, "private types"_L1, ""_L1, Section::Summary },
74 { "Private Functions"_L1, "private function"_L1, "private functions"_L1, ""_L1, Section::Summary },
75 { "Private Slots"_L1, "private slot"_L1, "private slots"_L1, ""_L1, Section::Summary },
76 { "Private Variables"_L1, "private variable"_L1, "private variables"_L1, ""_L1, Section::Summary },
77 { "Static Private Members"_L1, "static private member"_L1, "static private members"_L1, ""_L1, Section::Summary },
78 { "Related Non-Members"_L1, "related non-member"_L1, "related non-members"_L1, ""_L1, Section::Summary },
79 { "Macros"_L1, "macro"_L1, "macros"_L1, ""_L1, Section::Summary },
80 };
81}
82
83SectionVector makeCppClassDetailsSections()
84{
85 return {
86 { "Member Type Documentation"_L1, "member"_L1, "members"_L1, "types"_L1, Section::Details },
87 { "Property Documentation"_L1, "member"_L1, "members"_L1, "prop"_L1, Section::Details },
88 { "Member Function Documentation"_L1, "member"_L1, "members"_L1, "func"_L1, Section::Details },
89 { "Member Variable Documentation"_L1, "member"_L1, "members"_L1, "vars"_L1, Section::Details },
90 { "Related Non-Members"_L1, "member"_L1, "members"_L1, "relnonmem"_L1, Section::Details },
91 { "Macro Documentation"_L1, "member"_L1, "members"_L1, "macros"_L1, Section::Details },
92 };
93}
94
95SectionVector makeQmlTypeSummarySections()
96{
97 return {
98 { "Enumerations"_L1, "enumeration"_L1, "enumerations"_L1, ""_L1, Section::Summary },
99 { "Properties"_L1, "property"_L1, "properties"_L1, ""_L1, Section::Summary },
100 { "Attached Properties"_L1, "attached property"_L1, "attached properties"_L1, ""_L1, Section::Summary },
101 { "Signals"_L1, "signal"_L1, "signals"_L1, ""_L1, Section::Summary },
102 { "Signal Handlers"_L1, "signal handler"_L1, "signal handlers"_L1, ""_L1, Section::Summary },
103 { "Attached Signals"_L1, "attached signal"_L1, "attached signals"_L1, ""_L1, Section::Summary },
104 { "Methods"_L1, "method"_L1, "methods"_L1, ""_L1, Section::Summary },
105 { "Attached Methods"_L1, "attached method"_L1, "attached methods"_L1, ""_L1, Section::Summary },
106 };
107}
108
109SectionVector makeQmlTypeDetailsSections()
110{
111 return {
112 { "Enumeration Documentation"_L1, "member"_L1, "members"_L1, "qmlenum"_L1, Section::Details },
113 { "Property Documentation"_L1, "member"_L1, "members"_L1, "qmlprop"_L1, Section::Details },
114 { "Attached Property Documentation"_L1, "member"_L1, "members"_L1, "qmlattprop"_L1, Section::Details },
115 { "Signal Documentation"_L1, "signal"_L1, "signals"_L1, "qmlsig"_L1, Section::Details },
116 { "Signal Handler Documentation"_L1, "signal handler"_L1, "signal handlers"_L1, "qmlsighan"_L1, Section::Details },
117 { "Attached Signal Documentation"_L1, "signal"_L1, "signals"_L1, "qmlattsig"_L1, Section::Details },
118 { "Method Documentation"_L1, "member"_L1, "members"_L1, "qmlmeth"_L1, Section::Details },
119 { "Attached Method Documentation"_L1, "member"_L1, "members"_L1, "qmlattmeth"_L1, Section::Details },
120 };
121}
122
123SectionVector makeSinceSections()
124{
125 return {
126 { "New Namespaces"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
127 { "New Classes"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
128 { "New Member Functions"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
129 { "New Functions in Namespaces"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
130 { "New Global Functions"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
131 { "New Macros"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
132 { "New Enum Types"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
133 { "New Enum Values"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
134 { "New Type Aliases"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
135 { "New Properties"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
136 { "New Variables"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
137 { "New QML Types"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
138 { "New QML Enumeration Types"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
139 { "New QML Properties"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
140 { "New QML Signals"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
141 { "New QML Signal Handlers"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
142 { "New QML Methods"_L1, ""_L1, ""_L1, ""_L1, Section::Details },
143 };
144}
145
146} // anonymous namespace
147
148/*!
149 \class Section
150 \brief A class for containing the elements of one documentation section
151 */
152
153/*!
154 Construct a name for the \a node that can be used for sorting
155 a set of nodes into equivalence classes.
156 */
158{
159 QString nodeName{node->name()};
160
161 int numDigits = 0;
162 for (qsizetype i = nodeName.size() - 1; i > 0; --i) {
163 if (nodeName.at(i).digitValue() == -1)
164 break;
165 ++numDigits;
166 }
167
168 // we want 'qint8' to appear before 'qint16'
169 if (numDigits > 0) {
170 for (int i = 0; i < 4 - numDigits; ++i)
171 nodeName.insert(nodeName.size() - numDigits - 1, QLatin1Char('0'));
172 }
173
174 if (node->isClassNode())
175 return QLatin1Char('A') + nodeName;
176
177 if (node->isFunction(Genus::CPP)) {
178 const auto *fn = static_cast<const FunctionNode *>(node);
179
180 QString sortNo;
181 if (fn->isCtor())
182 sortNo = QLatin1String("C");
183 else if (fn->isCCtor())
184 sortNo = QLatin1String("D");
185 else if (fn->isMCtor())
186 sortNo = QLatin1String("E");
187 else if (fn->isDtor())
188 sortNo = QLatin1String("F");
189 else if (nodeName.startsWith(QLatin1String("operator")) && nodeName.size() > 8
190 && !nodeName[8].isLetterOrNumber())
191 sortNo = QLatin1String("H");
192 else
193 sortNo = QLatin1String("G");
194
195 return sortNo + nodeName + QLatin1Char(' ') + QString::number(fn->overloadNumber(), 36);
196 }
197
198 if (node->isFunction(Genus::QML))
199 return QLatin1Char('E') + nodeName + QLatin1Char(' ') +
200 QString::number(static_cast<const FunctionNode*>(node)->overloadNumber(), 36);
201
202 if (node->isProperty() || node->isVariable())
203 return QLatin1Char('G') + nodeName;
204
205 return QLatin1Char('B') + nodeName;
206}
207
208/*!
209 Inserts the \a node into this section if it is appropriate
210 for this section.
211 */
212void Section::insert(Node *node)
213{
214 bool irrelevant = false;
215 bool inherited = false;
216 if (!node->isRelatedNonmember()) {
217 Aggregate *p = node->parent();
218 if (!p->isNamespace() && p != m_aggregate) {
219 if (!p->isQmlType() || !p->isAbstract())
220 inherited = true;
221 }
222 }
223
224 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
225 const NodeContext context = node->createContext();
226
227 if (!InclusionFilter::isIncluded(policy, context)) {
228 irrelevant = true;
229 } else if (node->isFunction()) {
230 auto *func = static_cast<FunctionNode *>(node);
231 irrelevant = (inherited && (func->isSomeCtor() || func->isDtor()));
232 } else if (node->isClassNode() || node->isEnumType() || node->isTypedef()
233 || node->isVariable()) {
234 irrelevant = (inherited && m_style != AllMembers);
235 if (!irrelevant && m_style == Details && node->isTypedef()) {
236 const auto *tdn = static_cast<const TypedefNode *>(node);
237 if (tdn->associatedEnum())
238 irrelevant = true;
239 }
240 }
241
242 if (!irrelevant) {
243 QString key = sortName(node);
244 if (node->isDeprecated()) {
245 m_obsoleteMembers.push_back(node);
246 } else {
247 if (!inherited || m_style == AllMembers)
248 m_members.push_back(node);
249
250 if (inherited && (node->parent()->isClassNode() || node->parent()->isNamespace())) {
251 if (m_inheritedMembers.isEmpty()
252 || m_inheritedMembers.last().first != node->parent()) {
253 std::pair<Aggregate *, int> p(node->parent(), 0);
254 m_inheritedMembers.append(p);
255 }
256 m_inheritedMembers.last().second++;
257 }
258 }
259 }
260}
261
262/*!
263 Returns \c true if the \a node is a reimplemented member
264 function of the current class. If true, the \a node is
265 inserted into the reimplemented member map. True
266 is returned only if \a node is inserted into the map.
267 That is, false is returned if the \a node is already in
268 the map.
269 */
271{
272 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
273 const NodeContext context = node->createContext();
274
275 // Use specialized visibility check for documented reimplemented members
277 const auto *fn = static_cast<const FunctionNode *>(node);
278 if (!fn->overridesThis().isEmpty()) {
279 if (fn->parent() == m_aggregate) {
280 QString key = sortName(fn);
281 if (!m_reimplementedMemberMap.contains(key)) {
282 m_reimplementedMemberMap.insert(key, node);
283 return true;
284 }
285 }
286 }
287 }
288 return false;
289}
290
291/*!
292 If this section is not empty, convert its maps to sequential
293 structures for better traversal during doc generation.
294 */
296{
297 // TODO:TEMPORARY:INTERMEDIATE: Section uses a series of maps
298 // to internally manage the categorization of the various members
299 // of an aggregate. It further uses a secondary "flattened"
300 // (usually vector) version that is later used by consumers of a
301 // Section content.
302 //
303 // One of the uses of those maps is that of ordering, by using
304 // keys generated with `sortName`.
305 // Nonetheless, this is the only usage that comes from the keys,
306 // as they are neither necessary nor used outside of the internal
307 // code for Section.
308 //
309 // Hence, the codebase is moving towards removing the maps in
310 // favor of building a flattened, consumer ready, version of the
311 // categorization directly, cutting the intermediate conversion
312 // step.
313 //
314 // To do so while keeping as much of the old behavior as possible,
315 // we provide a sorting for the flattened version that is based on
316 // `sortName`, as the previous ordering was.
317 //
318 // This acts as a relatively heavy pessimization, as `sortName`,
319 // used as a comparator, can be called multiple times for each
320 // Node, while before it would have been called almost-once.
321 //
322 // Instead of fixing this issue, by for example caching the
323 // sortName of each Node instance, we temporarily keep the
324 // pessimization while the various maps are removed.
325 //
326 // When all the maps are removed, we can remove `sortName`, which
327 // produces strings to use as key requiring a few allocations and
328 // expensive operations, with an actual comparator function, which
329 // should be more lightweight and more than offset the
330 // multiple-calls.
331 static auto node_less_than = [](const Node* left, const Node* right) {
332 // For shared comment nodes, compare the names of the first child
333 // nodes instead of the names of the nodes themselves, which are
334 // usually empty.
335 if (left->isSharedCommentNode())
336 left = static_cast<const SharedCommentNode *>(left)->collective().first();
337 if (right->isSharedCommentNode())
338 right = static_cast<const SharedCommentNode *>(right)->collective().first();
339 return sortName(left) < sortName(right);
340 };
341
342 std::stable_sort(m_members.begin(), m_members.end(), node_less_than);
343 std::stable_sort(m_obsoleteMembers.begin(), m_obsoleteMembers.end(), node_less_than);
344
345 m_reimplementedMembers = m_reimplementedMemberMap.values().toVector();
346
347 for (auto &cn : m_classNodesList) {
348 std::stable_sort(cn.second.begin(), cn.second.end(), node_less_than);
349 }
350}
351
352/*!
353 \class Sections
354 \brief A class for creating vectors of collections for documentation
355
356 Each element in a vector is an instance of Section, which
357 contains all the elements that will be documented in one
358 section of a reference documentation page.
359
360 The constructor determines the appropriate section layout based on
361 the aggregate's node type (C++ class, QML type, or generic
362 namespace/header). Callers access the result through the unified
363 summarySections() and detailsSections() accessors without needing
364 to know which variant was selected.
365 */
366
367/*!
368 This constructor builds the section vectors based on the
369 type of the \a aggregate node.
370 */
371Sections::Sections(Aggregate *aggregate) : m_aggregate(aggregate)
372{
373 m_allMembers.setAggregate(m_aggregate);
374 switch (m_aggregate->nodeType()) {
375 case NodeType::Class:
376 case NodeType::Struct:
377 case NodeType::Union:
378 m_summarySections = makeCppClassSummarySections();
379 m_detailsSections = makeCppClassDetailsSections();
380 initAggregate(m_summarySections, m_aggregate);
381 initAggregate(m_detailsSections, m_aggregate);
382 buildStdCppClassRefPageSections();
383 break;
386 m_summarySections = makeQmlTypeSummarySections();
387 m_detailsSections = makeQmlTypeDetailsSections();
388 initAggregate(m_summarySections, m_aggregate);
389 initAggregate(m_detailsSections, m_aggregate);
390 buildStdQmlTypeRefPageSections();
391 break;
394 case NodeType::Proxy:
395 default:
396 m_summarySections = makeStdSummarySections();
397 m_detailsSections = makeStdDetailsSections();
398 initAggregate(m_summarySections, m_aggregate);
399 initAggregate(m_detailsSections, m_aggregate);
400 buildStdRefPageSections();
401 break;
402 }
403}
404
405/*!
406 This constructor builds the since sections from the \e since
407 node map, \a nsmap
408 */
410 : m_aggregate(nullptr), m_sinceSections(makeSinceSections())
411{
412 if (nsmap.isEmpty())
413 return;
414 for (auto it = nsmap.constBegin(); it != nsmap.constEnd(); ++it) {
415 Node *node = it.value();
416 switch (node->nodeType()) {
417 case NodeType::QmlType:
418 m_sinceSections[SinceQmlTypes].appendMember(node);
419 break;
420 case NodeType::Namespace:
421 m_sinceSections[SinceNamespaces].appendMember(node);
422 break;
423 case NodeType::Class:
424 case NodeType::Struct:
425 case NodeType::Union:
426 m_sinceSections[SinceClasses].appendMember(node);
427 break;
428 case NodeType::Enum: {
429 // The map can contain an enum node with \since, or an enum node
430 // with \value containing a since-clause. In the latter case,
431 // key() is an empty string.
432 if (!it.key().isEmpty())
433 m_sinceSections[SinceEnumTypes].appendMember(node);
434 else
435 m_sinceSections[SinceEnumValues].appendMember(node);
436 break;
437 }
438 case NodeType::Typedef:
439 case NodeType::TypeAlias:
440 m_sinceSections[SinceTypeAliases].appendMember(node);
441 break;
442 case NodeType::Function: {
443 const auto *fn = static_cast<const FunctionNode *>(node);
444 switch (fn->metaness()) {
445 case FunctionNode::QmlSignal:
446 m_sinceSections[SinceQmlSignals].appendMember(node);
447 break;
448 case FunctionNode::QmlSignalHandler:
449 m_sinceSections[SinceQmlSignalHandlers].appendMember(node);
450 break;
451 case FunctionNode::QmlMethod:
452 m_sinceSections[SinceQmlMethods].appendMember(node);
453 break;
454 default:
455 if (fn->isMacro())
456 m_sinceSections[SinceMacros].appendMember(node);
457 else {
458 Node *p = fn->parent();
459 if (p) {
460 if (p->isClassNode())
461 m_sinceSections[SinceMemberFunctions].appendMember(node);
462 else if (p->isNamespace()) {
463 if (p->name().isEmpty())
464 m_sinceSections[SinceGlobalFunctions].appendMember(node);
465 else
466 m_sinceSections[SinceNamespaceFunctions].appendMember(node);
467 } else
468 m_sinceSections[SinceGlobalFunctions].appendMember(node);
469 } else
470 m_sinceSections[SinceGlobalFunctions].appendMember(node);
471 }
472 break;
473 }
474 break;
475 }
476 case NodeType::Property:
477 m_sinceSections[SinceProperties].appendMember(node);
478 break;
479 case NodeType::SharedComment:
480 if (node->isPropertyGroup())
481 m_sinceSections[SinceQmlProperties].appendMember(node);
482 break;
483 case NodeType::Variable:
484 m_sinceSections[SinceVariables].appendMember(node);
485 break;
486 case NodeType::QmlProperty:
487 m_sinceSections[SinceQmlProperties].appendMember(node);
488 break;
489 case NodeType::QmlEnum:
490 m_sinceSections[SinceQmlEnumTypes].appendMember(node);
491 break;
492 default:
493 break;
494 }
495 }
496}
497
498/*!
499 Initialize the Aggregate in each Section of vector \a v with \a aggregate.
500 */
501void Sections::initAggregate(SectionVector &v, Aggregate *aggregate)
502{
503 for (Section &section : v)
504 section.setAggregate(aggregate);
505}
506
507/*!
508 Linearize the maps in each Section in \a v.
509 */
510void Sections::reduce(QList<Section> &v)
511{
512 for (Section &section : v)
513 section.reduce();
514}
515
516/*!
517 \internal
518
519 Returns the node to test when distributing \a node based on
520 Node::nodeType().
521
522 It returns either \a node itself, or if \a node is a shared comment
523 node, the first node in its collective.
524*/
526{
527 if (node && node->isSharedCommentNode() && node->hasDoc()) {
528 if (auto *scn = static_cast<SharedCommentNode *>(node); scn->collective().size())
529 return scn->collective().first(); // TODO: warn about mixed node types in collective?
530 }
531 return node;
532}
533
534/*!
535 This is a private helper function for buildStdRefPageSections().
536 */
537void Sections::stdRefPageSwitch(SectionVector &v, Node *n)
538{
540 switch (t->nodeType()) {
541 case NodeType::Namespace:
542 v[StdNamespaces].insert(n);
543 return;
544 case NodeType::Class:
545 case NodeType::Struct:
546 case NodeType::Union:
547 v[StdClasses].insert(n);
548 return;
549 case NodeType::Enum:
550 case NodeType::Typedef:
551 case NodeType::TypeAlias:
552 v[StdTypes].insert(n);
553 return;
554 case NodeType::Function: {
555 auto *func = static_cast<FunctionNode *>(t);
556 if (func->isMacro())
557 v[StdMacros].insert(n);
558 else
559 v[StdFunctions].insert(n);
560 }
561 return;
562 case NodeType::Variable: {
563 const auto *var = static_cast<const VariableNode *>(t);
564 if (!var->doc().isEmpty()) {
565 if (var->isStatic())
566 v[StdStaticVariables].insert(n);
567 else
568 v[StdVariables].insert(n);
569 }
570 }
571 return;
572 default:
573 return;
574 }
575}
576
577/*!
578 Build the section vectors for a standard reference page,
579 when the aggregate node is not a C++ class or a QML type.
580
581 If this is for a namespace page then if the namespace node
582 itself does not have documentation, only its children that
583 have documentation should be documented. In other words,
584 there are cases where a namespace is declared but does not
585 have documentation, but some of the elements declared in
586 that namespace do have documentation.
587
588 This special processing of namespaces that do not have a
589 documentation comment is meant to allow documenting its
590 members that do have documentation while avoiding posting
591 error messages for its members that are not documented.
592 */
593void Sections::buildStdRefPageSections()
594{
595 const NamespaceNode *ns = nullptr;
596 bool documentAll = true; // document all the children
597 if (m_aggregate->isNamespace()) {
598 ns = static_cast<const NamespaceNode *>(m_aggregate);
599 if (!ns->hasDoc())
600 documentAll = false; // only document children that have documentation
601 }
602 for (auto it = m_aggregate->constBegin(); it != m_aggregate->constEnd(); ++it) {
603 Node *n = *it;
604 if (documentAll || n->hasDoc()) {
605 stdRefPageSwitch(m_summarySections, n);
607 stdRefPageSwitch(m_detailsSections, n);
608 }
609 }
610 if (!m_aggregate->relatedByProxy().isEmpty()) {
611 const QList<Node *> &relatedBy = m_aggregate->relatedByProxy();
612 for (const auto &node : relatedBy)
613 stdRefPageSwitch(m_summarySections, node);
614 }
615 /*
616 If we are building the sections for the reference page
617 for a namespace node, include all the namespace node's
618 included children in the sections.
619 */
620 if (ns && !ns->includedChildren().isEmpty()) {
621 const QList<Node *> &children = ns->includedChildren();
622 for (const auto &child : children) {
623 if (documentAll || child->hasDoc())
624 stdRefPageSwitch(m_summarySections, child);
625 }
626 }
627 reduce(m_summarySections);
628 reduce(m_detailsSections);
629 m_allMembers.reduce();
630}
631
632/*!
633 Inserts the node \a n in one of the entries in the vector \a v
634 depending on the node's type, access attribute, and a few other
635 attributes if the node is a signal, slot, or function.
636 */
637void Sections::distributeNodeInSummaryVector(SectionVector &sv, Node *n)
638{
640 static_cast<SharedCommentNode *>(n)->sort();
641 return;
642 }
643 if (n->isFunction()) {
644 auto *fn = static_cast<FunctionNode *>(n);
645 if (fn->isRelatedNonmember()) {
646 if (fn->isMacro())
647 sv[Macros].insert(n);
648 else
649 sv[RelatedNonmembers].insert(n);
650 return;
651 }
652 if (fn->isIgnored())
653 return;
654 if (fn->isSlot()) {
655 if (fn->isPublic())
656 sv[PublicSlots].insert(fn);
657 else if (fn->isPrivate())
658 sv[PrivateSlots].insert(fn);
659 else
660 sv[ProtectedSlots].insert(fn);
661 } else if (fn->isSignal()) {
662 if (fn->isPublic() && fn->isInAPI())
663 sv[Signals].insert(fn);
664 } else if (fn->isPublic()) {
665 if (fn->isStatic() && fn->isInAPI())
666 sv[StaticPublicMembers].insert(fn);
667 else if (!sv[PublicFunctions].insertReimplementedMember(fn) && fn->isInAPI())
668 sv[PublicFunctions].insert(fn);
669 } else if (fn->isPrivate()) {
670 if (fn->isStatic())
671 sv[StaticPrivateMembers].insert(fn);
672 else if (!sv[PrivateFunctions].insertReimplementedMember(fn))
673 sv[PrivateFunctions].insert(fn);
674 } else { // protected
675 if (fn->isStatic())
676 sv[StaticProtectedMembers].insert(fn);
677 else if (!sv[ProtectedFunctions].insertReimplementedMember(fn))
678 sv[ProtectedFunctions].insert(fn);
679 }
680 return;
681 }
683 sv[RelatedNonmembers].insert(n);
684 return;
685 }
686 if (n->isVariable()) {
687 if (n->isStatic()) {
688 if (n->isPublic())
689 sv[StaticPublicMembers].insert(n);
690 else if (n->isPrivate())
691 sv[StaticPrivateMembers].insert(n);
692 else
693 sv[StaticProtectedMembers].insert(n);
694 } else {
695 if (n->isPublic())
696 sv[PublicVariables].insert(n);
697 else if (n->isProtected())
698 sv[ProtectedVariables].insert(n);
699 else if (n->isPrivate())
700 sv[PrivateVariables].insert(n);
701 }
702 return;
703 }
704 /*
705 Getting this far means the node is either a property
706 or some kind of type, like an enum or a typedef.
707 */
708 if (n->isTypedef() && (n->name() == QLatin1String("QtGadgetHelper")))
709 return;
710 if (n->isProperty())
711 sv[Properties].insert(n);
712 else if (n->isPublic() && n->isInAPI())
713 sv[PublicTypes].insert(n);
714 else if (n->isPrivate())
715 sv[PrivateTypes].insert(n);
716 else if (n->isProtected())
717 sv[ProtectedTypes].insert(n);
718}
719
720/*!
721 Inserts the node \a n in one of the entries in the vector \a v
722 depending on the node's type, access attribute, and a few other
723 attributes if the node is a signal, slot, or function.
724 */
725void Sections::distributeNodeInDetailsVector(SectionVector &dv, Node *n)
726{
728 return;
729
731 if (t->isFunction()) {
732 auto *fn = static_cast<FunctionNode *>(t);
733 if (fn->isRelatedNonmember()) {
734 if (fn->isMacro())
735 dv[DetailsMacros].insert(n);
736 else
737 dv[DetailsRelatedNonmembers].insert(n);
738 return;
739 }
740 if (fn->isIgnored())
741 return;
742 if (!fn->hasAssociatedProperties() || !fn->doc().isEmpty())
743 dv[DetailsMemberFunctions].insert(n);
744 return;
745 }
747 dv[DetailsRelatedNonmembers].insert(n);
748 return;
749 }
750 if (t->isEnumType(Genus::CPP) || t->isTypedef()) {
751 if (t->name() != QLatin1String("QtGadgetHelper"))
752 dv[DetailsMemberTypes].insert(n);
753 return;
754 }
755 if (t->isProperty())
756 dv[DetailsProperties].insert(n);
757 else if (t->isVariable() && !t->doc().isEmpty())
758 dv[DetailsMemberVariables].insert(n);
759}
760
761void Sections::distributeQmlNodeInDetailsVector(SectionVector &dv, Node *n)
762{
764 return;
765
767 if (t != n && n->isPropertyGroup()) {
768 dv[QmlProperties].insert(n);
769 return;
770 }
771
772 if (t->isQmlProperty()) {
773 auto *pn = static_cast<QmlPropertyNode *>(t);
774 if (pn->isAttached())
775 dv[QmlAttachedProperties].insert(n);
776 else
777 dv[QmlProperties].insert(n);
778 } else if (t->isEnumType(Genus::QML)) {
779 dv[QmlEnumTypes].insert(n);
780 } else if (t->isFunction()) {
781 auto *fn = static_cast<FunctionNode *>(t);
782 if (fn->isQmlSignal()) {
783 if (fn->isAttached())
784 dv[QmlAttachedSignals].insert(n);
785 else
786 dv[QmlSignals].insert(n);
787 } else if (fn->isQmlSignalHandler()) {
788 dv[QmlSignalHandlers].insert(n);
789 } else if (fn->isQmlMethod()) {
790 if (fn->isAttached())
791 dv[QmlAttachedMethods].insert(n);
792 else
793 dv[QmlMethods].insert(n);
794 }
795 }
796}
797
798/*!
799 Distributes a node \a n into the correct place in the summary section vector \a sv.
800 Nodes that are sharing a comment are handled recursively - for recursion, the \a
801 sharing parameter is set to \c true.
802 */
803void Sections::distributeQmlNodeInSummaryVector(SectionVector &sv, Node *n, bool sharing)
804{
805 if (n->isSharingComment() && !sharing)
806 return;
807 if (n->isQmlProperty()) {
808 auto *pn = static_cast<QmlPropertyNode *>(n);
809 if (pn->isAttached())
810 sv[QmlAttachedProperties].insert(pn);
811 else
812 sv[QmlProperties].insert(pn);
813 } else if (n->isEnumType(Genus::QML)) {
814 sv[QmlEnumTypes].insert(n);
815 } else if (n->isFunction()) {
816 auto *fn = static_cast<FunctionNode *>(n);
817 if (fn->isQmlSignal()) {
818 if (fn->isAttached())
819 sv[QmlAttachedSignals].insert(fn);
820 else
821 sv[QmlSignals].insert(fn);
822 } else if (fn->isQmlSignalHandler()) {
823 sv[QmlSignalHandlers].insert(fn);
824 } else if (fn->isQmlMethod()) {
825 if (fn->isAttached())
826 sv[QmlAttachedMethods].insert(fn);
827 else
828 sv[QmlMethods].insert(fn);
829 }
830 } else if (n->isSharedCommentNode()) {
831 auto *scn = static_cast<SharedCommentNode *>(n);
832 if (scn->isPropertyGroup()) {
833 sv[QmlProperties].insert(scn);
834 } else {
835 for (const auto &child : scn->collective())
836 distributeQmlNodeInSummaryVector(sv, child, true);
837 }
838 }
839}
840
841static void pushBaseClasses(QStack<ClassNode *> &stack, ClassNode *cn)
842{
843 const QList<RelatedClass> baseClasses = cn->baseClasses();
844 for (const auto &cls : baseClasses) {
845 if (cls.m_node)
846 stack.prepend(cls.m_node);
847 }
848}
849
850/*!
851 Build the section vectors for a standard reference page,
852 when the aggregate node is a C++.
853 */
854void Sections::buildStdCppClassRefPageSections()
855{
856 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
857
858 for (auto it = m_aggregate->constBegin(); it != m_aggregate->constEnd(); ++it) {
859 Node *n = *it;
860 const NodeContext context = n->createContext();
863 m_allMembers.insert(n);
864 }
865 distributeNodeInSummaryVector(m_summarySections, n);
866 distributeNodeInDetailsVector(m_detailsSections, n);
867 }
868 if (!m_aggregate->relatedByProxy().isEmpty()) {
869 const QList<Node *> relatedBy = m_aggregate->relatedByProxy();
870 for (const auto &node : relatedBy)
871 distributeNodeInSummaryVector(m_summarySections, node);
872 }
873
874 QStack<ClassNode *> stack;
875 QSet<ClassNode *> visited;
876 auto *cn = static_cast<ClassNode *>(m_aggregate);
877
878 pushBaseClasses(stack, cn);
879 while (!stack.isEmpty()) {
880 ClassNode *cur = stack.pop();
881 if (visited.contains(cur))
882 continue;
883 visited.insert(cur);
884 for (Node *n : cur->childNodes()) {
885 const NodeContext context = n->createContext();
886 if (InclusionFilter::isIncluded(policy, context) && !n->isProperty()
887 && !n->isRelatedNonmember() && !n->isSharedCommentNode()) {
888 m_allMembers.insert(n);
889 }
890 }
891 pushBaseClasses(stack, cur);
892 }
893 reduce(m_summarySections);
894 reduce(m_detailsSections);
895 m_allMembers.reduce();
896}
897
898/*!
899 Build the section vectors for a standard reference page,
900 when the aggregate node is a QML type.
901 */
902void Sections::buildStdQmlTypeRefPageSections()
903{
904 ClassNodes *classNodes = nullptr;
905
906 const Aggregate *qtn = m_aggregate;
907 while (qtn) {
908 if (!qtn->isAbstract() || !classNodes)
909 classNodes = &m_allMembers.classNodesList().emplace_back(static_cast<const QmlTypeNode*>(qtn), NodeVector{});
910 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
911 for (const auto n : qtn->childNodes()) {
912 const NodeContext context = n->createContext();
913 if (!InclusionFilter::isIncluded(policy, context))
914 continue;
915
916 // Skip overridden property/function documentation from abstract base type
917 if (qtn != m_aggregate && qtn->isAbstract()) {
918 NodeList candidates;
919 m_aggregate->findChildren(n->name(), candidates);
920 if (std::any_of(candidates.cbegin(), candidates.cend(), [&n](const Node *c) {
921 if (c->nodeType() == n->nodeType()) {
922 if (!n->isFunction() ||
923 compare(static_cast<const FunctionNode *>(n),
924 static_cast<const FunctionNode *>(c)) == 0)
925 return true;
926 }
927 return false;
928 })) {
929 continue;
930 }
931 }
932
933 if (!n->isSharedCommentNode() || n->isPropertyGroup()) {
934 m_allMembers.insert(n);
935 classNodes->second.push_back(n);
936 }
937
938
939 if (qtn == m_aggregate || qtn->isAbstract()) {
940 distributeQmlNodeInSummaryVector(m_summarySections, n);
941 distributeQmlNodeInDetailsVector(m_detailsSections, n);
942 }
943 }
944 if (qtn->qmlBaseNode() == qtn) {
945 qCDebug(lcQdoc, "error: circular type definition: '%s' inherits itself",
946 qPrintable(qtn->name()));
947 break;
948 }
949 qtn = qtn->qmlBaseNode();
950 }
951
952 reduce(m_summarySections);
953 reduce(m_detailsSections);
954 m_allMembers.reduce();
955}
956
957/*!
958 Returns true if any sections in this object contain obsolete
959 members. If it returns false, then \a summary_spv and \a details_spv
960 have not been modified. Otherwise, both vectors will contain pointers
961 to the sections that contain obsolete members.
962 */
964 SectionPtrVector *details_spv) const
965{
966 for (const auto &section : m_summarySections) {
967 if (!section.obsoleteMembers().isEmpty())
968 summary_spv->append(&section);
969 }
970 for (const auto &section : m_detailsSections) {
971 if (!section.obsoleteMembers().isEmpty())
972 details_spv->append(&section);
973 }
974 return !summary_spv->isEmpty();
975}
976
977QT_END_NAMESPACE
virtual QmlTypeNode * qmlBaseNode() const
If this Aggregate is a QmlTypeNode, this function returns a pointer to the QmlTypeNode that is its ba...
Definition aggregate.h:55
NodeList & relatedByProxy()
Returns a reference to a list of node pointers where each element points to a node in an index file f...
Definition aggregate.h:71
The ClassNode represents a C++ class.
Definition classnode.h:23
static bool isReimplementedMemberVisible(const InclusionPolicy &policy, const NodeContext &context)
static bool isIncluded(const InclusionPolicy &policy, const NodeContext &context)
This class represents a C++ namespace.
const NodeList & includedChildren() const
Returns a const reference to the namespace node's list of included children, which contains pointers ...
A class for containing the elements of one documentation section.
Definition sections.h:17
void insert(Node *node)
Inserts the node into this section if it is appropriate for this section.
Definition sections.cpp:212
void reduce()
If this section is not empty, convert its maps to sequential structures for better traversal during d...
Definition sections.cpp:295
@ Details
Definition sections.h:19
@ AllMembers
Definition sections.h:19
bool insertReimplementedMember(Node *node)
Returns true if the node is a reimplemented member function of the current class.
Definition sections.cpp:270
A class for creating vectors of collections for documentation.
Definition sections.h:80
Sections(Aggregate *aggregate)
This constructor builds the section vectors based on the type of the aggregate node.
Definition sections.cpp:371
Sections(const NodeMultiMap &nsmap)
This constructor builds the since sections from the since node map, nsmap.
Definition sections.cpp:409
bool hasObsoleteMembers(SectionPtrVector *summary_spv, SectionPtrVector *details_spv) const
Returns true if any sections in this object contain obsolete members.
Definition sections.cpp:963
NodeType
Definition genustypes.h:150
Combined button and popup list for selecting options.
QMultiMap< QString, Node * > NodeMultiMap
Definition node.h:50
QString sortName(const Node *node)
Construct a name for the node that can be used for sorting a set of nodes into equivalence classes.
Definition sections.cpp:157
static Node * nodeToTestForDistribution(Node *node)
Definition sections.cpp:525
static void pushBaseClasses(QStack< ClassNode * > &stack, ClassNode *cn)
Definition sections.cpp:841
std::pair< const QmlTypeNode *, NodeVector > ClassNodes
Definition sections.h:13
QList< const Section * > SectionPtrVector
Definition sections.h:77
QList< Section > SectionVector
Definition sections.h:76
The Node class is the base class for all the nodes in QDoc's parse tree.
virtual bool isStatic() const
Returns true if the FunctionNode represents a static function.
Definition node.h:152
bool isEnumType(Genus g) const
Definition node.h:97
virtual bool isAbstract() const
Returns true if the ClassNode or QmlTypeNode is marked abstract.
Definition node.h:135
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:108
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:126
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:121
bool isSharedCommentNode() const
Returns true if the node type is SharedComment.
Definition node.h:124
NodeType nodeType() const override
Returns this node's type.
Definition node.h:82
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:93
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:208
bool isVariable() const
Returns true if the node type is Variable.
Definition node.h:131
virtual bool isDeprecated() const
Returns true if this node's status is Deprecated.
Definition node.h:134
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:100
virtual bool isInAPI() const
Returns true if this node is considered to be part of the API as per the InclusionPolicy retrieved fr...
Definition node.cpp:917
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:112
NodeContext createContext() const
Definition node.cpp:174
virtual bool isPropertyGroup() const
Returns true if the node is a SharedCommentNode for documenting multiple C++ properties or multiple Q...
Definition node.h:151
bool isSharingComment() const
This function returns true if the node is sharing a comment with other nodes.
Definition node.h:246
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:932
bool isRelatedNonmember() const
Returns true if this is a related nonmember of something.
Definition node.h:122
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:143
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:120