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
nodeextractor.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5
6#include "aggregate.h"
7#include "atom.h"
8#include "classnode.h"
9#include "codemarker.h"
10#include "collectionnode.h"
12#include "config.h"
13#include "doc.h"
14#include "enumnode.h"
15#include "functionnode.h"
16#include "generator.h"
17#include "hrefresolver.h"
19#include "namespacenode.h"
20#include "ir/contentbuilder.h"
21#include "ir/signaturespan.h"
22#include "pagenode.h"
23#include "parameters.h"
24#include "propertynode.h"
26#include "qdocdatabase.h"
27#include "qmltypenode.h"
28#include "sections.h"
31#include "text.h"
32#include "tree.h"
33#include "typedefnode.h"
34#include "utilities.h"
35#include "textutils.h"
36#include "variablenode.h"
37
38#include "location.h"
39
40#include <QRegularExpression>
41
42#include <deque>
43
44QT_BEGIN_NAMESPACE
45
46using namespace Qt::Literals;
47
49{
50 const Location &loc = node->doc().location();
51 return [loc](QtMsgType type, const QString &message) {
52 switch (type) {
53 case QtWarningMsg:
54 loc.warning(message);
55 break;
56 default:
57 loc.warning(message);
58 break;
59 }
60 };
61}
62
63static QString resolveHref(const HrefResolver *resolver, const Node *target, const Node *relative)
64{
65 if (!resolver)
66 return target->url();
67 auto result = resolver->hrefForNode(target, relative);
68 if (const auto *href = std::get_if<QString>(&result))
69 return *href;
70 return {};
71}
72
73namespace NodeExtractor {
74
75/*!
76 \internal
77 Extract page-level metadata from a PageNode into a value-type struct.
78
79 This function reads classification, identity, brief, and body fields
80 from the given PageNode and returns them as an IR::PageMetadata value.
81 Body content is populated via ContentBuilder, which transforms the
82 atom chain into structured content blocks. Format-conditional atoms
83 are skipped unconditionally since the template generator builds a
84 format-agnostic IR.
85
86 For aggregate pages (classes, QML types, namespaces), member listings
87 are extracted via the Sections infrastructure and stored as frozen
88 SectionIR values.
89
90 The caller (TemplateGenerator) invokes this before passing the
91 result to IR::Builder, ensuring Builder never includes PageNode
92 or other Node subclass headers.
93*/
94IR::PageMetadata extractPageMetadata(const PageNode *pn, const HrefResolver *hrefResolver)
95{
96 Q_ASSERT_X(pn, "NodeExtractor::extractPageMetadata",
97 "PageNode pointer must be non-null");
98 IR::PageMetadata pm;
99
100 pm.nodeType = pn->nodeType();
101 pm.genus = pn->genus();
102 pm.status = pn->status();
103 pm.access = pn->access();
104
105 if (pn->isQmlType()) {
106 const auto *qcn = static_cast<const QmlTypeNode *>(pn);
107 QString suffix = qcn->isQmlBasicType() ? " QML Value Type"_L1 : " QML Type"_L1;
108 pm.title = pn->name() + suffix;
109 pm.fullTitle = pm.title;
110 } else if (pn->isClassNode() || pn->isNamespace() || pn->isHeader()) {
111 // plainFullName() produces qualified names for nested aggregates
112 // (e.g. "Outer::Inner"), matching the legacy generator behavior.
113 // For top-level types and headers the result is the same as name().
114 const auto *aggregate = static_cast<const Aggregate *>(pn);
115 pm.fullTitle = aggregate->plainFullName();
116 pm.title = pm.fullTitle;
117 } else {
118 pm.title = pn->title();
119 pm.fullTitle = pn->fullTitle();
120 }
121
122 pm.url = hrefResolver->fileName(pn);
123 const QString baseUrl = Config::instance().get(CONFIG_URL).asString();
124 if (!baseUrl.isEmpty() && !pm.url.isEmpty() && !pm.url.contains("://"_L1)) {
125 pm.url = baseUrl
126 + (baseUrl.endsWith(u'/') ? QString{} : QStringLiteral("/"))
127 + pm.url;
128 }
129 pm.since = pn->since();
130 pm.deprecatedSince = pn->deprecatedSince();
131 pm.brief = pn->doc().briefText().toString();
132
133 const Text &bodyText = pn->doc().body();
134 if (const Atom *firstAtom = bodyText.firstAtom()) {
135 // Offset section heading levels to account for page structure.
136 // QDoc's \section1 maps to level 1, but pages already use <h1>
137 // for the title. The legacy generators apply a node-type-dependent
138 // offset; we replicate the same mapping here.
139 const int headingOffset = [&] {
140 switch (pn->nodeType()) {
142 case NodeType::Class:
143 case NodeType::Struct:
144 case NodeType::Union:
145 case NodeType::Module:
146 return 2;
150 case NodeType::Page:
151 case NodeType::Group:
152 return 1;
153 default:
154 return 3;
155 }
156 }();
157 IR::ContentBuilder contentBuilder(IR::BriefHandling::Skip, headingOffset,
158 diagnosticHandlerFor(pn));
159 pm.body = contentBuilder.build(firstAtom);
160 }
161
162 if (pn->isAggregate()) {
163 const auto *aggregate = static_cast<const Aggregate *>(pn);
164 pm.summarySections = extractSummarySections(aggregate, hrefResolver);
165 pm.detailSections = extractDetailSections(aggregate, hrefResolver);
166 }
167
168 if (pn->isQmlType()) {
169 const auto *qcn = static_cast<const QmlTypeNode *>(pn);
170 pm.qmlTypeData = extractQmlTypeData(qcn, hrefResolver);
171 }
172
173 if (pn->isCollectionNode()) {
174 const auto *cn = static_cast<const CollectionNode *>(pn);
175 pm.collectionData = extractCollectionData(cn, hrefResolver);
176 }
177
178 if (pn->isClassNode() || pn->isNamespace() || pn->isHeader()) {
179 const auto *aggregate = static_cast<const Aggregate *>(pn);
180 pm.cppReferenceData = extractCppReferenceData(aggregate, hrefResolver);
181 }
182
183 pm.navigationData = extractNavigationData(pn, hrefResolver);
184
185 return pm;
186}
187
188/*!
189 \internal
190 Extract QML type metadata from a QmlTypeNode.
191
192 Populates import statement, inheritance chain, inherited-by list,
193 native C++ type link, and singleton/value-type flags. InclusionFilter
194 is applied to match the legacy generator's visibility filtering.
195*/
196IR::QmlTypeData extractQmlTypeData(const QmlTypeNode *qcn, const HrefResolver *hrefResolver)
197{
198 IR::QmlTypeData data;
199 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
200
201 if (!qcn->logicalModuleName().isEmpty()) {
202 bool includeImport = true;
203 const CollectionNode *collection = qcn->logicalModule();
204 if (collection) {
205 const NodeContext context = collection->createContext();
206 includeImport = InclusionFilter::isIncluded(policy, context);
207 }
208 if (includeImport) {
209 QStringList parts = QStringList()
210 << "import"_L1 << qcn->logicalModuleName() << qcn->logicalModuleVersion();
211 data.importStatement = parts.join(' '_L1).trimmed();
212 }
213 }
214
217
218 QmlTypeNode *base = qcn->qmlBaseNode();
219 while (base) {
220 const NodeContext context = base->createContext();
221 if (InclusionFilter::isIncluded(policy, context))
222 break;
223 base = base->qmlBaseNode();
224 }
225
226 NodeList subs;
227 QmlTypeNode::subclasses(qcn, subs, true);
228
229 if (base) {
230 IR::QmlTypeData::InheritsInfo inheritsInfo;
231 inheritsInfo.name = base->name();
232 inheritsInfo.href = resolveHref(hrefResolver, base, qcn);
233 const CollectionNode *baseModule = base->logicalModule();
234 if (baseModule) {
235 const NodeContext moduleContext = baseModule->createContext();
236 if (InclusionFilter::isIncluded(policy, moduleContext))
237 inheritsInfo.moduleName = base->logicalModuleName();
238 }
239 data.inherits = inheritsInfo;
240 }
241
242 if (!subs.isEmpty()) {
243 QList<IR::QmlTypeData::InheritedByEntry> filteredSubs;
244 for (const auto *sub : std::as_const(subs)) {
245 const NodeContext context = sub->createContext();
246 if (InclusionFilter::isIncluded(policy, context))
247 filteredSubs.append({sub->name(), resolveHref(hrefResolver, sub, qcn)});
248 }
249 std::sort(filteredSubs.begin(), filteredSubs.end(),
250 [](const IR::QmlTypeData::InheritedByEntry &a,
251 const IR::QmlTypeData::InheritedByEntry &b) {
252 return a.name < b.name;
253 });
254 data.inheritedBy = filteredSubs;
255 }
256
257 ClassNode *cn = qcn->classNode();
258 if (cn && cn->isQmlNativeType()) {
259 const NodeContext context = cn->createContext();
260 if (InclusionFilter::isIncluded(policy, context))
261 data.nativeType = IR::QmlTypeData::NativeTypeInfo{cn->name(), resolveHref(hrefResolver, cn, qcn)};
262 }
263
264 return data;
265}
266
267/*!
268 \internal
269 Extract collection metadata from a CollectionNode.
270
271 Populates module identity, CMake/qmake build variables, technology
272 preview state, and pre-sorted member listings. For C++ modules,
273 members are categorized into separate namespace and class lists.
274 For groups and QML modules, a single flat member list is produced.
275
276 All member lists are filtered through InclusionFilter (excluding
277 internal entries) and exclude deprecated nodes, then sorted
278 alphabetically by name (case-insensitive).
279*/
280IR::CollectionData extractCollectionData(const CollectionNode *cn, const HrefResolver *hrefResolver)
281{
282 IR::CollectionData data;
283
284 data.logicalModuleName = cn->logicalModuleName();
285 data.logicalModuleVersion = cn->logicalModuleVersion();
286 data.qtVariable = cn->qtVariable();
287
288 // CMake metadata synthesis. Documenters can declare CMake info
289 // through several command shapes (\cmakepackage, \cmakecomponent,
290 // \cmaketargetitem and the Qt-specific \qtcmakepackage and
291 // \qtcmaketargetitem) that populate different combinations of
292 // the package, component and target fields on the CollectionNode.
293 // Synthesize the package as Qt<major-version> when only a
294 // component is declared, so module pages render coherent
295 // find_package snippets for both the generic and Qt-flavored
296 // command families. The target_link_libraries line is
297 // synthesized only when a component is also available — a bare
298 // package name does not imply a target named package::package,
299 // so packages without a component leave the target empty and
300 // the template suppresses the target_link_libraries clause.
301 // When no field is set, leave the data fields empty so the
302 // template suppresses the CMake row entirely.
303 const QString rawCmakePackage = cn->cmakePackage();
304 const QString rawCmakeComponent = cn->cmakeComponent();
305 const QString rawCmakeTargetItem = cn->cmakeTargetItem();
306 if (!rawCmakePackage.isEmpty() || !rawCmakeComponent.isEmpty()) {
307 const QString package = rawCmakePackage.isEmpty()
308 ? "Qt"_L1 + QString::number(QT_VERSION_MAJOR)
309 : rawCmakePackage;
310 data.cmakePackage = package;
311 data.cmakeComponent = rawCmakeComponent;
312 if (!rawCmakeTargetItem.isEmpty())
313 data.cmakeTargetItem = rawCmakeTargetItem;
314 else if (!rawCmakeComponent.isEmpty())
315 data.cmakeTargetItem = package + "::"_L1 + rawCmakeComponent;
316 }
317
318 data.state = cn->state();
319
320 data.isModule = cn->isModule();
321 data.isQmlModule = cn->isQmlModule();
322 data.isGroup = cn->isGroup();
323 data.noAutoList = cn->noAutoList();
324
325 if (cn->noAutoList())
326 return data;
327
328 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
329
330 auto makeMemberEntry = [hrefResolver, cn](const Node *node) -> IR::CollectionData::MemberEntry {
331 return { node->name(), resolveHref(hrefResolver, node, cn), node->doc().briefText().toString() };
332 };
333
334 auto sortEntries = [](QList<IR::CollectionData::MemberEntry> &entries) {
335 std::sort(entries.begin(), entries.end(),
336 [](const IR::CollectionData::MemberEntry &a,
337 const IR::CollectionData::MemberEntry &b) {
338 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
339 });
340 };
341
342 if (cn->isModule()) {
344 for (auto *node : nsMap.values()) {
345 const NodeContext context = node->createContext();
346 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
347 data.namespaces.append(makeMemberEntry(node));
348 }
349 sortEntries(data.namespaces);
350
351 const NodeMap classMap = cn->getMembers([](const Node *n) { return n->isClassNode(); });
352 for (auto *node : classMap.values()) {
353 const NodeContext context = node->createContext();
354 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
355 data.classes.append(makeMemberEntry(node));
356 }
357 sortEntries(data.classes);
358 } else {
359 for (const auto *node : cn->members()) {
360 if (!node->isInAPI())
361 continue;
362 const NodeContext context = node->createContext();
363 if (InclusionFilter::isIncluded(policy, context) && !node->isDeprecated())
364 data.members.append(makeMemberEntry(node));
365 }
366 sortEntries(data.members);
367 }
368
369 return data;
370}
371
373
374/*!
375 \internal
376 Extract C++ reference page metadata from a class, namespace, or header.
377
378 Reads requisite table fields (header include, build-system snippets,
379 status), inheritance hierarchies, template declarations, comparison
380 operators, thread-safeness, and group associations. The result is a
381 value-type struct that captures everything the template generator needs
382 to render the requisites table and secondary sections without touching
383 the Node tree at render time.
384
385 All three aggregate page types (ClassNode, NamespaceNode, HeaderNode)
386 are handled, with ClassNode-specific sections gated on isClassNode().
387*/
388IR::CppReferenceData extractCppReferenceData(const Aggregate *aggregate, const HrefResolver *hrefResolver)
389{
390 IR::CppReferenceData data;
392
393 data.isNamespace = aggregate->isNamespace();
394 data.isHeader = aggregate->isHeader();
395 data.isInnerClass = aggregate->parent() && aggregate->parent()->isClassNode();
396 data.typeWord = aggregate->typeWord(false);
398
399 auto ancestors = aggregate->plainFullName().split("::"_L1);
400 ancestors.pop_back();
401 data.ancestorNames = ancestors;
402
403 if (aggregate->includeFile())
404 data.headerInclude = *aggregate->includeFile();
405
406 if (!aggregate->physicalModuleName().isEmpty()) {
407 const CollectionNode *cn =
408 qdb->getCollectionNode(aggregate->physicalModuleName(), NodeType::Module);
409 if (cn && (!cn->cmakeComponent().isEmpty() || !cn->cmakePackage().isEmpty())) {
410 const QString package = cn->cmakePackage().isEmpty()
411 ? "Qt"_L1 + QString::number(QT_VERSION_MAJOR)
412 : cn->cmakePackage();
413 QString findPkg;
414 if (cn->cmakeComponent().isEmpty())
415 findPkg = "find_package("_L1 + package + " REQUIRED)"_L1;
416 else
417 findPkg = "find_package("_L1 + package + " REQUIRED COMPONENTS "_L1
418 + cn->cmakeComponent() + ")"_L1;
419
420 QString target;
421 if (!cn->cmakeTargetItem().isEmpty()) {
422 target = cn->cmakeTargetItem();
423 } else if (cn->cmakeComponent().isEmpty()) {
424 target = package + "::"_L1 + package;
425 } else {
426 target = package + "::"_L1 + cn->cmakeComponent();
427 }
428
429 data.cmakeFindPackage = findPkg;
430 data.cmakeTargetLinkLibraries =
431 "target_link_libraries(mytarget PRIVATE "_L1 + target + ")"_L1;
432 }
433 if (cn && !cn->qtVariable().isEmpty())
434 data.qmakeVariable = "QT += "_L1 + cn->qtVariable();
435 }
436
437 auto statusOpt = formatStatus(aggregate, qdb);
438 if (statusOpt) {
439 data.statusText = *statusOpt;
440 if (aggregate->status() == Status::Deprecated)
441 data.statusCssClass = "deprecated"_L1;
442 else if (!aggregate->deprecatedSince().isEmpty())
443 data.statusCssClass = "pending-deprecation"_L1;
444 else if (aggregate->status() == Status::Preliminary)
445 data.statusCssClass = "preliminary"_L1;
446 else
447 data.statusCssClass = "status"_L1;
448 }
449
450 if (aggregate->isClassNode()) {
451 auto *classNode = const_cast<ClassNode *>(static_cast<const ClassNode *>(aggregate));
452
453 if (classNode->isQmlNativeType()) {
454 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
455 const NodeContext context = classNode->createContext();
456 if (InclusionFilter::isIncluded(policy, context)) {
457 QList<QmlTypeNode *> nativeTypes{classNode->qmlNativeTypes().cbegin(),
458 classNode->qmlNativeTypes().cend()};
459 if (!nativeTypes.isEmpty()) {
460 std::sort(nativeTypes.begin(), nativeTypes.end(), Node::nodeNameLessThan);
461 data.qmlNativeType = IR::CppReferenceData::QmlNativeTypeLink{
462 nativeTypes.first()->name(),
463 resolveHref(hrefResolver, nativeTypes.first(), aggregate)
464 };
465 }
466 }
467 }
468
469 const auto *metaTags = classNode->doc().metaTagMap();
470 if (metaTags && metaTags->contains(u"qdoc-suppress-inheritance"_s))
471 data.suppressInheritance = true;
472
473 if (!data.suppressInheritance) {
474 const auto &baseClasses = classNode->baseClasses();
475 for (const auto &bc : baseClasses) {
476 if (bc.m_node) {
477 data.baseClasses.append({
478 bc.m_node->plainFullName(),
479 resolveHref(hrefResolver, bc.m_node, aggregate),
480 bc.m_access
481 });
482 }
483 }
484
485 const auto &derivedClasses = classNode->derivedClasses();
486 for (const auto &dc : derivedClasses) {
487 if (dc.m_node) {
488 data.derivedClasses.append({
489 dc.m_node->plainFullName(),
490 resolveHref(hrefResolver, dc.m_node, aggregate)
491 });
492 }
493 }
494 std::sort(data.derivedClasses.begin(), data.derivedClasses.end(),
497 return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
498 });
499 }
500 }
501
502 if (aggregate->templateDecl()) {
503 data.templateDeclSpans = buildTemplateDeclSpans(&*aggregate->templateDecl());
504 }
505
506 const auto selfCategory = aggregate->comparisonCategory();
507 if (selfCategory != ComparisonCategory::None)
508 data.selfComparisonCategory = QString::fromStdString(comparisonCategoryAsString(selfCategory));
509
510 const auto *comparesMap = aggregate->doc().comparesWithMap();
511 if (comparesMap && !comparesMap->isEmpty()) {
512 for (auto [key, description] : comparesMap->asKeyValueRange()) {
513 IR::CppReferenceData::ComparisonEntry entry;
514 entry.category = QString::fromStdString(comparisonCategoryAsString(key));
515
516 const QStringList types{description.firstAtom()->string().split(';'_L1)};
517 entry.comparableTypes = types;
518
519 if (description.firstAtom()->next() != description.lastAtom()) {
520 Text descText = Text::subText(description.firstAtom()->next(),
521 description.lastAtom());
522 entry.description = descText.toString();
523 }
524 data.comparisonEntries.append(entry);
525 }
526 }
527
528 Node::ThreadSafeness ts = aggregate->threadSafeness();
529 if (ts != Node::UnspecifiedSafeness) {
531 switch (ts) {
532 case Node::NonReentrant:
533 tsInfo.level = "non-reentrant"_L1;
534 break;
535 case Node::Reentrant:
536 tsInfo.level = "reentrant"_L1;
537 break;
538 case Node::ThreadSafe:
539 tsInfo.level = "thread-safe"_L1;
540 break;
541 default:
542 break;
543 }
544
545 NodeList reentrant, threadsafe, nonreentrant;
546 bool hasExceptions = false;
547 for (const auto *child : aggregate->childNodes()) {
548 if (!child->isDeprecated()) {
549 switch (child->threadSafeness()) {
550 case Node::Reentrant:
551 reentrant.append(const_cast<Node *>(child));
552 if (ts == Node::ThreadSafe) hasExceptions = true;
553 break;
554 case Node::ThreadSafe:
555 threadsafe.append(const_cast<Node *>(child));
556 if (ts == Node::Reentrant) hasExceptions = true;
557 break;
558 case Node::NonReentrant:
559 nonreentrant.append(const_cast<Node *>(child));
560 hasExceptions = true;
561 break;
562 default:
563 break;
564 }
565 }
566 }
567 if (hasExceptions) {
568 for (const auto *node : std::as_const(reentrant)) {
569 tsInfo.reentrantExceptions.append({
570 node->plainFullName(),
571 resolveHref(hrefResolver, node, aggregate)
572 });
573 }
574 for (const auto *node : std::as_const(threadsafe)) {
575 tsInfo.threadSafeExceptions.append({
576 node->plainFullName(),
577 resolveHref(hrefResolver, node, aggregate)
578 });
579 }
580 for (const auto *node : std::as_const(nonreentrant)) {
581 tsInfo.nonReentrantExceptions.append({
582 node->plainFullName(),
583 resolveHref(hrefResolver, node, aggregate)
584 });
585 }
586 }
587 data.threadSafety = std::move(tsInfo);
588 }
589
590 const QStringList &groupNames = aggregate->groupNames();
591 if (!groupNames.isEmpty()) {
592 const auto &groupMap = qdb->groups();
593 for (const auto &groupName : groupNames) {
594 auto it = groupMap.find(groupName);
595 if (it == groupMap.end() || !*it)
596 continue;
597 CollectionNode *group = *it;
598 // TODO: mergeCollections() mutates the node tree during
599 // extraction, violating the principle that the new pipeline
600 // reads without side effects. Replace with an eager merge
601 // pass that runs before generation begins.
602 qdb->mergeCollections(group);
603 if (group->wasSeen()) {
604 data.groups.append({
605 group->fullTitle(),
606 resolveHref(hrefResolver, group, aggregate)
607 });
608 }
609 }
610 }
611
612 if (aggregate->isNamespace()) {
613 const auto *ns = static_cast<const NamespaceNode *>(aggregate);
614 if (!ns->hasDoc() && ns->docNode()) {
615 data.isPartialNamespace = true;
616 data.fullNamespaceHref = resolveHref(hrefResolver, ns->docNode(), aggregate);
617 data.fullNamespaceModuleName = ns->docNode()->tree()->camelCaseModuleName();
618 }
619 }
620
621 return data;
622}
623
624/*!
625 \internal
626 Build categorized summary sections for an aggregate node.
627
628 Delegates to the Sections class for member distribution, then extracts
629 results into frozen SectionIR values. The section variant (C++ class,
630 QML type, or generic) is chosen based on the aggregate's node type.
631*/
632QList<IR::SectionIR> extractSummarySections(const Aggregate *aggregate, const HrefResolver *hrefResolver)
633{
634 Sections sections(aggregate);
635
636 const auto &sv = sections.summarySections();
637
638 QList<IR::SectionIR> result;
639 for (const auto &section : sv) {
640 if (section.isEmpty())
641 continue;
642
643 IR::SectionIR irSection;
644 irSection.title = section.title();
645 irSection.id = TextUtils::asAsciiPrintable(section.title());
646 irSection.singular = section.singular();
647 irSection.plural = section.plural();
648
649 // SharedCommentNode groups several declarations under one doc comment.
650 // Expand them into individual MemberIR entries so each function appears
651 // in the member table. This means the IR member count may exceed the
652 // Section::members() count.
653 for (const auto *member : section.members()) {
654 if (member->isSharedCommentNode()) {
655 const auto *scn = static_cast<const SharedCommentNode *>(member);
656 for (const auto *child : scn->collective()) {
657 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate);
658 irMember.href = "#"_L1 + hrefResolver->anchorForNode(child);
659 irSection.members.append(irMember);
660 }
661 } else {
662 IR::MemberIR irMember = extractMemberIR(member, hrefResolver, aggregate);
663 irMember.href = "#"_L1 + hrefResolver->anchorForNode(member);
664 irSection.members.append(irMember);
665 }
666 }
667
668 for (const auto *reimpl : section.reimplementedMembers())
669 irSection.reimplementedMembers.append(extractMemberIR(reimpl, hrefResolver, aggregate));
670
671 for (const auto &[base, count] : section.inheritedMembers()) {
672 IR::InheritedMembersIR inherited;
673 inherited.className = base->plainFullName();
674 inherited.count = count;
675 inherited.href = resolveHref(hrefResolver, base, aggregate);
676 irSection.inheritedMembers.append(inherited);
677 }
678
679 result.append(irSection);
680 }
681 return result;
682}
683
684/*!
685 \internal
686 Build categorized detail sections for an aggregate node.
687
688 Iterates Sections::detailsSections() and extracts full member
689 documentation including body content, anchor IDs, and metadata.
690 SharedCommentNode groups share a single documentation body across
691 their children, with each child getting its own anchorId and synopsis.
692*/
693QList<IR::SectionIR> extractDetailSections(const Aggregate *aggregate, const HrefResolver *hrefResolver)
694{
695 Sections sections(aggregate);
696 const auto &sv = sections.detailsSections();
697
698 QList<IR::SectionIR> result;
699 for (const auto &section : sv) {
700 if (section.isEmpty())
701 continue;
702
703 IR::SectionIR irSection;
704 irSection.title = section.title();
705 irSection.id = TextUtils::asAsciiPrintable(section.title());
706 irSection.singular = section.singular();
707 irSection.plural = section.plural();
708
709 for (const auto *member : section.members()) {
710 if (member->isSharedCommentNode()) {
711 const auto *scn = static_cast<const SharedCommentNode *>(member);
712
713 QList<IR::ContentBlock> sharedBody;
714 const Text &bodyText = scn->doc().body();
715 if (const Atom *firstAtom = bodyText.firstAtom()) {
716 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
717 diagnosticHandlerFor(scn));
718 sharedBody = contentBuilder.build(firstAtom);
719 }
720
721 QList<IR::ContentBlock> sharedAlso;
722 const QList<Text> &alsoTexts = scn->doc().alsoList();
723 for (const Text &alsoText : alsoTexts) {
724 if (const Atom *firstAtom = alsoText.firstAtom()) {
725 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
726 diagnosticHandlerFor(scn));
727 sharedAlso.append(contentBuilder.build(firstAtom));
728 }
729 }
730
731 for (const auto *child : scn->collective()) {
732 IR::MemberIR irMember = extractMemberIR(child, hrefResolver, aggregate, MemberExtractionLevel::Detail);
733 irMember.body = sharedBody;
734 irMember.alsoList = sharedAlso;
735 irSection.members.append(irMember);
736 }
737 } else {
738 irSection.members.append(extractMemberIR(member, hrefResolver, aggregate, MemberExtractionLevel::Detail));
739 }
740 }
741
742 result.append(irSection);
743 }
744 return result;
745}
746
748{
749 switch (ts) {
750 case Node::Reentrant:
751 return "reentrant"_L1;
752 case Node::ThreadSafe:
753 return "thread-safe"_L1;
754 default:
755 return {};
756 }
757}
758
759/*!
760 \internal
761 Build a MemberIR from a single Node.
762
763 Extracts identity, classification, and type-specific data from the node.
764 FunctionNode provides signatures, parameters, and overload metadata.
765 EnumNode provides scoped/unscoped signature and enum value listings.
766 PropertyNode provides a qualified data type signature.
767
768 When \a level is MemberExtractionLevel::Detail, also populates
769 detail documentation fields: anchorId, synopsis, since,
770 threadSafety, comparisonCategory, noexcept metadata, body (via
771 ContentBuilder), and alsoList.
772*/
773IR::MemberIR extractMemberIR(const Node *node, const HrefResolver *hrefResolver, const Node *relative, MemberExtractionLevel level)
774{
775 const bool includeDetail = (level == MemberExtractionLevel::Detail);
776 IR::MemberIR member;
777
778 member.name = node->name();
779 member.fullName = node->plainFullName();
780 member.href = resolveHref(hrefResolver, node, relative);
781 member.brief = node->doc().briefText().toString();
782
783 member.nodeType = node->nodeType();
784 member.access = node->access();
785 member.status = node->status();
786
787 if (node->isFunction()) {
788 const auto *fn = static_cast<const FunctionNode *>(node);
789 member.signature = fn->signature(
791 member.isStatic = fn->isStatic();
792 member.isConst = fn->isConst();
793 member.isVirtual = !fn->isNonvirtual();
794 member.isSignal = fn->isSignal();
795 member.isSlot = fn->isSlot();
796 member.overloadNumber = fn->overloadNumber();
797 member.isPrimaryOverload = fn->isPrimaryOverload();
798
799 const Parameters &params = fn->parameters();
800 for (int i = 0; i < params.count(); ++i) {
801 IR::ParameterIR param;
802 param.type = params.at(i).type();
803 param.name = params.at(i).name();
804 param.defaultValue = params.at(i).defaultValue();
805 member.parameters.append(param);
806 }
807 } else if (node->isEnumType()) {
808 const auto *en = static_cast<const EnumNode *>(node);
809 member.signature = en->isScoped()
810 ? QStringLiteral("enum class %1").arg(en->name())
811 : QStringLiteral("enum %1").arg(en->name());
812
813 for (const auto &item : en->items()) {
814 IR::EnumValueIR ev;
815 ev.name = item.name();
816 ev.value = item.value();
817 ev.since = item.since();
818 member.enumValues.append(ev);
819 }
820 } else if (node->isQmlProperty()) {
821 const auto *qpn = static_cast<const QmlPropertyNode *>(node);
822 member.signature = qpn->name() + " : "_L1 + qpn->dataType();
823 member.dataType = qpn->dataType();
824 member.isAttached = qpn->isAttached();
825 member.isDefault = qpn->isDefault();
826 member.isReadOnly = qpn->isReadOnly();
827 member.isRequired = qpn->isRequired();
828 } else if (node->isProperty()) {
829 const auto *pn = static_cast<const PropertyNode *>(node);
830 member.signature = pn->name() + " : "_L1 + pn->qualifiedDataType();
831 } else if (node->isTypedef()) {
832 const auto *td = static_cast<const TypedefNode *>(node);
833 member.signature = td->associatedEnum()
834 ? "flags "_L1 + td->name()
835 : td->name();
836 } else if (node->nodeType() == NodeType::Variable) {
837 const auto *vn = static_cast<const VariableNode *>(node);
838 member.signature = vn->leftType() + vn->name() + vn->rightType();
839 } else {
840 member.signature = node->name();
841 }
842
843 if (includeDetail) {
844 member.anchorId = hrefResolver->anchorForNode(node);
845 member.synopsis = member.signature;
846 member.since = node->since();
847 member.threadSafety = threadSafenessString(node->threadSafeness());
848
850 if (!catStr.empty())
851 member.comparisonCategory = QString::fromStdString(catStr);
852
853 if (node->isFunction()) {
854 const auto *fn = static_cast<const FunctionNode *>(node);
855 const auto &noexcept_ = fn->getNoexcept();
856 if (noexcept_) {
857 member.isNoexcept = true;
858 member.noexceptNote = *noexcept_;
859 }
860 }
861
862 const Text &bodyText = node->doc().body();
863 if (const Atom *firstAtom = bodyText.firstAtom()) {
864 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
865 diagnosticHandlerFor(node));
866 member.body = contentBuilder.build(firstAtom);
867 }
868
869 const QList<Text> &alsoTexts = node->doc().alsoList();
870 for (const Text &alsoText : alsoTexts) {
871 if (const Atom *firstAtom = alsoText.firstAtom()) {
872 IR::ContentBuilder contentBuilder(IR::BriefHandling::Include, 0,
873 diagnosticHandlerFor(node));
874 QList<IR::ContentBlock> blocks = contentBuilder.build(firstAtom);
875 member.alsoList.append(blocks);
876 }
877 }
878 }
879
880 Section::Style spanStyle = includeDetail ? Section::Details : Section::Summary;
881 member.signatureSpans = buildSignatureSpans(node, hrefResolver, relative, spanStyle);
882
883 return member;
884}
885
886static QList<IR::SignatureSpan> buildTypeSpans(const QString &typeString)
887{
888 QList<IR::SignatureSpan> spans;
889 QString pendingWord;
890
891 for (int i = 0; i <= typeString.size(); ++i) {
892 QChar ch;
893 if (i != typeString.size())
894 ch = typeString.at(i);
895
896 QChar lower = ch.toLower();
897 if ((lower >= 'a'_L1 && lower <= 'z'_L1) || ch.digitValue() >= 0
898 || ch == '_'_L1 || ch == ':'_L1) {
899 pendingWord += ch;
900 } else {
901 if (!pendingWord.isEmpty()) {
902 bool isProbablyType = (pendingWord != "const"_L1);
903 IR::SignatureSpan span;
904 span.role = isProbablyType ? IR::SpanRole::Type : IR::SpanRole::Text;
905 span.text = pendingWord;
906 spans.append(span);
907 }
908 pendingWord.clear();
909
910 if (!ch.isNull()) {
911 IR::SignatureSpan span;
913 span.text = QString(ch);
914 spans.append(span);
915 }
916 }
917 }
918 return spans;
919}
920
922{
923 QString extraStr = CodeMarker::extraSynopsis(node, style);
924 if (extraStr.isEmpty())
925 return {};
926
927 // extraSynopsis may contain <@extref target="...">text</@extref> tags for
928 // cppreference links. Parse those into ExternalRef spans; everything else
929 // becomes Extra spans.
930 static const QRegularExpression extrefRegex(
931 u"<@extref target=\"([^\"]+)\">([^<]+)</@extref>"_s);
932
933 QList<IR::SignatureSpan> spans;
934 IR::SignatureSpan wrapper;
935 wrapper.role = IR::SpanRole::Extra;
936
937 qsizetype pos = 0;
938 auto it = extrefRegex.globalMatch(extraStr);
939 while (it.hasNext()) {
940 auto match = it.next();
941 if (match.capturedStart() > pos) {
942 IR::SignatureSpan textSpan;
943 textSpan.role = IR::SpanRole::Text;
944 textSpan.text = extraStr.mid(pos, match.capturedStart() - pos);
945 wrapper.children.append(textSpan);
946 }
947 IR::SignatureSpan ref;
949 ref.text = match.captured(2);
950 ref.href = "https://en.cppreference.com/w/cpp/language/"_L1 + match.captured(1);
951 wrapper.children.append(ref);
952 pos = match.capturedEnd();
953 }
954 if (pos < extraStr.size()) {
955 IR::SignatureSpan textSpan;
956 textSpan.role = IR::SpanRole::Text;
957 textSpan.text = extraStr.mid(pos);
958 wrapper.children.append(textSpan);
959 }
960
961 if (wrapper.children.isEmpty()) {
962 wrapper.text = extraStr;
963 }
964 spans.append(wrapper);
965 return spans;
966}
967
969{
970 if (!templateDecl)
971 return {};
972
973 IR::SignatureSpan declSpan;
975 declSpan.text = "template"_L1;
976
977 IR::SignatureSpan open;
979 open.text = "<"_L1;
980 declSpan.children.append(open);
981
982 bool first = true;
983 for (const auto &param : templateDecl->parameters) {
984 if (param.sfinae_constraint)
985 continue;
986 if (!first) {
987 IR::SignatureSpan comma;
988 comma.role = IR::SpanRole::Text;
989 comma.text = ", "_L1;
990 declSpan.children.append(comma);
991 }
992
993 switch (param.kind) {
994 case RelaxedTemplateParameter::Kind::TypeTemplateParameter:
995 case RelaxedTemplateParameter::Kind::TemplateTemplateParameter: {
996 IR::SignatureSpan kw;
997 kw.role = IR::SpanRole::Text;
998 kw.text = "typename"_L1;
999 declSpan.children.append(kw);
1000 break;
1001 }
1002 case RelaxedTemplateParameter::Kind::NonTypeTemplateParameter: {
1003 if (!param.valued_declaration.type.empty()) {
1004 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.type));
1005 declSpan.children.append(typeSpans);
1006 }
1007 break;
1008 }
1009 }
1010
1011 if (param.is_parameter_pack) {
1012 IR::SignatureSpan dots;
1013 dots.role = IR::SpanRole::Text;
1014 dots.text = "..."_L1;
1015 declSpan.children.append(dots);
1016 }
1017
1018 if (!param.valued_declaration.name.empty()) {
1019 IR::SignatureSpan space;
1020 space.role = IR::SpanRole::Text;
1021 space.text = " "_L1;
1022 declSpan.children.append(space);
1023
1024 IR::SignatureSpan nameSpan;
1025 nameSpan.role = IR::SpanRole::Parameter;
1026 nameSpan.text = QString::fromStdString(param.valued_declaration.name);
1027 declSpan.children.append(nameSpan);
1028 }
1029
1030 if (!param.valued_declaration.initializer.empty()) {
1031 IR::SignatureSpan eq;
1032 eq.role = IR::SpanRole::Text;
1033 eq.text = " = "_L1;
1034 declSpan.children.append(eq);
1035
1036 if (param.kind == RelaxedTemplateParameter::Kind::TypeTemplateParameter
1037 || param.kind == RelaxedTemplateParameter::Kind::TemplateTemplateParameter) {
1038 auto typeSpans = buildTypeSpans(QString::fromStdString(param.valued_declaration.initializer));
1039 declSpan.children.append(typeSpans);
1040 } else {
1041 IR::SignatureSpan val;
1042 val.role = IR::SpanRole::Text;
1043 val.text = QString::fromStdString(param.valued_declaration.initializer);
1044 declSpan.children.append(val);
1045 }
1046 }
1047
1048 first = false;
1049 }
1050
1051 IR::SignatureSpan close;
1053 close.text = ">"_L1;
1054 declSpan.children.append(close);
1055
1056 if (templateDecl->requires_clause && !templateDecl->requires_clause->empty()) {
1057 IR::SignatureSpan req;
1059 req.text = " requires "_L1 + QString::fromStdString(*templateDecl->requires_clause);
1060 declSpan.children.append(req);
1061 }
1062
1063 return { declSpan };
1064}
1065
1067 const HrefResolver *hrefResolver,
1068 const Node *relative,
1069 Section::Style style)
1070{
1071 Q_UNUSED(hrefResolver);
1072 QList<IR::SignatureSpan> spans;
1073
1074 auto appendText = [&spans](const QString &text) {
1075 IR::SignatureSpan span;
1077 span.text = text;
1078 spans.append(span);
1079 };
1080
1081 auto appendName = [&spans, node, hrefResolver, relative](const QString &name) {
1082 IR::SignatureSpan span;
1084 span.text = name;
1085 span.href = resolveHref(hrefResolver, node, relative);
1086 spans.append(span);
1087 };
1088
1089 auto appendTypeSpans = [&spans](const QString &type, bool trailingSpace) {
1090 auto typeSpans = buildTypeSpans(type);
1091 spans.append(typeSpans);
1092 if (trailingSpace && !type.isEmpty()
1093 && !type.endsWith('*'_L1) && !type.endsWith('&'_L1)) {
1094 IR::SignatureSpan space;
1096 space.text = " "_L1;
1097 spans.append(space);
1098 }
1099 };
1100
1101 // Extra qualifiers go first (prepended in CppCodeMarker)
1102 if (style != Section::AllMembers) {
1103 auto extras = buildExtraSpans(node, style);
1104 if (!extras.isEmpty()) {
1105 spans.append(extras);
1106 appendText(" "_L1);
1107 }
1108 }
1109
1110 // Name with parent prefix for Details style
1111 QString nameText = node->name();
1112 bool linkName = (style != Section::Details);
1113
1114 if (style == Section::Details) {
1116 && !node->parent()->name().isEmpty()
1117 && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
1118 nameText = node->parent()->name() + "::"_L1 + nameText;
1119 }
1120 }
1121
1122 switch (node->nodeType()) {
1124 case NodeType::Class:
1125 case NodeType::Struct:
1126 case NodeType::Union:
1127 appendText(Node::nodeTypeString(node->nodeType()) + " "_L1);
1128 if (linkName) {
1129 appendName(nameText);
1130 } else {
1131 IR::SignatureSpan span;
1133 span.text = nameText;
1134 spans.append(span);
1135 }
1136 break;
1137 case NodeType::Function: {
1138 const auto *func = static_cast<const FunctionNode *>(node);
1139
1140 if (style == Section::Details) {
1141 if (auto templateDecl = node->templateDecl()) {
1142 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl);
1143 spans.append(tmplSpans);
1144 appendText(" "_L1);
1145 }
1146 }
1147
1148 if (style == Section::Summary || style == Section::Accessors) {
1149 if (!func->isNonvirtual())
1150 appendText("virtual "_L1);
1151 }
1152
1153 if (style != Section::AllMembers && !func->returnType().isEmpty())
1154 appendTypeSpans(func->returnTypeString(), true);
1155
1156 if (linkName) {
1157 appendName(nameText);
1158 } else {
1159 IR::SignatureSpan span;
1161 span.text = nameText;
1162 spans.append(span);
1163 }
1164
1165 if (!func->isMacroWithoutParams()) {
1166 appendText("("_L1);
1167 if (!func->parameters().isEmpty()) {
1168 const Parameters &parameters = func->parameters();
1169 for (int i = 0; i < parameters.count(); ++i) {
1170 if (i > 0)
1171 appendText(", "_L1);
1172 const Parameter &param = parameters.at(i);
1173 QString pName = param.name();
1174 QString type = param.type();
1175 QString value = param.defaultValue();
1176 qsizetype insertPos = param.nameInsertionPoint();
1177 if (insertPos >= 0 && style != Section::AllMembers && !pName.isEmpty()) {
1178 appendTypeSpans(type.left(insertPos), false);
1179 IR::SignatureSpan paramSpan;
1180 paramSpan.role = IR::SpanRole::Parameter;
1181 paramSpan.text = pName;
1182 spans.append(paramSpan);
1183 appendTypeSpans(type.mid(insertPos), false);
1184 } else {
1185 bool trailingSpace = style != Section::AllMembers && !pName.isEmpty();
1186 appendTypeSpans(type, trailingSpace);
1187 if (style != Section::AllMembers && !pName.isEmpty()) {
1188 IR::SignatureSpan paramSpan;
1189 paramSpan.role = IR::SpanRole::Parameter;
1190 paramSpan.text = pName;
1191 spans.append(paramSpan);
1192 }
1193 }
1194 if (style != Section::AllMembers && !value.isEmpty())
1195 appendText(" = "_L1 + value);
1196 }
1197 }
1198 appendText(")"_L1);
1199 }
1200
1201 if (func->isConst())
1202 appendText(" const"_L1);
1203
1204 if (style == Section::Summary || style == Section::Accessors) {
1205 if (func->isFinal())
1206 appendText(" final"_L1);
1207 if (func->isOverride())
1208 appendText(" override"_L1);
1209 if (func->isPureVirtual())
1210 appendText(" = 0"_L1);
1211 if (func->isRef())
1212 appendText(" &"_L1);
1213 else if (func->isRefRef())
1214 appendText(" &&"_L1);
1215 } else if (style == Section::AllMembers) {
1216 if (!func->returnType().isEmpty() && func->returnType() != "void"_L1) {
1217 appendText(" : "_L1);
1218 appendTypeSpans(func->returnTypeString(), false);
1219 }
1220 } else {
1221 if (func->isRef())
1222 appendText(" &"_L1);
1223 else if (func->isRefRef())
1224 appendText(" &&"_L1);
1225 if (const auto &req = func->trailingRequiresClause(); req && !req->isEmpty())
1226 appendText(" requires "_L1 + *req);
1227 }
1228 break;
1229 }
1230 case NodeType::Enum: {
1231 const auto *enume = static_cast<const EnumNode *>(node);
1232 appendText("enum"_L1);
1233 if (enume->isScoped())
1234 appendText(" class"_L1);
1235 if (!enume->isAnonymous()) {
1236 appendText(" "_L1);
1237 if (linkName) {
1238 appendName(nameText);
1239 } else {
1240 IR::SignatureSpan span;
1242 span.text = nameText;
1243 spans.append(span);
1244 }
1245 }
1246 if (style == Section::Summary) {
1247 appendText(" { "_L1);
1248 const int MaxEnumValues = 6;
1249 QStringList documentedItems = enume->doc().enumItemNames();
1250 if (documentedItems.isEmpty()) {
1251 const auto &enumItems = enume->items();
1252 for (const auto &item : enumItems)
1253 documentedItems << item.name();
1254 }
1255 const QStringList omitItems = enume->doc().omitEnumItemNames();
1256 for (const auto &item : omitItems)
1257 documentedItems.removeAll(item);
1258
1259 if (documentedItems.size() > MaxEnumValues) {
1260 const QString last = documentedItems.last();
1261 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
1262 documentedItems += "..."_L1;
1263 documentedItems += last;
1264 }
1265 appendText(documentedItems.join(", "_L1));
1266 if (!documentedItems.isEmpty())
1267 appendText(" "_L1);
1268 appendText("}"_L1);
1269 }
1270 break;
1271 }
1272 case NodeType::TypeAlias: {
1273 if (style == Section::Details) {
1274 if (auto templateDecl = node->templateDecl()) {
1275 auto tmplSpans = buildTemplateDeclSpans(&*templateDecl);
1276 spans.append(tmplSpans);
1277 appendText(" "_L1);
1278 }
1279 }
1280 if (linkName) {
1281 appendName(nameText);
1282 } else {
1283 IR::SignatureSpan span;
1285 span.text = nameText;
1286 spans.append(span);
1287 }
1288 break;
1289 }
1290 case NodeType::Typedef: {
1291 if (static_cast<const TypedefNode *>(node)->associatedEnum())
1292 appendText("flags "_L1);
1293 if (linkName) {
1294 appendName(nameText);
1295 } else {
1296 IR::SignatureSpan span;
1298 span.text = nameText;
1299 spans.append(span);
1300 }
1301 break;
1302 }
1303 case NodeType::Property: {
1304 const auto *property = static_cast<const PropertyNode *>(node);
1305 if (linkName) {
1306 appendName(nameText);
1307 } else {
1308 IR::SignatureSpan span;
1310 span.text = nameText;
1311 spans.append(span);
1312 }
1313 appendText(" : "_L1);
1314 appendTypeSpans(property->qualifiedDataType(), false);
1315 break;
1316 }
1317 case NodeType::QmlProperty: {
1318 const auto *property = static_cast<const QmlPropertyNode *>(node);
1319 if (linkName) {
1320 appendName(nameText);
1321 } else {
1322 IR::SignatureSpan span;
1324 span.text = nameText;
1325 spans.append(span);
1326 }
1327 appendText(" : "_L1);
1328 appendTypeSpans(property->dataType(), false);
1329 break;
1330 }
1331 case NodeType::Variable: {
1332 const auto *variable = static_cast<const VariableNode *>(node);
1333 if (style == Section::AllMembers) {
1334 if (linkName) {
1335 appendName(nameText);
1336 } else {
1337 IR::SignatureSpan span;
1339 span.text = nameText;
1340 spans.append(span);
1341 }
1342 appendText(" : "_L1);
1343 appendTypeSpans(variable->dataType(), false);
1344 } else {
1345 appendTypeSpans(variable->leftType(), true);
1346 if (linkName) {
1347 appendName(nameText);
1348 } else {
1349 IR::SignatureSpan span;
1351 span.text = nameText;
1352 spans.append(span);
1353 }
1354 appendText(variable->rightType());
1355 }
1356 break;
1357 }
1358 default:
1359 if (linkName) {
1360 appendName(nameText);
1361 } else {
1362 IR::SignatureSpan span;
1364 span.text = nameText;
1365 spans.append(span);
1366 }
1367 break;
1368 }
1369
1370 return spans;
1371}
1372
1374 const HrefResolver *hrefResolver)
1375{
1376 QList<IR::SignatureSpan> spans;
1377
1378 auto appendText = [&spans](const QString &text) {
1379 IR::SignatureSpan span;
1381 span.text = text;
1382 spans.append(span);
1383 };
1384
1385 auto appendTypeSpans = [&spans](const QString &type, bool trailingSpace) {
1386 auto typeSpans = buildTypeSpans(type);
1387 spans.append(typeSpans);
1388 if (trailingSpace && !type.isEmpty()
1389 && !type.endsWith('*'_L1) && !type.endsWith('&'_L1)) {
1390 IR::SignatureSpan space;
1392 space.text = " "_L1;
1393 spans.append(space);
1394 }
1395 };
1396
1397 IR::SignatureSpan nameSpan;
1398 nameSpan.role = IR::SpanRole::Name;
1399 nameSpan.text = node->name();
1400 nameSpan.href = resolveHref(hrefResolver, node, node->parent());
1401
1402 if (node->isQmlProperty()) {
1403 const auto *pn = static_cast<const QmlPropertyNode *>(node);
1404 spans.append(nameSpan);
1405 appendText(" : "_L1);
1406 appendTypeSpans(pn->dataType(), false);
1407 } else if (node->isFunction(Genus::QML)) {
1408 const auto *func = static_cast<const FunctionNode *>(node);
1409 if (!func->returnType().isEmpty())
1410 appendTypeSpans(func->returnTypeString(), true);
1411 spans.append(nameSpan);
1412 appendText("("_L1);
1413 if (!func->parameters().isEmpty()) {
1414 const Parameters &parameters = func->parameters();
1415 for (int i = 0; i < parameters.count(); ++i) {
1416 if (i > 0)
1417 appendText(", "_L1);
1418 QString pName = parameters.at(i).name();
1419 QString type = parameters.at(i).type();
1420 if (!pName.isEmpty()) {
1421 appendTypeSpans(type, true);
1422 IR::SignatureSpan paramSpan;
1423 paramSpan.role = IR::SpanRole::Parameter;
1424 paramSpan.text = pName;
1425 spans.append(paramSpan);
1426 } else {
1427 IR::SignatureSpan paramSpan;
1428 paramSpan.role = IR::SpanRole::Parameter;
1429 paramSpan.text = type;
1430 spans.append(paramSpan);
1431 }
1432 }
1433 }
1434 appendText(")"_L1);
1435 } else {
1436 spans.append(nameSpan);
1437 }
1438
1439 auto extras = buildExtraSpans(node, Section::Summary);
1440 if (!extras.isEmpty()) {
1441 appendText(" "_L1);
1442 spans.append(extras);
1443 }
1444
1445 return spans;
1446}
1447
1448static QString plainTextFromSpans(const QList<IR::SignatureSpan> &spans)
1449{
1450 QString result;
1451 for (const auto &span : spans)
1452 result += span.plainText();
1453 return result;
1454}
1455
1456/*!
1457 \internal
1458 Build structured signature spans from Node data.
1459
1460 This function produces a QList of SignatureSpan values that carry
1461 semantic roles (Type, Name, Parameter, Extra, and so on) for each
1462 element of a member's synopsis. It parallels what CppCodeMarker's
1463 markedUpSynopsis() and markedUpQmlItem() produce as tagged strings,
1464 but outputs structured IR spans instead.
1465
1466 The \a style parameter controls level of detail: Summary includes
1467 virtual/override qualifiers, Details adds template declarations and
1468 parent prefixes, AllMembers uses a condensed format.
1469*/
1471 const HrefResolver *hrefResolver,
1472 const Node *relative,
1473 Section::Style style)
1474{
1475 if (node->isQmlNode() && !node->isEnumType())
1476 return buildQmlItemSpans(node, hrefResolver);
1477 return buildCppSynopsisSpans(node, hrefResolver, relative, style);
1478}
1479
1480/*!
1481 \internal
1482 Extract a grouped all-members listing for a QML type.
1483
1484 Constructs a Sections object from the QmlTypeNode, extracts
1485 allMembersSection().classNodesList() to group members by
1486 originating QML type, and builds AllMemberEntry items with
1487 QML-specific hints and property group nesting.
1488*/
1489static IR::AllMembersIR extractQmlAllMembersIR(const QmlTypeNode *qcn, const HrefResolver *hrefResolver)
1490{
1491 IR::AllMembersIR result;
1492 result.typeName = qcn->name();
1493 result.typeHref = resolveHref(hrefResolver, qcn, qcn);
1494 result.isQmlType = true;
1495
1496 Sections sections(qcn);
1497 ClassNodesList &groupedMembers = sections.allMembersSection().classNodesList();
1498 if (groupedMembers.isEmpty())
1499 return result;
1500
1501 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1502
1503 std::function<IR::AllMemberEntry(Node *)> buildEntry = [&](Node *node) -> IR::AllMemberEntry {
1504 IR::AllMemberEntry entry;
1505 entry.signatureSpans = buildQmlItemSpans(node, hrefResolver);
1506 entry.signature = plainTextFromSpans(entry.signatureSpans);
1507 entry.href = resolveHref(hrefResolver, node, qcn);
1508
1509 if (node->isQmlProperty()) {
1510 auto *qpn = static_cast<QmlPropertyNode *>(node);
1511 QStringList qmlHints = qpn->hints();
1512 if (qpn->isAttached() && !qmlHints.contains("attached"_L1))
1513 qmlHints << "attached"_L1;
1514 for (const auto &h : std::as_const(qmlHints))
1515 entry.hints.append(h);
1516 } else if (node->isAttached()) {
1517 entry.hints.append("attached"_L1);
1518 }
1519
1520 if (node->isPropertyGroup()) {
1521 entry.isPropertyGroup = true;
1522 const auto *scn = static_cast<SharedCommentNode *>(node);
1523 for (auto *child : scn->collective()) {
1524 const NodeContext childContext = child->createContext();
1525 if (!InclusionFilter::isIncluded(policy, childContext))
1526 continue;
1527 entry.children.append(buildEntry(child));
1528 }
1529 }
1530
1531 return entry;
1532 };
1533
1534 auto isVisible = [&policy](Node *node) {
1535 const NodeContext context = node->createContext();
1536 return InclusionFilter::isIncluded(policy, context)
1537 && !(node->isSharingComment() && node->sharedCommentNode()->isPropertyGroup());
1538 };
1539
1540 for (const auto &[originType, nodes] : groupedMembers) {
1541 Q_ASSERT(originType);
1542 if (nodes.isEmpty())
1543 continue;
1544
1545 IR::MemberGroup group;
1546 if (originType != qcn) {
1547 group.typeName = originType->name();
1548 group.typeHref = resolveHref(hrefResolver, originType, qcn);
1549 }
1550
1551 for (auto *node : nodes) {
1552 if (isVisible(node))
1553 group.members.append(buildEntry(node));
1554 }
1555
1556 result.memberGroups.append(group);
1557 }
1558
1559 return result;
1560}
1561
1562/*!
1563 \internal
1564 Extract a flat all-members listing for a C++ class or namespace.
1565
1566 Constructs a Sections object from the aggregate, extracts
1567 allMembersSection().members(), builds AllMemberEntry for each
1568 visible member, and returns an AllMembersIR with isQmlType=false.
1569*/
1570static IR::AllMembersIR extractCppAllMembersIR(const Aggregate *aggregate, const HrefResolver *hrefResolver)
1571{
1572 IR::AllMembersIR result;
1573 result.typeName = aggregate->plainFullName();
1574 result.typeHref = resolveHref(hrefResolver, aggregate, aggregate);
1575 result.isQmlType = false;
1576
1577 Sections sections(aggregate);
1578 const Section &allMembers = sections.allMembersSection();
1579 if (allMembers.isEmpty())
1580 return result;
1581
1582 const InclusionPolicy policy = Config::instance().createInclusionPolicy();
1583
1584 for (const auto *node : allMembers.members()) {
1585 if (node->name().isEmpty())
1586 continue;
1587 const NodeContext context = node->createContext();
1588 if (!InclusionFilter::isIncluded(policy, context))
1589 continue;
1590
1591 IR::AllMemberEntry entry;
1592 entry.signatureSpans = buildSignatureSpans(node, hrefResolver, aggregate, Section::AllMembers);
1593 entry.signature = plainTextFromSpans(entry.signatureSpans);
1594 entry.href = resolveHref(hrefResolver, node, aggregate);
1595 result.members.append(entry);
1596 }
1597
1598 return result;
1599}
1600
1601/*!
1602 \internal
1603 Extract all-members IR for a page node.
1604
1605 Dispatches to the QML or C++ extraction function based on the page
1606 type. Returns std::nullopt for page types that don't have member
1607 listing pages (generic pages, QML basic types) or when the listing
1608 would be empty.
1609*/
1610std::optional<IR::AllMembersIR> extractAllMembersIR(const PageNode *pn, const HrefResolver *hrefResolver)
1611{
1612 if (pn->isQmlType()) {
1613 const auto *qcn = static_cast<const QmlTypeNode *>(pn);
1614 if (qcn->isQmlBasicType())
1615 return std::nullopt;
1616 auto result = extractQmlAllMembersIR(qcn, hrefResolver);
1617 bool hasMember = false;
1618 for (const auto &group : std::as_const(result.memberGroups)) {
1619 if (!group.members.isEmpty()) {
1620 hasMember = true;
1621 break;
1622 }
1623 }
1624 if (!hasMember)
1625 return std::nullopt;
1626 return result;
1627 }
1628
1629 if (pn->isAggregate() && (pn->isClassNode() || pn->isNamespace())) {
1630 const auto *aggregate = static_cast<const Aggregate *>(pn);
1631 auto result = extractCppAllMembersIR(aggregate, hrefResolver);
1632 if (result.members.isEmpty())
1633 return std::nullopt;
1634 return result;
1635 }
1636
1637 return std::nullopt;
1638}
1639
1640/*!
1641 \internal
1642 Extract navigation metadata from a PageNode.
1643
1644 Reads navigation configuration values (homepage, landingpage,
1645 cppclassespage, qmltypespage) and the node's position in the
1646 documentation tree to produce a breadcrumb chain, sequential
1647 links (previous/next/start), and the configured TOC depth.
1648
1649 The breadcrumb chain follows page-type-specific logic ported
1650 from HtmlGenerator::generateNavigationBar(): static chain
1651 entries for API reference pages (class, QML type), a
1652 navigationParent() walk for generic pages with a 16-item
1653 circular reference cutoff, and a fallback to the first group
1654 page when no navigation parent exists.
1655
1656 Sequential links come from the node's pre-populated link map,
1657 set by QDocDatabase::updateNavigation() before generation runs.
1658*/
1659IR::NavigationData extractNavigationData(const PageNode *pn, const HrefResolver *hrefResolver)
1660{
1661 IR::NavigationData nav;
1662 const Config &config = Config::instance();
1664
1665 const QString navDot = CONFIG_NAVIGATION + Config::dot;
1666 const QString homepage = config.get(navDot + CONFIG_HOMEPAGE).asString();
1667 const QString hometitle = config.get(navDot + CONFIG_HOMETITLE).asString(homepage);
1668 const QString landingpage = config.get(navDot + CONFIG_LANDINGPAGE).asString();
1669 const QString landingtitle = config.get(navDot + CONFIG_LANDINGTITLE).asString(landingpage);
1670 const QString cppclassespage = config.get(navDot + CONFIG_CPPCLASSESPAGE).asString();
1671 const QString cppclassestitle = config.get(navDot + CONFIG_CPPCLASSESTITLE).asString("C++ Classes"_L1);
1672 const QString qmltypespage = config.get(navDot + CONFIG_QMLTYPESPAGE).asString();
1673 const QString qmltypestitle = config.get(navDot + CONFIG_QMLTYPESTITLE).asString("QML Types"_L1);
1674
1675 const QString pageTitle = pn->title();
1676 using CrumbState = IR::NavigationData::CrumbState;
1677
1678 auto resolveCrumb = [&](const QString &targetName)
1679 -> std::pair<QString, CrumbState> {
1680 const Node *target = qdb->findNodeForTarget(targetName, pn);
1681 if (!target)
1682 return {{}, CrumbState::Unresolved};
1683 if (target == pn)
1684 return {{}, CrumbState::Current};
1685 return {resolveHref(hrefResolver, target, pn), CrumbState::Link};
1686 };
1687
1688 if (!homepage.isEmpty()) {
1689 auto [href, state] = resolveCrumb(homepage);
1690 if (state == CrumbState::Current)
1691 return nav;
1692 nav.breadcrumbs.append({hometitle, std::move(href), state});
1693 }
1694
1695 if (!landingpage.isEmpty()) {
1696 auto [href, state] = resolveCrumb(landingpage);
1697 if (state != CrumbState::Current)
1698 nav.breadcrumbs.append({landingtitle, std::move(href), state});
1699 }
1700
1701 if (pn->isClassNode()) {
1702 if (!cppclassespage.isEmpty() && !cppclassestitle.isEmpty()) {
1703 auto [href, state] = resolveCrumb(cppclassespage);
1704 nav.breadcrumbs.append({cppclassestitle, std::move(href), state});
1705 }
1706
1707 const auto *moduleNode = qdb->getModuleNode(pn);
1708 QString moduleState;
1709 if (moduleNode && !moduleNode->state().isEmpty())
1710 moduleState = QStringLiteral(" (%1)").arg(moduleNode->state());
1711
1712 if (!pn->physicalModuleName().isEmpty() && moduleNode
1713 && (!moduleState.isEmpty() || moduleNode->title() != cppclassespage)) {
1714 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1715 resolveHref(hrefResolver, moduleNode, pn),
1716 CrumbState::Link});
1717 }
1718 nav.breadcrumbs.append({pn->name(), {}, CrumbState::Current});
1719 } else if (pn->isQmlType()) {
1720 if (!qmltypespage.isEmpty() && !qmltypestitle.isEmpty()) {
1721 auto [href, state] = resolveCrumb(qmltypespage);
1722 nav.breadcrumbs.append({qmltypestitle, std::move(href), state});
1723 }
1724
1725 const auto *moduleNode = qdb->getModuleNode(pn);
1726 QString moduleState;
1727 if (moduleNode && !moduleNode->state().isEmpty())
1728 moduleState = QStringLiteral(" (%1)").arg(moduleNode->state());
1729
1730 if (moduleNode
1731 && (!moduleState.isEmpty() || moduleNode->title() != qmltypespage)) {
1732 nav.breadcrumbs.append({moduleNode->name() + moduleState,
1733 resolveHref(hrefResolver, moduleNode, pn),
1734 CrumbState::Link});
1735 }
1736 nav.breadcrumbs.append({pn->name(), {}, CrumbState::Current});
1737 } else {
1738 auto currentNode = pn;
1739 std::deque<const Node *> navNodes;
1740 qsizetype navItems = 0;
1741 while (currentNode->navigationParent() && ++navItems < 16) {
1742 if (std::find(navNodes.cbegin(), navNodes.cend(),
1743 currentNode->navigationParent()) == navNodes.cend())
1744 navNodes.push_front(currentNode->navigationParent());
1745 currentNode = currentNode->navigationParent();
1746 }
1747 if (navNodes.empty()) {
1748 const QStringList groups = pn->groupNames();
1749 for (const auto &groupName : groups) {
1750 const auto *groupNode = qdb->findNodeByNameAndType(
1751 QStringList{groupName}, &Node::isGroup);
1752 if (groupNode && !groupNode->title().isEmpty()) {
1753 navNodes.push_front(groupNode);
1754 break;
1755 }
1756 }
1757 }
1758 for (const auto *navNode : navNodes) {
1759 if (navNode->isPageNode())
1760 nav.breadcrumbs.append({navNode->title(),
1761 resolveHref(hrefResolver, navNode, pn),
1762 CrumbState::Link});
1763 }
1764 if (!nav.breadcrumbs.isEmpty())
1765 nav.breadcrumbs.append({pageTitle, {}, CrumbState::Current});
1766 }
1767
1768 const auto &linkMap = pn->links();
1769 if (linkMap.contains(Node::PreviousLink)) {
1770 const auto &linkPair = linkMap[Node::PreviousLink];
1771 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1772 QString href;
1773 QString title;
1774 if (target && target != pn) {
1775 href = resolveHref(hrefResolver, target, pn);
1776 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1777 ? target->title() : linkPair.second;
1778 } else {
1779 href = linkPair.first;
1780 title = linkPair.second;
1781 }
1782 nav.previousLink = IR::NavigationData::LinkEntry{title, href};
1783 }
1784 if (linkMap.contains(Node::NextLink)) {
1785 const auto &linkPair = linkMap[Node::NextLink];
1786 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1787 QString href;
1788 QString title;
1789 if (target && target != pn) {
1790 href = resolveHref(hrefResolver, target, pn);
1791 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1792 ? target->title() : linkPair.second;
1793 } else {
1794 href = linkPair.first;
1795 title = linkPair.second;
1796 }
1797 nav.nextLink = IR::NavigationData::LinkEntry{title, href};
1798 }
1799 if (linkMap.contains(Node::StartLink)) {
1800 const auto &linkPair = linkMap[Node::StartLink];
1801 const Node *target = qdb->findNodeForTarget(linkPair.first, pn);
1802 QString href;
1803 QString title;
1804 if (target && target != pn) {
1805 href = resolveHref(hrefResolver, target, pn);
1806 title = (linkPair.first == linkPair.second && !target->title().isEmpty())
1807 ? target->title() : linkPair.second;
1808 } else {
1809 href = linkPair.first;
1810 title = linkPair.second;
1811 }
1812 nav.startLink = IR::NavigationData::LinkEntry{title, href};
1813 }
1814
1815 const QString formatDot = "HTML"_L1 + Config::dot;
1816 nav.tocDepth = config.get(formatDot + "tocdepth"_L1).asInt();
1817
1818 return nav;
1819}
1820
1821} // namespace NodeExtractor
1822
1823QT_END_NAMESPACE
bool hasObsoleteMembers() const
Returns true if this aggregate contains at least one child that is marked obsolete.
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
The ClassNode represents a C++ class.
Definition classnode.h:23
bool isQmlNativeType()
Definition classnode.h:54
A class for holding the members of a collection of doc pages.
NodeMap getMembers(NodeType type) const
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:95
const Location & location() const
Returns the starting location of a qdoc comment.
Definition doc.cpp:89
const Text & body() const
Definition doc.cpp:114
Text briefText(bool inclusive=false) const
Definition doc.cpp:126
Converts Atom chains to QList<IR::ContentBlock> trees.
static bool isIncluded(const InclusionPolicy &policy, const NodeContext &context)
The Location class provides a way to mark a location in a file.
Definition location.h:20
A PageNode is a Node that generates a documentation page.
Definition pagenode.h:19
const PageNode * navigationParent() const
Definition pagenode.h:47
bool noAutoList() const
Returns the value of the no auto-list flag.
Definition pagenode.h:42
The Parameter class describes one function parameter.
Definition parameter.h:14
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
const CollectionNode * getModuleNode(const Node *relative)
Returns the collection node representing the module that relative node belongs to,...
const CNMap & groups()
Returns a const reference to the collection of all group nodes in the primary tree.
ClassNode * classNode() const override
If this is a QmlTypeNode, this function returns the pointer to the C++ ClassNode that this QML type r...
Definition qmltypenode.h:27
static void subclasses(const Node *base, NodeList &subs, bool recurse=false)
Loads the list subs with the nodes of all the subclasses of base.
bool isSingleton() const
Definition qmltypenode.h:31
QmlTypeNode * qmlBaseNode() const override
If this Aggregate is a QmlTypeNode, this function returns a pointer to the QmlTypeNode that is its ba...
Definition qmltypenode.h:54
CollectionNode * logicalModule() const override
If this is a QmlTypeNode, a pointer to its QML module is returned, which is a pointer to a Collection...
Definition qmltypenode.h:47
A class for containing the elements of one documentation section.
Definition sections.h:17
ClassNodesList & classNodesList()
Definition sections.h:52
@ Summary
Definition sections.h:19
@ Details
Definition sections.h:19
@ Accessors
Definition sections.h:19
@ AllMembers
Definition sections.h:19
bool isEmpty() const
Definition sections.h:35
A class for creating vectors of collections for documentation.
Definition sections.h:80
SectionVector & summarySections()
Definition sections.h:150
Sections(const Aggregate *aggregate)
This constructor builds the section vectors based on the type of the aggregate node.
Definition sections.cpp:371
SectionVector & detailsSections()
Definition sections.h:151
Section & allMembersSection()
Definition sections.h:152
Definition text.h:12
const Atom * firstAtom() const
Definition text.h:34
static std::string comparisonCategoryAsString(ComparisonCategory category)
#define CONFIG_CPPCLASSESTITLE
Definition config.h:384
#define CONFIG_HOMETITLE
Definition config.h:404
#define CONFIG_HOMEPAGE
Definition config.h:403
#define CONFIG_URL
Definition config.h:457
#define CONFIG_QMLTYPESPAGE
Definition config.h:463
#define CONFIG_CPPCLASSESPAGE
Definition config.h:383
#define CONFIG_NAVIGATION
Definition config.h:427
#define CONFIG_LANDINGPAGE
Definition config.h:416
#define CONFIG_LANDINGTITLE
Definition config.h:417
#define CONFIG_QMLTYPESTITLE
Definition config.h:464
NodeType
Definition genustypes.h:150
Definition builder.cpp:14
static QList< IR::SignatureSpan > buildTypeSpans(const QString &typeString)
static QList< IR::SignatureSpan > buildQmlItemSpans(const Node *node, const HrefResolver *hrefResolver)
static QList< IR::SignatureSpan > buildTemplateDeclSpans(const RelaxedTemplateDeclaration *templateDecl)
QList< IR::SignatureSpan > buildSignatureSpans(const Node *node, const HrefResolver *hrefResolver, const Node *relative, Section::Style style)
IR::CollectionData extractCollectionData(const CollectionNode *cn, const HrefResolver *hrefResolver)
IR::MemberIR extractMemberIR(const Node *node, const HrefResolver *hrefResolver, const Node *relative, MemberExtractionLevel level)
std::optional< IR::AllMembersIR > extractAllMembersIR(const PageNode *pn, const HrefResolver *hrefResolver)
QList< IR::SectionIR > extractSummarySections(const Aggregate *aggregate, const HrefResolver *hrefResolver)
static QString plainTextFromSpans(const QList< IR::SignatureSpan > &spans)
static IR::AllMembersIR extractQmlAllMembersIR(const QmlTypeNode *qcn, const HrefResolver *hrefResolver)
IR::QmlTypeData extractQmlTypeData(const QmlTypeNode *qcn, const HrefResolver *hrefResolver)
QList< IR::SectionIR > extractDetailSections(const Aggregate *aggregate, const HrefResolver *hrefResolver)
IR::PageMetadata extractPageMetadata(const PageNode *pn, const HrefResolver *hrefResolver)
static QList< IR::SignatureSpan > buildCppSynopsisSpans(const Node *node, const HrefResolver *hrefResolver, const Node *relative, Section::Style style)
static QString threadSafenessString(Node::ThreadSafeness ts)
IR::CppReferenceData extractCppReferenceData(const Aggregate *aggregate, const HrefResolver *hrefResolver)
static IR::AllMembersIR extractCppAllMembersIR(const Aggregate *aggregate, const HrefResolver *hrefResolver)
IR::NavigationData extractNavigationData(const PageNode *pn, const HrefResolver *hrefResolver)
static QList< IR::SignatureSpan > buildExtraSpans(const Node *node, Section::Style style)
QList< Node * > NodeList
Definition node.h:45
QMap< QString, Node * > NodeMap
Definition node.h:48
static QString resolveHref(const HrefResolver *resolver, const Node *target, const Node *relative)
static IR::DiagnosticHandler diagnosticHandlerFor(const Node *node)
MemberExtractionLevel
QList< ClassNodes > ClassNodesList
Definition sections.h:14
A single entry in an all-members listing page.
Definition member.h:98
Intermediate representation of the all-members listing page.
Definition member.h:115
Intermediate representation of a single documentable member.
Definition member.h:35
bool isPrimaryOverload
Definition member.h:48
bool isConst
Definition member.h:53
int overloadNumber
Definition member.h:47
bool isAttached
Definition member.h:59
bool isDefault
Definition member.h:60
bool isSlot
Definition member.h:56
bool isRequired
Definition member.h:62
bool isStatic
Definition member.h:52
bool isNoexcept
Definition member.h:72
bool isSignal
Definition member.h:55
Access access
Definition member.h:43
NodeType nodeType
Definition member.h:42
bool isReadOnly
Definition member.h:61
bool isVirtual
Definition member.h:54
Status status
Definition member.h:44
Intermediate representation of a function parameter.
Definition member.h:21
Represents a single span within a structured signature.
The Node class is the base class for all the nodes in QDoc's parse tree.
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:236
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:120
bool isGroup() const
Returns true if the node type is Group.
Definition node.h:105
bool isNamespace() const
Returns true if the node type is Namespace.
Definition node.h:109
bool isTypedef() const
Returns true if the node type is Typedef.
Definition node.h:127
bool isQmlBasicType() const
Returns true if the node type is QmlBasicType.
Definition node.h:118
ComparisonCategory comparisonCategory() const
Definition node.h:185
bool isQmlType() const
Returns true if the node type is QmlType or QmlValueType.
Definition node.h:122
bool isHeader() const
Returns true if the node type is HeaderFile.
Definition node.h:106
NodeType nodeType() const override
Returns this node's type.
Definition node.h:82
Genus genus() const override
Returns this node's Genus.
Definition node.h:85
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:94
virtual Status status() const
Returns the node's status value.
Definition node.h:240
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:209
virtual bool isAggregate() const
Returns true if this node is an aggregate, which means it inherits Aggregate and can therefore have c...
Definition node.h:137
static bool nodeNameLessThan(const Node *first, const Node *second)
Returns true if the node n1 is less than node n2.
Definition node.cpp:111
bool isProxyNode() const
Returns true if the node type is Proxy.
Definition node.h:114
const std::optional< RelaxedTemplateDeclaration > & templateDecl() const
Definition node.h:244
Access access() const
Returns the node's Access setting, which can be Public, Protected, or Private.
Definition node.h:229
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
ThreadSafeness threadSafeness() const
Returns the thread safeness value for whatever this node represents.
Definition node.cpp:845
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:113
NodeContext createContext() const
Definition node.cpp:175
bool isModule() const
Returns true if the node type is Module.
Definition node.h:108
ThreadSafeness
An unsigned char that specifies the degree of thread-safeness of the element.
Definition node.h:58
@ UnspecifiedSafeness
Definition node.h:59
bool isRelatedNonmember() const
Returns true if this is a related nonmember of something.
Definition node.h:123
virtual bool isClassNode() const
Returns true if this is an instance of ClassNode.
Definition node.h:144
virtual bool isCollectionNode() const
Returns true if this is an instance of CollectionNode.
Definition node.h:145
bool isQmlModule() const
Returns true if the node type is QmlModule.
Definition node.h:119
@ SignatureReturnType
Definition node.h:68
@ SignatureDefaultValues
Definition node.h:67
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:121
A class for parsing and managing a function parameter list.
Definition main.cpp:28
const Parameter & at(int i) const
Definition parameters.h:36
int count() const
Definition parameters.h:34