summaryrefslogtreecommitdiffstats
path: root/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp')
-rw-r--r--src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp594
1 files changed, 594 insertions, 0 deletions
diff --git a/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp b/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
new file mode 100644
index 000000000..7fb26db0c
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/cppcodemarker.cpp
@@ -0,0 +1,594 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "cppcodemarker.h"
+
+#include "access.h"
+#include "enumnode.h"
+#include "functionnode.h"
+#include "namespacenode.h"
+#include "propertynode.h"
+#include "qmlpropertynode.h"
+#include "text.h"
+#include "tree.h"
+#include "typedefnode.h"
+#include "variablenode.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qregularexpression.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+/*!
+ Returns \c true.
+ */
+bool CppCodeMarker::recognizeCode(const QString & /* code */)
+{
+ return true;
+}
+
+/*!
+ Returns \c true if \a ext is any of a list of file extensions
+ for the C++ language.
+ */
+bool CppCodeMarker::recognizeExtension(const QString &extension)
+{
+ QByteArray ext = extension.toLatin1();
+ return ext == "c" || ext == "c++" || ext == "qdoc" || ext == "qtt" || ext == "qtx"
+ || ext == "cc" || ext == "cpp" || ext == "cxx" || ext == "ch" || ext == "h"
+ || ext == "h++" || ext == "hh" || ext == "hpp" || ext == "hxx";
+}
+
+/*!
+ Returns \c true if \a lang is either "C" or "Cpp".
+ */
+bool CppCodeMarker::recognizeLanguage(const QString &lang)
+{
+ return lang == QLatin1String("C") || lang == QLatin1String("Cpp");
+}
+
+/*!
+ Returns the type of atom used to represent C++ code in the documentation.
+*/
+Atom::AtomType CppCodeMarker::atomType() const
+{
+ return Atom::Code;
+}
+
+QString CppCodeMarker::markedUpCode(const QString &code, const Node *relative,
+ const Location &location)
+{
+ return addMarkUp(code, relative, location);
+}
+
+QString CppCodeMarker::markedUpSynopsis(const Node *node, const Node * /* relative */,
+ Section::Style style)
+{
+ const int MaxEnumValues = 6;
+ const FunctionNode *func;
+ const VariableNode *variable;
+ const EnumNode *enume;
+ QString synopsis;
+ QString name;
+
+ name = taggedNode(node);
+ if (style != Section::Details)
+ name = linkTag(node, name);
+ name = "<@name>" + name + "</@name>";
+
+ if (style == Section::Details) {
+ if (!node->isRelatedNonmember() && !node->isProxyNode() && !node->parent()->name().isEmpty()
+ && !node->parent()->isHeader() && !node->isProperty() && !node->isQmlNode()) {
+ name.prepend(taggedNode(node->parent()) + "::");
+ }
+ }
+
+ switch (node->nodeType()) {
+ case Node::Namespace:
+ case Node::Class:
+ case Node::Struct:
+ case Node::Union:
+ synopsis = Node::nodeTypeString(node->nodeType());
+ synopsis += QLatin1Char(' ') + name;
+ break;
+ case Node::Function:
+ func = (const FunctionNode *)node;
+ if (style == Section::Details) {
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ synopsis = protect((*templateDecl).to_qstring()) + QLatin1Char(' ');
+ }
+ if (style != Section::AllMembers && !func->returnType().isEmpty())
+ synopsis += typified(func->returnType(), true);
+ synopsis += name;
+ if (!func->isMacroWithoutParams()) {
+ synopsis += QLatin1Char('(');
+ if (!func->parameters().isEmpty()) {
+ const Parameters &parameters = func->parameters();
+ for (int i = 0; i < parameters.count(); ++i) {
+ if (i > 0)
+ synopsis += ", ";
+ QString name = parameters.at(i).name();
+ QString type = parameters.at(i).type();
+ QString value = parameters.at(i).defaultValue();
+ bool trailingSpace = style != Section::AllMembers && !name.isEmpty();
+ synopsis += typified(type, trailingSpace);
+ if (style != Section::AllMembers && !name.isEmpty())
+ synopsis += "<@param>" + protect(name) + "</@param>";
+ if (style != Section::AllMembers && !value.isEmpty())
+ synopsis += " = " + protect(value);
+ }
+ }
+ synopsis += QLatin1Char(')');
+ }
+ if (func->isConst())
+ synopsis += " const";
+
+ if (style == Section::Summary || style == Section::Accessors) {
+ if (!func->isNonvirtual())
+ synopsis.prepend("virtual ");
+ if (func->isFinal())
+ synopsis.append(" final");
+ if (func->isOverride())
+ synopsis.append(" override");
+ if (func->isPureVirtual())
+ synopsis.append(" = 0");
+ if (func->isRef())
+ synopsis.append(" &");
+ else if (func->isRefRef())
+ synopsis.append(" &&");
+ } else if (style == Section::AllMembers) {
+ if (!func->returnType().isEmpty() && func->returnType() != "void")
+ synopsis += " : " + typified(func->returnType());
+ } else {
+ if (func->isRef())
+ synopsis.append(" &");
+ else if (func->isRefRef())
+ synopsis.append(" &&");
+ }
+ break;
+ case Node::Enum:
+ enume = static_cast<const EnumNode *>(node);
+ synopsis = "enum ";
+ if (enume->isScoped())
+ synopsis += "class ";
+ synopsis += name;
+ if (style == Section::Summary) {
+ synopsis += " { ";
+
+ QStringList documentedItems = enume->doc().enumItemNames();
+ if (documentedItems.isEmpty()) {
+ const auto &enumItems = enume->items();
+ for (const auto &item : enumItems)
+ documentedItems << item.name();
+ }
+ const QStringList omitItems = enume->doc().omitEnumItemNames();
+ for (const auto &item : omitItems)
+ documentedItems.removeAll(item);
+
+ if (documentedItems.size() > MaxEnumValues) {
+ // Take the last element and keep it safe, then elide the surplus.
+ const QString last = documentedItems.last();
+ documentedItems = documentedItems.mid(0, MaxEnumValues - 1);
+ documentedItems += "&hellip;";
+ documentedItems += last;
+ }
+ synopsis += documentedItems.join(QLatin1String(", "));
+
+ if (!documentedItems.isEmpty())
+ synopsis += QLatin1Char(' ');
+ synopsis += QLatin1Char('}');
+ }
+ break;
+ case Node::TypeAlias:
+ if (style == Section::Details) {
+ auto templateDecl = node->templateDecl();
+ if (templateDecl)
+ synopsis += protect((*templateDecl).to_qstring()) + QLatin1Char(' ');
+ }
+ synopsis += name;
+ break;
+ case Node::Typedef:
+ if (static_cast<const TypedefNode *>(node)->associatedEnum())
+ synopsis = "flags ";
+ synopsis += name;
+ break;
+ case Node::Property: {
+ auto property = static_cast<const PropertyNode *>(node);
+ synopsis = name + " : " + typified(property->qualifiedDataType());
+ break;
+ }
+ case Node::QmlProperty: {
+ auto property = static_cast<const QmlPropertyNode *>(node);
+ synopsis = name + " : " + typified(property->dataType());
+ break;
+ }
+ case Node::Variable:
+ variable = static_cast<const VariableNode *>(node);
+ if (style == Section::AllMembers) {
+ synopsis = name + " : " + typified(variable->dataType());
+ } else {
+ synopsis = typified(variable->leftType(), true) + name + protect(variable->rightType());
+ }
+ break;
+ default:
+ synopsis = name;
+ }
+
+ QString extra = CodeMarker::extraSynopsis(node, style);
+ if (!extra.isEmpty()) {
+ extra.prepend(u"<@extra>"_s);
+ extra.append(u"</@extra> "_s);
+ }
+
+ return extra + synopsis;
+}
+
+/*!
+ */
+QString CppCodeMarker::markedUpQmlItem(const Node *node, bool summary)
+{
+ QString name = taggedQmlNode(node);
+ QString synopsis;
+
+ if (summary) {
+ name = linkTag(node, name);
+ } else if (node->isQmlProperty()) {
+ const auto *pn = static_cast<const QmlPropertyNode *>(node);
+ if (pn->isAttached())
+ name.prepend(pn->element() + QLatin1Char('.'));
+ }
+ name = "<@name>" + name + "</@name>";
+ if (node->isQmlProperty()) {
+ const auto *pn = static_cast<const QmlPropertyNode *>(node);
+ synopsis = name + " : " + typified(pn->dataType());
+ } else if (node->isFunction(Node::QML)) {
+ const auto *func = static_cast<const FunctionNode *>(node);
+ if (!func->returnType().isEmpty())
+ synopsis = typified(func->returnType(), true) + name;
+ else
+ synopsis = name;
+ synopsis += QLatin1Char('(');
+ if (!func->parameters().isEmpty()) {
+ const Parameters &parameters = func->parameters();
+ for (int i = 0; i < parameters.count(); ++i) {
+ if (i > 0)
+ synopsis += ", ";
+ QString name = parameters.at(i).name();
+ QString type = parameters.at(i).type();
+ QString paramName;
+ if (!name.isEmpty()) {
+ synopsis += typified(type, true);
+ paramName = name;
+ } else {
+ paramName = type;
+ }
+ synopsis += "<@param>" + protect(paramName) + "</@param>";
+ }
+ }
+ synopsis += QLatin1Char(')');
+ } else {
+ synopsis = name;
+ }
+
+ QString extra = CodeMarker::extraSynopsis(node, summary ? Section::Summary : Section::Details);
+ if (!extra.isEmpty()) {
+ extra.prepend(u" <@extra>"_s);
+ extra.append(u"</@extra>"_s);
+ }
+
+ return synopsis + extra;
+}
+
+QString CppCodeMarker::markedUpName(const Node *node)
+{
+ QString name = linkTag(node, taggedNode(node));
+ if (node->isFunction() && !node->isMacro())
+ name += "()";
+ return name;
+}
+
+QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, const Node *relative)
+{
+ const auto *node = relative->parent();
+
+ if (relative->isQmlProperty()) {
+ const auto *qpn = static_cast<const QmlPropertyNode*>(relative);
+ if (qpn->enumNode() && !enumValue.startsWith("%1."_L1.arg(qpn->enumPrefix())))
+ return "%1<@op>.</@op>%2"_L1.arg(qpn->enumPrefix(), enumValue);
+ }
+
+ if (!relative->isEnumType()) {
+ return enumValue;
+ }
+
+ QStringList parts;
+ while (!node->isHeader() && node->parent()) {
+ parts.prepend(markedUpName(node));
+ if (node->parent() == relative || node->parent()->name().isEmpty())
+ break;
+ node = node->parent();
+ }
+ if (static_cast<const EnumNode *>(relative)->isScoped())
+ parts.append(relative->name());
+
+ parts.append(enumValue);
+ return parts.join(QLatin1String("<@op>::</@op>"));
+}
+
+QString CppCodeMarker::markedUpInclude(const QString &include)
+{
+ return "<@preprocessor>#include &lt;<@headerfile>" + include + "</@headerfile>&gt;</@preprocessor>";
+}
+
+/*
+ @char
+ @class
+ @comment
+ @function
+ @keyword
+ @number
+ @op
+ @preprocessor
+ @string
+ @type
+*/
+
+QString CppCodeMarker::addMarkUp(const QString &in, const Node * /* relative */,
+ const Location & /* location */)
+{
+ static QSet<QString> types{
+ QLatin1String("bool"), QLatin1String("char"), QLatin1String("double"),
+ QLatin1String("float"), QLatin1String("int"), QLatin1String("long"),
+ QLatin1String("short"), QLatin1String("signed"), QLatin1String("unsigned"),
+ QLatin1String("uint"), QLatin1String("ulong"), QLatin1String("ushort"),
+ QLatin1String("uchar"), QLatin1String("void"), QLatin1String("qlonglong"),
+ QLatin1String("qulonglong"), QLatin1String("qint"), QLatin1String("qint8"),
+ QLatin1String("qint16"), QLatin1String("qint32"), QLatin1String("qint64"),
+ QLatin1String("quint"), QLatin1String("quint8"), QLatin1String("quint16"),
+ QLatin1String("quint32"), QLatin1String("quint64"), QLatin1String("qreal"),
+ QLatin1String("cond")
+ };
+
+ static QSet<QString> keywords{
+ QLatin1String("and"), QLatin1String("and_eq"), QLatin1String("asm"), QLatin1String("auto"),
+ QLatin1String("bitand"), QLatin1String("bitor"), QLatin1String("break"),
+ QLatin1String("case"), QLatin1String("catch"), QLatin1String("class"),
+ QLatin1String("compl"), QLatin1String("const"), QLatin1String("const_cast"),
+ QLatin1String("continue"), QLatin1String("default"), QLatin1String("delete"),
+ QLatin1String("do"), QLatin1String("dynamic_cast"), QLatin1String("else"),
+ QLatin1String("enum"), QLatin1String("explicit"), QLatin1String("export"),
+ QLatin1String("extern"), QLatin1String("false"), QLatin1String("for"),
+ QLatin1String("friend"), QLatin1String("goto"), QLatin1String("if"),
+ QLatin1String("include"), QLatin1String("inline"), QLatin1String("monitor"),
+ QLatin1String("mutable"), QLatin1String("namespace"), QLatin1String("new"),
+ QLatin1String("not"), QLatin1String("not_eq"), QLatin1String("operator"),
+ QLatin1String("or"), QLatin1String("or_eq"), QLatin1String("private"),
+ QLatin1String("protected"), QLatin1String("public"), QLatin1String("register"),
+ QLatin1String("reinterpret_cast"), QLatin1String("return"), QLatin1String("sizeof"),
+ QLatin1String("static"), QLatin1String("static_cast"), QLatin1String("struct"),
+ QLatin1String("switch"), QLatin1String("template"), QLatin1String("this"),
+ QLatin1String("throw"), QLatin1String("true"), QLatin1String("try"),
+ QLatin1String("typedef"), QLatin1String("typeid"), QLatin1String("typename"),
+ QLatin1String("union"), QLatin1String("using"), QLatin1String("virtual"),
+ QLatin1String("volatile"), QLatin1String("wchar_t"), QLatin1String("while"),
+ QLatin1String("xor"), QLatin1String("xor_eq"), QLatin1String("synchronized"),
+ // Qt specific
+ QLatin1String("signals"), QLatin1String("slots"), QLatin1String("emit")
+ };
+
+ QString code = in;
+ QString out;
+ QStringView text;
+ int braceDepth = 0;
+ int parenDepth = 0;
+ int i = 0;
+ int start = 0;
+ int finish = 0;
+ QChar ch;
+ static const QRegularExpression classRegExp(QRegularExpression::anchoredPattern("Qt?(?:[A-Z3]+[a-z][A-Za-z]*|t)"));
+ static const QRegularExpression functionRegExp(QRegularExpression::anchoredPattern("q([A-Z][a-z]+)+"));
+ static const QRegularExpression findFunctionRegExp(QStringLiteral("^\\s*\\("));
+ bool atEOF = false;
+
+ auto readChar = [&]() {
+ if (i < code.size())
+ ch = code[i++];
+ else
+ atEOF = true;
+ };
+
+ readChar();
+ while (!atEOF) {
+ QString tag;
+ bool target = false;
+
+ if (ch.isLetter() || ch == '_') {
+ QString ident;
+ do {
+ ident += ch;
+ finish = i;
+ readChar();
+ } while (!atEOF && (ch.isLetterOrNumber() || ch == '_'));
+
+ if (classRegExp.match(ident).hasMatch()) {
+ tag = QStringLiteral("type");
+ } else if (functionRegExp.match(ident).hasMatch()) {
+ tag = QStringLiteral("func");
+ target = true;
+ } else if (types.contains(ident)) {
+ tag = QStringLiteral("type");
+ } else if (keywords.contains(ident)) {
+ tag = QStringLiteral("keyword");
+ } else if (braceDepth == 0 && parenDepth == 0) {
+ if (code.indexOf(findFunctionRegExp, i - 1) == i - 1)
+ tag = QStringLiteral("func");
+ target = true;
+ }
+ } else if (ch.isDigit()) {
+ do {
+ finish = i;
+ readChar();
+ } while (!atEOF && (ch.isLetterOrNumber() || ch == '.' || ch == '\''));
+ tag = QStringLiteral("number");
+ } else {
+ switch (ch.unicode()) {
+ case '+':
+ case '-':
+ case '!':
+ case '%':
+ case '^':
+ case '&':
+ case '*':
+ case ',':
+ case '.':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '[':
+ case ']':
+ case '|':
+ case '~':
+ finish = i;
+ readChar();
+ tag = QStringLiteral("op");
+ break;
+ case '"':
+ finish = i;
+ readChar();
+
+ while (!atEOF && ch != '"') {
+ if (ch == '\\')
+ readChar();
+ readChar();
+ }
+ finish = i;
+ readChar();
+ tag = QStringLiteral("string");
+ break;
+ case '#':
+ finish = i;
+ readChar();
+ while (!atEOF && ch != '\n') {
+ if (ch == '\\')
+ readChar();
+ finish = i;
+ readChar();
+ }
+ tag = QStringLiteral("preprocessor");
+ break;
+ case '\'':
+ finish = i;
+ readChar();
+
+ while (!atEOF && ch != '\'') {
+ if (ch == '\\')
+ readChar();
+ readChar();
+ }
+ finish = i;
+ readChar();
+ tag = QStringLiteral("char");
+ break;
+ case '(':
+ finish = i;
+ readChar();
+ ++parenDepth;
+ break;
+ case ')':
+ finish = i;
+ readChar();
+ --parenDepth;
+ break;
+ case ':':
+ finish = i;
+ readChar();
+ if (!atEOF && ch == ':') {
+ finish = i;
+ readChar();
+ tag = QStringLiteral("op");
+ }
+ break;
+ case '/':
+ finish = i;
+ readChar();
+ if (!atEOF && ch == '/') {
+ do {
+ finish = i;
+ readChar();
+ } while (!atEOF && ch != '\n');
+ tag = QStringLiteral("comment");
+ } else if (ch == '*') {
+ bool metAster = false;
+ bool metAsterSlash = false;
+
+ finish = i;
+ readChar();
+
+ while (!metAsterSlash) {
+ if (atEOF)
+ break;
+ if (ch == '*')
+ metAster = true;
+ else if (metAster && ch == '/')
+ metAsterSlash = true;
+ else
+ metAster = false;
+ finish = i;
+ readChar();
+ }
+ tag = QStringLiteral("comment");
+ } else {
+ tag = QStringLiteral("op");
+ }
+ break;
+ case '{':
+ finish = i;
+ readChar();
+ braceDepth++;
+ break;
+ case '}':
+ finish = i;
+ readChar();
+ braceDepth--;
+ break;
+ default:
+ finish = i;
+ readChar();
+ }
+ }
+
+ text = QStringView{code}.mid(start, finish - start);
+ start = finish;
+
+ if (!tag.isEmpty()) {
+ out += QStringLiteral("<@");
+ out += tag;
+ if (target) {
+ out += QStringLiteral(" target=\"");
+ out += text;
+ out += QStringLiteral("()\"");
+ }
+ out += QStringLiteral(">");
+ }
+
+ appendProtectedString(&out, text);
+
+ if (!tag.isEmpty()) {
+ out += QStringLiteral("</@");
+ out += tag;
+ out += QStringLiteral(">");
+ }
+ }
+
+ if (start < code.size()) {
+ appendProtectedString(&out, QStringView{code}.mid(start));
+ }
+
+ return out;
+}
+
+QT_END_NAMESPACE