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
cppcodemarker.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
5
6#include "access.h"
7#include "enumnode.h"
8#include "functionnode.h"
9#include "genustypes.h"
10#include "namespacenode.h"
11#include "propertynode.h"
13#include "text.h"
14#include "tree.h"
15#include "typedefnode.h"
16#include "variablenode.h"
17
18#include <QtCore/qdebug.h>
19#include <QtCore/qregularexpression.h>
20
22
23using namespace Qt::StringLiterals;
24
25/*!
26 Returns \c true.
27 */
28bool CppCodeMarker::recognizeCode(const QString & /* code */)
29{
30 return true;
31}
32
33/*!
34 Returns \c true if \a ext is any of a list of file extensions
35 for the C++ language.
36 */
37bool CppCodeMarker::recognizeExtension(const QString &extension)
38{
39 QByteArray ext = extension.toLatin1();
40 return ext == "c" || ext == "c++" || ext == "qdoc" || ext == "qtt" || ext == "qtx"
41 || ext == "cc" || ext == "cpp" || ext == "cxx" || ext == "ch" || ext == "h"
42 || ext == "h++" || ext == "hh" || ext == "hpp" || ext == "hxx";
43}
44
45/*!
46 Returns \c true if \a lang is either "C" or "Cpp".
47 */
48bool CppCodeMarker::recognizeLanguage(const QString &lang)
49{
50 return lang == QLatin1String("C") || lang == QLatin1String("Cpp");
51}
52
53/*!
54 Returns the type of atom used to represent C++ code in the documentation.
55*/
57{
58 return Atom::Code;
59}
60
61QString CppCodeMarker::markedUpCode(const QString &code, const Node *relative,
62 const Location &location)
63{
64 return addMarkUp(code, relative, location);
65}
66
67QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relative */,
68 Section::Style style)
69{
70 const int MaxEnumValues = 6;
71 const FunctionNode *func;
72 const VariableNode *variable;
73 const EnumNode *enume;
74 QString synopsis;
75 QString name;
76
77 name = taggedNode(node);
78 if (style != Section::Details)
79 name = linkTag(node, name);
80 name = "<@name>" + name + "</@name>";
81
82 if (style == Section::Details) {
83 if (!node->isRelatedNonmember() && !node->isProxyNode() && !node->parent()->name().isEmpty()
84 && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
85 name.prepend(taggedNode(node->parent()) + "::");
86 }
87 }
88
89 switch (node->nodeType()) {
91 case NodeType::Class:
93 case NodeType::Union:
94 synopsis = Node::nodeTypeString(node->nodeType());
95 synopsis += QLatin1Char(' ') + name;
96 break;
98 func = (const FunctionNode *)node;
99 if (style == Section::Details) {
100 auto templateDecl = node->templateDecl();
101 if (templateDecl)
102 synopsis = protect((*templateDecl).to_qstring()) + QLatin1Char(' ');
103 }
104 if (style != Section::AllMembers && !func->returnType().isEmpty())
105 synopsis += typified(func->returnTypeString(), true);
106 synopsis += name;
107 if (!func->isMacroWithoutParams()) {
108 synopsis += QLatin1Char('(');
109 if (!func->parameters().isEmpty()) {
110 const Parameters &parameters = func->parameters();
111 for (int i = 0; i < parameters.count(); ++i) {
112 if (i > 0)
113 synopsis += ", ";
114 QString name = parameters.at(i).name();
115 QString type = parameters.at(i).type();
116 QString value = parameters.at(i).defaultValue();
117 bool trailingSpace = style != Section::AllMembers && !name.isEmpty();
118 synopsis += typified(type, trailingSpace);
119 if (style != Section::AllMembers && !name.isEmpty())
120 synopsis += "<@param>" + protect(name) + "</@param>";
121 if (style != Section::AllMembers && !value.isEmpty())
122 synopsis += " = " + protect(value);
123 }
124 }
125 synopsis += QLatin1Char(')');
126 }
127 if (func->isConst())
128 synopsis += " const";
129
130 if (style == Section::Summary || style == Section::Accessors) {
131 if (!func->isNonvirtual())
132 synopsis.prepend("virtual ");
133 if (func->isFinal())
134 synopsis.append(" final");
135 if (func->isOverride())
136 synopsis.append(" override");
137 if (func->isPureVirtual())
138 synopsis.append(" = 0");
139 if (func->isRef())
140 synopsis.append(" &");
141 else if (func->isRefRef())
142 synopsis.append(" &&");
143 } else if (style == Section::AllMembers) {
144 if (!func->returnType().isEmpty() && func->returnType() != "void")
145 synopsis += " : " + typified(func->returnTypeString());
146 } else {
147 if (func->isRef())
148 synopsis.append(" &");
149 else if (func->isRefRef())
150 synopsis.append(" &&");
151 }
152 break;
153 case NodeType::Enum:
154 enume = static_cast<const EnumNode *>(node);
155 synopsis = "enum";
156 if (enume->isScoped())
157 synopsis += " class";
158 if (!enume->isAnonymous())
159 synopsis += " %1"_L1.arg(name);
160 else if (style != Section::Details)
161 synopsis = linkTag(node, synopsis); // Unnamed enum: Make `enum` a link to details
162 if (style == Section::Summary) {
163 synopsis += " { ";
164
165 QStringList documentedItems = enume->doc().enumItemNames();
166 if (documentedItems.isEmpty()) {
167 const auto &enumItems = enume->items();
168 for (const auto &item : enumItems)
169 documentedItems << item.name();
170 }
171 const QStringList omitItems = enume->doc().omitEnumItemNames();
172 for (const auto &item : omitItems)
173 documentedItems.removeAll(item);
174
175 if (documentedItems.size() > MaxEnumValues) {
176 // Take the last element and keep it safe, then elide the surplus.
177 const QString last = documentedItems.last();
178 documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
179 documentedItems += "&hellip;";
180 documentedItems += last;
181 }
182 synopsis += documentedItems.join(QLatin1String(", "));
183
184 if (!documentedItems.isEmpty())
185 synopsis += QLatin1Char(' ');
186 synopsis += QLatin1Char('}');
187 }
188 break;
190 if (style == Section::Details) {
191 auto templateDecl = node->templateDecl();
192 if (templateDecl)
193 synopsis += protect((*templateDecl).to_qstring()) + QLatin1Char(' ');
194 }
195 synopsis += name;
196 break;
198 if (static_cast<const TypedefNode *>(node)->associatedEnum())
199 synopsis = "flags ";
200 synopsis += name;
201 break;
202 case NodeType::Property: {
203 auto property = static_cast<const PropertyNode *>(node);
204 synopsis = name + " : " + typified(property->qualifiedDataType());
205 break;
206 }
208 auto property = static_cast<const QmlPropertyNode *>(node);
209 synopsis = name + " : " + typified(property->dataType());
210 break;
211 }
213 variable = static_cast<const VariableNode *>(node);
214 if (style == Section::AllMembers) {
215 synopsis = name + " : " + typified(variable->dataType());
216 } else {
217 synopsis = typified(variable->leftType(), true) + name + protect(variable->rightType());
218 }
219 break;
220 default:
221 synopsis = std::move(name);
222 }
223
224 QString extra = CodeMarker::extraSynopsis(node, style);
225 if (!extra.isEmpty()) {
226 extra.prepend(u"<@extra>"_s);
227 extra.append(u"</@extra> "_s);
228 }
229
230 return extra + synopsis;
231}
232
233/*!
234 */
235QString CppCodeMarker::markedUpQmlItem(const Node *node, bool summary)
236{
237 QString name = taggedQmlNode(node);
238 QString synopsis;
239
240 if (summary) {
241 name = linkTag(node, name);
242 } else if (node->isQmlProperty()) {
243 const auto *pn = static_cast<const QmlPropertyNode *>(node);
244 if (pn->isAttached())
245 name.prepend(pn->element() + QLatin1Char('.'));
246 }
247 name = "<@name>" + name + "</@name>";
248 if (node->isQmlProperty()) {
249 const auto *pn = static_cast<const QmlPropertyNode *>(node);
250 synopsis = name + " : " + typified(pn->dataType());
251 } else if (node->isFunction(Genus::QML)) {
252 const auto *func = static_cast<const FunctionNode *>(node);
253 if (!func->returnType().isEmpty())
254 synopsis = typified(func->returnTypeString(), true) + name;
255 else
256 synopsis = name;
257 synopsis += QLatin1Char('(');
258 if (!func->parameters().isEmpty()) {
259 const Parameters &parameters = func->parameters();
260 for (int i = 0; i < parameters.count(); ++i) {
261 if (i > 0)
262 synopsis += ", ";
263 QString name = parameters.at(i).name();
264 QString type = parameters.at(i).type();
265 QString paramName;
266 if (!name.isEmpty()) {
267 synopsis += typified(type, true);
268 paramName = std::move(name);
269 } else {
270 paramName = std::move(type);
271 }
272 synopsis += "<@param>" + protect(paramName) + "</@param>";
273 }
274 }
275 synopsis += QLatin1Char(')');
276 } else {
277 synopsis = std::move(name);
278 }
279
280 QString extra = CodeMarker::extraSynopsis(node, summary ? Section::Summary : Section::Details);
281 if (!extra.isEmpty()) {
282 extra.prepend(u" <@extra>"_s);
283 extra.append(u"</@extra>"_s);
284 }
285
286 return synopsis + extra;
287}
288
290{
291 QString name = linkTag(node, taggedNode(node));
292 if (node->isFunction() && !node->isMacro())
293 name += "()";
294 return name;
295}
296
297QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, const Node *relative)
298{
299 const auto *node = relative->parent();
300
301 const NativeEnum *nativeEnum{nullptr};
302 if (auto *ne_if = dynamic_cast<const NativeEnumInterface *>(relative))
303 nativeEnum = ne_if->nativeEnum();
304
305 if (nativeEnum && nativeEnum->enumNode()
306 && !enumValue.startsWith("%1."_L1.arg(nativeEnum->prefix())))
307 return "%1<@op>.</@op>%2"_L1.arg(nativeEnum->prefix(), enumValue);
308
309 if (!relative->isEnumType()) {
310 return enumValue;
311 }
312
313 QStringList parts;
314 while (!node->isHeader() && node->parent()) {
315 parts.prepend(markedUpName(node));
316 if (node->parent() == relative || node->parent()->name().isEmpty())
317 break;
318 node = node->parent();
319 }
320 if (static_cast<const EnumNode *>(relative)->isScoped())
321 parts.append(relative->name());
322
323 parts.append(enumValue);
324 const auto &delim = (relative->genus() == Genus::QML) ? "."_L1 : "::"_L1;
325 return parts.join("<@op>%1</@op>"_L1.arg(delim));
326}
327
328QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
329 const Location & /* location */)
330{
331 static QSet<QString> types{
332 QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"),
333 QLatin1String("float"), QLatin1String("int"), QLatin1String("long"),
334 QLatin1String("short"), QLatin1String("signed"), QLatin1String("unsigned"),
335 QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"),
336 QLatin1String("uchar"), QLatin1String("void"), QLatin1String("qlonglong"),
337 QLatin1String("qulonglong"), QLatin1String("qint"), QLatin1String("qint8"),
338 QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
339 QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"),
340 QLatin1String("quint32"), QLatin1String("quint64"), QLatin1String("qreal"),
341 QLatin1String("cond")
342 };
343
344 static QSet<QString> keywords{
345 QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"), QLatin1String("auto"),
346 QLatin1String("bitand"), QLatin1String("bitor"), QLatin1String("break"),
347 QLatin1String("case"), QLatin1String("catch"), QLatin1String("class"),
348 QLatin1String("compl"), QLatin1String("const"), QLatin1String("const_cast"),
349 QLatin1String("continue"), QLatin1String("default"), QLatin1String("delete"),
350 QLatin1String("do"), QLatin1String("dynamic_cast"), QLatin1String("else"),
351 QLatin1String("enum"), QLatin1String("explicit"), QLatin1String("export"),
352 QLatin1String("extern"), QLatin1String("false"), QLatin1String("for"),
353 QLatin1String("friend"), QLatin1String("goto"), QLatin1String("if"),
354 QLatin1String("include"), QLatin1String("inline"), QLatin1String("monitor"),
355 QLatin1String("mutable"), QLatin1String("namespace"), QLatin1String("new"),
356 QLatin1String("not"), QLatin1String("not_eq"), QLatin1String("operator"),
357 QLatin1String("or"), QLatin1String("or_eq"), QLatin1String("private"),
358 QLatin1String("protected"), QLatin1String("public"), QLatin1String("register"),
359 QLatin1String("reinterpret_cast"), QLatin1String("return"), QLatin1String("sizeof"),
360 QLatin1String("static"), QLatin1String("static_cast"), QLatin1String("struct"),
361 QLatin1String("switch"), QLatin1String("template"), QLatin1String("this"),
362 QLatin1String("throw"), QLatin1String("true"), QLatin1String("try"),
363 QLatin1String("typedef"), QLatin1String("typeid"), QLatin1String("typename"),
364 QLatin1String("union"), QLatin1String("using"), QLatin1String("virtual"),
365 QLatin1String("volatile"), QLatin1String("wchar_t"), QLatin1String("while"),
366 QLatin1String("xor"), QLatin1String("xor_eq"), QLatin1String("synchronized"),
367 // Qt specific
368 QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
369 };
370
371 QString code = in;
372 QString out;
373 QStringView text;
374 int braceDepth = 0;
375 int parenDepth = 0;
376 int i = 0;
377 int start = 0;
378 int finish = 0;
379 QChar ch;
380 static const QRegularExpression classRegExp(QRegularExpression::anchoredPattern("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)"));
381 static const QRegularExpression functionRegExp(QRegularExpression::anchoredPattern("q([A-Z][a-z]+)+"));
382 static const QRegularExpression findFunctionRegExp(QStringLiteral("^\\s*\\‍("));
383 bool atEOF = false;
384
385 auto readChar = [&]() {
386 if (i < code.size())
387 ch = code[i++];
388 else
389 atEOF = true;
390 };
391
392 readChar();
393 while (!atEOF) {
394 QString tag;
395 bool target = false;
396
397 if (ch.isLetter() || ch == '_') {
398 QString ident;
399 do {
400 ident += ch;
401 finish = i;
402 readChar();
403 } while (!atEOF && (ch.isLetterOrNumber() || ch == '_'));
404
405 if (classRegExp.match(ident).hasMatch()) {
406 tag = QStringLiteral("type");
407 } else if (functionRegExp.match(ident).hasMatch()) {
408 tag = QStringLiteral("func");
409 target = true;
410 } else if (types.contains(ident)) {
411 tag = QStringLiteral("type");
412 } else if (keywords.contains(ident)) {
413 tag = QStringLiteral("keyword");
414 } else if (braceDepth == 0 && parenDepth == 0) {
415 if (code.indexOf(findFunctionRegExp, i - 1) == i - 1)
416 tag = QStringLiteral("func");
417 target = true;
418 }
419 } else if (ch.isDigit()) {
420 do {
421 finish = i;
422 readChar();
423 } while (!atEOF && (ch.isLetterOrNumber() || ch == '.' || ch == '\''));
424 tag = QStringLiteral("number");
425 } else {
426 switch (ch.unicode()) {
427 case '+':
428 case '-':
429 case '!':
430 case '%':
431 case '^':
432 case '&':
433 case '*':
434 case ',':
435 case '.':
436 case '<':
437 case '=':
438 case '>':
439 case '?':
440 case '[':
441 case ']':
442 case '|':
443 case '~':
444 finish = i;
445 readChar();
446 tag = QStringLiteral("op");
447 break;
448 case '"':
449 finish = i;
450 readChar();
451
452 while (!atEOF && ch != '"') {
453 if (ch == '\\')
454 readChar();
455 readChar();
456 }
457 finish = i;
458 readChar();
459 tag = QStringLiteral("string");
460 break;
461 case '#':
462 finish = i;
463 readChar();
464 while (!atEOF && ch != '\n') {
465 if (ch == '\\')
466 readChar();
467 finish = i;
468 readChar();
469 }
470 tag = QStringLiteral("preprocessor");
471 break;
472 case '\'':
473 finish = i;
474 readChar();
475
476 while (!atEOF && ch != '\'') {
477 if (ch == '\\')
478 readChar();
479 readChar();
480 }
481 finish = i;
482 readChar();
483 tag = QStringLiteral("char");
484 break;
485 case '(':
486 finish = i;
487 readChar();
488 ++parenDepth;
489 break;
490 case ')':
491 finish = i;
492 readChar();
493 --parenDepth;
494 break;
495 case ':':
496 finish = i;
497 readChar();
498 if (!atEOF && ch == ':') {
499 finish = i;
500 readChar();
501 tag = QStringLiteral("op");
502 }
503 break;
504 case '/':
505 finish = i;
506 readChar();
507 if (!atEOF && ch == '/') {
508 do {
509 finish = i;
510 readChar();
511 } while (!atEOF && ch != '\n');
512 tag = QStringLiteral("comment");
513 } else if (ch == '*') {
514 bool metAster = false;
515 bool metAsterSlash = false;
516
517 finish = i;
518 readChar();
519
520 while (!metAsterSlash) {
521 if (atEOF)
522 break;
523 if (ch == '*')
524 metAster = true;
525 else if (metAster && ch == '/')
526 metAsterSlash = true;
527 else
528 metAster = false;
529 finish = i;
530 readChar();
531 }
532 tag = QStringLiteral("comment");
533 } else {
534 tag = QStringLiteral("op");
535 }
536 break;
537 case '{':
538 finish = i;
539 readChar();
540 braceDepth++;
541 break;
542 case '}':
543 finish = i;
544 readChar();
545 braceDepth--;
546 break;
547 default:
548 finish = i;
549 readChar();
550 }
551 }
552
553 text = QStringView{code}.mid(start, finish - start);
554 start = finish;
555
556 if (!tag.isEmpty()) {
557 out += QStringLiteral("<@");
558 out += tag;
559 if (target) {
560 out += QStringLiteral(" target=\"");
561 out += text;
562 out += QStringLiteral("()\"");
563 }
564 out += QStringLiteral(">");
565 }
566
567 appendProtectedString(&out, text);
568
569 if (!tag.isEmpty()) {
570 out += QStringLiteral("</@");
571 out += tag;
572 out += QStringLiteral(">");
573 }
574 }
575
576 if (start < code.size()) {
577 appendProtectedString(&out, QStringView{code}.mid(start));
578 }
579
580 return out;
581}
582
583QT_END_NAMESPACE
The Atom class is the fundamental unit for representing documents internally.
Definition atom.h:19
AtomType
\value AnnotatedList \value AutoLink \value BaseName \value BriefLeft \value BriefRight \value C \val...
Definition atom.h:21
@ Code
Definition atom.h:31
QString markedUpQmlItem(const Node *node, bool summary) override
bool recognizeExtension(const QString &ext) override
Returns true if ext is any of a list of file extensions for the C++ language.
QString markedUpName(const Node *node) override
QString markedUpEnumValue(const QString &enumValue, const Node *relative) override
Atom::AtomType atomType() const override
Returns the type of atom used to represent C++ code in the documentation.
bool recognizeLanguage(const QString &lang) override
Returns true if lang is either "C" or "Cpp".
QString markedUpSynopsis(const Node *node, const Node *relative, Section::Style style) override
QString markedUpCode(const QString &code, const Node *relative, const Location &location) override
bool recognizeCode(const QString &code) override
Returns true.
bool isScoped() const
Definition enumnode.h:33
This node is used to represent any kind of function being documented.
bool isMacroWithoutParams() const
bool isOverride() const
const Parameters & parameters() const
bool isPureVirtual() const override
bool isRef() const
bool isNonvirtual() const
bool isConst() const
bool isRefRef() const
bool isFinal() const
The Location class provides a way to mark a location in a file.
Definition location.h:20
Interface implemented by Node subclasses that can refer to a C++ enum.
Definition nativeenum.h:28
virtual const NativeEnum * nativeEnum() const =0
Encapsulates information about native (C++) enum values.
Definition nativeenum.h:14
This class describes one instance of using the Q_PROPERTY macro.
bool isAttached() const override
Returns true if the QML property or QML method node is marked as attached.
A class for containing the elements of one documentation section.
Definition sections.h:17
@ Summary
Definition sections.h:19
@ Details
Definition sections.h:19
@ Accessors
Definition sections.h:19
@ AllMembers
Definition sections.h:19
const EnumNode * associatedEnum() const
Definition typedefnode.h:27
NodeType
Definition genustypes.h:150
The Node class is the base class for all the nodes in QDoc's parse tree.
const Doc & doc() const
Returns a reference to the node's Doc data member.
Definition node.h:242
bool isQmlNode() const
Returns true if this node's Genus value is QML.
Definition node.h:126
bool isHeader() const
Returns true if the node type is HeaderFile.
Definition node.h:112
NodeType nodeType() const override
Returns this node's type.
Definition node.h:89
virtual bool isMacro() const
returns true if either FunctionNode::isMacroWithParams() or FunctionNode::isMacroWithoutParams() retu...
Definition node.h:154
bool isEnumType() const
Returns true if the node type is Enum.
Definition node.h:100
Aggregate * parent() const
Returns the node's parent pointer.
Definition node.h:215
bool isProxyNode() const
Returns true if the node type is Proxy.
Definition node.h:120
bool isFunction(Genus g=Genus::DontCare) const
Returns true if this is a FunctionNode and its Genus is set to g.
Definition node.h:107
bool isProperty() const
Returns true if the node type is Property.
Definition node.h:119
bool isRelatedNonmember() const
Returns true if this is a related nonmember of something.
Definition node.h:129
bool isQmlProperty() const
Returns true if the node type is QmlProperty.
Definition node.h:127
A class for parsing and managing a function parameter list.
Definition main.cpp:28
bool isEmpty() const
Definition parameters.h:70
const Parameter & at(int i) const
Definition parameters.h:74
int count() const
Definition parameters.h:72