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

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