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

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