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
8QT_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 */
50FunctionNode::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 */
83FunctionNode::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 */
112Node *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 */
125QString 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 */
146void 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
158static QMap<QString, FunctionNode::Metaness> metanessMap_;
159static 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
179static QMap<QString, FunctionNode::Metaness> topicMetanessMap_;
180static 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 */
195Node::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 */
224FunctionNode::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 */
235FunctionNode::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 */
247void 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 */
266void 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*/
278void 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 */
297FunctionNode *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 */
324QString 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 */
342QString 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 */
385void 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*/
396bool 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 */
426QString 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 */
459bool 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 */
501bool 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 */
526bool 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
537QT_END_NAMESPACE
538

source code of qttools/src/qdoc/qdoc/functionnode.cpp