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 // Respect existing prefixes in \value arguments of \qmlenum topic
310 if (relative->isEnumType(Genus::QML)
311 && enumValue.section(' ', 0, 0).contains('.'_L1)) {
312 return "<@op>%1</@op>"_L1.arg(enumValue);
313 }
314
315 if (!relative->isEnumType())
316 return enumValue;
317
318 QStringList parts;
319 while (!node->isHeader() && node->parent()) {
320 parts.prepend(markedUpName(node));
321 if (node->parent() == relative || node->parent()->name().isEmpty())
322 break;
323 node = node->parent();
324 }
325 if (static_cast<const EnumNode *>(relative)->isScoped())
326 parts.append(relative->name());
327
328 parts.append(enumValue);
329 const auto &delim = (relative->genus() == Genus::QML) ? "."_L1 : "::"_L1;
330 return parts.join("<@op>%1</@op>"_L1.arg(delim));
331}
332
333QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
334 const Location & /* location */)
335{
336 static QSet<QString> types{
337 QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"),
338 QLatin1String("float"), QLatin1String("int"), QLatin1String("long"),
339 QLatin1String("short"), QLatin1String("signed"), QLatin1String("unsigned"),
340 QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"),
341 QLatin1String("uchar"), QLatin1String("void"), QLatin1String("qlonglong"),
342 QLatin1String("qulonglong"), QLatin1String("qint"), QLatin1String("qint8"),
343 QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
344 QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"),
345 QLatin1String("quint32"), QLatin1String("quint64"), QLatin1String("qreal"),
346 QLatin1String("cond")
347 };
348
349 static QSet<QString> keywords{
350 QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"), QLatin1String("auto"),
351 QLatin1String("bitand"), QLatin1String("bitor"), QLatin1String("break"),
352 QLatin1String("case"), QLatin1String("catch"), QLatin1String("class"),
353 QLatin1String("compl"), QLatin1String("const"), QLatin1String("const_cast"),
354 QLatin1String("continue"), QLatin1String("default"), QLatin1String("delete"),
355 QLatin1String("do"), QLatin1String("dynamic_cast"), QLatin1String("else"),
356 QLatin1String("enum"), QLatin1String("explicit"), QLatin1String("export"),
357 QLatin1String("extern"), QLatin1String("false"), QLatin1String("for"),
358 QLatin1String("friend"), QLatin1String("goto"), QLatin1String("if"),
359 QLatin1String("include"), QLatin1String("inline"), QLatin1String("monitor"),
360 QLatin1String("mutable"), QLatin1String("namespace"), QLatin1String("new"),
361 QLatin1String("not"), QLatin1String("not_eq"), QLatin1String("operator"),
362 QLatin1String("or"), QLatin1String("or_eq"), QLatin1String("private"),
363 QLatin1String("protected"), QLatin1String("public"), QLatin1String("register"),
364 QLatin1String("reinterpret_cast"), QLatin1String("return"), QLatin1String("sizeof"),
365 QLatin1String("static"), QLatin1String("static_cast"), QLatin1String("struct"),
366 QLatin1String("switch"), QLatin1String("template"), QLatin1String("this"),
367 QLatin1String("throw"), QLatin1String("true"), QLatin1String("try"),
368 QLatin1String("typedef"), QLatin1String("typeid"), QLatin1String("typename"),
369 QLatin1String("union"), QLatin1String("using"), QLatin1String("virtual"),
370 QLatin1String("volatile"), QLatin1String("wchar_t"), QLatin1String("while"),
371 QLatin1String("xor"), QLatin1String("xor_eq"), QLatin1String("synchronized"),
372 // Qt specific
373 QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
374 };
375
376 QString code = in;
377 QString out;
378 QStringView text;
379 int braceDepth = 0;
380 int parenDepth = 0;
381 int i = 0;
382 int start = 0;
383 int finish = 0;
384 QChar ch;
385 static const QRegularExpression classRegExp(QRegularExpression::anchoredPattern("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)"));
386 static const QRegularExpression functionRegExp(QRegularExpression::anchoredPattern("q([A-Z][a-z]+)+"));
387 static const QRegularExpression findFunctionRegExp(QStringLiteral("^\\s*\\‍("));
388 bool atEOF = false;
389
390 auto readChar = [&]() {
391 if (i < code.size())
392 ch = code[i++];
393 else
394 atEOF = true;
395 };
396
397 readChar();
398 while (!atEOF) {
399 QString tag;
400 bool target = false;
401
402 if (ch.isLetter() || ch == '_') {
403 QString ident;
404 do {
405 ident += ch;
406 finish = i;
407 readChar();
408 } while (!atEOF && (ch.isLetterOrNumber() || ch == '_'));
409
410 if (classRegExp.match(ident).hasMatch()) {
411 tag = QStringLiteral("type");
412 } else if (functionRegExp.match(ident).hasMatch()) {
413 tag = QStringLiteral("func");
414 target = true;
415 } else if (types.contains(ident)) {
416 tag = QStringLiteral("type");
417 } else if (keywords.contains(ident)) {
418 tag = QStringLiteral("keyword");
419 } else if (braceDepth == 0 && parenDepth == 0) {
420 if (code.indexOf(findFunctionRegExp, i - 1) == i - 1)
421 tag = QStringLiteral("func");
422 target = true;
423 }
424 } else if (ch.isDigit()) {
425 do {
426 finish = i;
427 readChar();
428 } while (!atEOF && (ch.isLetterOrNumber() || ch == '.' || ch == '\''));
429 tag = QStringLiteral("number");
430 } else {
431 switch (ch.unicode()) {
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 case '?':
445 case '[':
446 case ']':
447 case '|':
448 case '~':
449 finish = i;
450 readChar();
451 tag = QStringLiteral("op");
452 break;
453 case '"':
454 finish = i;
455 readChar();
456
457 while (!atEOF && ch != '"') {
458 if (ch == '\\')
459 readChar();
460 readChar();
461 }
462 finish = i;
463 readChar();
464 tag = QStringLiteral("string");
465 break;
466 case '#':
467 finish = i;
468 readChar();
469 while (!atEOF && ch != '\n') {
470 if (ch == '\\')
471 readChar();
472 finish = i;
473 readChar();
474 }
475 tag = QStringLiteral("preprocessor");
476 break;
477 case '\'':
478 finish = i;
479 readChar();
480
481 while (!atEOF && ch != '\'') {
482 if (ch == '\\')
483 readChar();
484 readChar();
485 }
486 finish = i;
487 readChar();
488 tag = QStringLiteral("char");
489 break;
490 case '(':
491 finish = i;
492 readChar();
493 ++parenDepth;
494 break;
495 case ')':
496 finish = i;
497 readChar();
498 --parenDepth;
499 break;
500 case ':':
501 finish = i;
502 readChar();
503 if (!atEOF && ch == ':') {
504 finish = i;
505 readChar();
506 tag = QStringLiteral("op");
507 }
508 break;
509 case '/':
510 finish = i;
511 readChar();
512 if (!atEOF && ch == '/') {
513 do {
514 finish = i;
515 readChar();
516 } while (!atEOF && ch != '\n');
517 tag = QStringLiteral("comment");
518 } else if (ch == '*') {
519 bool metAster = false;
520 bool metAsterSlash = false;
521
522 finish = i;
523 readChar();
524
525 while (!metAsterSlash) {
526 if (atEOF)
527 break;
528 if (ch == '*')
529 metAster = true;
530 else if (metAster && ch == '/')
531 metAsterSlash = true;
532 else
533 metAster = false;
534 finish = i;
535 readChar();
536 }
537 tag = QStringLiteral("comment");
538 } else {
539 tag = QStringLiteral("op");
540 }
541 break;
542 case '{':
543 finish = i;
544 readChar();
545 braceDepth++;
546 break;
547 case '}':
548 finish = i;
549 readChar();
550 braceDepth--;
551 break;
552 default:
553 finish = i;
554 readChar();
555 }
556 }
557
558 text = QStringView{code}.mid(start, finish - start);
559 start = finish;
560
561 if (!tag.isEmpty()) {
562 out += QStringLiteral("<@");
563 out += tag;
564 if (target) {
565 out += QStringLiteral(" target=\"");
566 out += text;
567 out += QStringLiteral("()\"");
568 }
569 out += QStringLiteral(">");
570 }
571
572 appendProtectedString(&out, text);
573
574 if (!tag.isEmpty()) {
575 out += QStringLiteral("</@");
576 out += tag;
577 out += QStringLiteral(">");
578 }
579 }
580
581 if (start < code.size()) {
582 appendProtectedString(&out, QStringView{code}.mid(start));
583 }
584
585 return out;
586}
587
588QT_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
Combined button and popup list for selecting options.
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