summaryrefslogtreecommitdiffstats
path: root/src/qdoc/generator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/generator.cpp')
-rw-r--r--src/qdoc/generator.cpp2231
1 files changed, 0 insertions, 2231 deletions
diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp
deleted file mode 100644
index 8e9a8698c..000000000
--- a/src/qdoc/generator.cpp
+++ /dev/null
@@ -1,2231 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*
- generator.cpp
-*/
-#include "generator.h"
-
-#include "access.h"
-#include "aggregate.h"
-#include "classnode.h"
-#include "codemarker.h"
-#include "collectionnode.h"
-#include "config.h"
-#include "doc.h"
-#include "editdistance.h"
-#include "enumnode.h"
-#include "examplenode.h"
-#include "functionnode.h"
-#include "loggingcategory.h"
-#include "node.h"
-#include "openedlist.h"
-#include "propertynode.h"
-#include "qdocdatabase.h"
-#include "qmltypenode.h"
-#include "quoter.h"
-#include "separator.h"
-#include "sharedcommentnode.h"
-#include "tokenizer.h"
-#include "typedefnode.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qregularexpression.h>
-
-#ifndef QT_BOOTSTRAPPED
-# include "QtCore/qurl.h"
-#endif
-
-QT_BEGIN_NAMESPACE
-
-Generator *Generator::currentGenerator_;
-QStringList Generator::exampleDirs;
-QStringList Generator::exampleImgExts;
-QMap<QString, QMap<QString, QString>> Generator::fmtLeftMaps;
-QMap<QString, QMap<QString, QString>> Generator::fmtRightMaps;
-QList<Generator *> Generator::generators;
-QStringList Generator::imageDirs;
-QStringList Generator::imageFiles;
-QMap<QString, QStringList> Generator::imgFileExts;
-QString Generator::outDir_;
-QString Generator::outSubdir_;
-QStringList Generator::outFileNames_;
-QSet<QString> Generator::outputFormats;
-QHash<QString, QString> Generator::outputPrefixes;
-QHash<QString, QString> Generator::outputSuffixes;
-QString Generator::project_;
-bool Generator::noLinkErrors_ = false;
-bool Generator::autolinkErrors_ = false;
-bool Generator::redirectDocumentationToDevNull_ = false;
-bool Generator::useOutputSubdirs_ = true;
-QmlTypeNode *Generator::qmlTypeContext_ = nullptr;
-
-static QRegularExpression tag("</?@[^>]*>");
-static QLatin1String amp("&amp;");
-static QLatin1String gt("&gt;");
-static QLatin1String lt("&lt;");
-static QLatin1String quot("&quot;");
-
-/*!
- Constructs the generator base class. Prepends the newly
- constructed generator to the list of output generators.
- Sets a pointer to the QDoc database singleton, which is
- available to the generator subclasses.
- */
-Generator::Generator()
-{
- m_qdb = QDocDatabase::qdocDB();
- generators.prepend(this);
-}
-
-/*!
- Destroys the generator after removing it from the list of
- output generators.
- */
-Generator::~Generator()
-{
- generators.removeAll(this);
-}
-
-void Generator::appendFullName(Text &text, const Node *apparentNode, const Node *relative,
- const Node *actualNode)
-{
- if (actualNode == nullptr)
- actualNode = apparentNode;
- text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, apparentNode->plainFullName(relative))
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
-}
-
-void Generator::appendFullName(Text &text, const Node *apparentNode, const QString &fullName,
- const Node *actualNode)
-{
- if (actualNode == nullptr)
- actualNode = apparentNode;
- text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode))
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, fullName)
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
-}
-
-void Generator::appendFullNames(Text &text, const NodeList &nodes, const Node *relative)
-{
- int index = 0;
- for (const auto &node : nodes) {
- appendFullName(text, node, relative);
- text << comma(index++, nodes.count());
- }
-}
-
-/*!
- Append the signature for the function named in \a node to
- \a text, so that is is a link to the documentation for that
- function.
- */
-void Generator::appendSignature(Text &text, const Node *node)
-{
- text << Atom(Atom::LinkNode, CodeMarker::stringForNode(node))
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, node->signature(false, true))
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
-}
-
-/*!
- Generate a bullet list of function signatures. The function
- nodes are in \a nodes. It uses the \a relative node and the
- \a marker for the generation.
- */
-void Generator::signatureList(const NodeList &nodes, const Node *relative, CodeMarker *marker)
-{
- Text text;
- int count = 0;
- text << Atom(Atom::ListLeft, QString("bullet"));
- for (const auto &node : nodes) {
- text << Atom(Atom::ListItemNumber, QString::number(++count));
- text << Atom(Atom::ListItemLeft, QString("bullet"));
- appendSignature(text, node);
- text << Atom(Atom::ListItemRight, QString("bullet"));
- }
- text << Atom(Atom::ListRight, QString("bullet"));
- generateText(text, relative, marker);
-}
-
-int Generator::appendSortedNames(Text &text, const ClassNode *cn, const QList<RelatedClass> &rc)
-{
- QMap<QString, Text> classMap;
- for (const auto &relatedClass : rc) {
- ClassNode *rcn = relatedClass.m_node;
- if (rcn && rcn->isInAPI()) {
- Text className;
- appendFullName(className, rcn, cn);
- classMap[className.toString().toLower()] = className;
- }
- }
-
- int index = 0;
- const QStringList classNames = classMap.keys();
- for (const auto &className : classNames) {
- text << classMap[className];
- text << comma(index++, classNames.count());
- }
- return index;
-}
-
-int Generator::appendSortedQmlNames(Text &text, const Node *base, const NodeList &subs)
-{
- QMap<QString, Text> classMap;
-
- for (const auto sub : subs) {
- Text text;
- if (!base->isQtQuickNode() || !sub->isQtQuickNode()
- || (base->logicalModuleName() == sub->logicalModuleName())) {
- appendFullName(text, sub, base);
- classMap[text.toString().toLower()] = text;
- }
- }
-
- int index = 0;
- const QStringList names = classMap.keys();
- for (const auto &name : names) {
- text << classMap[name];
- text << comma(index++, names.count());
- }
- return index;
-}
-
-/*!
- Creates the file named \a fileName in the output directory
- and returns a QFile pointing to this file. In particular,
- this method deals with errors when opening the file:
- the returned QFile is always valid and can be written to.
-
- \sa beginFilePage()
- */
-QFile *Generator::openSubPageFile(const Node *node, const QString &fileName)
-{
- QString path = outputDir() + QLatin1Char('/');
- if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty()
- && !outputDir().endsWith(node->outputSubdirectory())) {
- path += node->outputSubdirectory() + QLatin1Char('/');
- }
- path += fileName;
-
- auto outPath = redirectDocumentationToDevNull_ ? QStringLiteral("/dev/null") : path;
- auto outFile = new QFile(outPath);
- if (!redirectDocumentationToDevNull_ && outFile->exists()) {
- node->location().error(QStringLiteral("Output file already exists; overwriting %1")
- .arg(outFile->fileName()));
- }
- if (!outFile->open(QFile::WriteOnly)) {
- node->location().fatal(
- QStringLiteral("Cannot open output file '%1'").arg(outFile->fileName()));
- }
- qCDebug(lcQdoc, "Writing: %s", qPrintable(path));
- outFileNames_ << fileName;
- return outFile;
-}
-
-/*!
- Creates the file named \a fileName in the output directory.
- Attaches a QTextStream to the created file, which is written
- to all over the place using out(). This function does not
- store the \a fileName in the \a node as the output file name.
-
- \sa beginSubPage()
- */
-void Generator::beginFilePage(const Node *node, const QString &fileName)
-{
- QFile *outFile = openSubPageFile(node, fileName);
- QTextStream *out = new QTextStream(outFile);
- outStreamStack.push(out);
-}
-
-/*!
- Creates the file named \a fileName in the output directory.
- Attaches a QTextStream to the created file, which is written
- to all over the place using out(). This function calls another
- function, \c beginFilePage(), which is really just most of what
- this function used to contain. We needed a different version
- that doesn't store the \a fileName in the \a node as the output
- file name.
-
- \sa beginFilePage()
-*/
-void Generator::beginSubPage(const Node *node, const QString &fileName)
-{
- beginFilePage(node, fileName);
- const_cast<Node *>(node)->setOutputFileName(fileName);
-}
-
-/*!
- Flush the text stream associated with the subpage, and
- then pop it off the text stream stack and delete it.
- This terminates output of the subpage.
- */
-void Generator::endSubPage()
-{
- outStreamStack.top()->flush();
- delete outStreamStack.top()->device();
- delete outStreamStack.pop();
-}
-
-/*
- the code below is effectively equivalent to:
- input.replace(QRegularExpression("[^A-Za-z0-9]+"), " ");
- input = input.trimmed();
- input.replace(QLatin1Char(' '), QLatin1Char('-'));
- input = input.toLower();
- as this function accounted for ~8% of total running time
- we optimize a bit...
-*/
-static void transmogrify(QString &input, QString &output)
-{
- // +5 prevents realloc in fileName() below
- output.reserve(input.size() + 5);
- bool begun = false;
- for (int i = 0; i != input.size(); ++i) {
- QChar c = input.at(i);
- uint u = c.unicode();
- if (u >= 'A' && u <= 'Z')
- u += 'a' - 'A';
- if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
- output += QLatin1Char(u);
- begun = true;
- } else if (begun) {
- output += QLatin1Char('-');
- begun = false;
- }
- }
- while (output.endsWith(QLatin1Char('-')))
- output.chop(1);
-}
-
-QString Generator::fileBase(const Node *node)
-{
- if (!node->isPageNode() && !node->isCollectionNode())
- node = node->parent();
-
- if (node->hasFileNameBase())
- return node->fileNameBase();
-
- QString base;
- if (node->isCollectionNode()) {
- base = node->name() + outputSuffix(node);
- if (base.endsWith(".html"))
- base.truncate(base.length() - 5);
-
- if (node->isQmlModule())
- base.append("-qmlmodule");
- else if (node->isJsModule())
- base.append("-jsmodule");
- else if (node->isModule())
- base.append("-module");
- // Why not add "-group" for group pages?
- } else if (node->isTextPageNode()) {
- base = node->name();
- if (base.endsWith(".html"))
- base.truncate(base.length() - 5);
-
- if (node->isExample()) {
- QString modPrefix(node->physicalModuleName());
- if (modPrefix.isEmpty()) {
- modPrefix = project_;
- }
- base.prepend(modPrefix.toLower() + QLatin1Char('-'));
- }
- if (node->isExample()) {
- base.append(QLatin1String("-example"));
- }
- } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
- || node->isJsBasicType()) {
- base = node->name();
- /*
- To avoid file name conflicts in the html directory,
- we prepend a prefix (by default, "qml-") and an optional suffix
- to the file name. The suffix, if one exists, is appended to the
- module name.
- */
- if (!node->logicalModuleName().isEmpty()
- && (!node->logicalModule()->isInternal() || Config::instance().showInternal()))
- base.prepend(node->logicalModuleName() + outputSuffix(node) + QLatin1Char('-'));
-
- base.prepend(outputPrefix(node));
- } else if (node->isProxyNode()) {
- base = node->name();
- base.append("-proxy");
- } else {
- const Node *p = node;
- forever {
- const Node *pp = p->parent();
- base.prepend(p->name());
- if (pp == nullptr || pp->name().isEmpty() || pp->isTextPageNode())
- break;
- base.prepend(QLatin1Char('-'));
- p = pp;
- }
- if (node->isNamespace() && !node->name().isEmpty()) {
- const NamespaceNode *ns = static_cast<const NamespaceNode *>(node);
- if (!ns->isDocumentedHere()) {
- base.append(QLatin1String("-sub-"));
- base.append(ns->tree()->camelCaseModuleName());
- }
- }
- }
-
- QString res;
- transmogrify(base, res);
- Node *n = const_cast<Node *>(node);
- n->setFileNameBase(res);
- return res;
-}
-
-/*!
- Constructs an href link from an example file name, which
- is a path to the example file. If \a fileExtension is
- empty (default value), retrieve the file extension from
- the generator.
- */
-QString Generator::linkForExampleFile(const QString &path, const Node *parent,
- const QString &fileExt)
-{
- QString link = path;
- QString modPrefix(parent->physicalModuleName());
- if (modPrefix.isEmpty())
- modPrefix = project_;
- link.prepend(modPrefix.toLower() + QLatin1Char('-'));
-
- QString res;
- transmogrify(link, res);
- res.append(QLatin1Char('.'));
- res.append(fileExt);
- if (fileExt.isEmpty())
- res.append(fileExtension());
- return res;
-}
-
-/*!
- Helper function to construct a title for a file or image page
- included in an example.
-*/
-QString Generator::exampleFileTitle(const ExampleNode *relative, const QString &fileName)
-{
- QString suffix;
- if (relative->files().contains(fileName))
- suffix = QLatin1String(" Example File");
- else if (relative->images().contains(fileName))
- suffix = QLatin1String(" Image File");
- else
- return suffix;
-
- return fileName.mid(fileName.lastIndexOf(QLatin1Char('/')) + 1) + suffix;
-}
-
-/*!
- If the \a node has a URL, return the URL as the file name.
- Otherwise, construct the file name from the fileBase() and
- either the provided \a extension or fileExtension(), and
- return the constructed name.
- */
-QString Generator::fileName(const Node *node, const QString &extension) const
-{
- if (!node->url().isEmpty())
- return node->url();
-
- QString name = fileBase(node) + QLatin1Char('.');
- return extension.isNull() ? name + fileExtension() : name + extension;
-}
-
-QString Generator::cleanRef(const QString &ref)
-{
- QString clean;
-
- if (ref.isEmpty())
- return clean;
-
- clean.reserve(ref.size() + 20);
- const QChar c = ref[0];
- const uint u = c.unicode();
-
- if ((u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z') || (u >= '0' && u <= '9')) {
- clean += c;
- } else if (u == '~') {
- clean += "dtor.";
- } else if (u == '_') {
- clean += "underscore.";
- } else {
- clean += QLatin1Char('A');
- }
-
- for (int i = 1; i < ref.length(); i++) {
- const QChar c = ref[i];
- const uint u = c.unicode();
- if ((u >= 'a' && u <= 'z') || (u >= 'A' && u <= 'Z') || (u >= '0' && u <= '9') || u == '-'
- || u == '_' || u == ':' || u == '.') {
- clean += c;
- } else if (c.isSpace()) {
- clean += QLatin1Char('-');
- } else if (u == '!') {
- clean += "-not";
- } else if (u == '&') {
- clean += "-and";
- } else if (u == '<') {
- clean += "-lt";
- } else if (u == '=') {
- clean += "-eq";
- } else if (u == '>') {
- clean += "-gt";
- } else if (u == '#') {
- clean += QLatin1Char('#');
- } else {
- clean += QLatin1Char('-');
- clean += QString::number(static_cast<int>(u), 16);
- }
- }
- return clean;
-}
-
-QMap<QString, QString> &Generator::formattingLeftMap()
-{
- return fmtLeftMaps[format()];
-}
-
-QMap<QString, QString> &Generator::formattingRightMap()
-{
- return fmtRightMaps[format()];
-}
-
-/*!
- Returns the full document location.
- */
-QString Generator::fullDocumentLocation(const Node *node, bool useSubdir)
-{
- if (node == nullptr)
- return QString();
- if (!node->url().isEmpty())
- return node->url();
-
- QString parentName;
- QString anchorRef;
- QString fdl;
-
- /*
- If the useSubdir parameter is set, then the output is
- being sent to subdirectories of the output directory.
- Prepend the subdirectory name + '/' to the result.
- */
- if (useSubdir) {
- fdl = node->outputSubdirectory();
- if (!fdl.isEmpty())
- fdl.append(QLatin1Char('/'));
- }
- if (node->isNamespace()) {
- /*
- The root namespace has no name - check for this before creating
- an attribute containing the location of any documentation.
- */
- if (!fileBase(node).isEmpty())
- parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
- else
- return QString();
- } else if (node->isQmlType() || node->isQmlBasicType() || node->isJsType()
- || node->isJsBasicType()) {
- QString fb = fileBase(node);
- if (fb.startsWith(outputPrefix(node)))
- return fb + QLatin1Char('.') + currentGenerator()->fileExtension();
- else {
- QString mq;
- if (!node->logicalModuleName().isEmpty()) {
- mq = node->logicalModuleName().replace(QChar('.'), QChar('-'));
- mq = mq.toLower() + QLatin1Char('-');
- }
- return fdl + outputPrefix(node) + mq + fileBase(node) + QLatin1Char('.')
- + currentGenerator()->fileExtension();
- }
- } else if (node->isTextPageNode() || node->isCollectionNode()) {
- parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
- } else if (fileBase(node).isEmpty())
- return QString();
-
- Node *parentNode = nullptr;
-
- if ((parentNode = node->parent())) {
- // use the parent's name unless the parent is the root namespace
- if (!node->parent()->isNamespace() || !node->parent()->name().isEmpty())
- parentName = fullDocumentLocation(node->parent());
- }
-
- switch (node->nodeType()) {
- case Node::Class:
- case Node::Struct:
- case Node::Union:
- case Node::Namespace:
- case Node::Proxy:
- parentName = fileBase(node) + QLatin1Char('.') + currentGenerator()->fileExtension();
- break;
- case Node::Function: {
- const FunctionNode *fn = static_cast<const FunctionNode *>(node);
- switch (fn->metaness()) {
- case FunctionNode::JsSignal:
- case FunctionNode::QmlSignal:
- anchorRef = QLatin1Char('#') + node->name() + "-signal";
- break;
- case FunctionNode::JsSignalHandler:
- case FunctionNode::QmlSignalHandler:
- anchorRef = QLatin1Char('#') + node->name() + "-signal-handler";
- break;
- case FunctionNode::JsMethod:
- case FunctionNode::QmlMethod:
- anchorRef = QLatin1Char('#') + node->name() + "-method";
- break;
- default:
- if (fn->isDtor())
- anchorRef = "#dtor." + fn->name().mid(1);
- else if (fn->hasOneAssociatedProperty() && fn->doc().isEmpty())
- return fullDocumentLocation(fn->firstAssociatedProperty());
- else if (fn->overloadNumber() > 0)
- anchorRef = QLatin1Char('#') + cleanRef(fn->name()) + QLatin1Char('-')
- + QString::number(fn->overloadNumber());
- else
- anchorRef = QLatin1Char('#') + cleanRef(fn->name());
- break;
- }
- break;
- }
- /*
- Use node->name() instead of fileBase(node) as
- the latter returns the name in lower-case. For
- HTML anchors, we need to preserve the case.
- */
- case Node::Enum:
- anchorRef = QLatin1Char('#') + node->name() + "-enum";
- break;
- case Node::TypeAlias:
- anchorRef = QLatin1Char('#') + node->name() + "-alias";
- break;
- case Node::Typedef: {
- const TypedefNode *tdef = static_cast<const TypedefNode *>(node);
- if (tdef->associatedEnum()) {
- return fullDocumentLocation(tdef->associatedEnum());
- }
- anchorRef = QLatin1Char('#') + node->name() + "-typedef";
- break;
- }
- case Node::Property:
- anchorRef = QLatin1Char('#') + node->name() + "-prop";
- break;
- case Node::JsProperty:
- case Node::QmlProperty:
- if (node->isAttached())
- anchorRef = QLatin1Char('#') + node->name() + "-attached-prop";
- else
- anchorRef = QLatin1Char('#') + node->name() + "-prop";
- break;
- case Node::Variable:
- anchorRef = QLatin1Char('#') + node->name() + "-var";
- break;
- case Node::JsType:
- case Node::QmlType:
- case Node::Page:
- case Node::Group:
- case Node::HeaderFile:
- case Node::Module:
- case Node::JsModule:
- case Node::QmlModule: {
- parentName = fileBase(node);
- parentName.replace(QLatin1Char('/'), QLatin1Char('-'))
- .replace(QLatin1Char('.'), QLatin1Char('-'));
- parentName += QLatin1Char('.') + currentGenerator()->fileExtension();
- } break;
- default:
- break;
- }
-
- if (!node->isClassNode() && !node->isNamespace()) {
- if (node->isObsolete())
- parentName.replace(QLatin1Char('.') + currentGenerator()->fileExtension(),
- "-obsolete." + currentGenerator()->fileExtension());
- }
-
- return fdl + parentName.toLower() + anchorRef;
-}
-
-void Generator::generateAlsoList(const Node *node, CodeMarker *marker)
-{
- QList<Text> alsoList = node->doc().alsoList();
- supplementAlsoList(node, alsoList);
-
- if (!alsoList.isEmpty()) {
- Text text;
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "See also "
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
-
- for (int i = 0; i < alsoList.size(); ++i)
- text << alsoList.at(i) << separator(i, alsoList.size());
-
- text << Atom::ParaRight;
- generateText(text, node, marker);
- }
-}
-
-const Atom *Generator::generateAtomList(const Atom *atom, const Node *relative, CodeMarker *marker,
- bool generate, int &numAtoms)
-{
- while (atom != nullptr) {
- if (atom->type() == Atom::FormatIf) {
- int numAtoms0 = numAtoms;
- bool rightFormat = canHandleFormat(atom->string());
- atom = generateAtomList(atom->next(), relative, marker, generate && rightFormat,
- numAtoms);
- if (atom == nullptr)
- return nullptr;
-
- if (atom->type() == Atom::FormatElse) {
- ++numAtoms;
- atom = generateAtomList(atom->next(), relative, marker, generate && !rightFormat,
- numAtoms);
- if (atom == nullptr)
- return nullptr;
- }
-
- if (atom->type() == Atom::FormatEndif) {
- if (generate && numAtoms0 == numAtoms) {
- relative->location().warning(QStringLiteral("Output format %1 not handled %2")
- .arg(format())
- .arg(outFileName()));
- Atom unhandledFormatAtom(Atom::UnhandledFormat, format());
- generateAtomList(&unhandledFormatAtom, relative, marker, generate, numAtoms);
- }
- atom = atom->next();
- }
- } else if (atom->type() == Atom::FormatElse || atom->type() == Atom::FormatEndif) {
- return atom;
- } else {
- int n = 1;
- if (generate) {
- n += generateAtom(atom, relative, marker);
- numAtoms += n;
- }
- while (n-- > 0)
- atom = atom->next();
- }
- }
- return nullptr;
-}
-
-/*!
- Generate the body of the documentation from the qdoc comment
- found with the entity represented by the \a node.
- */
-void Generator::generateBody(const Node *node, CodeMarker *marker)
-{
- const FunctionNode *fn = node->isFunction() ? static_cast<const FunctionNode *>(node) : nullptr;
- if (!node->hasDoc() && !node->hasSharedDoc()) {
- /*
- Test for special function, like a destructor or copy constructor,
- that has no documentation.
- */
- if (fn) {
- if (fn->isDtor()) {
- Text text;
- text << "Destroys the instance of ";
- text << fn->parent()->name() << ".";
- if (fn->isVirtual())
- text << " The destructor is virtual.";
- out() << "<p>";
- generateText(text, node, marker);
- out() << "</p>";
- } else if (fn->isCtor()) {
- Text text;
- text << "Default constructs an instance of ";
- text << fn->parent()->name() << ".";
- out() << "<p>";
- generateText(text, node, marker);
- out() << "</p>";
- } else if (fn->isCCtor()) {
- Text text;
- text << "Copy constructor.";
- out() << "<p>";
- generateText(text, node, marker);
- out() << "</p>";
- } else if (fn->isMCtor()) {
- Text text;
- text << "Move-copy constructor.";
- out() << "<p>";
- generateText(text, node, marker);
- out() << "</p>";
- } else if (fn->isCAssign()) {
- Text text;
- text << "Copy-assignment operator.";
- out() << "<p>";
- generateText(text, node, marker);
- out() << "</p>";
- } else if (fn->isMAssign()) {
- Text text;
- text << "Move-assignment operator.";
- out() << "<p>";
- generateText(text, node, marker);
- out() << "</p>";
- } else if (!node->isWrapper() && !node->isMarkedReimp()) {
- if (!fn->isIgnored()) // undocumented functions added by Q_OBJECT
- node->location().warning(QStringLiteral("No documentation for '%1'")
- .arg(node->plainSignature()));
- }
- } else if (!node->isWrapper() && !node->isMarkedReimp()) {
- // Don't require documentation of things defined in Q_GADGET
- if (node->name() != QLatin1String("QtGadgetHelper"))
- node->location().warning(
- QStringLiteral("No documentation for '%1'").arg(node->plainSignature()));
- }
- } else if (!node->isSharingComment()) {
- // Reimplements clause and type alias info precede body text
- if (fn && !fn->overridesThis().isEmpty())
- generateReimplementsClause(fn, marker);
- else if (node->isTypeAlias())
- generateAddendum(node, TypeAlias, marker, false);
- else if (node->isProperty()) {
- if (static_cast<const PropertyNode *>(node)->propertyType() != PropertyNode::Standard)
- generateAddendum(node, BindableProperty, marker);
- }
-
- if (!generateText(node->doc().body(), node, marker)) {
- if (node->isMarkedReimp())
- return;
- }
-
- if (fn) {
- if (fn->isQmlSignal())
- generateAddendum(node, QmlSignalHandler, marker);
- if (fn->isPrivateSignal())
- generateAddendum(node, PrivateSignal, marker);
- if (fn->isInvokable())
- generateAddendum(node, Invokable, marker);
- if (fn->hasAssociatedProperties())
- generateAddendum(node, AssociatedProperties, marker);
- }
-
- // Generate warnings
- if (node->isEnumType()) {
- const EnumNode *enume = static_cast<const EnumNode *>(node);
-
- QSet<QString> definedItems;
- const QList<EnumItem> &items = enume->items();
- for (const auto &item : items)
- definedItems.insert(item.name());
-
- const auto &documentedItemList = enume->doc().enumItemNames();
- QSet<QString> documentedItems(documentedItemList.cbegin(), documentedItemList.cend());
- const QSet<QString> allItems = definedItems + documentedItems;
- if (allItems.count() > definedItems.count()
- || allItems.count() > documentedItems.count()) {
- for (const auto &it : allItems) {
- if (!definedItems.contains(it)) {
- QString details;
- QString best = nearestName(it, definedItems);
- if (!best.isEmpty() && !documentedItems.contains(best))
- details = QStringLiteral("Maybe you meant '%1'?").arg(best);
-
- node->doc().location().warning(
- QStringLiteral("No such enum item '%1' in %2")
- .arg(it)
- .arg(node->plainFullName()),
- details);
- } else if (!documentedItems.contains(it)) {
- node->doc().location().warning(
- QStringLiteral("Undocumented enum item '%1' in %2")
- .arg(it)
- .arg(node->plainFullName()));
- }
- }
- }
- } else if (fn) {
- const QSet<QString> declaredNames = fn->parameters().getNames();
- const QSet<QString> documentedNames = fn->doc().parameterNames();
- if (declaredNames != documentedNames) {
- for (const auto &name : declaredNames) {
- if (!documentedNames.contains(name)) {
- if (fn->isActive() || fn->isPreliminary()) {
- if (!fn->isMarkedReimp() && !fn->isOverload()) {
- fn->doc().location().warning(
- QStringLiteral("Undocumented parameter '%1' in %2")
- .arg(name)
- .arg(node->plainFullName()));
- }
- }
- }
- }
- for (const auto &name : documentedNames) {
- if (!declaredNames.contains(name)) {
- QString best = nearestName(name, declaredNames);
- QString details;
- if (!best.isEmpty())
- details = QStringLiteral("Maybe you meant '%1'?").arg(best);
- fn->doc().location().warning(QStringLiteral("No such parameter '%1' in %2")
- .arg(name)
- .arg(fn->plainFullName()),
- details);
- }
- }
- }
- /*
- This return value check should be implemented
- for all functions with a return type.
- mws 13/12/2018
- */
- if (!fn->isObsolete() && fn->returnsBool() && !fn->isMarkedReimp()
- && !fn->isOverload()) {
- if (!fn->doc().body().contains("return"))
- node->doc().location().warning(
- QStringLiteral("Undocumented return value "
- "(hint: use 'return' or 'returns' in the text"));
- }
- }
- }
- generateRequiredLinks(node, marker);
-}
-
-/*!
- Generates either a link to the project folder for example \a node, or a list
- of links files/images if 'url.examples config' variable is not defined.
-
- Does nothing for non-example nodes.
-*/
-void Generator::generateRequiredLinks(const Node *node, CodeMarker *marker)
-{
- if (!node->isExample())
- return;
-
- const ExampleNode *en = static_cast<const ExampleNode *>(node);
- QString exampleUrl = Config::instance().getString(CONFIG_URL + Config::dot + CONFIG_EXAMPLES);
-
- if (exampleUrl.isEmpty()) {
- if (!en->noAutoList()) {
- generateFileList(en, marker, false); // files
- generateFileList(en, marker, true); // images
- }
- } else {
- generateLinkToExample(en, marker, exampleUrl);
- }
-}
-
-/*!
- Generates an external link to the project folder for example \a node.
- The path to the example replaces a placeholder '\1' character if
- one is found in the \a baseUrl string. If no such placeholder is found,
- the path is appended to \a baseUrl, after a '/' character if \a baseUrl did
- not already end in one.
-*/
-void Generator::generateLinkToExample(const ExampleNode *en, CodeMarker *marker,
- const QString &baseUrl)
-{
- QString exampleUrl(baseUrl);
- QString link;
-#ifndef QT_BOOTSTRAPPED
- link = QUrl(exampleUrl).host();
-#endif
- if (!link.isEmpty())
- link.prepend(" @ ");
- link.prepend("Example project");
-
- const QLatin1Char separator('/');
- const QLatin1Char placeholder('\1');
- if (!exampleUrl.contains(placeholder)) {
- if (!exampleUrl.endsWith(separator))
- exampleUrl += separator;
- exampleUrl += placeholder;
- }
-
- // Construct a path to the example; <install path>/<example name>
- QString pathRoot;
- QStringMultiMap *metaTagMap = en->doc().metaTagMap();
- if (metaTagMap)
- pathRoot = metaTagMap->value(QLatin1String("installpath"));
- if (pathRoot.isEmpty())
- pathRoot = Config::instance().getString(CONFIG_EXAMPLESINSTALLPATH);
- QStringList path = QStringList() << pathRoot << en->name();
- path.removeAll({});
-
- Text text;
- text << Atom::ParaLeft
- << Atom(Atom::Link, exampleUrl.replace(placeholder, path.join(separator)))
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << Atom(Atom::String, link)
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom::ParaRight;
-
- generateText(text, nullptr, marker);
-}
-
-void Generator::addImageToCopy(const ExampleNode *en, const QString &file)
-{
- QDir dirInfo;
- QString userFriendlyFilePath;
- const QString prefix("/images/used-in-examples/");
- QString srcPath = Config::findFile(en->location(), QStringList(), exampleDirs, file,
- exampleImgExts, &userFriendlyFilePath);
- outFileNames_ << prefix.mid(1) + userFriendlyFilePath;
- userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/'));
- QString imgOutDir = outDir_ + prefix + userFriendlyFilePath;
- if (!dirInfo.mkpath(imgOutDir))
- en->location().fatal(QStringLiteral("Cannot create output directory '%1'").arg(imgOutDir));
- Config::copyFile(en->location(), srcPath, file, imgOutDir);
-}
-
-/*!
- This function is called when the documentation for an example is
- being formatted. It outputs a list of files for the example, which
- can be the example's source files or the list of images used by the
- example. The images are copied into a subtree of
- \c{...doc/html/images/used-in-examples/...}
-*/
-void Generator::generateFileList(const ExampleNode *en, CodeMarker *marker, bool images)
-{
- Text text;
- OpenedList openedList(OpenedList::Bullet);
- QString tag;
- QStringList paths;
- Atom::AtomType atomType = Atom::ExampleFileLink;
-
- if (images) {
- paths = en->images();
- tag = "Images:";
- atomType = Atom::ExampleImageLink;
- } else { // files
- paths = en->files();
- tag = "Files:";
- }
- std::sort(paths.begin(), paths.end(), Generator::comparePaths);
-
- text << Atom::ParaLeft << tag << Atom::ParaRight;
- text << Atom(Atom::ListLeft, openedList.styleString());
-
- QString path;
- for (const auto &file : qAsConst(paths)) {
- if (images) {
- if (!file.isEmpty())
- addImageToCopy(en, file);
- } else {
- generateExampleFilePage(en, file, marker);
- }
-
- openedList.next();
- text << Atom(Atom::ListItemNumber, openedList.numberString())
- << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft
- << Atom(atomType, file) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << file
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom::ParaRight
- << Atom(Atom::ListItemRight, openedList.styleString());
- path = file;
- }
- text << Atom(Atom::ListRight, openedList.styleString());
- if (!paths.isEmpty())
- generateText(text, en, marker);
-}
-
-void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker)
-{
- if (!classe->baseClasses().isEmpty()) {
- Text text;
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
- << "Inherits: " << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
-
- int index = 0;
- const QList<RelatedClass> &baseClasses = classe->baseClasses();
- for (const auto &cls : baseClasses) {
- if (cls.m_node) {
- appendFullName(text, cls.m_node, classe);
-
- if (cls.m_access == Access::Protected) {
- text << " (protected)";
- } else if (cls.m_access == Access::Private) {
- text << " (private)";
- }
- text << separator(index++, classe->baseClasses().count());
- }
- }
- text << Atom::ParaRight;
- generateText(text, classe, marker);
- }
-}
-
-/*!
- Recursive writing of HTML files from the root \a node.
- */
-void Generator::generateDocumentation(Node *node)
-{
- if (!node->url().isNull())
- return;
- if (node->isIndexNode())
- return;
- if (node->isInternal() && !m_showInternal)
- return;
- if (node->isExternalPage())
- return;
-
- /*
- Obtain a code marker for the source file.
- */
- CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath());
-
- if (node->parent() != nullptr) {
- if (node->isCollectionNode()) {
- /*
- A collection node collects: groups, C++ modules,
- QML modules or JavaScript modules. Testing for a
- CollectionNode must be done before testing for a
- TextPageNode because a CollectionNode is a PageNode
- at this point.
-
- Don't output an HTML page for the collection
- node unless the \group, \module, \qmlmodule or
- \jsmodule command was actually seen by qdoc in
- the qdoc comment for the node.
-
- A key prerequisite in this case is the call to
- mergeCollections(cn). We must determine whether
- this group, module, QML module, or JavaScript
- module has members in other modules. We know at
- this point that cn's members list contains only
- members in the current module. Therefore, before
- outputting the page for cn, we must search for
- members of cn in the other modules and add them
- to the members list.
- */
- CollectionNode *cn = static_cast<CollectionNode *>(node);
- if (cn->wasSeen()) {
- m_qdb->mergeCollections(cn);
- beginSubPage(node, fileName(node));
- generateCollectionNode(cn, marker);
- endSubPage();
- } else if (cn->isGenericCollection()) {
- // Currently used only for the module's related orphans page
- // but can be generalized for other kinds of collections if
- // other use cases pop up.
- QString name = cn->name().toLower();
- name.replace(QChar(' '), QString("-"));
- QString filename =
- cn->tree()->physicalModuleName() + "-" + name + "." + fileExtension();
- beginSubPage(node, filename);
- generateGenericCollectionPage(cn, marker);
- endSubPage();
- }
- } else if (node->isTextPageNode()) {
- beginSubPage(node, fileName(node));
- generatePageNode(static_cast<PageNode *>(node), marker);
- endSubPage();
- } else if (node->isAggregate()) {
- if ((node->isClassNode() || node->isHeader() || node->isNamespace())
- && node->docMustBeGenerated()) {
- beginSubPage(node, fileName(node));
- generateCppReferencePage(static_cast<Aggregate *>(node), marker);
- endSubPage();
- } else if (node->isQmlType() || node->isJsType()) {
- beginSubPage(node, fileName(node));
- QmlTypeNode *qcn = static_cast<QmlTypeNode *>(node);
- generateQmlTypePage(qcn, marker);
- endSubPage();
- } else if (node->isQmlBasicType() || node->isJsBasicType()) {
- beginSubPage(node, fileName(node));
- QmlBasicTypeNode *qbtn = static_cast<QmlBasicTypeNode *>(node);
- generateQmlBasicTypePage(qbtn, marker);
- endSubPage();
- } else if (node->isProxyNode()) {
- beginSubPage(node, fileName(node));
- generateProxyPage(static_cast<Aggregate *>(node), marker);
- endSubPage();
- }
- }
- }
-
- if (node->isAggregate()) {
- Aggregate *aggregate = static_cast<Aggregate *>(node);
- const NodeList &children = aggregate->childNodes();
- for (auto *node : children) {
- if (node->isPageNode() && !node->isPrivate())
- generateDocumentation(node);
- }
- }
-}
-
-/*!
- Generate a list of maintainers in the output
- */
-void Generator::generateMaintainerList(const Aggregate *node, CodeMarker *marker)
-{
- QStringList sl = getMetadataElements(node, "maintainer");
-
- if (!sl.isEmpty()) {
- Text text;
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
- << "Maintained by: " << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
-
- for (int i = 0; i < sl.size(); ++i)
- text << sl.at(i) << separator(i, sl.size());
-
- text << Atom::ParaRight;
- generateText(text, node, marker);
- }
-}
-
-/*!
- Output the "Inherit by" list for the QML element,
- if it is inherited by any other elements.
- */
-void Generator::generateQmlInheritedBy(const QmlTypeNode *qcn, CodeMarker *marker)
-{
- if (qcn) {
- NodeList subs;
- QmlTypeNode::subclasses(qcn, subs);
- if (!subs.isEmpty()) {
- Text text;
- text << Atom::ParaLeft << "Inherited by ";
- appendSortedQmlNames(text, qcn, subs);
- text << Atom::ParaRight;
- generateText(text, qcn, marker);
- }
- }
-}
-
-/*!
- Extract sections of markup text surrounded by \e qmltext
- and \e endqmltext and output them.
- */
-bool Generator::generateQmlText(const Text &text, const Node *relative, CodeMarker *marker,
- const QString & /* qmlName */)
-{
- const Atom *atom = text.firstAtom();
- bool result = false;
-
- if (atom != nullptr) {
- initializeTextOutput();
- while (atom) {
- if (atom->type() != Atom::QmlText)
- atom = atom->next();
- else {
- atom = atom->next();
- while (atom && (atom->type() != Atom::EndQmlText)) {
- int n = 1 + generateAtom(atom, relative, marker);
- while (n-- > 0)
- atom = atom->next();
- }
- }
- }
- result = true;
- }
- return result;
-}
-
-void Generator::generateReimplementsClause(const FunctionNode *fn, CodeMarker *marker)
-{
- if (!fn->overridesThis().isEmpty()) {
- if (fn->parent()->isClassNode()) {
- ClassNode *cn = static_cast<ClassNode *>(fn->parent());
- const FunctionNode *overrides = cn->findOverriddenFunction(fn);
- if (overrides && !overrides->isPrivate() && !overrides->parent()->isPrivate()) {
- if (overrides->hasDoc()) {
- Text text;
- text << Atom::ParaLeft << "Reimplements: ";
- QString fullName =
- overrides->parent()->name() + "::" + overrides->signature(false, true);
- appendFullName(text, overrides->parent(), fullName, overrides);
- text << "." << Atom::ParaRight;
- generateText(text, fn, marker);
- } else {
- fn->doc().location().warning(
- QStringLiteral("Illegal \\reimp; no documented virtual function for %1")
- .arg(overrides->plainSignature()));
- }
- return;
- }
- const PropertyNode *sameName = cn->findOverriddenProperty(fn);
- if (sameName && sameName->hasDoc()) {
- Text text;
- text << Atom::ParaLeft << "Reimplements an access function for property: ";
- QString fullName = sameName->parent()->name() + "::" + sameName->name();
- appendFullName(text, sameName->parent(), fullName, sameName);
- text << "." << Atom::ParaRight;
- generateText(text, fn, marker);
- }
- }
- }
-}
-
-QString Generator::formatSince(const Node *node)
-{
- QStringList since = node->since().split(QLatin1Char(' '));
-
- // If there is only one argument, assume it is the Qt version number.
- if (since.count() == 1)
- return "Qt " + since[0];
-
- // Otherwise, use the original <project> <version> string.
- return node->since();
-}
-
-void Generator::generateSince(const Node *node, CodeMarker *marker)
-{
- if (!node->since().isEmpty()) {
- Text text;
- text << Atom::ParaLeft << "This " << typeString(node) << " was introduced ";
- if (node->isEnumType())
- text << "or modified ";
- text << "in " << formatSince(node) << "." << Atom::ParaRight;
- generateText(text, node, marker);
- }
-}
-
-void Generator::generateStatus(const Node *node, CodeMarker *marker)
-{
- Text text;
-
- switch (node->status()) {
- case Node::Active:
- // Do nothing.
- break;
- case Node::Preliminary:
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This "
- << typeString(node) << " is under development and is subject to change."
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << Atom::ParaRight;
- break;
- case Node::Deprecated:
- text << Atom::ParaLeft;
- if (node->isAggregate())
- text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
- text << "This " << typeString(node) << " is deprecated.";
- if (node->isAggregate())
- text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
- text << Atom::ParaRight;
- break;
- case Node::Obsolete:
- text << Atom::ParaLeft;
- if (node->isAggregate())
- text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
- text << "This " << typeString(node) << " is obsolete.";
- if (node->isAggregate())
- text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
- text << " It is provided to keep old source code working. "
- << "We strongly advise against "
- << "using it in new code." << Atom::ParaRight;
- break;
- case Node::Internal:
- default:
- break;
- }
- generateText(text, node, marker);
-}
-
-/*!
- Generates an addendum note of type \a type for \a node, using \a marker
- as the code marker.
-*/
-void Generator::generateAddendum(const Node *node, Addendum type, CodeMarker *marker,
- bool generateNote)
-{
- Q_ASSERT(node && !node->name().isEmpty());
- Text text;
- text << Atom::ParaLeft;
-
- if (generateNote) {
- text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
- << "Note: " << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
- }
-
- switch (type) {
- case Invokable:
- text << "This function can be invoked via the meta-object system and from QML. See "
- << Atom(Atom::Link, "Q_INVOKABLE")
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << "Q_INVOKABLE"
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << ".";
- break;
- case PrivateSignal:
- text << "This is a private signal. It can be used in signal connections "
- "but cannot be emitted by the user.";
- break;
- case QmlSignalHandler:
- {
- QString handler(node->name());
- handler[0] = handler[0].toTitleCase();
- handler.prepend(QLatin1String("on"));
- text << "The corresponding handler is "
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_TELETYPE) << handler
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_TELETYPE) << ".";
- break;
- }
- case AssociatedProperties:
- {
- if (!node->isFunction())
- return;
- const FunctionNode *fn = static_cast<const FunctionNode *>(node);
- NodeList nodes = fn->associatedProperties();
- if (nodes.isEmpty())
- return;
- std::sort(nodes.begin(), nodes.end(), Node::nodeNameLessThan);
- for (const auto *n : qAsConst(nodes)) {
- QString msg;
- const PropertyNode *pn = static_cast<const PropertyNode *>(n);
- switch (pn->role(fn)) {
- case PropertyNode::Getter:
- msg = QStringLiteral("Getter function");
- break;
- case PropertyNode::Setter:
- msg = QStringLiteral("Setter function");
- break;
- case PropertyNode::Resetter:
- msg = QStringLiteral("Resetter function");
- break;
- case PropertyNode::Notifier:
- msg = QStringLiteral("Notifier signal");
- break;
- default:
- continue;
- }
- text << msg << " for property " << Atom(Atom::Link, pn->name())
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << pn->name()
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << ". ";
- }
- break;
- }
- case TypeAlias:
- {
- if (!node->isTypeAlias())
- return;
- const auto *ta = static_cast<const TypeAliasNode *>(node);
- text << "This is a type alias for ";
- if (ta->aliasedNode() && ta->aliasedNode()->isInAPI()) {
- text << Atom(Atom::LinkNode, CodeMarker::stringForNode(ta->aliasedNode()))
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << Atom(Atom::String, ta->aliasedNode()->plainFullName(ta->parent()))
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << ".";
- } else {
- text << Atom(Atom::String, ta->aliasedType()) << ".";
- }
- break;
- }
- case BindableProperty:
- {
- text << "This property supports "
- << Atom(Atom::Link, "QProperty")
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << "QProperty"
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
- text << " bindings.";
- break;
- }
- default:
- return;
- }
-
- text << Atom::ParaRight;
- generateText(text, node, marker);
-}
-
-/*!
- Generate the documentation for \a relative. i.e. \a relative
- is the node that represents the entity where a qdoc comment
- was found, and \a text represents the qdoc comment.
- */
-bool Generator::generateText(const Text &text, const Node *relative, CodeMarker *marker)
-{
- bool result = false;
- if (text.firstAtom() != nullptr) {
- int numAtoms = 0;
- initializeTextOutput();
- generateAtomList(text.firstAtom(), relative, marker, true, numAtoms);
- result = true;
- }
- return result;
-}
-
-/*
- The node is an aggregate, typically a class node, which has
- a threadsafeness level. This function checks all the children
- of the node to see if they are exceptions to the node's
- threadsafeness. If there are any exceptions, the exceptions
- are added to the appropriate set (reentrant, threadsafe, and
- nonreentrant, and true is returned. If there are no exceptions,
- the three node lists remain empty and false is returned.
- */
-bool Generator::hasExceptions(const Node *node, NodeList &reentrant, NodeList &threadsafe,
- NodeList &nonreentrant)
-{
- bool result = false;
- Node::ThreadSafeness ts = node->threadSafeness();
- const NodeList &children = static_cast<const Aggregate *>(node)->childNodes();
- for (auto child : children) {
- if (!child->isObsolete()) {
- switch (child->threadSafeness()) {
- case Node::Reentrant:
- reentrant.append(child);
- if (ts == Node::ThreadSafe)
- result = true;
- break;
- case Node::ThreadSafe:
- threadsafe.append(child);
- if (ts == Node::Reentrant)
- result = true;
- break;
- case Node::NonReentrant:
- nonreentrant.append(child);
- result = true;
- break;
- default:
- break;
- }
- }
- }
- return result;
-}
-
-static void startNote(Text &text)
-{
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
- << "Note:" << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " ";
-}
-
-/*!
- Generates text that explains how threadsafe and/or reentrant
- \a node is.
- */
-void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker)
-{
- Text text, rlink, tlink;
- NodeList reentrant;
- NodeList threadsafe;
- NodeList nonreentrant;
- Node::ThreadSafeness ts = node->threadSafeness();
- bool exceptions = false;
-
- rlink << Atom(Atom::Link, "reentrant") << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << "reentrant" << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
-
- tlink << Atom(Atom::Link, "thread-safe") << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
- << "thread-safe" << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
-
- switch (ts) {
- case Node::UnspecifiedSafeness:
- break;
- case Node::NonReentrant:
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
- << "Warning:" << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " This "
- << typeString(node) << " is not " << rlink << "." << Atom::ParaRight;
- break;
- case Node::Reentrant:
- case Node::ThreadSafe:
- startNote(text);
- if (node->isAggregate()) {
- exceptions = hasExceptions(node, reentrant, threadsafe, nonreentrant);
- text << "All functions in this " << typeString(node) << " are ";
- if (ts == Node::ThreadSafe)
- text << tlink;
- else
- text << rlink;
-
- if (!exceptions || (ts == Node::Reentrant && !threadsafe.isEmpty()))
- text << ".";
- else
- text << " with the following exceptions:";
- } else {
- text << "This " << typeString(node) << " is ";
- if (ts == Node::ThreadSafe)
- text << tlink;
- else
- text << rlink;
- text << ".";
- }
- text << Atom::ParaRight;
- break;
- default:
- break;
- }
- generateText(text, node, marker);
-
- if (exceptions) {
- text.clear();
- if (ts == Node::Reentrant) {
- if (!nonreentrant.isEmpty()) {
- startNote(text);
- text << "These functions are not " << rlink << ":" << Atom::ParaRight;
- signatureList(nonreentrant, node, marker);
- }
- if (!threadsafe.isEmpty()) {
- text.clear();
- startNote(text);
- text << "These functions are also " << tlink << ":" << Atom::ParaRight;
- generateText(text, node, marker);
- signatureList(threadsafe, node, marker);
- }
- } else { // thread-safe
- if (!reentrant.isEmpty()) {
- startNote(text);
- text << "These functions are only " << rlink << ":" << Atom::ParaRight;
- signatureList(reentrant, node, marker);
- }
- if (!nonreentrant.isEmpty()) {
- text.clear();
- startNote(text);
- text << "These functions are not " << rlink << ":" << Atom::ParaRight;
- signatureList(nonreentrant, node, marker);
- }
- }
- }
-}
-
-/*!
- Returns the string containing an example code of the input node,
- if it is an overloaded signal. Otherwise, returns an empty string.
- */
-QString Generator::getOverloadedSignalCode(const Node *node)
-{
- if (!node->isFunction())
- return QString();
- const auto func = static_cast<const FunctionNode *>(node);
- if (!func->isSignal() || !func->hasOverloads())
- return QString();
-
- // Compute a friendly name for the object of that instance.
- // e.g: "QAbstractSocket" -> "abstractSocket"
- QString objectName = node->parent()->name();
- if (objectName.size() >= 2) {
- if (objectName[0] == 'Q')
- objectName = objectName.mid(1);
- objectName[0] = objectName[0].toLower();
- }
-
- // We have an overloaded signal, show an example. Note, for const
- // overloaded signals, one should use Q{Const,NonConst}Overload, but
- // it is very unlikely that we will ever have public API overloading
- // signals by const.
- QString code = "connect(" + objectName + ", QOverload<";
- code += func->parameters().generateTypeList();
- code += ">::of(&" + func->parent()->name() + "::" + func->name() + "),\n [=](";
- code += func->parameters().generateTypeAndNameList();
- code += "){ /* ... */ });";
-
- return code;
-}
-
-/*!
- If the node is an overloaded signal, add a node with an example on how to connect to it
- */
-void Generator::generateOverloadedSignal(const Node *node, CodeMarker *marker)
-{
- QString code = getOverloadedSignalCode(node);
- if (code.isEmpty())
- return;
-
- Text text;
- text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD)
- << "Note:" << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " Signal "
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_ITALIC) << node->name()
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_ITALIC)
- << " is overloaded in this class. "
- "To connect to this signal by using the function pointer syntax, Qt "
- "provides a convenient helper for obtaining the function pointer as "
- "shown in this example:"
- << Atom(Atom::Code, marker->markedUpCode(code, node, node->location()));
-
- generateText(text, node, marker);
-}
-
-/*!
- Traverses the database recursively to generate all the documentation.
- */
-void Generator::generateDocs()
-{
- currentGenerator_ = this;
- generateDocumentation(m_qdb->primaryTreeRoot());
-}
-
-Generator *Generator::generatorForFormat(const QString &format)
-{
- for (const auto &generator : qAsConst(generators)) {
- if (generator->format() == format)
- return generator;
- }
- return nullptr;
-}
-
-/*!
- Looks up the tag \a t in the map of metadata values for the
- current topic in \a inner. If values for the tag are found,
- they are returned in a string list.
-
- \note If \a t is found in the metadata map, all the pairs
- having the key \a t are erased. i.e. Once you call this
- function for a particular \a t, you consume \a t.
- */
-QStringList Generator::getMetadataElements(const Aggregate *inner, const QString &t)
-{
- QStringList result;
- QStringMultiMap *metaTagMap = inner->doc().metaTagMap();
- if (metaTagMap)
- result = metaTagMap->values(t);
- if (!result.isEmpty())
- metaTagMap->remove(t);
- return result;
-}
-
-/*!
- Returns a relative path name for an image.
- */
-QString Generator::imageFileName(const Node *relative, const QString &fileBase)
-{
- QString userFriendlyFilePath;
- QString filePath = Config::findFile(relative->doc().location(), imageFiles, imageDirs, fileBase,
- imgFileExts[format()], &userFriendlyFilePath);
-
- if (filePath.isEmpty())
- return QString();
-
- QString path = Config::copyFile(relative->doc().location(), filePath, userFriendlyFilePath,
- outputDir() + QLatin1String("/images"));
- int images_slash = path.lastIndexOf("images/");
- QString relImagePath;
- if (images_slash != -1)
- relImagePath = path.mid(images_slash);
- return relImagePath;
-}
-
-QString Generator::indent(int level, const QString &markedCode)
-{
- if (level == 0)
- return markedCode;
-
- QString t;
- int column = 0;
-
- int i = 0;
- while (i < markedCode.length()) {
- if (markedCode.at(i) == QLatin1Char('\n')) {
- column = 0;
- } else {
- if (column == 0) {
- for (int j = 0; j < level; j++)
- t += QLatin1Char(' ');
- }
- column++;
- }
- t += markedCode.at(i++);
- }
- return t;
-}
-
-void Generator::initialize()
-{
- Config &config = Config::instance();
- outputFormats = config.getOutputFormats();
- redirectDocumentationToDevNull_ = config.getBool(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL);
-
- imageFiles = config.getCanonicalPathList(CONFIG_IMAGES);
- imageDirs = config.getCanonicalPathList(CONFIG_IMAGEDIRS);
- exampleDirs = config.getCanonicalPathList(CONFIG_EXAMPLEDIRS);
- exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS);
-
- QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS;
- for (const auto &ext : config.subVars(imagesDotFileExtensions))
- imgFileExts[ext] = config.getStringList(imagesDotFileExtensions + Config::dot + ext);
-
- for (auto &g : generators) {
- if (outputFormats.contains(g->format())) {
- currentGenerator_ = g;
- g->initializeGenerator();
- }
- }
-
- for (const auto &n : config.subVars(CONFIG_FORMATTING)) {
- QString formattingDotName = CONFIG_FORMATTING + Config::dot + n;
- for (const auto &f : config.subVars(formattingDotName)) {
- QString def = config.getString(formattingDotName + Config::dot + f);
- if (!def.isEmpty()) {
- int numParams = Config::numParams(def);
- int numOccs = def.count("\1");
- if (numParams != 1) {
- config.lastLocation().warning(QStringLiteral("Formatting '%1' must "
- "have exactly one "
- "parameter (found %2)")
- .arg(n)
- .arg(numParams));
- } else if (numOccs > 1) {
- config.lastLocation().fatal(QStringLiteral("Formatting '%1' must "
- "contain exactly one "
- "occurrence of '\\1' "
- "(found %2)")
- .arg(n)
- .arg(numOccs));
- } else {
- int paramPos = def.indexOf("\1");
- fmtLeftMaps[f].insert(n, def.left(paramPos));
- fmtRightMaps[f].insert(n, def.mid(paramPos + 1));
- }
- }
- }
- }
-
- project_ = config.getString(CONFIG_PROJECT);
- outDir_ = config.getOutputDir();
- outSubdir_ = outDir_.mid(outDir_.lastIndexOf('/') + 1);
-
- outputPrefixes.clear();
- QStringList items = config.getStringList(CONFIG_OUTPUTPREFIXES);
- if (!items.isEmpty()) {
- for (const auto &prefix : items)
- outputPrefixes[prefix] = config.getString(CONFIG_OUTPUTPREFIXES + Config::dot + prefix);
- } else {
- outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-");
- outputPrefixes[QLatin1String("JS")] = QLatin1String("js-");
- }
-
- outputSuffixes.clear();
- for (const auto &suffix : config.getStringList(CONFIG_OUTPUTSUFFIXES))
- outputSuffixes[suffix] = config.getString(CONFIG_OUTPUTSUFFIXES + Config::dot + suffix);
-
- noLinkErrors_ = config.getBool(CONFIG_NOLINKERRORS);
- autolinkErrors_ = config.getBool(CONFIG_AUTOLINKERRORS);
-}
-
-/*!
- Creates template-specific subdirs (e.g. /styles and /scripts for HTML)
- and copies the files to them.
- */
-void Generator::copyTemplateFiles(const QString &configVar, const QString &subDir)
-{
- Config &config = Config::instance();
- QStringList files = config.getCanonicalPathList(configVar, true);
- if (!files.isEmpty()) {
- QDir dirInfo;
- QString templateDir = outDir_ + QLatin1Char('/') + subDir;
- if (!dirInfo.exists(templateDir) && !dirInfo.mkdir(templateDir)) {
- config.lastLocation().fatal(
- QStringLiteral("Cannot create %1 directory '%2'").arg(subDir, templateDir));
- } else {
- for (const auto &file : files) {
- if (!file.isEmpty())
- Config::copyFile(config.lastLocation(), file, file, templateDir);
- }
- }
- }
-}
-
-/*!
- Reads format-specific variables from config, sets output
- (sub)directories, creates them on the filesystem and copies the
- template-specific files.
- */
-void Generator::initializeFormat()
-{
- Config &config = Config::instance();
- outFileNames_.clear();
- useOutputSubdirs_ = true;
- if (config.getBool(format() + Config::dot + "nosubdirs"))
- resetUseOutputSubdirs();
-
- if (outputFormats.isEmpty())
- return;
-
- outDir_ = config.getOutputDir(format());
- if (outDir_.isEmpty()) {
- config.lastLocation().fatal(QStringLiteral("No output directory specified in "
- "configuration file or on the command line"));
- } else {
- outSubdir_ = outDir_.mid(outDir_.lastIndexOf('/') + 1);
- }
-
- QDir dirInfo;
- if (dirInfo.exists(outDir_)) {
- if (!config.generating() && Generator::useOutputSubdirs()) {
- if (!Config::removeDirContents(outDir_))
- config.lastLocation().error(
- QStringLiteral("Cannot empty output directory '%1'").arg(outDir_));
- }
- } else if (!dirInfo.mkpath(outDir_)) {
- config.lastLocation().fatal(
- QStringLiteral("Cannot create output directory '%1'").arg(outDir_));
- }
-
- // Output directory exists, which is enough for prepare phase.
- if (config.preparing())
- return;
-
- if (!dirInfo.exists(outDir_ + "/images") && !dirInfo.mkdir(outDir_ + "/images"))
- config.lastLocation().fatal(
- QStringLiteral("Cannot create images directory '%1'").arg(outDir_ + "/images"));
-
- copyTemplateFiles(format() + Config::dot + CONFIG_STYLESHEETS, "style");
- copyTemplateFiles(format() + Config::dot + CONFIG_SCRIPTS, "scripts");
- copyTemplateFiles(format() + Config::dot + CONFIG_EXTRAIMAGES, "images");
-
- // Use a format-specific .quotinginformation if defined, otherwise a global value
- if (config.subVars(format()).contains(CONFIG_QUOTINGINFORMATION))
- m_quoting = config.getBool(format() + Config::dot + CONFIG_QUOTINGINFORMATION);
- else
- m_quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
-}
-
-/*!
- Appends each directory path in \a moreImageDirs to the
- list of image directories.
- */
-void Generator::augmentImageDirs(QSet<QString> &moreImageDirs)
-{
- if (moreImageDirs.isEmpty())
- return;
- for (const auto &it : moreImageDirs)
- imageDirs.append(it);
-}
-
-/*!
- Updates the generator's m_showInternal from the Config.
- */
-void Generator::initializeGenerator()
-{
- m_showInternal = Config::instance().showInternal();
-}
-
-bool Generator::matchAhead(const Atom *atom, Atom::AtomType expectedAtomType)
-{
- return atom->next() && atom->next()->type() == expectedAtomType;
-}
-
-/*!
- Used for writing to the current output stream. Returns a
- reference to the current output stream, which is then used
- with the \c {<<} operator for writing.
- */
-QTextStream &Generator::out()
-{
- return *outStreamStack.top();
-}
-
-QString Generator::outFileName()
-{
- return QFileInfo(static_cast<QFile *>(out().device())->fileName()).fileName();
-}
-
-QString Generator::outputPrefix(const Node *node)
-{
- // Prefix is applied to QML and JS types
- if (node->isQmlType() || node->isQmlBasicType())
- return outputPrefixes[QLatin1String("QML")];
- if (node->isJsType() || node->isJsBasicType())
- return outputPrefixes[QLatin1String("JS")];
- return QString();
-}
-
-QString Generator::outputSuffix(const Node *node)
-{
- // Suffix is applied to QML and JS types, as
- // well as module pages.
- if (node->isQmlModule() || node->isQmlType() || node->isQmlBasicType())
- return outputSuffixes[QLatin1String("QML")];
- if (node->isJsModule() || node->isJsType() || node->isJsBasicType())
- return outputSuffixes[QLatin1String("JS")];
- return QString();
-}
-
-bool Generator::parseArg(const QString &src, const QString &tag, int *pos, int n,
- QStringView *contents, QStringView *par1, bool debug)
-{
-#define SKIP_CHAR(c) \
- if (debug) \
- qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \
- if (i >= n || src[i] != c) { \
- if (debug) \
- qDebug() << " char '" << c << "' not found"; \
- return false; \
- } \
- ++i;
-
-#define SKIP_SPACE \
- while (i < n && src[i] == ' ') \
- ++i;
-
- int i = *pos;
- int j = i;
-
- // assume "<@" has been parsed outside
- // SKIP_CHAR('<');
- // SKIP_CHAR('@');
-
- if (tag != QStringView(src).mid(i, tag.length())) {
- return false;
- }
-
- if (debug)
- qDebug() << "haystack:" << src << "needle:" << tag << "i:" << i;
-
- // skip tag
- i += tag.length();
-
- // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)");
- if (par1) {
- SKIP_SPACE;
- // read parameter name
- j = i;
- while (i < n && src[i].isLetter())
- ++i;
- if (src[i] == '=') {
- if (debug)
- qDebug() << "read parameter" << QString(src.data() + j, i - j);
- SKIP_CHAR('=');
- SKIP_CHAR('"');
- // skip parameter name
- j = i;
- while (i < n && src[i] != '"')
- ++i;
- *par1 = QStringView(src).mid(j, i - j);
- SKIP_CHAR('"');
- SKIP_SPACE;
- } else {
- if (debug)
- qDebug() << "no optional parameter found";
- }
- }
- SKIP_SPACE;
- SKIP_CHAR('>');
-
- // find contents up to closing "</@tag>
- j = i;
- for (; true; ++i) {
- if (i + 4 + tag.length() > n)
- return false;
- if (src[i] != '<')
- continue;
- if (src[i + 1] != '/')
- continue;
- if (src[i + 2] != '@')
- continue;
- if (tag != QStringView(src).mid(i + 3, tag.length()))
- continue;
- if (src[i + 3 + tag.length()] != '>')
- continue;
- break;
- }
-
- *contents = QStringView(src).mid(j, i - j);
-
- i += tag.length() + 4;
-
- *pos = i;
- if (debug)
- qDebug() << " tag " << tag << " found: pos now: " << i;
- return true;
-#undef SKIP_CHAR
-}
-
-QString Generator::plainCode(const QString &markedCode)
-{
- QString t = markedCode;
- t.replace(tag, QString());
- t.replace(quot, QLatin1String("\""));
- t.replace(gt, QLatin1String(">"));
- t.replace(lt, QLatin1String("<"));
- t.replace(amp, QLatin1String("&"));
- return t;
-}
-
-void Generator::setImageFileExtensions(const QStringList &extensions)
-{
- imgFileExts[format()] = extensions;
-}
-
-void Generator::singularPlural(Text &text, const NodeList &nodes)
-{
- if (nodes.count() == 1)
- text << " is";
- else
- text << " are";
-}
-
-int Generator::skipAtoms(const Atom *atom, Atom::AtomType type) const
-{
- int skipAhead = 0;
- atom = atom->next();
- while (atom && atom->type() != type) {
- skipAhead++;
- atom = atom->next();
- }
- return skipAhead;
-}
-
-/*!
- Resets the variables used during text output.
- */
-void Generator::initializeTextOutput()
-{
- m_inLink = false;
- m_inContents = false;
- m_inSectionHeading = false;
- m_inTableHeader = false;
- m_numTableRows = 0;
- m_threeColumnEnumValueTable = true;
- m_link.clear();
- m_sectionNumber.clear();
-}
-
-void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
-{
- if (node->isFunction() && !node->isMacro()) {
- const auto fn = static_cast<const FunctionNode *>(node);
- if (fn->overloadNumber() == 0) {
- QString alternateName;
- const FunctionNode *alternateFunc = nullptr;
-
- if (fn->name().startsWith("set") && fn->name().size() >= 4) {
- alternateName = fn->name()[3].toLower();
- alternateName += fn->name().mid(4);
- alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
-
- if (!alternateFunc) {
- alternateName = "is" + fn->name().mid(3);
- alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
- if (!alternateFunc) {
- alternateName = "has" + fn->name().mid(3);
- alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
- }
- }
- } else if (!fn->name().isEmpty()) {
- alternateName = "set";
- alternateName += fn->name()[0].toUpper();
- alternateName += fn->name().mid(1);
- alternateFunc = fn->parent()->findFunctionChild(alternateName, QString());
- }
-
- if (alternateFunc && alternateFunc->access() != Access::Private) {
- int i;
- for (i = 0; i < alsoList.size(); ++i) {
- if (alsoList.at(i).toString().contains(alternateName))
- break;
- }
-
- if (i == alsoList.size()) {
- alternateName += "()";
-
- Text also;
- also << Atom(Atom::Link, alternateName)
- << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << alternateName
- << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
- alsoList.prepend(also);
- }
- }
- }
- }
-}
-
-void Generator::terminate()
-{
- for (const auto &generator : qAsConst(generators)) {
- if (outputFormats.contains(generator->format()))
- generator->terminateGenerator();
- }
-
- fmtLeftMaps.clear();
- fmtRightMaps.clear();
- imgFileExts.clear();
- imageFiles.clear();
- imageDirs.clear();
- outDir_.clear();
-}
-
-void Generator::terminateGenerator() {}
-
-/*!
- Trims trailing whitespace off the \a string and returns
- the trimmed string.
- */
-QString Generator::trimmedTrailing(const QString &string, const QString &prefix,
- const QString &suffix)
-{
- QString trimmed = string;
- while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace())
- trimmed.truncate(trimmed.length() - 1);
-
- trimmed.append(suffix);
- trimmed.prepend(prefix);
- return trimmed;
-}
-
-QString Generator::typeString(const Node *node)
-{
- switch (node->nodeType()) {
- case Node::Namespace:
- return "namespace";
- case Node::Class:
- return "class";
- case Node::Struct:
- return "struct";
- case Node::Union:
- return "union";
- case Node::QmlType:
- case Node::QmlBasicType:
- case Node::JsBasicType:
- return "type";
- case Node::Page:
- return "documentation";
- case Node::Enum:
- return "enum";
- case Node::Typedef:
- return "typedef";
- case Node::TypeAlias:
- return "alias";
- case Node::Function: {
- const auto fn = static_cast<const FunctionNode *>(node);
- switch (fn->metaness()) {
- case FunctionNode::JsSignal:
- case FunctionNode::QmlSignal:
- return "signal";
- case FunctionNode::JsSignalHandler:
- case FunctionNode::QmlSignalHandler:
- return "signal handler";
- case FunctionNode::JsMethod:
- case FunctionNode::QmlMethod:
- return "method";
- default:
- break;
- }
- return "function";
- }
- case Node::Property:
- case Node::QmlProperty:
- return "property";
- case Node::Module:
- case Node::JsModule:
- case Node::QmlModule:
- return "module";
- case Node::SharedComment: {
- const auto &collective = static_cast<const SharedCommentNode *>(node)->collective();
- return collective.first()->nodeTypeString();
- }
- default:
- return "documentation";
- }
-}
-
-void Generator::unknownAtom(const Atom *atom)
-{
- Location::internalError(QStringLiteral("unknown atom type '%1' in %2 generator")
- .arg(atom->typeString())
- .arg(format()));
-}
-
-QT_END_NAMESPACE