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 Returns true if the class or struct represented by this class
164 node must be documented. If this function returns true, then
165 qdoc must find a qdoc comment for this class. If it returns
166 false, then the class need not be documented.
167 */
169{
170 if (!hasDoc() || isPrivate() || isInternal() || isDontDocument())
171 return false;
172 if (declLocation().fileName().endsWith("_p.h"_L1) && !hasDoc())
173 return false;
174
175 return true;
176}
177
178/*!
179 \brief Detects circular relationships in class hierarchies.
180
181 Traverses the hierarchy in the specified \a direction (Base or Derived),
182 checking only private/internal nodes to detect cycles that could cause
183 infinite loops during base/derived class promotion.
184
185 If \a cyclePath is provided, it will be filled with the names of
186 classes forming the cycle (e.g., ["A", "B", "C", "A"]).
187
188 Returns true if a cycle is detected.
189
190 \sa detectCycleRecursive().
191 */
192bool ClassNode::hasCircularRelationship(HierarchyDirection direction,
193 QStringList *cyclePath) const
194{
195 QMap<const ClassNode*, Color> colors;
196 QList<const ClassNode*> path;
197
198 bool cycleFound = detectCycleRecursive(direction, colors, path);
199
200 if (cycleFound && cyclePath) {
201 cyclePath->clear();
202
203 if (!path.isEmpty()) {
204 const ClassNode *repeatedNode = path.last();
205 auto first = path.indexOf(repeatedNode);
206 if (first != -1 && first < path.size() - 1)
207 path = path.mid(first);
208 }
209
210 for (const ClassNode *node : path)
211 cyclePath->append(node->name());
212 }
213
214 return cycleFound;
215}
216
217/*!
218 Recursive helper for cycle detection using three-color DFS.
219
220 Colors represent node states:
221 \list
222 \li White: Unvisited.
223 \li Gray: Currently being processed (on recursion stack).
224 \li Black: Fully processed (all descendants explored).
225 \endlist
226
227 Returns true if a cycle is detected. The \a path list will contain
228 the nodes that form the cycle.
229
230 \sa hasCircularRelationship().
231*/
232bool ClassNode::detectCycleRecursive(HierarchyDirection direction,
233 QMap<const ClassNode*, Color> &colors,
234 QList<const ClassNode*> &path) const
235{
236 colors[this] = Color::Gray;
237 path.append(this);
238
239 const QList<RelatedClass> &related = (direction == HierarchyDirection::Base)
240 ? baseClasses()
241 : derivedClasses();
242
243 for (const auto &relatedClass : related) {
244 const ClassNode *rc = relatedClass.m_node;
245 if (rc == nullptr)
246 rc = QDocDatabase::qdocDB()->findClassNode(relatedClass.m_path);
247
248 if (rc != nullptr && (rc->isPrivate() || rc->isInternal() || rc->isDontDocument())) {
249 if (rc == this) {
250 qCDebug(lcQdoc) << "Skipping self-reference (CRTP-like) in" << this->name();
251 continue;
252 }
253
254 Color rcColor = colors.value(rc, Color::White);
255
256 if (rcColor == Color::Gray) {
257 path.append(rc);
258 return true;
259 }
260
261 if (rcColor == Color::White) {
262 if (rc->detectCycleRecursive(direction, colors, path))
263 return true;
264 }
265 }
266 }
267
268 colors[this] = Color::Black;
269 path.removeLast();
270
271 return false;
272}
273
274/*!
275 Checks if this class has circular inheritance by traversing its
276 base class hierarchy. Returns true if a cycle is detected.
277
278 If \a cyclePath is provided, it will be filled with the names of
279 classes forming the cycle (e.g., ["A", "B", "C", "A"]).
280
281 This method is used to detect problematic inheritance patterns
282 before performing operations like base class promotion that could
283 infinite loop on circular hierarchies.
284
285 \sa hasCircularDerivedClasses(), hasCircularRelationship().
286 */
287bool ClassNode::hasCircularInheritance(QStringList *cyclePath) const
288{
289 return hasCircularRelationship(HierarchyDirection::Base, cyclePath);
290}
291
292/*!
293 Checks if this class has circular derived class relationships by
294 traversing its derived class hierarchy. Returns true if a cycle is detected.
295
296 If \a cyclePath is provided, it will be filled with the names of
297 classes forming the cycle.
298
299 This is similar to hasCircularInheritance() but checks the derived
300 direction instead of the base direction.
301
302 \sa hasCircularInheritance(), hasCircularRelationship.
303 */
304bool ClassNode::hasCircularDerivedClasses(QStringList *cyclePath) const
305{
306 return hasCircularRelationship(HierarchyDirection::Derived, cyclePath);
307}
308
309/*!
310 A base class of this class node was private or internal.
311 That node's list of \a bases is traversed in this function.
312 Each of its public base classes is promoted to be a base
313 class of this node for documentation purposes. For each
314 private or internal class node in \a bases, this function
315 is called recursively with the list of base classes from
316 that private or internal class node.
317 */
318void ClassNode::promotePublicBases(const QList<RelatedClass> &bases)
319{
320 if (!bases.isEmpty()) {
321 for (qsizetype i = bases.size() - 1; i >= 0; --i) {
322 ClassNode *bc = bases.at(i).m_node;
323 if (bc == nullptr)
324 bc = QDocDatabase::qdocDB()->findClassNode(bases.at(i).m_path);
325 if (bc != nullptr) {
326 if (bc->isPrivate() || bc->isInternal())
327 promotePublicBases(bc->baseClasses());
328 else
329 m_bases.append(bases.at(i));
330 }
331 }
332 }
333}
334
335/*!
336 Remove private and internal bases classes from this class's list
337 of base classes. When a base class is removed from the list, add
338 its base classes to this class's list of base classes.
339 */
341{
342 int i;
343 i = 0;
344 QSet<ClassNode *> found;
345
346 // Remove private and duplicate base classes.
347 while (i < m_bases.size()) {
348 ClassNode *bc = m_bases.at(i).m_node;
349 if (bc == nullptr)
350 bc = QDocDatabase::qdocDB()->findClassNode(m_bases.at(i).m_path);
351 if (bc != nullptr
352 && (bc->isPrivate() || bc->isInternal() || bc->isDontDocument()
353 || found.contains(bc))) {
354 RelatedClass rc = m_bases.at(i);
355 m_bases.removeAt(i);
356 m_ignoredBases.append(rc);
357 QStringList cyclePath;
358 if (bc->hasCircularInheritance(&cyclePath))
359 bc->location().warning("Circular inheritance detected: %1"_L1.arg(cyclePath.join(" -> ")));
360 else
361 promotePublicBases(bc->baseClasses());
362 } else {
363 ++i;
364 }
365 found.insert(bc);
366 }
367
368 i = 0;
369 while (i < m_derived.size()) {
370 ClassNode *dc = m_derived.at(i).m_node;
371 if (dc != nullptr && (dc->isPrivate() || dc->isInternal() || dc->isDontDocument())) {
372 QStringList derivedCyclePath;
373 if (dc->hasCircularDerivedClasses(&derivedCyclePath)) {
374 dc->location().warning(QStringLiteral("Circular derived class relationship detected: %1").arg(derivedCyclePath.join(" -> ")));
375 m_derived.removeAt(i);
376 } else {
377 m_derived.removeAt(i);
378 const QList<RelatedClass> &dd = dc->derivedClasses();
379 for (qsizetype j = dd.size() - 1; j >= 0; --j) {
380 // Skip CRTP self-references to avoid infinite loops
381 if (dd.at(j).m_node != dc) {
382 m_derived.insert(i, dd.at(j));
383 } else {
384 qCDebug(lcQdoc) << "Skipping CRTP self-reference in derived class promotion for"
385 << dc->name();
386 }
387 }
388 }
389 } else {
390 ++i;
391 }
392 }
393}
394
395/*!
396 */
398{
399 for (const auto &baseClass : std::as_const(baseClasses())) {
400 ClassNode *cn = baseClass.m_node;
401 if (cn) {
402 Node *n = cn->findNonfunctionChild(pn->name(), &Node::isProperty);
403 if (n) {
404 auto *baseProperty = static_cast<PropertyNode *>(n);
405 cn->resolvePropertyOverriddenFromPtrs(baseProperty);
406 pn->setOverriddenFrom(baseProperty);
407 } else
408 cn->resolvePropertyOverriddenFromPtrs(pn);
409 }
410 }
411}
412
413/*!
414 Returns the display name for this class node. For anonymous structs,
415 returns "(unnamed struct)" instead of the file-based unique identifier.
416*/
418{
419 if (isAnonymous()) {
420 switch (nodeType()) {
421 case NodeType::Struct:
422 return "(unnamed struct)"_L1;
423 case NodeType::Union:
424 return "(unnamed union)"_L1;
425 case NodeType::Class:
426 return "(unnamed class)"_L1;
427 default:
428 break;
429 }
430 }
431 return Node::plainName();
432}
433
434QT_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.
bool docMustBeGenerated() const override
Returns true if the class or struct represented by this class node must be documented.
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:99
bool isPrivate() const
Returns true if this node's access is Private.
Definition node.h:118
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:848
NodeType nodeType() const override
Returns this node's type.
Definition node.h:89
const Location & location() const
If this node's definition location is empty, this function returns this node's declaration location.
Definition node.h:238
bool hasDoc() const
Returns true if this node is documented, or it represents a documented node read from the index ('had...
Definition node.cpp:905
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.