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