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