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 "functionnode.h" |
5 | |
6 | #include "propertynode.h" |
7 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | /*! |
11 | \class FunctionNode |
12 | |
13 | This node is used to represent any kind of function being |
14 | documented. It can represent a C++ class member function, a C++ |
15 | global function, a QML method, or a macro, with or without |
16 | parameters. |
17 | |
18 | A C++ function can be a signal a slot, a constructor of any |
19 | kind, a destructor, a copy or move assignment operator, or |
20 | just a plain old member function or global function. |
21 | |
22 | A QML method can be a plain old method, or a |
23 | signal or signal handler. |
24 | |
25 | If the function is not an overload, its overload flag is |
26 | false. If it is an overload, its overload flag is true. |
27 | If it is not an overload but it has overloads, its next |
28 | overload pointer will point to an overload function. If it |
29 | is an overload function, its overload flag is true, and it |
30 | may or may not have a non-null next overload pointer. |
31 | |
32 | So all the overloads of a function are in a linked list |
33 | using the next overload pointer. If a function has no |
34 | overloads, its overload flag is false and its overload |
35 | pointer is null. |
36 | |
37 | The function node also has an overload number. If the |
38 | node's overload flag is set, this overload number is |
39 | positive; otherwise, the overload number is 0. |
40 | */ |
41 | |
42 | /*! |
43 | Construct a function node for a C++ function. It's parent |
44 | is \a parent, and it's name is \a name. |
45 | |
46 | \note The function node's overload flag is set to false, and |
47 | its overload number is set to 0. These data members are set |
48 | in normalizeOverloads(), when all the overloads are known. |
49 | */ |
50 | FunctionNode::FunctionNode(Aggregate *parent, const QString &name) |
51 | : Node(Function, parent, name), |
52 | m_const(false), |
53 | m_default(false), |
54 | m_static(false), |
55 | m_reimpFlag(false), |
56 | m_attached(false), |
57 | m_overloadFlag(false), |
58 | m_isFinal(false), |
59 | m_isOverride(false), |
60 | m_isRef(false), |
61 | m_isRefRef(false), |
62 | m_isInvokable(false), |
63 | m_explicit{false}, |
64 | m_constexpr{false}, |
65 | m_metaness(Plain), |
66 | m_virtualness(NonVirtual), |
67 | m_overloadNumber(0), |
68 | m_nextOverload(nullptr) |
69 | { |
70 | // nothing |
71 | } |
72 | |
73 | /*! |
74 | Construct a function node for a QML method or signal, specified |
75 | by ther Metaness value \a type. It's parent is \a parent, and |
76 | it's name is \a name. If \a attached is true, it is an attached |
77 | method or signal. |
78 | |
79 | \note The function node's overload flag is set to false, and |
80 | its overload number is set to 0. These data members are set |
81 | in normalizeOverloads(), when all the overloads are known. |
82 | */ |
83 | FunctionNode::FunctionNode(Metaness kind, Aggregate *parent, const QString &name, bool attached) |
84 | : Node(Function, parent, name), |
85 | m_const(false), |
86 | m_default(false), |
87 | m_static(false), |
88 | m_reimpFlag(false), |
89 | m_attached(attached), |
90 | m_overloadFlag(false), |
91 | m_isFinal(false), |
92 | m_isOverride(false), |
93 | m_isRef(false), |
94 | m_isRefRef(false), |
95 | m_isInvokable(false), |
96 | m_explicit{false}, |
97 | m_constexpr{false}, |
98 | m_metaness(kind), |
99 | m_virtualness(NonVirtual), |
100 | m_overloadNumber(0), |
101 | m_nextOverload(nullptr) |
102 | { |
103 | setGenus(getGenus(metaness: m_metaness)); |
104 | if (!isCppNode() && name.startsWith(s: "__" )) |
105 | setStatus(Internal); |
106 | } |
107 | |
108 | /*! |
109 | Clone this node on the heap and make the clone a child of |
110 | \a parent. Return the pointer to the clone. |
111 | */ |
112 | Node *FunctionNode::clone(Aggregate *parent) |
113 | { |
114 | auto *fn = new FunctionNode(*this); // shallow copy |
115 | fn->setParent(nullptr); |
116 | fn->setNextOverload(nullptr); |
117 | parent->addChild(child: fn); |
118 | return fn; |
119 | } |
120 | |
121 | /*! |
122 | Returns this function's virtualness value as a string |
123 | for use as an attribute value in index files. |
124 | */ |
125 | QString FunctionNode::virtualness() const |
126 | { |
127 | switch (m_virtualness) { |
128 | case FunctionNode::NormalVirtual: |
129 | return QLatin1String("virtual" ); |
130 | case FunctionNode::PureVirtual: |
131 | return QLatin1String("pure" ); |
132 | case FunctionNode::NonVirtual: |
133 | default: |
134 | break; |
135 | } |
136 | return QLatin1String("non" ); |
137 | } |
138 | |
139 | /*! |
140 | Sets the function node's virtualness value based on the value |
141 | of string \a value, which is the value of the function's \e{virtual} |
142 | attribute in an index file. If \a value is \e{pure}, and if the |
143 | parent() is a C++ class, set the parent's \e abstract flag to |
144 | \c {true}. |
145 | */ |
146 | void FunctionNode::setVirtualness(const QString &value) |
147 | { |
148 | if (value == QLatin1String("pure" )) { |
149 | m_virtualness = PureVirtual; |
150 | if (parent() && parent()->isClassNode()) |
151 | parent()->setAbstract(true); |
152 | return; |
153 | } |
154 | |
155 | m_virtualness = (value == QLatin1String("virtual" )) ? NormalVirtual : NonVirtual; |
156 | } |
157 | |
158 | static QMap<QString, FunctionNode::Metaness> metanessMap_; |
159 | static void buildMetanessMap() |
160 | { |
161 | metanessMap_["plain" ] = FunctionNode::Plain; |
162 | metanessMap_["signal" ] = FunctionNode::Signal; |
163 | metanessMap_["slot" ] = FunctionNode::Slot; |
164 | metanessMap_["constructor" ] = FunctionNode::Ctor; |
165 | metanessMap_["copy-constructor" ] = FunctionNode::CCtor; |
166 | metanessMap_["move-constructor" ] = FunctionNode::MCtor; |
167 | metanessMap_["destructor" ] = FunctionNode::Dtor; |
168 | metanessMap_["macro" ] = FunctionNode::MacroWithParams; |
169 | metanessMap_["macrowithparams" ] = FunctionNode::MacroWithParams; |
170 | metanessMap_["macrowithoutparams" ] = FunctionNode::MacroWithoutParams; |
171 | metanessMap_["copy-assign" ] = FunctionNode::CAssign; |
172 | metanessMap_["move-assign" ] = FunctionNode::MAssign; |
173 | metanessMap_["native" ] = FunctionNode::Native; |
174 | metanessMap_["qmlsignal" ] = FunctionNode::QmlSignal; |
175 | metanessMap_["qmlsignalhandler" ] = FunctionNode::QmlSignalHandler; |
176 | metanessMap_["qmlmethod" ] = FunctionNode::QmlMethod; |
177 | } |
178 | |
179 | static QMap<QString, FunctionNode::Metaness> topicMetanessMap_; |
180 | static void buildTopicMetanessMap() |
181 | { |
182 | topicMetanessMap_["fn" ] = FunctionNode::Plain; |
183 | topicMetanessMap_["qmlsignal" ] = FunctionNode::QmlSignal; |
184 | topicMetanessMap_["qmlattachedsignal" ] = FunctionNode::QmlSignal; |
185 | topicMetanessMap_["qmlmethod" ] = FunctionNode::QmlMethod; |
186 | topicMetanessMap_["qmlattachedmethod" ] = FunctionNode::QmlMethod; |
187 | } |
188 | |
189 | /*! |
190 | Determines the Genus value for this FunctionNode given the |
191 | Metaness value \a metaness. Returns the Genus value. \a metaness must be |
192 | one of the values of Metaness. If not, Node::DontCare is |
193 | returned. |
194 | */ |
195 | Node::Genus FunctionNode::getGenus(FunctionNode::Metaness metaness) |
196 | { |
197 | switch (metaness) { |
198 | case FunctionNode::Plain: |
199 | case FunctionNode::Signal: |
200 | case FunctionNode::Slot: |
201 | case FunctionNode::Ctor: |
202 | case FunctionNode::Dtor: |
203 | case FunctionNode::CCtor: |
204 | case FunctionNode::MCtor: |
205 | case FunctionNode::MacroWithParams: |
206 | case FunctionNode::MacroWithoutParams: |
207 | case FunctionNode::Native: |
208 | case FunctionNode::CAssign: |
209 | case FunctionNode::MAssign: |
210 | return Node::CPP; |
211 | case FunctionNode::QmlSignal: |
212 | case FunctionNode::QmlSignalHandler: |
213 | case FunctionNode::QmlMethod: |
214 | return Node::QML; |
215 | } |
216 | |
217 | return Node::DontCare; |
218 | } |
219 | |
220 | /*! |
221 | This static function converts the string \a value to an enum |
222 | value for the kind of function named by \a value. |
223 | */ |
224 | FunctionNode::Metaness FunctionNode::getMetaness(const QString &value) |
225 | { |
226 | if (metanessMap_.isEmpty()) |
227 | buildMetanessMap(); |
228 | return metanessMap_[value]; |
229 | } |
230 | |
231 | /*! |
232 | This static function converts the topic string \a topic to an enum |
233 | value for the kind of function this FunctionNode represents. |
234 | */ |
235 | FunctionNode::Metaness FunctionNode::getMetanessFromTopic(const QString &topic) |
236 | { |
237 | if (topicMetanessMap_.isEmpty()) |
238 | buildTopicMetanessMap(); |
239 | return topicMetanessMap_[topic]; |
240 | } |
241 | |
242 | /*! |
243 | Sets the function node's overload number to \a number. If \a number |
244 | is 0, the function node's overload flag is set to false. If |
245 | \a number is greater than 0, the overload flag is set to true. |
246 | */ |
247 | void FunctionNode::setOverloadNumber(signed short number) |
248 | { |
249 | m_overloadNumber = number; |
250 | m_overloadFlag = (number > 0); |
251 | } |
252 | |
253 | /*! |
254 | Appends \a functionNode to the linked list of overloads for this function. |
255 | |
256 | \note Although this function appends an overload function to the list of |
257 | overloads for this function's name, it does not set the function's |
258 | overload number or it's overload flag. If the function has the |
259 | \c{\\overload} in its QDoc comment, that will set the overload |
260 | flag. But qdoc treats the \c{\\overload} command as a hint that the |
261 | function should be documented as an overload. The hint is almost |
262 | always correct, but QDoc reserves the right to decide which function |
263 | should be the primary function and which functions are the overloads. |
264 | These decisions are made in Aggregate::normalizeOverloads(). |
265 | */ |
266 | void FunctionNode::appendOverload(FunctionNode *functionNode) |
267 | { |
268 | auto current = this; |
269 | while (current->m_nextOverload) |
270 | current = current->m_nextOverload; |
271 | current->m_nextOverload = functionNode; |
272 | functionNode->m_nextOverload = nullptr; |
273 | } |
274 | |
275 | /*! |
276 | Removes \a functionNode from the linked list of function overloads. |
277 | */ |
278 | void FunctionNode::removeOverload(FunctionNode *functionNode) |
279 | { |
280 | auto head = this; |
281 | auto **indirect = &head; |
282 | while ((*indirect) != functionNode) { |
283 | if (!(*indirect)->m_nextOverload) |
284 | return; |
285 | indirect = &(*indirect)->m_nextOverload; |
286 | } |
287 | *indirect = functionNode->m_nextOverload; |
288 | } |
289 | |
290 | /*! |
291 | Returns the primary function - the first function |
292 | from the linked list of overloads that is \e not |
293 | marked as an overload. If found, the primary function |
294 | is removed from the list and returned. Otherwise |
295 | returns \c nullptr. |
296 | */ |
297 | FunctionNode *FunctionNode::findPrimaryFunction() |
298 | { |
299 | auto current = this; |
300 | while (current->m_nextOverload && current->m_nextOverload->isOverload()) |
301 | current = current->m_nextOverload; |
302 | |
303 | auto primary = current->m_nextOverload; |
304 | if (primary) |
305 | current->m_nextOverload = primary->m_nextOverload; |
306 | |
307 | return primary; |
308 | } |
309 | |
310 | /*! |
311 | \fn void FunctionNode::setReimpFlag() |
312 | |
313 | Sets the function node's reimp flag to \c true, which means |
314 | the \e {\\reimp} command was used in the qdoc comment. It is |
315 | supposed to mean that the function reimplements a virtual |
316 | function in a base class. |
317 | */ |
318 | |
319 | /*! |
320 | Returns a string representing the kind of function this |
321 | Function node represents, which depends on the Metaness |
322 | value. |
323 | */ |
324 | QString FunctionNode::kindString() const |
325 | { |
326 | switch (m_metaness) { |
327 | case FunctionNode::QmlSignal: |
328 | return "QML signal" ; |
329 | case FunctionNode::QmlSignalHandler: |
330 | return "QML signal handler" ; |
331 | case FunctionNode::QmlMethod: |
332 | return "QML method" ; |
333 | default: |
334 | return "function" ; |
335 | } |
336 | } |
337 | |
338 | /*! |
339 | Returns a string representing the Metaness enum value for |
340 | this function. It is used in index files. |
341 | */ |
342 | QString FunctionNode::metanessString() const |
343 | { |
344 | switch (m_metaness) { |
345 | case FunctionNode::Plain: |
346 | return "plain" ; |
347 | case FunctionNode::Signal: |
348 | return "signal" ; |
349 | case FunctionNode::Slot: |
350 | return "slot" ; |
351 | case FunctionNode::Ctor: |
352 | return "constructor" ; |
353 | case FunctionNode::CCtor: |
354 | return "copy-constructor" ; |
355 | case FunctionNode::MCtor: |
356 | return "move-constructor" ; |
357 | case FunctionNode::Dtor: |
358 | return "destructor" ; |
359 | case FunctionNode::MacroWithParams: |
360 | return "macrowithparams" ; |
361 | case FunctionNode::MacroWithoutParams: |
362 | return "macrowithoutparams" ; |
363 | case FunctionNode::Native: |
364 | return "native" ; |
365 | case FunctionNode::CAssign: |
366 | return "copy-assign" ; |
367 | case FunctionNode::MAssign: |
368 | return "move-assign" ; |
369 | case FunctionNode::QmlSignal: |
370 | return "qmlsignal" ; |
371 | case FunctionNode::QmlSignalHandler: |
372 | return "qmlsignalhandler" ; |
373 | case FunctionNode::QmlMethod: |
374 | return "qmlmethod" ; |
375 | default: |
376 | return "plain" ; |
377 | } |
378 | } |
379 | |
380 | /*! |
381 | Adds the "associated" property \a p to this function node. |
382 | The function might be the setter or getter for a property, |
383 | for example. |
384 | */ |
385 | void FunctionNode::addAssociatedProperty(PropertyNode *p) |
386 | { |
387 | m_associatedProperties.append(t: p); |
388 | } |
389 | |
390 | /*! |
391 | \reimp |
392 | |
393 | Returns \c true if this is an access function for an obsolete property, |
394 | otherwise calls the base implementation of isDeprecated(). |
395 | */ |
396 | bool FunctionNode::isDeprecated() const |
397 | { |
398 | auto it = std::find_if_not(first: m_associatedProperties.begin(), last: m_associatedProperties.end(), |
399 | pred: [](const Node *p) -> bool { return p->isDeprecated(); }); |
400 | |
401 | if (!m_associatedProperties.isEmpty() && it == m_associatedProperties.end()) |
402 | return true; |
403 | |
404 | return Node::isDeprecated(); |
405 | } |
406 | |
407 | /*! \fn unsigned char FunctionNode::overloadNumber() const |
408 | Returns the overload number for this function. |
409 | */ |
410 | |
411 | /*! |
412 | Reconstructs and returns the function's signature. |
413 | |
414 | Specific parts of the signature are included according to |
415 | flags in \a options: |
416 | |
417 | \value Node::SignaturePlain |
418 | Plain signature |
419 | \value Node::SignatureDefaultValues |
420 | Include any default argument values |
421 | \value Node::SignatureReturnType |
422 | Include return type |
423 | \value Node::SignatureTemplateParams |
424 | Include \c {template <parameter_list>} if one exists |
425 | */ |
426 | QString FunctionNode::signature(Node::SignatureOptions options) const |
427 | { |
428 | QStringList elements; |
429 | |
430 | if (options & Node::SignatureTemplateParams) |
431 | elements << templateDecl(); |
432 | if (options & Node::SignatureReturnType) |
433 | elements << m_returnType; |
434 | elements.removeAll(t: QString()); |
435 | |
436 | if (!isMacroWithoutParams()) { |
437 | elements << name() + QLatin1Char('(') |
438 | + m_parameters.signature(includeValues: options & Node::SignatureDefaultValues) |
439 | + QLatin1Char(')'); |
440 | if (!isMacro()) { |
441 | if (isConst()) |
442 | elements << QStringLiteral("const" ); |
443 | if (isRef()) |
444 | elements << QStringLiteral("&" ); |
445 | else if (isRefRef()) |
446 | elements << QStringLiteral("&&" ); |
447 | } |
448 | } else { |
449 | elements << name(); |
450 | } |
451 | return elements.join(sep: QLatin1Char(' ')); |
452 | } |
453 | |
454 | /*! |
455 | Compares this FunctionNode to \a node. If \a sameParent is \c true, compares |
456 | also the parent of the two nodes. Returns \c true if they describe |
457 | the same function. |
458 | */ |
459 | bool FunctionNode::compare(const Node *node, bool sameParent) const |
460 | { |
461 | if (!node || !node->isFunction()) |
462 | return false; |
463 | |
464 | const auto *functionNode = static_cast<const FunctionNode *>(node); |
465 | if (metaness() != functionNode->metaness()) |
466 | return false; |
467 | if (sameParent && parent() != functionNode->parent()) |
468 | return false; |
469 | if (m_returnType != functionNode->returnType()) |
470 | return false; |
471 | if (isConst() != functionNode->isConst()) |
472 | return false; |
473 | if (isAttached() != functionNode->isAttached()) |
474 | return false; |
475 | const Parameters &p = functionNode->parameters(); |
476 | if (m_parameters.count() != p.count()) |
477 | return false; |
478 | if (!p.isEmpty()) { |
479 | for (int i = 0; i < p.count(); ++i) { |
480 | if (m_parameters.at(i).type() != p.at(i).type()) |
481 | return false; |
482 | } |
483 | } |
484 | return true; |
485 | } |
486 | |
487 | /*! |
488 | In some cases, it is ok for a public function to be not documented. |
489 | For example, the macro Q_OBJECT adds several functions to the API of |
490 | a class, but these functions are normally not meant to be documented. |
491 | So if a function node doesn't have documentation, then if its name is |
492 | in the list of functions that it is ok not to document, this function |
493 | returns true. Otherwise, it returns false. |
494 | |
495 | These are the member function names added by macros. Usually they |
496 | are not documented, but they can be documented, so this test avoids |
497 | reporting an error if they are not documented. |
498 | |
499 | But maybe we should generate a standard text for each of them? |
500 | */ |
501 | bool FunctionNode::isIgnored() const |
502 | { |
503 | if (!hasDoc() && !hasSharedDoc()) { |
504 | if (name().startsWith(s: QLatin1String("qt_" )) || name() == QLatin1String("metaObject" ) |
505 | || name() == QLatin1String("tr" ) || name() == QLatin1String("trUtf8" ) |
506 | || name() == QLatin1String("d_func" )) { |
507 | return true; |
508 | } |
509 | QString s = signature(options: Node::SignatureReturnType); |
510 | if (s.contains(s: QLatin1String("enum_type" )) && s.contains(s: QLatin1String("operator|" ))) |
511 | return true; |
512 | } |
513 | return false; |
514 | } |
515 | |
516 | /*! |
517 | Returns true if this function has overloads. Otherwise false. |
518 | First, if this function node's overload pointer is not nullptr, |
519 | return true. Next, if this function node's overload flag is true |
520 | return true. Finally, if this function's parent Aggregate has a |
521 | function by the same name as this one in its function map and |
522 | that function has overloads, return true. Otherwise return false. |
523 | |
524 | There is a failsafe way to test it under any circumstances. |
525 | */ |
526 | bool FunctionNode::hasOverloads() const |
527 | { |
528 | if (m_nextOverload != nullptr) |
529 | return true; |
530 | if (m_overloadFlag) |
531 | return true; |
532 | if (parent()) |
533 | return parent()->hasOverloads(fn: this); |
534 | return false; |
535 | } |
536 | |
537 | QT_END_NAMESPACE |
538 | |