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

source code of qttools/src/qdoc/qdoc/src/qdoc/sections.cpp