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 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | /*! |
14 | \class ClassNode |
15 | \brief The ClassNode represents a C++ class. |
16 | |
17 | It is also used to represent a C++ struct or union. There are some |
18 | actual uses for structs, but I don't think any unions have been |
19 | documented yet. |
20 | */ |
21 | |
22 | /*! |
23 | Adds the base class \a node to this class's list of base |
24 | classes. The base class has the specified \a access. This |
25 | is a resolved base class. |
26 | */ |
27 | void ClassNode::addResolvedBaseClass(Access access, ClassNode *node) |
28 | { |
29 | m_bases.append(t: RelatedClass(access, node)); |
30 | node->m_derived.append(t: RelatedClass(access, this)); |
31 | } |
32 | |
33 | /*! |
34 | Adds the derived class \a node to this class's list of derived |
35 | classes. The derived class inherits this class with \a access. |
36 | */ |
37 | void ClassNode::addDerivedClass(Access access, ClassNode *node) |
38 | { |
39 | m_derived.append(t: RelatedClass(access, node)); |
40 | } |
41 | |
42 | /*! |
43 | Add an unresolved base class to this class node's list of |
44 | base classes. The unresolved base class will be resolved |
45 | before the generate phase of qdoc. In an unresolved base |
46 | class, the pointer to the base class node is 0. |
47 | */ |
48 | void ClassNode::addUnresolvedBaseClass(Access access, const QStringList &path) |
49 | { |
50 | m_bases.append(t: RelatedClass(access, path)); |
51 | } |
52 | |
53 | /*! |
54 | Search the child list to find the property node with the |
55 | specified \a name. |
56 | */ |
57 | PropertyNode *ClassNode::findPropertyNode(const QString &name) |
58 | { |
59 | Node *n = findNonfunctionChild(name, &Node::isProperty); |
60 | |
61 | if (n) |
62 | return static_cast<PropertyNode *>(n); |
63 | |
64 | PropertyNode *pn = nullptr; |
65 | |
66 | const QList<RelatedClass> &bases = baseClasses(); |
67 | if (!bases.isEmpty()) { |
68 | for (const RelatedClass &base : bases) { |
69 | ClassNode *cn = base.m_node; |
70 | if (cn) { |
71 | pn = cn->findPropertyNode(name); |
72 | if (pn) |
73 | break; |
74 | } |
75 | } |
76 | } |
77 | const QList<RelatedClass> &ignoredBases = ignoredBaseClasses(); |
78 | if (!ignoredBases.isEmpty()) { |
79 | for (const RelatedClass &base : ignoredBases) { |
80 | ClassNode *cn = base.m_node; |
81 | if (cn) { |
82 | pn = cn->findPropertyNode(name); |
83 | if (pn) |
84 | break; |
85 | } |
86 | } |
87 | } |
88 | |
89 | return pn; |
90 | } |
91 | |
92 | /*! |
93 | This function does a recursive search of this class node's |
94 | base classes looking for one that has a QML element. If it |
95 | finds one, it returns the pointer to that QML element. If |
96 | it doesn't find one, it returns null. |
97 | */ |
98 | QmlTypeNode *ClassNode::findQmlBaseNode() |
99 | { |
100 | QmlTypeNode *result = nullptr; |
101 | const QList<RelatedClass> &bases = baseClasses(); |
102 | |
103 | if (!bases.isEmpty()) { |
104 | for (const RelatedClass &base : bases) { |
105 | ClassNode *cn = base.m_node; |
106 | if (cn && cn->qmlElement()) { |
107 | return cn->qmlElement(); |
108 | } |
109 | } |
110 | for (const RelatedClass &base : bases) { |
111 | ClassNode *cn = base.m_node; |
112 | if (cn) { |
113 | result = cn->findQmlBaseNode(); |
114 | if (result != nullptr) { |
115 | return result; |
116 | } |
117 | } |
118 | } |
119 | } |
120 | return result; |
121 | } |
122 | |
123 | /*! |
124 | \a fn is an overriding function in this class or in a class |
125 | derived from this class. Find the node for the function that |
126 | \a fn overrides in this class's children or in one of this |
127 | class's base classes. Return a pointer to the overridden |
128 | function or return 0. |
129 | |
130 | This should be revised because clang provides the path to the |
131 | overridden function. mws 15/12/2018 |
132 | */ |
133 | FunctionNode *ClassNode::findOverriddenFunction(const FunctionNode *fn) |
134 | { |
135 | for (auto &bc : m_bases) { |
136 | ClassNode *cn = bc.m_node; |
137 | if (cn == nullptr) { |
138 | cn = QDocDatabase::qdocDB()->findClassNode(path: bc.m_path); |
139 | bc.m_node = cn; |
140 | } |
141 | if (cn != nullptr) { |
142 | FunctionNode *result = cn->findFunctionChild(clone: fn); |
143 | if (result != nullptr && !result->isInternal() && !result->isNonvirtual() |
144 | && result->hasDoc()) |
145 | return result; |
146 | result = cn->findOverriddenFunction(fn); |
147 | if (result != nullptr && !result->isNonvirtual()) |
148 | return result; |
149 | } |
150 | } |
151 | return nullptr; |
152 | } |
153 | |
154 | /*! |
155 | \a fn is an overriding function in this class or in a class |
156 | derived from this class. Find the node for the property that |
157 | \a fn overrides in this class's children or in one of this |
158 | class's base classes. Return a pointer to the overridden |
159 | property or return 0. |
160 | */ |
161 | PropertyNode *ClassNode::findOverriddenProperty(const FunctionNode *fn) |
162 | { |
163 | for (auto &baseClass : m_bases) { |
164 | ClassNode *cn = baseClass.m_node; |
165 | if (cn == nullptr) { |
166 | cn = QDocDatabase::qdocDB()->findClassNode(path: baseClass.m_path); |
167 | baseClass.m_node = cn; |
168 | } |
169 | if (cn != nullptr) { |
170 | const NodeList &children = cn->childNodes(); |
171 | for (const auto &child : children) { |
172 | if (child->isProperty()) { |
173 | auto *pn = static_cast<PropertyNode *>(child); |
174 | if (pn->name() == fn->name() || pn->hasAccessFunction(name: fn->name())) { |
175 | if (pn->hasDoc()) |
176 | return pn; |
177 | } |
178 | } |
179 | } |
180 | PropertyNode *result = cn->findOverriddenProperty(fn); |
181 | if (result != nullptr) |
182 | return result; |
183 | } |
184 | } |
185 | return nullptr; |
186 | } |
187 | |
188 | /*! |
189 | Returns true if the class or struct represented by this class |
190 | node must be documented. If this function returns true, then |
191 | qdoc must find a qdoc comment for this class. If it returns |
192 | false, then the class need not be documented. |
193 | */ |
194 | bool ClassNode::docMustBeGenerated() const |
195 | { |
196 | if (!hasDoc() || isPrivate() || isInternal() || isDontDocument()) |
197 | return false; |
198 | if (declLocation().fileName().endsWith(s: QLatin1String("_p.h" )) && !hasDoc()) |
199 | return false; |
200 | |
201 | return true; |
202 | } |
203 | |
204 | /*! |
205 | A base class of this class node was private or internal. |
206 | That node's list of \a bases is traversed in this function. |
207 | Each of its public base classes is promoted to be a base |
208 | class of this node for documentation purposes. For each |
209 | private or internal class node in \a bases, this function |
210 | is called recursively with the list of base classes from |
211 | that private or internal class node. |
212 | */ |
213 | void ClassNode::promotePublicBases(const QList<RelatedClass> &bases) |
214 | { |
215 | if (!bases.isEmpty()) { |
216 | for (qsizetype i = bases.size() - 1; i >= 0; --i) { |
217 | ClassNode *bc = bases.at(i).m_node; |
218 | if (bc == nullptr) |
219 | bc = QDocDatabase::qdocDB()->findClassNode(path: bases.at(i).m_path); |
220 | if (bc != nullptr) { |
221 | if (bc->isPrivate() || bc->isInternal()) |
222 | promotePublicBases(bases: bc->baseClasses()); |
223 | else |
224 | m_bases.append(t: bases.at(i)); |
225 | } |
226 | } |
227 | } |
228 | } |
229 | |
230 | /*! |
231 | Remove private and internal bases classes from this class's list |
232 | of base classes. When a base class is removed from the list, add |
233 | its base classes to this class's list of base classes. |
234 | */ |
235 | void ClassNode::removePrivateAndInternalBases() |
236 | { |
237 | int i; |
238 | i = 0; |
239 | QSet<ClassNode *> found; |
240 | |
241 | // Remove private and duplicate base classes. |
242 | while (i < m_bases.size()) { |
243 | ClassNode *bc = m_bases.at(i).m_node; |
244 | if (bc == nullptr) |
245 | bc = QDocDatabase::qdocDB()->findClassNode(path: m_bases.at(i).m_path); |
246 | if (bc != nullptr |
247 | && (bc->isPrivate() || bc->isInternal() || bc->isDontDocument() |
248 | || found.contains(value: bc))) { |
249 | RelatedClass rc = m_bases.at(i); |
250 | m_bases.removeAt(i); |
251 | m_ignoredBases.append(t: rc); |
252 | promotePublicBases(bases: bc->baseClasses()); |
253 | } else { |
254 | ++i; |
255 | } |
256 | found.insert(value: bc); |
257 | } |
258 | |
259 | i = 0; |
260 | while (i < m_derived.size()) { |
261 | ClassNode *dc = m_derived.at(i).m_node; |
262 | if (dc != nullptr && (dc->isPrivate() || dc->isInternal() || dc->isDontDocument())) { |
263 | m_derived.removeAt(i); |
264 | const QList<RelatedClass> &dd = dc->derivedClasses(); |
265 | for (qsizetype j = dd.size() - 1; j >= 0; --j) |
266 | m_derived.insert(i, t: dd.at(i: j)); |
267 | } else { |
268 | ++i; |
269 | } |
270 | } |
271 | } |
272 | |
273 | /*! |
274 | */ |
275 | void ClassNode::resolvePropertyOverriddenFromPtrs(PropertyNode *pn) |
276 | { |
277 | for (const auto &baseClass : std::as_const(t&: baseClasses())) { |
278 | ClassNode *cn = baseClass.m_node; |
279 | if (cn) { |
280 | Node *n = cn->findNonfunctionChild(name: pn->name(), &Node::isProperty); |
281 | if (n) { |
282 | auto *baseProperty = static_cast<PropertyNode *>(n); |
283 | cn->resolvePropertyOverriddenFromPtrs(pn: baseProperty); |
284 | pn->setOverriddenFrom(baseProperty); |
285 | } else |
286 | cn->resolvePropertyOverriddenFromPtrs(pn); |
287 | } |
288 | } |
289 | } |
290 | |
291 | QT_END_NAMESPACE |
292 | |