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