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
classnode.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "classnode.h"
5
6#include "functionnode.h"
7#include "propertynode.h"
8#include "qdocdatabase.h"
9#include "qmltypenode.h"
10#include "utilities.h"
11
12#include <QtCore/qlatin1stringview.h>
13
15
16using namespace Qt::StringLiterals;
17
18/*!
19 \class ClassNode
20 \brief The ClassNode represents a C++ class.
21
22 It is also used to represent a C++ struct or union. There are some
23 actual uses for structs, but I don't think any unions have been
24 documented yet.
25 */
26
27/*!
28 Adds the base class \a node to this class's list of base
29 classes. The base class has the specified \a access. This
30 is a resolved base class.
31 */
33{
34 m_bases.append(RelatedClass(access, node));
35 node->m_derived.append(RelatedClass(access, this));
36}
37
38/*!
39 Adds the derived class \a node to this class's list of derived
40 classes. The derived class inherits this class with \a access.
41 */
43{
44 m_derived.append(RelatedClass(access, node));
45}
46
47/*!
48 Add an unresolved base class to this class node's list of
49 base classes. The unresolved base class will be resolved
50 before the generate phase of qdoc. In an unresolved base
51 class, the pointer to the base class node is 0.
52 */
53void ClassNode::addUnresolvedBaseClass(Access access, const QStringList &path)
54{
55 m_bases.append(RelatedClass(access, path));
56}
57
58/*!
59 Search the child list to find the property node with the
60 specified \a name.
61 */
63{
64 Node *n = findNonfunctionChild(name, &Node::isProperty);
65
66 if (n)
67 return static_cast<PropertyNode *>(n);
68
69 PropertyNode *pn = nullptr;
70
71 const QList<RelatedClass> &bases = baseClasses();
72 if (!bases.isEmpty()) {
73 for (const RelatedClass &base : bases) {
74 ClassNode *cn = base.m_node;
75 if (cn) {
76 pn = cn->findPropertyNode(name);
77 if (pn)
78 break;
79 }
80 }
81 }
82 const QList<RelatedClass> &ignoredBases = ignoredBaseClasses();
83 if (!ignoredBases.isEmpty()) {
84 for (const RelatedClass &base : ignoredBases) {
85 ClassNode *cn = base.m_node;
86 if (cn) {
87 pn = cn->findPropertyNode(name);
88 if (pn)
89 break;
90 }
91 }
92 }
93
94 return pn;
95}
96
97/*!
98 \a fn is an overriding function in this class or in a class
99 derived from this class. Find the node for the function that
100 \a fn overrides in this class's children or in one of this
101 class's base classes. Return a pointer to the overridden
102 function or return 0.
103
104 This should be revised because clang provides the path to the
105 overridden function. mws 15/12/2018
106 */
108{
109 for (auto &bc : m_bases) {
110 ClassNode *cn = bc.m_node;
111 if (cn == nullptr) {
112 cn = QDocDatabase::qdocDB()->findClassNode(bc.m_path);
113 bc.m_node = cn;
114 }
115 if (cn != nullptr) {
116 FunctionNode *result = cn->findFunctionChild(fn);
117 if (result != nullptr && !result->isInternal() && !result->isNonvirtual()
118 && result->hasDoc())
119 return result;
120 result = cn->findOverriddenFunction(fn);
121 if (result != nullptr && !result->isNonvirtual())
122 return result;
123 }
124 }
125 return nullptr;
126}
127
128/*!
129 \a fn is an overriding function in this class or in a class
130 derived from this class. Find the node for the property that
131 \a fn overrides in this class's children or in one of this
132 class's base classes. Return a pointer to the overridden
133 property or return 0.
134 */
136{
137 for (auto &baseClass : m_bases) {
138 ClassNode *cn = baseClass.m_node;
139 if (cn == nullptr) {
140 cn = QDocDatabase::qdocDB()->findClassNode(baseClass.m_path);
141 baseClass.m_node = cn;
142 }
143 if (cn != nullptr) {
144 const NodeList &children = cn->childNodes();
145 for (const auto &child : children) {
146 if (child->isProperty()) {
147 auto *pn = static_cast<PropertyNode *>(child);
148 if (pn->name() == fn->name() || pn->hasAccessFunction(fn->name())) {
149 if (pn->hasDoc())
150 return pn;
151 }
152 }
153 }
154 PropertyNode *result = cn->findOverriddenProperty(fn);
155 if (result != nullptr)
156 return result;
157 }
158 }
159 return nullptr;
160}
161
162/*!
163 \fn bool ClassNode::docMustBeGenerated() const
164
165 Returns \c true if the class or struct represented by this node
166 matches the inclusion policy and is therefore part of the
167 documented API.
168
169*/
170
171/*!
172 \brief Detects circular relationships in class hierarchies.
173
174 Traverses the hierarchy in the specified \a direction (Base or Derived),
175 checking only private/internal nodes to detect cycles that could cause
176 infinite loops during base/derived class promotion.
177
178 If \a cyclePath is provided, it will be filled with the names of
179 classes forming the cycle (e.g., ["A", "B", "C", "A"]).
180
181 Returns true if a cycle is detected.
182
183 \sa detectCycleRecursive().
184 */
185bool ClassNode::hasCircularRelationship(HierarchyDirection direction,
186 QStringList *cyclePath) const
187{
188 QMap<const ClassNode*, Color> colors;
189 QList<const ClassNode*> path;
190
191 bool cycleFound = detectCycleRecursive(direction, colors, path);
192
193 if (cycleFound && cyclePath) {
194 cyclePath->clear();
195
196 if (!path.isEmpty()) {
197 const ClassNode *repeatedNode = path.last();
198 auto first = path.indexOf(repeatedNode);
199 if (first != -1 && first < path.size() - 1)
200 path = path.mid(first);
201 }
202
203 for (const ClassNode *node : path)
204 cyclePath->append(node->name());
205 }
206
207 return cycleFound;
208}
209
210/*!
211 Recursive helper for cycle detection using three-color DFS.
212
213 Colors represent node states:
214 \list
215 \li White: Unvisited.
216 \li Gray: Currently being processed (on recursion stack).
217 \li Black: Fully processed (all descendants explored).
218 \endlist
219
220 Returns true if a cycle is detected. The \a path list will contain
221 the nodes that form the cycle.
222
223 \sa hasCircularRelationship().
224*/
225bool ClassNode::detectCycleRecursive(HierarchyDirection direction,
226 QMap<const ClassNode*, Color> &colors,
227 QList<const ClassNode*> &path) const
228{
229 colors[this] = Color::Gray;
230 path.append(this);
231
232 const QList<RelatedClass> &related = (direction == HierarchyDirection::Base)
233 ? baseClasses()
234 : derivedClasses();
235
236 for (const auto &relatedClass : related) {
237 const ClassNode *rc = relatedClass.m_node;
238 if (rc == nullptr)
239 rc = QDocDatabase::qdocDB()->findClassNode(relatedClass.m_path);
240
241 if (rc != nullptr && (rc->isPrivate() || rc->isInternal() || rc->isDontDocument())) {
242 if (rc == this) {
243 qCDebug(lcQdoc) << "Skipping self-reference (CRTP-like) in" << this->name();
244 continue;
245 }
246
247 Color rcColor = colors.value(rc, Color::White);
248
249 if (rcColor == Color::Gray) {
250 path.append(rc);
251 return true;
252 }
253
254 if (rcColor == Color::White) {
255 if (rc->detectCycleRecursive(direction, colors, path))
256 return true;
257 }
258 }
259 }
260
261 colors[this] = Color::Black;
262 path.removeLast();
263
264 return false;
265}
266
267/*!
268 Checks if this class has circular inheritance by traversing its
269 base class hierarchy. Returns true if a cycle is detected.
270
271 If \a cyclePath is provided, it will be filled with the names of
272 classes forming the cycle (e.g., ["A", "B", "C", "A"]).
273
274 This method is used to detect problematic inheritance patterns
275 before performing operations like base class promotion that could
276 infinite loop on circular hierarchies.
277
278 \sa hasCircularDerivedClasses(), hasCircularRelationship().
279 */
280bool ClassNode::hasCircularInheritance(QStringList *cyclePath) const
281{
282 return hasCircularRelationship(HierarchyDirection::Base, cyclePath);
283}
284
285/*!
286 Checks if this class has circular derived class relationships by
287 traversing its derived class hierarchy. Returns true if a cycle is detected.
288
289 If \a cyclePath is provided, it will be filled with the names of
290 classes forming the cycle.
291
292 This is similar to hasCircularInheritance() but checks the derived
293 direction instead of the base direction.
294
295 \sa hasCircularInheritance(), hasCircularRelationship.
296 */
297bool ClassNode::hasCircularDerivedClasses(QStringList *cyclePath) const
298{
299 return hasCircularRelationship(HierarchyDirection::Derived, cyclePath);
300}
301
302/*!
303 A base class of this class node was private or internal.
304 That node's list of \a bases is traversed in this function.
305 Each of its public base classes is promoted to be a base
306 class of this node for documentation purposes. For each
307 private or internal class node in \a bases, this function
308 is called recursively with the list of base classes from
309 that private or internal class node.
310 */
311void ClassNode::promotePublicBases(const QList<RelatedClass> &bases)
312{
313 if (!bases.isEmpty()) {
314 for (qsizetype i = bases.size() - 1; i >= 0; --i) {
315 ClassNode *bc = bases.at(i).m_node;
316 if (bc == nullptr)
317 bc = QDocDatabase::qdocDB()->findClassNode(bases.at(i).m_path);
318 if (bc != nullptr) {
319 if (bc->isPrivate() || bc->isInternal())
320 promotePublicBases(bc->baseClasses());
321 else
322 m_bases.append(bases.at(i));
323 }
324 }
325 }
326}
327
328/*!
329 Remove private and internal bases classes from this class's list
330 of base classes. When a base class is removed from the list, add
331 its base classes to this class's list of base classes.
332 */
334{
335 int i;
336 i = 0;
337 QSet<ClassNode *> found;
338
339 // Remove private and duplicate base classes.
340 while (i < m_bases.size()) {
341 ClassNode *bc = m_bases.at(i).m_node;
342 if (bc == nullptr)
343 bc = QDocDatabase::qdocDB()->findClassNode(m_bases.at(i).m_path);
344 if (bc != nullptr
345 && (bc->isPrivate() || bc->isInternal() || bc->isDontDocument()
346 || found.contains(bc))) {
347 RelatedClass rc = m_bases.at(i);
348 m_bases.removeAt(i);
349 m_ignoredBases.append(rc);
350 QStringList cyclePath;
351 if (bc->hasCircularInheritance(&cyclePath))
352 bc->location().warning("Circular inheritance detected: %1"_L1.arg(cyclePath.join(" -> ")));
353 else
354 promotePublicBases(bc->baseClasses());
355 } else {
356 ++i;
357 }
358 found.insert(bc);
359 }
360
361 i = 0;
362 while (i < m_derived.size()) {
363 ClassNode *dc = m_derived.at(i).m_node;
364 if (dc != nullptr && (dc->isPrivate() || dc->isInternal() || dc->isDontDocument())) {
365 QStringList derivedCyclePath;
366 if (dc->hasCircularDerivedClasses(&derivedCyclePath)) {
367 dc->location().warning(QStringLiteral("Circular derived class relationship detected: %1").arg(derivedCyclePath.join(" -> ")));
368 m_derived.removeAt(i);
369 } else {
370 m_derived.removeAt(i);
371 const QList<RelatedClass> &dd = dc->derivedClasses();
372 for (qsizetype j = dd.size() - 1; j >= 0; --j) {
373 // Skip CRTP self-references to avoid infinite loops
374 if (dd.at(j).m_node != dc) {
375 m_derived.insert(i, dd.at(j));
376 } else {
377 qCDebug(lcQdoc) << "Skipping CRTP self-reference in derived class promotion for"
378 << dc->name();
379 }
380 }
381 }
382 } else {
383 ++i;
384 }
385 }
386}
387
388/*!
389 */
391{
392 for (const auto &baseClass : std::as_const(baseClasses())) {
393 ClassNode *cn = baseClass.m_node;
394 if (cn) {
395 Node *n = cn->findNonfunctionChild(pn->name(), &Node::isProperty);
396 if (n) {
397 auto *baseProperty = static_cast<PropertyNode *>(n);
398 cn->resolvePropertyOverriddenFromPtrs(baseProperty);
399 pn->setOverriddenFrom(baseProperty);
400 } else
401 cn->resolvePropertyOverriddenFromPtrs(pn);
402 }
403 }
404}
405
406/*!
407 Returns the display name for this class node. For anonymous structs,
408 returns "(unnamed struct)" instead of the file-based unique identifier.
409*/
411{
412 if (isAnonymous()) {
413 switch (nodeType()) {
414 case NodeType::Struct:
415 return "(unnamed struct)"_L1;
416 case NodeType::Union:
417 return "(unnamed union)"_L1;
418 case NodeType::Class:
419 return "(unnamed class)"_L1;
420 default:
421 break;
422 }
423 }
424 return Node::plainName();
425}
426
427QT_END_NAMESPACE
Access
Definition access.h:11
The ClassNode represents a C++ class.
Definition classnode.h:23
PropertyNode * findPropertyNode(const QString &name)
Search the child list to find the property node with the specified name.
Definition classnode.cpp:62
void addResolvedBaseClass(Access access, ClassNode *node)
Adds the base class node to this class's list of base classes.
Definition classnode.cpp:32
PropertyNode * findOverriddenProperty(const FunctionNode *fn)
fn is an overriding function in this class or in a class derived from this class.
bool isAnonymous() const
Definition classnode.h:60
bool hasCircularDerivedClasses(QStringList *cyclePath=nullptr) const
Checks if this class has circular derived class relationships by traversing its derived class hierarc...
FunctionNode * findOverriddenFunction(const FunctionNode *fn)
fn is an overriding function in this class or in a class derived from this class.
QString plainName() const override
Returns the display name for this class node.
HierarchyDirection
Definition classnode.h:63
bool hasCircularInheritance(QStringList *cyclePath=nullptr) const
Checks if this class has circular inheritance by traversing its base class hierarchy.
void removePrivateAndInternalBases()
Remove private and internal bases classes from this class's list of base classes.
void resolvePropertyOverriddenFromPtrs(PropertyNode *pn)
void addDerivedClass(Access access, ClassNode *node)
Adds the derived class node to this class's list of derived classes.
Definition classnode.cpp:42
void addUnresolvedBaseClass(Access access, const QStringList &path)
Add an unresolved base class to this class node's list of base classes.
Definition classnode.cpp:53
This node is used to represent any kind of function being documented.
This class describes one instance of using the Q_PROPERTY macro.
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static QDocDatabase * qdocDB()
Creates the singleton.
Combined button and popup list for selecting options.
The Node class is the base class for all the nodes in QDoc's parse tree.
bool isDontDocument() const
Returns true if this node's status is DontDocument.
Definition node.h:92
bool isPrivate() const
Returns true if this node's access is Private.
Definition node.h:111
virtual bool isInternal() const
Returns true if the node's status is Internal, or if its parent is a class with Internal status.
Definition node.cpp:862
NodeType nodeType() const override
Returns this node's type.
Definition node.h:82
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:231
A struct for indicating that a ClassNode is related in some way to another ClassNode.
RelatedClass(Access access, ClassNode *node)
This is the constructor used when the related class has been resolved.