summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/qdoc/clangcodeparser.cpp1658
-rw-r--r--src/qdoc/clangcodeparser.h81
-rw-r--r--src/qdoc/codechunk.cpp27
-rw-r--r--src/qdoc/codeparser.cpp20
-rw-r--r--src/qdoc/codeparser.h10
-rw-r--r--src/qdoc/config.cpp3
-rw-r--r--src/qdoc/config.h6
-rw-r--r--src/qdoc/cppcodemarker.cpp41
-rw-r--r--src/qdoc/cppcodeparser.cpp1543
-rw-r--r--src/qdoc/cppcodeparser.h57
-rw-r--r--src/qdoc/doc.cpp225
-rw-r--r--src/qdoc/doc/qdoc-manual-cmdindex.qdoc2
-rw-r--r--src/qdoc/doc/qdoc-manual-markupcmds.qdoc127
-rw-r--r--src/qdoc/generator.cpp16
-rw-r--r--src/qdoc/htmlgenerator.cpp52
-rw-r--r--src/qdoc/location.cpp19
-rw-r--r--src/qdoc/main.cpp89
-rw-r--r--src/qdoc/node.cpp420
-rw-r--r--src/qdoc/node.h90
-rw-r--r--src/qdoc/puredocparser.cpp22
-rw-r--r--src/qdoc/puredocparser.h3
-rw-r--r--src/qdoc/qdoc.pro11
-rw-r--r--src/qdoc/qdocdatabase.h2
-rw-r--r--src/qdoc/qdocindexfiles.cpp17
-rw-r--r--src/qdoc/qmlcodeparser.cpp8
-rw-r--r--src/qdoc/qmlcodeparser.h1
-rw-r--r--src/qdoc/qmlvisitor.cpp2
-rw-r--r--src/qdoc/tree.cpp49
-rw-r--r--src/qdoc/tree.h2
-rw-r--r--src/src.pro3
30 files changed, 2475 insertions, 2131 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp
new file mode 100644
index 000000000..44da9d7ed
--- /dev/null
+++ b/src/qdoc/clangcodeparser.cpp
@@ -0,0 +1,1658 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ clangcodeparser.cpp
+*/
+
+#include <qfile.h>
+#include <stdio.h>
+#include <errno.h>
+#include "codechunk.h"
+#include "config.h"
+#include "clangcodeparser.h"
+#include "qdocdatabase.h"
+#include <qdebug.h>
+#include <qscopedvaluerollback.h>
+#include <qelapsedtimer.h>
+#include <qtemporarydir.h>
+#include "generator.h"
+
+#include <clang-c/Index.h>
+
+QT_BEGIN_NAMESPACE
+
+static CXTranslationUnit_Flags flags_ = (CXTranslationUnit_Flags)0;
+static CXIndex index_ = 0;
+
+/*!
+ Call clang_visitChildren on the given cursor with the lambda as a callback
+ T can be any functor that is callable with a CXCursor parameter and returns a CXChildVisitResult
+ (in other word compatible with function<CXChildVisitResult(CXCursor)>
+ */
+template <typename T> bool visitChildrenLambda(CXCursor cursor, T &&lambda)
+{
+ CXCursorVisitor visitor = [](CXCursor c, CXCursor , CXClientData client_data) -> CXChildVisitResult
+ { return (*static_cast<T*>(client_data))(c); };
+ return clang_visitChildren(cursor, visitor, &lambda);
+}
+
+/*!
+ convert a CXString to a QString, and dispose the CXString
+ */
+static QString fromCXString(CXString &&string)
+{
+ QString ret = QString::fromUtf8(clang_getCString(string));
+ clang_disposeString(string);
+ return ret;
+}
+
+
+/*!
+ convert a CXSourceLocation to a qdoc Location
+ */
+static Location fromCXSourceLocation(CXSourceLocation location)
+{
+ unsigned int line, column;
+ CXString file;
+ clang_getPresumedLocation(location, &file, &line, &column);
+ Location l(fromCXString(std::move(file)));
+ l.setColumnNo(column);
+ l.setLineNo(line);
+ return l;
+}
+
+/*!
+ convert a CX_CXXAccessSpecifier to Node::Access
+ */
+static Node::Access fromCX_CXXAccessSpecifier(CX_CXXAccessSpecifier spec) {
+ switch (spec) {
+ case CX_CXXPrivate: return Node::Private;
+ case CX_CXXProtected: return Node::Protected;
+ case CX_CXXPublic: return Node::Public;
+ default: return Node::Public;
+ }
+}
+
+/*!
+ Returns the spelling in the file for a source range
+ */
+static QString getSpelling(CXSourceRange range)
+{
+ auto start = clang_getRangeStart(range);
+ auto end = clang_getRangeEnd(range);
+ CXFile file1, file2;
+ unsigned int offset1, offset2;
+ clang_getFileLocation(start, &file1, nullptr, nullptr, &offset1);
+ clang_getFileLocation(end, &file2, nullptr, nullptr, &offset2);
+ if (file1 != file2 || offset2 <= offset1)
+ return QString();
+ QFile file(fromCXString(clang_getFileName(file1)));
+ if (!file.open(QFile::ReadOnly))
+ return QString();
+ file.seek(offset1);
+ return QString::fromUtf8(file.read(offset2 - offset1));
+}
+
+
+/*!
+ Returns the function name from a given cursor representing a
+ function declaration. This is usually clang_getCursorSpelling, but
+ not for the conversion function in which case it is a bit more complicated
+ */
+QString functionName(CXCursor cursor)
+{
+ if (clang_getCursorKind(cursor) == CXCursor_ConversionFunction) {
+ // For a CXCursor_ConversionFunction we don't want the spelling which would be something like
+ // "operator type-parameter-0-0" or "operator unsigned int".
+ // we want the actual name as spelled;
+ QString type = fromCXString(clang_getTypeSpelling(clang_getCursorResultType(cursor)));
+ if (type.isEmpty())
+ return fromCXString(clang_getCursorSpelling(cursor));
+ return QLatin1String("operator ") + type;
+ }
+
+ QString name = fromCXString(clang_getCursorSpelling(cursor));
+
+ // Remove template stuff from constructor and destructor but not from operator<
+ auto ltLoc = name.indexOf('<');
+ if (ltLoc > 0 && !name.startsWith("operator<"))
+ name = name.left(ltLoc);
+ return name;
+}
+
+/*!
+ Find the node from the QDocDatabase \a qdb that corrseponds to the declaration
+ represented by the cursor \a cur, if it exists.
+ */
+static Node *findNodeForCursor(QDocDatabase* qdb, CXCursor cur) {
+ auto kind = clang_getCursorKind(cur);
+ if (clang_isInvalid(kind))
+ return nullptr;
+ if (kind == CXCursor_TranslationUnit)
+ return qdb->primaryTreeRoot();
+
+ Node *p = findNodeForCursor(qdb, clang_getCursorSemanticParent(cur));
+ if (!p)
+ return nullptr;
+ if (!p->isAggregate())
+ return nullptr;
+ auto parent = static_cast<Aggregate *>(p);
+
+ switch (kind) {
+ case CXCursor_Namespace:
+ return parent->findChildNode(fromCXString(clang_getCursorSpelling(cur)), Node::Namespace);
+ case CXCursor_StructDecl:
+ case CXCursor_ClassDecl:
+ case CXCursor_UnionDecl:
+ case CXCursor_ClassTemplate:
+ return parent->findChildNode(fromCXString(clang_getCursorSpelling(cur)), Node::Class);
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_CXXMethod:
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ case CXCursor_ConversionFunction: {
+ NodeList candidates;
+ parent->findChildren(functionName(cur), candidates);
+ if (candidates.isEmpty())
+ return nullptr;
+ CXType funcType = clang_getCursorType(cur);
+ auto numArg = clang_getNumArgTypes(funcType);
+ bool isVariadic = clang_isFunctionTypeVariadic(funcType);
+ QVarLengthArray<QString, 20> args;
+ for (Node *candidate : qAsConst(candidates)) {
+ if (!candidate->isFunction())
+ continue;
+ auto fn = static_cast<FunctionNode*>(candidate);
+ const auto &funcParams = fn->parameters();
+ const int actualArg = numArg - fn->isPrivateSignal();
+ if (funcParams.count() != (actualArg + isVariadic))
+ continue;
+ if (fn->isConst() != bool(clang_CXXMethod_isConst(cur)))
+ continue;
+ if (isVariadic && funcParams.last().dataType() != QLatin1String("..."))
+ continue;
+ bool different = false;
+ for (int i = 0; i < actualArg; i++) {
+ if (args.size() <= i)
+ args.append(fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i))));
+ QString t1 = funcParams.at(i).dataType();
+ QString t2 = args.at(i);
+ auto p2 = parent;
+ while (p2 && t1 != t2) {
+ QString parentScope = p2->name() + QLatin1String("::");
+ t1 = t1.remove(parentScope);
+ t2 = t2.remove(parentScope);
+ p2 = p2->parent();
+ }
+ if (t1 != t2) {
+ different = true;
+ break;
+ }
+ }
+ if (!different)
+ return fn;
+ }
+ return nullptr;
+ }
+ case CXCursor_EnumDecl:
+ return parent->findChildNode(fromCXString(clang_getCursorSpelling(cur)), Node::Enum);
+ case CXCursor_FieldDecl:
+ case CXCursor_VarDecl:
+ return parent->findChildNode(fromCXString(clang_getCursorSpelling(cur)), Node::Variable);
+ case CXCursor_TypedefDecl:
+ return parent->findChildNode(fromCXString(clang_getCursorSpelling(cur)), Node::Typedef);
+ default:
+ return nullptr;
+ }
+}
+
+/*!
+ Find the function node from the QDocDatabase \a qdb that
+ corrseponds to the declaration represented by the cursor
+ \a cur, if it exists.
+ */
+static Node *findFunctionNodeForCursor(QDocDatabase* qdb, CXCursor cur) {
+ auto kind = clang_getCursorKind(cur);
+ if (clang_isInvalid(kind))
+ return nullptr;
+ if (kind == CXCursor_TranslationUnit)
+ return qdb->primaryTreeRoot();
+
+ Node *p = findNodeForCursor(qdb, clang_getCursorSemanticParent(cur));
+ if (!p || !p->isAggregate())
+ return nullptr;
+ auto parent = static_cast<Aggregate *>(p);
+
+ switch (kind) {
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_CXXMethod:
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ case CXCursor_ConversionFunction: {
+ NodeList candidates;
+ parent->findChildren(functionName(cur), candidates);
+ if (candidates.isEmpty())
+ return nullptr;
+ CXType funcType = clang_getCursorType(cur);
+ auto numArg = clang_getNumArgTypes(funcType);
+ bool isVariadic = clang_isFunctionTypeVariadic(funcType);
+ QVarLengthArray<QString, 20> args;
+ for (Node *candidate : qAsConst(candidates)) {
+ if (!candidate->isFunction())
+ continue;
+ auto fn = static_cast<FunctionNode*>(candidate);
+ const auto &funcParams = fn->parameters();
+ if (funcParams.count() != (numArg + isVariadic))
+ continue;
+ if (fn->isConst() != bool(clang_CXXMethod_isConst(cur)))
+ continue;
+ if (isVariadic && funcParams.last().dataType() != QLatin1String("..."))
+ continue;
+ bool different = false;
+ for (int i = 0; i < numArg; i++) {
+ if (args.size() <= i)
+ args.append(fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i))));
+ QString t1 = funcParams.at(i).dataType();
+ QString t2 = args.at(i);
+ auto p2 = parent;
+ while (p2 && t1 != t2) {
+ QString parentScope = p2->name() + QLatin1String("::");
+ t1 = t1.remove(parentScope);
+ t2 = t2.remove(parentScope);
+ p2 = p2->parent();
+ }
+ if (t1 != t2) {
+ different = true;
+ break;
+ }
+ }
+ if (!different)
+ return fn;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+class ClangVisitor {
+public:
+ ClangVisitor(QDocDatabase *qdb, const QSet<QString> &allHeaders)
+ : qdb_(qdb), parent_(qdb->primaryTreeRoot()), allHeaders_(allHeaders) { }
+
+ QDocDatabase* qdocDB() { return qdb_; }
+
+ CXChildVisitResult visitChildren(CXCursor cursor)
+ {
+ auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
+ auto loc = clang_getCursorLocation(cur);
+ if (clang_Location_isFromMainFile(loc))
+ return visitSource(cur, loc);
+ CXFile file;
+ clang_getFileLocation(loc, &file, nullptr, nullptr, nullptr);
+ bool isInteresting = false;
+ auto it = isInterestingCache_.find(file);
+ if (it != isInterestingCache_.end()) {
+ isInteresting = *it;
+ } else {
+ QFileInfo fi(fromCXString(clang_getFileName(file)));
+ isInteresting = allHeaders_.contains(fi.canonicalFilePath());
+ isInterestingCache_[file] = isInteresting;
+ }
+
+ if (isInteresting) {
+ return visitHeader(cur, loc);
+ }
+
+ return CXChildVisit_Continue;
+ });
+ return ret ? CXChildVisit_Break : CXChildVisit_Continue;
+ }
+
+ /*
+ Not sure about all the possibilities, when the cursor
+ location is not in the main file.
+ */
+ CXChildVisitResult visitFnArg(CXCursor cursor, Node** fnNode, bool &ignoreSignature)
+ {
+ auto ret = visitChildrenLambda(cursor, [&](CXCursor cur) {
+ auto loc = clang_getCursorLocation(cur);
+ if (clang_Location_isFromMainFile(loc))
+ return visitFnSignature(cur, loc, fnNode, ignoreSignature);
+ return CXChildVisit_Continue;
+ });
+ return ret ? CXChildVisit_Break : CXChildVisit_Continue;
+ }
+
+ Node *nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc);
+private:
+ /*!
+ SimpleLoc represents a simple location in the main source file,
+ which can be used as a key in a QMap.
+ */
+ struct SimpleLoc
+ {
+ unsigned int line, column;
+ friend bool operator<(const SimpleLoc &a, const SimpleLoc &b) {
+ return a.line != b.line ? a.line < b.line : a.column < b.column;
+ }
+ };
+ /*!
+ \variable ClangVisitor::declMap_
+ Map of all the declarations in the source file so we can match them
+ with a documentation comment.
+ */
+ QMap<SimpleLoc, CXCursor> declMap_;
+
+ QDocDatabase* qdb_;
+ Aggregate *parent_;
+ QSet<QString> allHeaders_;
+ QHash<CXFile, bool> isInterestingCache_; // doing a canonicalFilePath is slow, so keep a cache.
+
+ /*!
+ Returns true if the symbol should be ignored for the documentation.
+ */
+ bool ignoredSymbol(const QString &symbolName) {
+ if (symbolName.startsWith(QLatin1String("qt_")))
+ return true;
+ if (symbolName == QLatin1String("QPrivateSignal"))
+ return true;
+ if (parent_->name() != "QObject" && parent_->name() != "QMetaType"
+ && (symbolName == QLatin1String("metaObject") || symbolName == QLatin1String("tr")
+ || symbolName == QLatin1String("trUtf8")))
+ return true;
+ return false;
+ }
+
+ /*!
+ The type parameters do not need to be fully qualified
+ This function removes the ClassName:: if needed.
+
+ example: 'QLinkedList::iterator' -> 'iterator'
+ */
+ QString adjustTypeName(const QString &typeName) {
+ auto parent = parent_->parent();
+ if (parent && parent->isClass()) {
+ QStringRef typeNameConstRemoved(&typeName);
+ if (typeNameConstRemoved.startsWith(QLatin1String("const ")))
+ typeNameConstRemoved = typeName.midRef(6);
+
+ auto parentName = parent->fullName();
+ if (typeNameConstRemoved.startsWith(parentName)
+ && typeNameConstRemoved.mid(parentName.size(), 2) == QLatin1String("::")) {
+ QString result = typeName;
+ result.remove(typeNameConstRemoved.position(), parentName.size() + 2);
+ return result;
+ }
+ }
+ return typeName;
+ }
+
+ CXChildVisitResult visitSource(CXCursor cursor, CXSourceLocation loc);
+ CXChildVisitResult visitHeader(CXCursor cursor, CXSourceLocation loc);
+ CXChildVisitResult visitFnSignature(CXCursor cursor, CXSourceLocation loc, Node** fnNode, bool &ignoreSignature);
+ void parseProperty(const QString &spelling, const Location &loc);
+ void readParameterNamesAndAttributes(FunctionNode* fn, CXCursor cursor);
+ Aggregate *getSemanticParent(CXCursor cursor);
+};
+
+/*!
+ Visits a cursor in the .cpp file.
+ This fills the declMap_
+ */
+CXChildVisitResult ClangVisitor::visitSource(CXCursor cursor, CXSourceLocation loc)
+{
+ auto kind = clang_getCursorKind(cursor);
+ if (clang_isDeclaration(kind)) {
+ SimpleLoc l;
+ clang_getPresumedLocation(loc, nullptr, &l.line, &l.column);
+ declMap_.insert(l, cursor);
+ return CXChildVisit_Recurse;
+ }
+ return CXChildVisit_Continue;
+}
+
+/*!
+ If the semantic and lexical parent cursors of \a cursor are
+ not the same, find the Aggregate node for the semantic parent
+ cursor and return it. Otherwise return the current parent.
+ */
+Aggregate *ClangVisitor::getSemanticParent(CXCursor cursor)
+{
+ CXCursor sp = clang_getCursorSemanticParent(cursor);
+ CXCursor lp = clang_getCursorLexicalParent(cursor);
+ if (!clang_equalCursors(sp, lp) && clang_isDeclaration(clang_getCursorKind(sp))) {
+ Node* spn = findNodeForCursor(qdb_, sp);
+ if (spn && spn->isAggregate()) {
+ return static_cast<Aggregate*>(spn);
+ }
+ }
+ return parent_;
+}
+
+CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocation , Node** fnNode, bool &ignoreSignature)
+{
+ switch (clang_getCursorKind(cursor)) {
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_CXXMethod:
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ case CXCursor_ConversionFunction: {
+ ignoreSignature = false;
+ if (ignoredSymbol(functionName(cursor))) {
+ *fnNode = 0;
+ ignoreSignature = true;
+ } else {
+ *fnNode = findFunctionNodeForCursor(qdb_, cursor);
+ if (*fnNode && (*fnNode)->isFunction()) {
+ FunctionNode* fn = static_cast<FunctionNode*>(*fnNode);
+ readParameterNamesAndAttributes(fn, cursor);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return CXChildVisit_Continue;
+}
+
+CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
+{
+ auto kind = clang_getCursorKind(cursor);
+ switch (kind) {
+ case CXCursor_TypeAliasDecl: {
+ QString spelling = getSpelling(clang_getCursorExtent(cursor));
+ QStringList typeAlias = spelling.split(QChar('='));
+ if (typeAlias.size() == 2) {
+ typeAlias[0] = typeAlias[0].trimmed();
+ typeAlias[1] = typeAlias[1].trimmed();
+ int lastBlank = typeAlias[0].lastIndexOf(QChar(' '));
+ if (lastBlank > 0) {
+ typeAlias[0] = typeAlias[0].right(typeAlias[0].size() - (lastBlank + 1));
+ TypeAliasNode* ta = new TypeAliasNode(parent_, typeAlias[0], typeAlias[1]);
+ ta->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
+ ta->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+ }
+ }
+ return CXChildVisit_Continue;
+ }
+ case CXCursor_StructDecl:
+ case CXCursor_UnionDecl:
+ if (fromCXString(clang_getCursorSpelling(cursor)).isEmpty()) // anonymous struct or union
+ return CXChildVisit_Continue;
+ case CXCursor_ClassDecl:
+ case CXCursor_ClassTemplate: {
+
+ if (!clang_isCursorDefinition(cursor))
+ return CXChildVisit_Continue;
+
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit
+ return CXChildVisit_Continue;
+
+ QString className = fromCXString(clang_getCursorSpelling(cursor));
+
+ Aggregate* semanticParent = getSemanticParent(cursor);
+ if (semanticParent && semanticParent->findChildNode(className, Node::Class)) {
+ return CXChildVisit_Continue;
+ }
+
+ ClassNode *classe = new ClassNode(semanticParent, className);
+ classe->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
+ classe->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+
+ if (kind == CXCursor_ClassTemplate) {
+ QString displayName = fromCXString(clang_getCursorSpelling(cursor));
+ classe->setTemplateStuff(displayName.mid(className.size()));
+ }
+
+ QScopedValueRollback<Aggregate *> setParent(parent_, classe);
+ return visitChildren(cursor);
+
+ }
+ case CXCursor_CXXBaseSpecifier: {
+ if (!parent_->isClass())
+ return CXChildVisit_Continue;
+ auto access = fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor));
+ auto type = clang_getCursorType(cursor);
+ auto baseCursor = clang_getTypeDeclaration(type);
+ auto baseNode = findNodeForCursor(qdb_, baseCursor);
+ auto classe = static_cast<ClassNode*>(parent_);
+ if (!baseNode || !baseNode->isClass()) {
+ QString bcName = fromCXString(clang_getCursorSpelling(baseCursor));
+ classe->addUnresolvedBaseClass(access, QStringList(bcName), bcName);
+ return CXChildVisit_Continue;
+ }
+ auto baseClasse = static_cast<ClassNode*>(baseNode);
+ classe->addResolvedBaseClass(access, baseClasse);
+ return CXChildVisit_Continue;
+ }
+ case CXCursor_Namespace: {
+ QString namespaceName = fromCXString(clang_getCursorDisplayName(cursor));
+ NamespaceNode* ns = 0;
+ if (parent_)
+ ns = static_cast<NamespaceNode*>(parent_->findChildNode(namespaceName, Node::Namespace));
+ if (!ns) {
+ ns = new NamespaceNode(parent_, namespaceName);
+ ns->setAccess(Node::Public);
+ ns->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+ }
+ QScopedValueRollback<Aggregate *> setParent(parent_, ns);
+ return visitChildren(cursor);
+ }
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_CXXMethod:
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ case CXCursor_ConversionFunction: {
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit
+ return CXChildVisit_Continue;
+ QString name = functionName(cursor);
+ if (ignoredSymbol(name))
+ return CXChildVisit_Continue;
+
+ CXType funcType = clang_getCursorType(cursor);
+
+ FunctionNode* fn = new FunctionNode(Node::Function, parent_, name, false);
+
+ CXSourceRange range = clang_Cursor_getCommentRange(cursor);
+ if (!clang_Range_isNull(range)) {
+ QString comment = getSpelling(range);
+ if (comment.startsWith("//!")) {
+ int tag = comment.indexOf(QChar('['));
+ if (tag > 0) {
+ int end = comment.indexOf(QChar(']'), tag);
+ if (end > 0)
+ fn->setTag(comment.mid(tag, 1 + end - tag));
+ }
+ }
+ }
+ fn->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
+ fn->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+ if (kind == CXCursor_Constructor
+ // a constructor template is classified as CXCursor_FunctionTemplate
+ || (kind == CXCursor_FunctionTemplate && name == parent_->name()))
+ fn->setMetaness(FunctionNode::Ctor);
+ else if (kind == CXCursor_Destructor)
+ fn->setMetaness(FunctionNode::Dtor);
+ else
+ fn->setReturnType(adjustTypeName(fromCXString(
+ clang_getTypeSpelling(clang_getResultType(funcType)))));
+
+ fn->setStatic(clang_CXXMethod_isStatic(cursor));
+ fn->setConst(clang_CXXMethod_isConst(cursor));
+ fn->setVirtualness(!clang_CXXMethod_isVirtual(cursor) ? FunctionNode::NonVirtual
+ : clang_CXXMethod_isPureVirtual(cursor) ? FunctionNode::PureVirtual
+ : FunctionNode::NormalVirtual);
+ CXRefQualifierKind refQualKind = clang_Type_getCXXRefQualifier(funcType);
+ if (refQualKind == CXRefQualifier_LValue)
+ fn->setRef(true);
+ else if (refQualKind == CXRefQualifier_RValue)
+ fn->setRefRef(true);
+ // For virtual functions, determine what it overrides
+ // (except for destructor for which we do not want to classify as overridden)
+ if (!fn->isNonvirtual() && kind != CXCursor_Destructor) {
+ CXCursor *overridden;
+ unsigned int numOverridden = 0;
+ clang_getOverriddenCursors(cursor, &overridden, &numOverridden);
+ for (uint i = 0; i < numOverridden; ++i) {
+ auto n = findNodeForCursor(qdb_, overridden[i]);
+ if (n && n->isFunction()) {
+ fn->setReimplementedFrom(static_cast<FunctionNode *>(n));
+ }
+ }
+ clang_disposeOverriddenCursors(overridden);
+ }
+ auto numArg = clang_getNumArgTypes(funcType);
+ QVector<Parameter> pvect;
+ pvect.reserve(numArg);
+ for (int i = 0; i < numArg; ++i) {
+ CXType argType = clang_getArgType(funcType, i);
+ if (fn->isCtor()) {
+ if (fromCXString(clang_getTypeSpelling(clang_getPointeeType(argType))) == name) {
+ if (argType.kind == CXType_RValueReference)
+ fn->setMetaness(FunctionNode::MCtor);
+ else if (argType.kind == CXType_LValueReference)
+ fn->setMetaness(FunctionNode::CCtor);
+ }
+ } else if ((kind == CXCursor_CXXMethod) && (name == QLatin1String("operator="))) {
+ if (argType.kind == CXType_RValueReference)
+ fn->setMetaness(FunctionNode::MAssign);
+ else if (argType.kind == CXType_LValueReference)
+ fn->setMetaness(FunctionNode::CAssign);
+ }
+ pvect.append(Parameter(adjustTypeName(fromCXString(clang_getTypeSpelling(argType)))));
+ }
+ if (pvect.size() > 0 && pvect.last().dataType().endsWith(QLatin1String("::QPrivateSignal"))) {
+ pvect.pop_back(); // remove the QPrivateSignal argument
+ fn->setPrivateSignal();
+ }
+ if (clang_isFunctionTypeVariadic(funcType))
+ pvect.append(Parameter(QStringLiteral("...")));
+ fn->setParameters(pvect);
+ readParameterNamesAndAttributes(fn, cursor);
+ return CXChildVisit_Continue;
+ }
+#if CINDEX_VERSION >= 36
+ case CXCursor_FriendDecl: {
+ // Friend functions are declared in the enclosing namespace
+ Aggregate *ns = parent_;
+ while (ns && ns->isClass())
+ ns = ns->parent();
+ QScopedValueRollback<Aggregate *> setParent(parent_, ns);
+ // Visit the friend functions
+ return visitChildren(cursor);
+ }
+#endif
+ case CXCursor_EnumDecl: {
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another tu
+ return CXChildVisit_Continue;
+ QString enumTypeName = fromCXString(clang_getCursorSpelling(cursor));
+ if (enumTypeName.isEmpty())
+ enumTypeName = "anonymous";
+ auto en = new EnumNode(parent_, enumTypeName);
+ en->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
+ en->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+ // Enum values
+ visitChildrenLambda(cursor, [&](CXCursor cur) {
+ if (clang_getCursorKind(cur) != CXCursor_EnumConstantDecl)
+ return CXChildVisit_Continue;
+
+
+ QString value;
+ visitChildrenLambda(cur, [&](CXCursor cur) {
+ if (clang_isExpression(clang_getCursorKind(cur))) {
+ value = getSpelling(clang_getCursorExtent(cur));
+ return CXChildVisit_Break;
+ }
+ return CXChildVisit_Continue;
+ });
+ if (value.isEmpty()) {
+ QLatin1String hex("0x");
+ if (!en->items().isEmpty() && en->items().last().value().startsWith(hex)) {
+ value = hex + QString::number(clang_getEnumConstantDeclValue(cur), 16);
+ } else {
+ value = QString::number(clang_getEnumConstantDeclValue(cur));
+ }
+ }
+
+ en->addItem(EnumItem(fromCXString(clang_getCursorSpelling(cur)), value));
+ return CXChildVisit_Continue;
+ });
+ return CXChildVisit_Continue;
+ }
+ case CXCursor_FieldDecl:
+ case CXCursor_VarDecl: {
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit
+ return CXChildVisit_Continue;
+ auto access = fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor));
+ auto var = new VariableNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
+ var->setAccess(access);
+ var->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+ var->setLeftType(fromCXString(clang_getTypeSpelling(clang_getCursorType(cursor))));
+ var->setStatic(kind == CXCursor_VarDecl && parent_->isClass());
+ return CXChildVisit_Continue;
+ }
+ case CXCursor_TypedefDecl: {
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit
+ return CXChildVisit_Continue;
+ TypedefNode* td = new TypedefNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
+ td->setAccess(fromCX_CXXAccessSpecifier(clang_getCXXAccessSpecifier(cursor)));
+ td->setLocation(fromCXSourceLocation(clang_getCursorLocation(cursor)));
+ // Search to see if this is a Q_DECLARE_FLAGS (if the type is QFlags<ENUM>)
+ visitChildrenLambda(cursor, [&](CXCursor cur) {
+ if (clang_getCursorKind(cur) != CXCursor_TemplateRef
+ || fromCXString(clang_getCursorSpelling(cur)) != QLatin1String("QFlags"))
+ return CXChildVisit_Continue;
+ // Found QFlags<XXX>
+ visitChildrenLambda(cursor, [&](CXCursor cur) {
+ if (clang_getCursorKind(cur) != CXCursor_TypeRef)
+ return CXChildVisit_Continue;
+ auto *en = findNodeForCursor(qdb_, clang_getTypeDeclaration(clang_getCursorType(cur)));
+ if (en && en->isEnumType())
+ static_cast<EnumNode*>(en)->setFlagsType(td);
+ return CXChildVisit_Break;
+ });
+ return CXChildVisit_Break;
+ });
+ return CXChildVisit_Continue;
+ }
+ default:
+ if (clang_isDeclaration(kind) && parent_->isClass()) {
+ // maybe a static_assert (which is not exposed from the clang API)
+ QString spelling = getSpelling(clang_getCursorExtent(cursor));
+ if (spelling.startsWith(QLatin1String("Q_PROPERTY"))
+ || spelling.startsWith(QLatin1String("QDOC_PROPERTY"))
+ || spelling.startsWith(QLatin1String("Q_OVERRIDE"))) {
+ parseProperty(spelling, fromCXSourceLocation(loc));
+ }
+ }
+ return CXChildVisit_Continue;
+ }
+}
+
+void ClangVisitor::readParameterNamesAndAttributes(FunctionNode* fn, CXCursor cursor)
+{
+ auto pvect = fn->parameters();
+ // Visit the parameters and attributes
+ int i = 0;
+ visitChildrenLambda(cursor, [&](CXCursor cur) {
+ auto kind = clang_getCursorKind(cur);
+ if (kind == CXCursor_AnnotateAttr) {
+ QString annotation = fromCXString(clang_getCursorDisplayName(cur));
+ if (annotation == QLatin1String("qt_slot")) {
+ fn->setMetaness(FunctionNode::Slot);
+ } else if (annotation == QLatin1String("qt_signal")) {
+ fn->setMetaness(FunctionNode::Signal);
+ }
+ } else if (kind == CXCursor_ParmDecl) {
+ if (i >= pvect.count())
+ return CXChildVisit_Break; // Attributes comes before parameters so we can break.
+ QString name = fromCXString(clang_getCursorSpelling(cur));
+ if (!name.isEmpty())
+ pvect[i].setName(name);
+ // Find the default value
+ visitChildrenLambda(cur, [&](CXCursor cur) {
+ if (clang_isExpression(clang_getCursorKind(cur))) {
+ QString defaultValue = getSpelling(clang_getCursorExtent(cur));
+ if (defaultValue.startsWith('=')) // In some cases, the = is part of the range.
+ defaultValue = defaultValue.midRef(1).trimmed().toString();
+ if (defaultValue.isEmpty())
+ defaultValue = QStringLiteral("...");
+ pvect[i].setDefaultValue(defaultValue);
+ return CXChildVisit_Break;
+ }
+ return CXChildVisit_Continue;
+ });
+ i++;
+ }
+ return CXChildVisit_Continue;
+ });
+ fn->setParameters(pvect);
+}
+
+void ClangVisitor::parseProperty(const QString& spelling, const Location& loc)
+{
+ int lpIdx = spelling.indexOf(QChar('('));
+ int rpIdx = spelling.lastIndexOf(QChar(')'));
+ if (lpIdx <= 0 || rpIdx <= lpIdx)
+ return;
+ QString signature = spelling.mid(lpIdx + 1, rpIdx - lpIdx - 1);
+ signature = signature.simplified();
+ QStringList part = signature.split(QChar(' '));
+ if (part.size() < 2)
+ return;
+ QString type = part.at(0);
+ QString name = part.at(1);
+ if (name.at(0) == QChar('*')) {
+ type.append(QChar('*'));
+ name.remove(0,1);
+ }
+ auto *property = new PropertyNode(parent_, name);
+ property->setAccess(Node::Public);
+ property->setLocation(loc);
+ property->setDataType(type);
+ int i = 2;
+ while (i < part.size()) {
+ QString key = part.at(i++);
+ // Keywords with no associated values
+ if (key == "CONSTANT") {
+ property->setConstant();
+ } else if (key == "FINAL") {
+ property->setFinal();
+ }
+ if (i < part.size()) {
+ QString value = part.at(i++);
+ if (key == "READ") {
+ qdb_->addPropertyFunction(property, value, PropertyNode::Getter);
+ } else if (key == "WRITE") {
+ qdb_->addPropertyFunction(property, value, PropertyNode::Setter);
+ property->setWritable(true);
+ } else if (key == "STORED") {
+ property->setStored(value.toLower() == "true");
+ } else if (key == "DESIGNABLE") {
+ QString v = value.toLower();
+ if (v == "true")
+ property->setDesignable(true);
+ else if (v == "false")
+ property->setDesignable(false);
+ else {
+ property->setDesignable(false);
+ property->setRuntimeDesFunc(value);
+ }
+ } else if (key == "RESET") {
+ qdb_->addPropertyFunction(property, value, PropertyNode::Resetter);
+ } else if (key == "NOTIFY") {
+ qdb_->addPropertyFunction(property, value, PropertyNode::Notifier);
+ } else if (key == "REVISION") {
+ int revision;
+ bool ok;
+ revision = value.toInt(&ok);
+ if (ok)
+ property->setRevision(revision);
+ else
+ loc.warning(ClangCodeParser::tr("Invalid revision number: %1").arg(value));
+ } else if (key == "SCRIPTABLE") {
+ QString v = value.toLower();
+ if (v == "true")
+ property->setScriptable(true);
+ else if (v == "false")
+ property->setScriptable(false);
+ else {
+ property->setScriptable(false);
+ property->setRuntimeScrFunc(value);
+ }
+ }
+ }
+ }
+}
+
+/*!
+ Given a comment at location \a loc, return a Node for this comment
+ \a nextCommentLoc is the location of the next comment so the declaration
+ must be inbetween.
+ Returns nullptr if no suitable declaration was found between the two comments.
+ */
+Node* ClangVisitor::nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc)
+{
+ ClangVisitor::SimpleLoc docloc;
+ clang_getPresumedLocation(loc, nullptr, &docloc.line, &docloc.column);
+ auto decl_it = declMap_.upperBound(docloc);
+ if (decl_it == declMap_.end())
+ return nullptr;
+
+ unsigned int declLine = decl_it.key().line;
+ unsigned int nextCommentLine;
+ clang_getPresumedLocation(nextCommentLoc, nullptr, &nextCommentLine, nullptr);
+ if (nextCommentLine < declLine)
+ return nullptr; // there is another comment before the declaration, ignore it.
+
+ // make sure the previous decl was finished.
+ if (decl_it != declMap_.begin()) {
+ CXSourceLocation prevDeclEnd = clang_getRangeEnd(clang_getCursorExtent(*(decl_it-1)));
+ unsigned int prevDeclLine;
+ clang_getPresumedLocation(prevDeclEnd, nullptr, &prevDeclLine, nullptr);
+ if (prevDeclLine >= docloc.line) {
+ // The previous declaration was still going. This is only valid if the previous
+ // declaration is a parent of the next declaration.
+ auto parent = clang_getCursorLexicalParent(*decl_it);
+ if (!clang_equalCursors(parent, *(decl_it-1)))
+ return nullptr;
+ }
+ }
+ auto *node = findNodeForCursor(qdb_, *decl_it);
+ // borrow the parameter name from the definition
+ if (node && node->isFunction())
+ readParameterNamesAndAttributes(static_cast<FunctionNode*>(node), *decl_it);
+ return node;
+}
+
+/*!
+ The destructor is trivial.
+ */
+ClangCodeParser::~ClangCodeParser()
+{
+ // nothing.
+}
+
+/*!
+ Get the include paths from the qdoc configuration database
+ \a config. Call the initializeParser() in the base class.
+ Get the defines list from the qdocconf database.
+ */
+void ClangCodeParser::initializeParser(const Config &config)
+{
+ const auto args = config.getStringList(CONFIG_INCLUDEPATHS);
+ includePaths_.resize(args.size());
+ std::transform(args.begin(), args.end(), includePaths_.begin(),
+ [](const QString &s) { return s.toUtf8(); });
+ CppCodeParser::initializeParser(config);
+ pchFileDir_.reset(nullptr);
+ allHeaders_.clear();
+ pchName_.clear();
+ defines_.clear();
+ QSet<QString> accepted;
+ {
+ const QStringList tmpDefines = config.getStringList(CONFIG_CLANGDEFINES);
+ for (const QString &def : tmpDefines) {
+ if (!accepted.contains(def)) {
+ QByteArray tmp("-D");
+ tmp.append(def.toUtf8());
+ defines_.append(tmp.constData());
+ accepted.insert(def);
+ }
+ }
+ }
+ {
+ const QStringList tmpDefines = config.getStringList(CONFIG_DEFINES);
+ for (const QString &def : tmpDefines) {
+ if (!accepted.contains(def) && !def.contains(QChar('*'))) {
+ QByteArray tmp("-D");
+ tmp.append(def.toUtf8());
+ defines_.append(tmp.constData());
+ accepted.insert(def);
+ }
+ }
+ }
+}
+
+/*!
+ */
+void ClangCodeParser::terminateParser()
+{
+ CppCodeParser::terminateParser();
+}
+
+/*!
+ */
+QString ClangCodeParser::language()
+{
+ return "Clang";
+}
+
+/*!
+ Returns a list of extensions for header files.
+ */
+QStringList ClangCodeParser::headerFileNameFilter()
+{
+ return QStringList() << "*.ch" << "*.h" << "*.h++" << "*.hh" << "*.hpp" << "*.hxx";
+}
+
+/*!
+ Returns a list of extensions for source files, i.e. not
+ header files.
+ */
+QStringList ClangCodeParser::sourceFileNameFilter()
+{
+ return QStringList() << "*.c++" << "*.cc" << "*.cpp" << "*.cxx" << "*.mm";
+}
+
+/*!
+ Parse the C++ header file identified by \a filePath and add
+ the parsed contents to the database. The \a location is used
+ for reporting errors.
+ */
+void ClangCodeParser::parseHeaderFile(const Location & /*location*/, const QString &filePath)
+{
+ QFileInfo fi(filePath);
+ allHeaders_.insert(fi.canonicalFilePath());
+}
+
+static const char *defaultArgs_[] = {
+ "-std=c++14",
+ "-fPIC",
+ "-fno-exceptions", // Workaround for clang bug http://reviews.llvm.org/D17988
+ "-DQ_QDOC",
+ "-DQT_DISABLE_DEPRECATED_BEFORE=0",
+ "-DQT_ANNOTATE_CLASS(type,...)=static_assert(sizeof(#__VA_ARGS__), #type);",
+ "-DQT_ANNOTATE_CLASS2(type,a1,a2)=static_assert(sizeof(#a1, #a2), #type);",
+ "-DQT_ANNOTATE_FUNCTION(a)=__attribute__((annotate(#a)))",
+ "-DQT_ANNOTATE_ACCESS_SPECIFIER(a)=__attribute__((annotate(#a)))",
+ "-Wno-constant-logical-operand",
+#ifdef Q_OS_WIN
+ "-fms-compatibility-version=19",
+#endif
+ "-I" CLANG_RESOURCE_DIR
+};
+
+/*!
+ Load the default arguments and the defines into \a args.
+ Clear \a args first.
+ */
+void ClangCodeParser::getDefaultArgs()
+{
+ args_.clear();
+ args_.insert(args_.begin(), std::begin(defaultArgs_), std::end(defaultArgs_));
+ // Add the defines from the qdocconf file.
+ for (const auto &p : qAsConst(defines_))
+ args_.push_back(p.constData());
+}
+
+/*!
+ Load the include paths into \a moreArgs.
+ */
+void ClangCodeParser::getMoreArgs()
+{
+ if (includePaths_.isEmpty() || includePaths_.at(0) != QByteArray("-I")) {
+ /*
+ The include paths provided are inadequate. Make a list
+ of reasonable places to look for include files and use
+ that list instead.
+ */
+ QList<QString> headers = allHeaders_.values();
+ QString filePath = headers.at(0);
+ auto forest = qdb_->searchOrder();
+
+ QByteArray version = qdb_->version().toUtf8();
+ QString basicIncludeDir = QDir::cleanPath(QString(Config::installDir + "/../include"));
+ moreArgs_ += "-I" + basicIncludeDir.toLatin1();
+ moreArgs_ += "-I" + QDir::cleanPath(QString(filePath + "/../")).toLatin1();
+ moreArgs_ += "-I" + QDir::cleanPath(QString(filePath + "/../../")).toLatin1();
+ for (const auto &s : forest) {
+ QString module = basicIncludeDir +"/" + s->camelCaseModuleName();
+ moreArgs_ += QString("-I" + module).toLatin1();
+ moreArgs_ += QString("-I" + module + "/" + qdb_->version()).toLatin1();
+ moreArgs_ += QString("-I" + module + "/" + qdb_->version() + "/" + module).toLatin1();
+ }
+ for (int i=0; i<includePaths_.size(); ++i) {
+ if (!includePaths_.at(i).startsWith("-"))
+ moreArgs_ += "-I" + includePaths_.at(i);
+ else
+ moreArgs_ += includePaths_.at(i);
+ }
+ }
+ else {
+ moreArgs_ = includePaths_;
+ }
+}
+
+/*!
+ Building the PCH must be possible when there are no .cpp
+ files, so it is moved here to its own member function, and
+ it is called after the list of header files is complete.
+ */
+void ClangCodeParser::buildPCH()
+{
+ if (!pchFileDir_) {
+ pchFileDir_.reset(new QTemporaryDir(QDir::tempPath() + QLatin1String("/qdoc_pch")));
+ if (pchFileDir_->isValid()) {
+ //const QByteArray module = qdb_->primaryTreeRoot()->tree()->camelCaseModuleName().toUtf8();
+ const QByteArray module = moduleHeader().toUtf8();
+ QByteArray header;
+ QByteArray privateHeaderDir;
+ // Find the path to the module's header (e.g. QtGui/QtGui) to be used
+ // as pre-compiled header
+ for (const auto &p : qAsConst(includePaths_)) {
+ if (p.endsWith(module)) {
+ QByteArray candidate = p + "/" + module;
+ if (QFile::exists(QString::fromUtf8(candidate))) {
+ header = candidate;
+ break;
+ }
+ }
+ }
+ if (header.isEmpty()) {
+ for (const auto &p : qAsConst(includePaths_)) {
+ QByteArray candidate = p + "/" + module;
+ if (QFile::exists(QString::fromUtf8(candidate))) {
+ header = candidate;
+ break;
+ }
+ }
+ }
+ // Find the path to the module's private header directory (e.g.
+ // include/QtGui/5.8.0/QtGui/private) to use for including all
+ // the private headers in the PCH.
+ for (const auto &p : qAsConst(includePaths_)) {
+ if (p.endsWith(module)) {
+ QByteArray candidate = p + "/private";
+ if (QFile::exists(QString::fromUtf8(candidate))) {
+ privateHeaderDir = candidate;
+ break;
+ }
+ }
+ }
+ if (header.isEmpty()) {
+ QByteArray installDocDir = Config::installDir.toUtf8();
+ const QByteArray candidate = installDocDir + "/../include/" + module + "/" + module;
+ if (QFile::exists(QString::fromUtf8(candidate)))
+ header = candidate;
+ }
+ if (header.isEmpty()) {
+ qWarning() << "(qdoc) Could not find the module header in the include path for module"
+ << module << " (include paths: "<< includePaths_ << ")";
+ } else {
+ args_.push_back("-xc++");
+ CXTranslationUnit tu;
+ QString tmpHeader = pchFileDir_->path() + "/" + module;
+ if (QFile::copy(header, tmpHeader) && !privateHeaderDir.isEmpty()) {
+ privateHeaderDir = QDir::cleanPath(privateHeaderDir.constData()).toLatin1();
+ const char *const headerPath = privateHeaderDir.constData();
+ const QStringList pheaders = QDir(headerPath).entryList();
+ QFile tmpHeaderFile(tmpHeader);
+ if (tmpHeaderFile.open(QIODevice::Text | QIODevice::Append)) {
+ for (const QString &phead : pheaders) {
+ if (phead.endsWith("_p.h")) {
+ QByteArray entry;
+ entry = "#include \"";
+ entry += headerPath;
+ entry += '/';
+ entry += phead.toLatin1();
+ entry += "\"\n";
+ tmpHeaderFile.write(entry);
+ }
+ }
+ }
+ }
+ CXErrorCode err = clang_parseTranslationUnit2(index_,
+ tmpHeader.toLatin1().data(),
+ args_.data(), static_cast<int>(args_.size()), nullptr, 0,
+ flags_ | CXTranslationUnit_ForSerialization, &tu);
+ if (!err && tu) {
+ pchName_ = pchFileDir_->path().toUtf8() + "/" + module + ".pch";
+ auto error = clang_saveTranslationUnit(tu, pchName_.constData(), clang_defaultSaveOptions(tu));
+ if (error) {
+ qWarning() << "(qdoc) Could not save PCH file for " << module << error;
+ pchName_.clear();
+ }
+ // Visit the header now, as token from pre-compiled header won't be visited later
+ CXCursor cur = clang_getTranslationUnitCursor(tu);
+ ClangVisitor visitor(qdb_, allHeaders_);
+ visitor.visitChildren(cur);
+
+ clang_disposeTranslationUnit(tu);
+ } else {
+ pchFileDir_->remove();
+ qWarning() << "(qdoc) Could not create PCH file for "
+ << tmpHeader
+ << " error code:" << err;
+ }
+ args_.pop_back(); // remove the "-xc++";
+ }
+ }
+ qdb_->resolveInheritance();
+ }
+}
+
+/*!
+ Precompile the header files for the current module.
+ */
+void ClangCodeParser::precompileHeaders()
+{
+ getDefaultArgs();
+ getMoreArgs();
+ for (const auto &p : qAsConst(moreArgs_))
+ args_.push_back(p.constData());
+
+ flags_ = (CXTranslationUnit_Flags) (CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing);
+ index_ = clang_createIndex(1, 0);
+ buildPCH();
+ clang_disposeIndex(index_);
+}
+
+/*!
+ Get ready to parse the C++ cpp file identified by \a filePath
+ and add its parsed contents to the database. \a location is
+ used for reporting errors.
+
+ Call matchDocsAndStuff() to do all the parsing and tree building.
+ */
+void ClangCodeParser::parseSourceFile(const Location& /*location*/, const QString& filePath)
+{
+ currentFile_ = filePath;
+ flags_ = (CXTranslationUnit_Flags) (CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing);
+ index_ = clang_createIndex(1, 0);
+
+ getDefaultArgs();
+ if (!pchName_.isEmpty() && !filePath.endsWith(".mm")) {
+ args_.push_back("-w");
+ args_.push_back("-include-pch");
+ args_.push_back(pchName_.constData());
+ }
+ for (const auto &p : qAsConst(moreArgs_))
+ args_.push_back(p.constData());
+
+ CXTranslationUnit tu;
+ CXErrorCode err = clang_parseTranslationUnit2(index_, filePath.toLocal8Bit(), args_.data(),
+ static_cast<int>(args_.size()), nullptr, 0, flags_, &tu);
+ if (err || !tu) {
+ qWarning() << "(qdoc) Could not parse source file" << filePath << " error code:" << err;
+ clang_disposeIndex(index_);
+ return;
+ }
+
+ CXCursor cur = clang_getTranslationUnitCursor(tu);
+ ClangVisitor visitor(qdb_, allHeaders_);
+ visitor.visitChildren(cur);
+
+ CXToken *tokens;
+ unsigned int numTokens = 0;
+ clang_tokenize(tu, clang_getCursorExtent(cur), &tokens, &numTokens);
+
+ const QSet<QString>& topicCommandsAllowed = topicCommands();
+ const QSet<QString>& otherMetacommandsAllowed = otherMetaCommands();
+ const QSet<QString>& metacommandsAllowed = topicCommandsAllowed + otherMetacommandsAllowed;
+
+ for (unsigned int i = 0; i < numTokens; ++i) {
+ if (clang_getTokenKind(tokens[i]) != CXToken_Comment)
+ continue;
+ QString comment = fromCXString(clang_getTokenSpelling(tu, tokens[i]));
+ if (!comment.startsWith("/*!"))
+ continue;
+
+ auto loc = fromCXSourceLocation(clang_getTokenLocation(tu, tokens[i]));
+ auto end_loc = fromCXSourceLocation(clang_getRangeEnd(clang_getTokenExtent(tu, tokens[i])));
+ Doc::trimCStyleComment(loc,comment);
+ Doc doc(loc, end_loc, comment, metacommandsAllowed, topicCommandsAllowed);
+
+
+ /*
+ * Doc parses the comment.
+ */
+
+ QString topic;
+ bool isQmlPropertyTopic = false;
+ bool isJsPropertyTopic = false;
+
+ const TopicList& topics = doc.topicsUsed();
+ if (!topics.isEmpty()) {
+ topic = topics[0].topic;
+ if (topic.startsWith("qml")) {
+ if ((topic == COMMAND_QMLPROPERTY) ||
+ (topic == COMMAND_QMLPROPERTYGROUP) ||
+ (topic == COMMAND_QMLATTACHEDPROPERTY)) {
+ isQmlPropertyTopic = true;
+ }
+ } else if (topic.startsWith("js")) {
+ if ((topic == COMMAND_JSPROPERTY) ||
+ (topic == COMMAND_JSPROPERTYGROUP) ||
+ (topic == COMMAND_JSATTACHEDPROPERTY)) {
+ isJsPropertyTopic = true;
+ }
+ }
+ }
+ NodeList nodes;
+ DocList docs;
+
+ if (topic.isEmpty()) {
+ CXSourceLocation commentLoc = clang_getTokenLocation(tu, tokens[i]);
+ Node *n = nullptr;
+ if (i + 1 < numTokens) {
+ // Try to find the next declaration.
+ CXSourceLocation nextCommentLoc = commentLoc;
+ while (i + 2 < numTokens && clang_getTokenKind(tokens[i+1]) != CXToken_Comment)
+ ++i; // already skip all the tokens that are not comments
+ nextCommentLoc = clang_getTokenLocation(tu, tokens[i+1]);
+ n = visitor.nodeForCommentAtLocation(commentLoc, nextCommentLoc);
+ }
+
+ if (n) {
+ nodes.append(n);
+ docs.append(doc);
+ } else if (CodeParser::isWorthWarningAbout(doc)) {
+ doc.location().warning(tr("Cannot tie this documentation to anything"),
+ tr("I found a /*! ... */ comment, but there was no "
+ "topic command (e.g., '\\%1', '\\%2') in the "
+ "comment and no function definition following "
+ "the comment.")
+ .arg(COMMAND_FN).arg(COMMAND_PAGE));
+ }
+ } else if (isQmlPropertyTopic || isJsPropertyTopic) {
+ Doc nodeDoc = doc;
+ processQmlProperties(nodeDoc, nodes, docs, isJsPropertyTopic);
+ } else {
+ ArgList args;
+ const QSet<QString>& topicCommandsUsed = topicCommandsAllowed & doc.metaCommandsUsed();
+ if (topicCommandsUsed.count() > 0) {
+ topic = *topicCommandsUsed.constBegin();
+ args = doc.metaCommandArgs(topic);
+ }
+ if (topicCommandsUsed.count() > 1) {
+ QString topicList;
+ QSet<QString>::ConstIterator t = topicCommandsUsed.constBegin();
+ while (t != topicCommandsUsed.constEnd()) {
+ topicList += " \\" + *t + QLatin1Char(',');
+ ++t;
+ }
+ topicList[topicList.lastIndexOf(',')] = '.';
+ int i = topicList.lastIndexOf(',');
+ topicList[i] = ' ';
+ topicList.insert(i+1,"and");
+ doc.location().warning(tr("Multiple topic commands found in comment: %1").arg(topicList));
+ }
+ ArgList::ConstIterator a = args.constBegin();
+ Node *node = 0;
+ SharedCommentNode* scn = 0;
+ int count = args.size();
+ while (a != args.constEnd()) {
+ Doc nodeDoc = doc;
+ if ((count > 1) && (topic == COMMAND_FN)) {
+ node = parseFnArg(doc.location(), a->first);
+ if (node != 0) {
+ if (scn == 0) {
+ scn = new SharedCommentNode(node->parent(), count);
+ nodes.append(scn);
+ docs.append(nodeDoc);
+ }
+ scn->append(node);
+ node->setCollectiveNode(scn);
+ }
+ }
+ else {
+ if (topic == COMMAND_FN) {
+ node = parseFnArg(doc.location(), a->first);
+ } else if (topic == COMMAND_MACRO) {
+ node = parseMacroArg(doc.location(), a->first);
+ } else if (topic == COMMAND_QMLSIGNAL ||
+ topic == COMMAND_QMLMETHOD ||
+ topic == COMMAND_QMLATTACHEDSIGNAL ||
+ topic == COMMAND_QMLATTACHEDMETHOD ||
+ topic == COMMAND_JSSIGNAL ||
+ topic == COMMAND_JSMETHOD ||
+ topic == COMMAND_JSATTACHEDSIGNAL ||
+ topic == COMMAND_JSATTACHEDMETHOD) {
+ node = parseOtherFuncArg(topic, doc.location(), a->first);
+ } else {
+ node = processTopicCommand(nodeDoc, topic, *a);
+ }
+ if (node != 0) {
+ nodes.append(node);
+ docs.append(nodeDoc);
+ }
+ }
+ ++a;
+ }
+ }
+
+ NodeList::Iterator n = nodes.begin();
+ QList<Doc>::Iterator d = docs.begin();
+ while (n != nodes.end()) {
+ processOtherMetaCommands(*d, *n);
+ (*n)->setDoc(*d);
+ checkModuleInclusion(*n);
+ if ((*n)->isAggregate() && ((Aggregate *)*n)->includes().isEmpty()) {
+ Aggregate *m = static_cast<Aggregate *>(*n);
+ while (m->parent() && m->physicalModuleName().isEmpty()) {
+ m = m->parent();
+ }
+ if (m == *n)
+ ((Aggregate *)*n)->addInclude((*n)->name());
+ else
+ ((Aggregate *)*n)->setIncludes(m->includes());
+ }
+ ++d;
+ ++n;
+ }
+ }
+
+ clang_disposeTokens(tu, tokens, numTokens);
+ clang_disposeTranslationUnit(tu);
+ clang_disposeIndex(index_);
+}
+
+/*!
+ */
+Node* ClangCodeParser::parseOtherFuncArg(const QString& topic,
+ const Location& location,
+ const QString& funcArg)
+{
+ if (Generator::preparing() && !Generator::singleExec())
+ return 0;
+
+ QString funcName;
+ QString returnType;
+
+ int leftParen = funcArg.indexOf(QChar('('));
+ if (leftParen > 0)
+ funcName = funcArg.left(leftParen);
+ else
+ funcName = funcArg;
+ int firstBlank = funcName.indexOf(QChar(' '));
+ if (firstBlank > 0) {
+ returnType = funcName.left(firstBlank);
+ funcName = funcName.right(funcName.length() - firstBlank - 1);
+ }
+
+ QStringList colonSplit(funcName.split("::"));
+ if (colonSplit.size() < 2) {
+ QString msg = "Unrecognizable QML module/component qualifier for " + funcArg;
+ location.warning(tr(msg.toLatin1().data()));
+ return 0;
+ }
+ QString moduleName;
+ QString elementName;
+ if (colonSplit.size() > 2) {
+ moduleName = colonSplit[0];
+ elementName = colonSplit[1];
+ } else {
+ elementName = colonSplit[0];
+ }
+ funcName = colonSplit.last();
+
+ Aggregate *aggregate = qdb_->findQmlType(moduleName, elementName);
+ bool attached = false;
+ if (!aggregate)
+ aggregate = qdb_->findQmlBasicType(moduleName, elementName);
+ if (!aggregate)
+ return 0;
+
+ Node::NodeType nodeType = Node::QmlMethod;
+ if (topic == COMMAND_QMLSIGNAL || topic == COMMAND_JSSIGNAL) {
+ nodeType = Node::QmlSignal;
+ } else if (topic == COMMAND_QMLATTACHEDSIGNAL || topic == COMMAND_JSATTACHEDSIGNAL) {
+ nodeType = Node::QmlSignal;
+ attached = true;
+ } else if (topic == COMMAND_QMLATTACHEDMETHOD || topic == COMMAND_JSATTACHEDMETHOD) {
+ attached = true;
+ } else {
+ Q_ASSERT(topic == COMMAND_QMLMETHOD || topic == COMMAND_JSMETHOD);
+ }
+
+ QString params;
+ QStringList leftParenSplit = funcArg.split('(');
+ if (leftParenSplit.size() > 1) {
+ QStringList rightParenSplit = leftParenSplit[1].split(')');
+ if (rightParenSplit.size() > 0)
+ params = rightParenSplit[0];
+ }
+ FunctionNode *funcNode = static_cast<FunctionNode*>(new FunctionNode(nodeType, aggregate, funcName, attached));
+ funcNode->setAccess(Node::Public);
+ funcNode->setLocation(location);
+ funcNode->setReturnType(returnType);
+ funcNode->setParameters(params);
+ return funcNode;
+}
+
+/*!
+ Parse the macroArg ad hoc, without using clang and without
+ using the old qdoc C++ parser.
+ */
+Node* ClangCodeParser::parseMacroArg(const Location& location, const QString& macroArg)
+{
+ if (Generator::preparing() && !Generator::singleExec())
+ return 0;
+ FunctionNode* newMacroNode = 0;
+ QStringList leftParenSplit = macroArg.split('(');
+ if (leftParenSplit.size() > 0) {
+ QString macroName;
+ FunctionNode* oldMacroNode = 0;
+ QStringList blankSplit = leftParenSplit[0].split(' ');
+ if (blankSplit.size() > 0) {
+ macroName = blankSplit.last();
+ oldMacroNode = static_cast<FunctionNode*>(qdb_->findMacroNode(macroName));
+ }
+ QString returnType;
+ if (blankSplit.size() > 1) {
+ blankSplit.removeLast();
+ returnType = blankSplit.join(' ');
+ }
+ QString params;
+ if (leftParenSplit.size() > 1) {
+ const QString &afterParen = leftParenSplit.at(1);
+ int rightParen = afterParen.indexOf(')');
+ if (rightParen >= 0)
+ params = afterParen.left(rightParen);
+ }
+ int i = 0;
+ while (i < macroName.length() && !macroName.at(i).isLetter())
+ i++;
+ if (i > 0) {
+ returnType += QChar(' ') + macroName.left(i);
+ macroName = macroName.mid(i);
+ }
+ newMacroNode = static_cast<FunctionNode*>(new FunctionNode(qdb_->primaryTreeRoot(), macroName));
+ newMacroNode->setAccess(Node::Public);
+ newMacroNode->setLocation(location);
+ if (params.isEmpty())
+ newMacroNode->setMetaness(FunctionNode::MacroWithoutParams);
+ else
+ newMacroNode->setMetaness(FunctionNode::MacroWithParams);
+ newMacroNode->setReturnType(returnType);
+ newMacroNode->setParameters(params);
+ if (oldMacroNode && newMacroNode->compare(oldMacroNode)) {
+ location.warning(ClangCodeParser::tr("\\macro %1 documented more than once").arg(macroArg));
+ oldMacroNode->doc().location().warning(tr("(The previous doc is here)"));
+ }
+ }
+ return newMacroNode;
+ }
+
+/*!
+ Use clang to parse the function signature from a function
+ command. \a location is used for reporting errors. \a fnArg
+ is the string to parse. It is always a function decl.
+ */
+Node* ClangCodeParser::parseFnArg(const Location& location, const QString& fnArg)
+{
+ Node* fnNode = 0;
+ if (Generator::preparing() && !Generator::singleExec())
+ return fnNode;
+ /*
+ If the \fn command begins with a tag, then don't try to
+ parse the \fn command with clang. Use the tag to search
+ for the correct function node. It is an error if it can
+ not be found. Return 0 in that case.
+ */
+ if (fnArg.startsWith('[')) {
+ int end = fnArg.indexOf(QChar(']', 0));
+ if (end > 1) {
+ QString tag = fnArg.left(end + 1);
+ fnNode = qdb_->findFunctionNodeForTag(tag);
+ if (!fnNode) {
+ location.error(ClangCodeParser::tr("tag \\fn %1 not used in any include file in current module").arg(tag));
+ } else {
+ /*
+ The function node was found. Use the formal
+ parameter names from the \FN command, because
+ they will be the names used in the documentation.
+ */
+ FunctionNode* fn = static_cast<FunctionNode*>(fnNode);
+ QStringList leftParenSplit = fnArg.split('(');
+ if (leftParenSplit.size() > 1) {
+ QStringList rightParenSplit = leftParenSplit[1].split(')');
+ if (rightParenSplit.size() > 0) {
+ QString params = rightParenSplit[0];
+ if (!params.isEmpty()) {
+ QStringList commaSplit = params.split(',');
+ QVector<Parameter>& pvect = fn->parameters();
+ if (pvect.size() == commaSplit.size()) {
+ for (int i = 0; i < pvect.size(); ++i) {
+ QStringList blankSplit = commaSplit[i].split(' ');
+ if (blankSplit.size() > 0) {
+ QString pName = blankSplit.last();
+ int j = 0;
+ while (j < pName.length() && !pName.at(i).isLetter())
+ j++;
+ if (j > 0)
+ pName = pName.mid(j);
+ if (!pName.isEmpty() && pName != pvect[i].name())
+ pvect[i].setName(pName);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return fnNode;
+ }
+ CXTranslationUnit_Flags flags = (CXTranslationUnit_Flags) (CXTranslationUnit_Incomplete |
+ CXTranslationUnit_SkipFunctionBodies |
+ CXTranslationUnit_KeepGoing);
+ CXIndex index = clang_createIndex(1, 0);
+
+ std::vector<const char *> args(std::begin(defaultArgs_), std::end(defaultArgs_));
+ // Add the defines from the qdocconf file.
+ for (const auto &p : qAsConst(defines_))
+ args.push_back(p.constData());
+ if (!pchName_.isEmpty()) {
+ args.push_back("-w");
+ args.push_back("-include-pch");
+ args.push_back(pchName_.constData());
+ }
+ CXTranslationUnit tu;
+ QByteArray fn = fnArg.toUtf8();
+ if (!fn.endsWith(";"))
+ fn += "{ }";
+ const char *dummyFileName = "/fn_dummyfile.cpp";
+ CXUnsavedFile unsavedFile { dummyFileName, fn.constData(),
+ static_cast<unsigned long>(fn.size()) };
+ CXErrorCode err = clang_parseTranslationUnit2(index, dummyFileName,
+ args.data(),
+ args.size(),
+ &unsavedFile,
+ 1,
+ flags,
+ &tu);
+
+ if (err || !tu) {
+ location.error(ClangCodeParser::tr("clang could not parse \\fn %1").arg(fnArg));
+ clang_disposeTranslationUnit(tu);
+ clang_disposeIndex(index);
+ return fnNode;
+ } else {
+ /*
+ Always visit the tu if one is constructed, because
+ it might be possible to find the correct node, even
+ if clang detected diagnostics. Only bother to report
+ the diagnostics if they stop us finding the node.
+ */
+ CXCursor cur = clang_getTranslationUnitCursor(tu);
+ ClangVisitor visitor(qdb_, allHeaders_);
+ bool ignoreSignature = false;
+ visitor.visitFnArg(cur, &fnNode, ignoreSignature);
+ if (fnNode == 0) {
+ unsigned diagnosticCount = clang_getNumDiagnostics(tu);
+ if (diagnosticCount > 0) {
+ location.warning(ClangCodeParser::tr("clang found diagnostics parsing \\fn %1").arg(fnArg));
+ for (unsigned i = 0; i < diagnosticCount; ++i) {
+ CXDiagnostic diagnostic = clang_getDiagnostic(tu, i);
+ location.report(tr(" %1").arg(fromCXString(clang_formatDiagnostic(diagnostic, 0))));
+ }
+ }
+ }
+ }
+ clang_disposeTranslationUnit(tu);
+ clang_disposeIndex(index);
+ return fnNode;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/clangcodeparser.h b/src/qdoc/clangcodeparser.h
new file mode 100644
index 000000000..7d9cf31cf
--- /dev/null
+++ b/src/qdoc/clangcodeparser.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CLANGCODEPARSER_H
+#define CLANGCODEPARSER_H
+
+#include <QTemporaryDir>
+
+#include "cppcodeparser.h"
+
+QT_BEGIN_NAMESPACE
+
+class ClangCodeParser : public CppCodeParser
+{
+ Q_DECLARE_TR_FUNCTIONS(QDoc::ClangCodeParser)
+
+public:
+ ~ClangCodeParser();
+
+ virtual void initializeParser(const Config& config) Q_DECL_OVERRIDE;
+ virtual void terminateParser() Q_DECL_OVERRIDE;
+ virtual QString language() Q_DECL_OVERRIDE;
+ virtual QStringList headerFileNameFilter() Q_DECL_OVERRIDE;
+ virtual QStringList sourceFileNameFilter() Q_DECL_OVERRIDE;
+ virtual void parseHeaderFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE;
+ virtual void parseSourceFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE;
+ virtual void precompileHeaders() Q_DECL_OVERRIDE;
+ virtual Node *parseFnArg(const Location &location, const QString &fnArg) Q_DECL_OVERRIDE;
+ virtual Node *parseMacroArg(const Location &location, const QString &macroArg) Q_DECL_OVERRIDE;
+ virtual Node *parseOtherFuncArg(const QString &topic,
+ const Location &location,
+ const QString &funcArg) Q_DECL_OVERRIDE;
+
+ private:
+ void getDefaultArgs();
+ void getMoreArgs();
+ void buildPCH();
+
+private:
+ QSet<QString> allHeaders_;
+ QVector<QByteArray> includePaths_;
+ QScopedPointer<QTemporaryDir> pchFileDir_;
+ QByteArray pchName_;
+ QVector<QByteArray> defines_;
+ std::vector<const char *> args_;
+ QVector<QByteArray> moreArgs_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/codechunk.cpp b/src/qdoc/codechunk.cpp
index e3b130da1..36865f782 100644
--- a/src/qdoc/codechunk.cpp
+++ b/src/qdoc/codechunk.cpp
@@ -37,7 +37,7 @@
QT_BEGIN_NAMESPACE
-enum { Other, Alnum, Gizmo, Comma, LParen, RParen, RAngle, Colon };
+enum { Other, Alnum, Gizmo, Comma, LBrace, RBrace, RAngle, Colon, Paren };
// entries 128 and above are Other
static const int charCategory[256] = {
@@ -48,7 +48,7 @@ static const int charCategory[256] = {
// ! " # $ % & '
Other, Other, Other, Other, Other, Gizmo, Gizmo, Other,
// ( ) * + , - . /
- LParen, RParen, Gizmo, Gizmo, Comma, Other, Other, Gizmo,
+ Paren, Paren, Gizmo, Gizmo, Comma, Other, Other, Gizmo,
// 0 1 2 3 4 5 6 7
Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
// 8 9 : ; < = > ?
@@ -68,19 +68,20 @@ static const int charCategory[256] = {
// p q r s t u v w
Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum, Alnum,
// x y z { | } ~
- Alnum, Alnum, Alnum, LParen, Gizmo, RParen, Other, Other
+ Alnum, Alnum, Alnum, LBrace, Gizmo, RBrace, Other, Other
};
-static const bool needSpace[8][8] = {
- /* [ a + , ( ) > : */
- /* [ */ { false, false, false, false, false, true, false, false },
- /* a */ { false, true, true, false, false, true, false, false },
- /* + */ { false, true, false, false, false, true, false, true },
- /* , */ { true, true, true, true, true, true, true, true },
- /* ( */ { true, true, true, false, true, false, true, true },
- /* ) */ { true, true, true, false, true, true, true, true },
- /* > */ { true, true, true, false, true, true, true, false },
- /* : */ { false, false, true, true, true, true, true, false }
+static const bool needSpace[9][9] = {
+ /* [ a + , { } > : ) */
+ /* [ */ { false, false, false, false, false, true, false, false, false },
+ /* a */ { false, true, true, false, false, true, false, false, false },
+ /* + */ { false, true, false, false, false, true, false, true, false },
+ /* , */ { true, true, true, true, true, true, true, true, false },
+ /* { */ { false, false, false, false, false, false, false, false, false },
+ /* } */ { false, false, false, false, false, false, false, false, false },
+ /* > */ { true, true, true, false, true, true, true, false, false },
+ /* : */ { false, false, true, true, true, true, true, false, false },
+ /* ( */ { false, false, false, false, false, false, false, false, false },
};
static int category( QChar ch )
diff --git a/src/qdoc/codeparser.cpp b/src/qdoc/codeparser.cpp
index 2b8e28f91..c344ad763 100644
--- a/src/qdoc/codeparser.cpp
+++ b/src/qdoc/codeparser.cpp
@@ -112,11 +112,6 @@ void CodeParser::parseHeaderFile(const Location& location, const QString& filePa
parseSourceFile(location, filePath);
}
-void CodeParser::doneParsingHeaderFiles()
-{
- doneParsingSourceFiles();
-}
-
/*!
All the code parsers in the static list are initialized here,
after the qdoc configuration variables have been set.
@@ -359,6 +354,21 @@ void CodeParser::setLink(Node* node, Node::LinkType linkType, const QString& arg
}
/*!
+ \brief Test for whether a doc comment warrants warnings.
+
+ Returns true if qdoc should report that it has found something
+ wrong with the qdoc comment in \a doc. Sometimes, qdoc should
+ not report the warning, for example, when the comment contains
+ the \c internal command, which normally means qdoc will not use
+ the comment in the documentation anyway, so there is no point
+ in reporting warnings about it.
+ */
+bool CodeParser::isWorthWarningAbout(const Doc &doc)
+{
+ return (showInternal_ || !doc.metaCommandsUsed().contains(QStringLiteral("internal")));
+}
+
+/*!
Returns \c true if the file being parsed is a .h file.
*/
bool CodeParser::isParsingH() const
diff --git a/src/qdoc/codeparser.h b/src/qdoc/codeparser.h
index cd24987c6..6659e3eed 100644
--- a/src/qdoc/codeparser.h
+++ b/src/qdoc/codeparser.h
@@ -53,13 +53,17 @@ public:
virtual QStringList sourceFileNameFilter() = 0;
virtual void parseHeaderFile(const Location& location, const QString& filePath);
virtual void parseSourceFile(const Location& location, const QString& filePath) = 0;
- virtual void doneParsingHeaderFiles();
- virtual void doneParsingSourceFiles() = 0;
+ virtual void precompileHeaders() { }
+ virtual Node *parseFnArg(const Location &, const QString &) { return 0; }
+ virtual Node *parseMacroArg(const Location &, const QString &) { return 0; }
+ virtual Node *parseOtherFuncArg(const QString &, const Location &, const QString &) { return 0; }
bool isParsingH() const;
bool isParsingCpp() const;
bool isParsingQdoc() const;
const QString& currentFile() const { return currentFile_; }
+ const QString& moduleHeader() const { return moduleHeader_; }
+ void setModuleHeader(const QString& t) { moduleHeader_ = t; }
void checkModuleInclusion(Node* n);
static void initialize(const Config& config);
@@ -68,6 +72,7 @@ public:
static CodeParser *parserForHeaderFile(const QString &filePath);
static CodeParser *parserForSourceFile(const QString &filePath);
static void setLink(Node* node, Node::LinkType linkType, const QString& arg);
+ static bool isWorthWarningAbout(const Doc &doc);
protected:
const QSet<QString>& commonMetaCommands();
@@ -78,6 +83,7 @@ protected:
static void extractPageLinkAndDesc(const QString& arg,
QString* link,
QString* desc);
+ QString moduleHeader_;
QString currentFile_;
QDocDatabase* qdb_;
diff --git a/src/qdoc/config.cpp b/src/qdoc/config.cpp
index e116b24fb..4df13f7a8 100644
--- a/src/qdoc/config.cpp
+++ b/src/qdoc/config.cpp
@@ -47,6 +47,7 @@ QString ConfigStrings::AUTOLINKERRORS = QStringLiteral("autolinkerrors");
QString ConfigStrings::BASE = QStringLiteral("base");
QString ConfigStrings::BASEDIR = QStringLiteral("basedir");
QString ConfigStrings::BUILDVERSION = QStringLiteral("buildversion");
+QString ConfigStrings::CLANGDEFINES = QStringLiteral("clangdefines");
QString ConfigStrings::CODEINDENT = QStringLiteral("codeindent");
QString ConfigStrings::CODEPREFIX = QStringLiteral("codeprefix");
QString ConfigStrings::CODESUFFIX = QStringLiteral("codesuffix");
@@ -75,11 +76,13 @@ QString ConfigStrings::IGNOREDIRECTIVES = QStringLiteral("ignoredirectives");
QString ConfigStrings::IGNORETOKENS = QStringLiteral("ignoretokens");
QString ConfigStrings::IMAGEDIRS = QStringLiteral("imagedirs");
QString ConfigStrings::IMAGES = QStringLiteral("images");
+QString ConfigStrings::INCLUDEPATHS = QStringLiteral("includepaths");
QString ConfigStrings::INDEXES = QStringLiteral("indexes");
QString ConfigStrings::LANDINGPAGE = QStringLiteral("landingpage");
QString ConfigStrings::LANGUAGE = QStringLiteral("language");
QString ConfigStrings::MACRO = QStringLiteral("macro");
QString ConfigStrings::MANIFESTMETA = QStringLiteral("manifestmeta");
+QString ConfigStrings::MODULEHEADER = QStringLiteral("moduleheader");
QString ConfigStrings::NATURALLANGUAGE = QStringLiteral("naturallanguage");
QString ConfigStrings::NAVIGATION = QStringLiteral("navigation");
QString ConfigStrings::NOLINKERRORS = QStringLiteral("nolinkerrors");
diff --git a/src/qdoc/config.h b/src/qdoc/config.h
index f4781eb10..71dbb658a 100644
--- a/src/qdoc/config.h
+++ b/src/qdoc/config.h
@@ -160,6 +160,7 @@ struct ConfigStrings
static QString BASE;
static QString BASEDIR;
static QString BUILDVERSION;
+ static QString CLANGDEFINES;
static QString CODEINDENT;
static QString CODEPREFIX;
static QString CODESUFFIX;
@@ -188,11 +189,13 @@ struct ConfigStrings
static QString IGNORETOKENS;
static QString IMAGEDIRS;
static QString IMAGES;
+ static QString INCLUDEPATHS;
static QString INDEXES;
static QString LANDINGPAGE;
static QString LANGUAGE;
static QString MACRO;
static QString MANIFESTMETA;
+ static QString MODULEHEADER;
static QString NATURALLANGUAGE;
static QString NAVIGATION;
static QString NOLINKERRORS;
@@ -241,6 +244,7 @@ struct ConfigStrings
#define CONFIG_BASE ConfigStrings::BASE
#define CONFIG_BASEDIR ConfigStrings::BASEDIR
#define CONFIG_BUILDVERSION ConfigStrings::BUILDVERSION
+#define CONFIG_CLANGDEFINES ConfigStrings::CLANGDEFINES
#define CONFIG_CODEINDENT ConfigStrings::CODEINDENT
#define CONFIG_CODEPREFIX ConfigStrings::CODEPREFIX
#define CONFIG_CODESUFFIX ConfigStrings::CODESUFFIX
@@ -269,11 +273,13 @@ struct ConfigStrings
#define CONFIG_IGNORETOKENS ConfigStrings::IGNORETOKENS
#define CONFIG_IMAGEDIRS ConfigStrings::IMAGEDIRS
#define CONFIG_IMAGES ConfigStrings::IMAGES
+#define CONFIG_INCLUDEPATHS ConfigStrings::INCLUDEPATHS
#define CONFIG_INDEXES ConfigStrings::INDEXES
#define CONFIG_LANDINGPAGE ConfigStrings::LANDINGPAGE
#define CONFIG_LANGUAGE ConfigStrings::LANGUAGE
#define CONFIG_MACRO ConfigStrings::MACRO
#define CONFIG_MANIFESTMETA ConfigStrings::MANIFESTMETA
+#define CONFIG_MODULEHEADER ConfigStrings::MODULEHEADER
#define CONFIG_NATURALLANGUAGE ConfigStrings::NATURALLANGUAGE
#define CONFIG_NAVIGATION ConfigStrings::NAVIGATION
#define CONFIG_NOLINKERRORS ConfigStrings::NOLINKERRORS
diff --git a/src/qdoc/cppcodemarker.cpp b/src/qdoc/cppcodemarker.cpp
index 40a3e8b81..8488260fd 100644
--- a/src/qdoc/cppcodemarker.cpp
+++ b/src/qdoc/cppcodemarker.cpp
@@ -163,7 +163,6 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node,
const QString &paramName = hasName ? (*p).name() : (*p).dataType();
if (style != Subpage || !hasName)
synopsis += "<@param>" + protect(paramName) + "</@param>";
- synopsis += protect((*p).rightType());
if (style != Subpage && !(*p).defaultValue().isEmpty())
synopsis += " = " + protect((*p).defaultValue());
++p;
@@ -185,14 +184,24 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node,
synopsis.append(" = 0");
else if (func->isDeleted())
synopsis.append(" = delete");
- else if (func->isDefaulted())
+ else if (func->isImplicit() || func->isDefaulted())
synopsis.append(" = default");
+ if (func->isRef())
+ synopsis.append(" &");
+ else if (func->isRefRef())
+ synopsis.append(" &&");
}
else if (style == Subpage) {
if (!func->returnType().isEmpty() && func->returnType() != "void")
synopsis += " : " + typified(func->returnType());
}
else {
+ if (func->isRef())
+ synopsis.append(" &");
+ else if (func->isRefRef())
+ synopsis.append(" &&");
+ if (func->isImplicit() || func->isDefaulted())
+ synopsis.append(" = default");
QStringList bracketed;
if (func->isStatic()) {
bracketed += "static";
@@ -349,7 +358,6 @@ QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary)
synopsis += typified((*p).dataType(), true);
const QString &paramName = hasName ? (*p).name() : (*p).dataType();
synopsis += "<@param>" + protect(paramName) + "</@param>";
- synopsis += protect((*p).rightType());
++p;
}
}
@@ -381,7 +389,7 @@ QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary)
QString CppCodeMarker::markedUpName(const Node *node)
{
QString name = linkTag(node, taggedNode(node));
- if (node->type() == Node::Function)
+ if (node->isFunction() && !node->isMacro())
name += "()";
return name;
}
@@ -582,7 +590,7 @@ QList<Section> CppCodeMarker::sections(const Aggregate *inner,
insert(publicFunctions, *c, style, status);
}
}
- else {
+ else if ((*c)->type() != Node::SharedComment) {
insert(publicTypes, *c, style, status);
}
break;
@@ -694,8 +702,15 @@ QList<Section> CppCodeMarker::sections(const Aggregate *inner,
}
else if ((*c)->type() == Node::Function) {
FunctionNode *function = static_cast<FunctionNode *>(*c);
- if (!function->hasAssociatedProperties() || !function->doc().isEmpty())
- insert(memberFunctions, function, style, status);
+ if (!function->isInCollective()) {
+ if (!function->hasAssociatedProperties() || !function->doc().isEmpty())
+ insert(memberFunctions, function, style, status);
+ }
+ }
+ else if ((*c)->type() == Node::SharedComment) {
+ SharedCommentNode *scn = static_cast<SharedCommentNode *>(*c);
+ if (!scn->doc().isEmpty())
+ insert(memberFunctions, scn, style, status);
}
++c;
}
@@ -918,7 +933,7 @@ QString CppCodeMarker::addMarkUp(const QString &in,
readChar();
- while (ch != EOF) {
+ while (ch != QChar(EOF)) {
QString tag;
bool target = false;
@@ -977,7 +992,7 @@ QString CppCodeMarker::addMarkUp(const QString &in,
finish = i;
readChar();
- while (ch != EOF && ch != '"') {
+ while (ch != QChar(EOF) && ch != '"') {
if (ch == '\\')
readChar();
readChar();
@@ -989,7 +1004,7 @@ QString CppCodeMarker::addMarkUp(const QString &in,
case '#':
finish = i;
readChar();
- while (ch != EOF && ch != '\n') {
+ while (ch != QChar(EOF) && ch != '\n') {
if (ch == '\\')
readChar();
finish = i;
@@ -1001,7 +1016,7 @@ QString CppCodeMarker::addMarkUp(const QString &in,
finish = i;
readChar();
- while (ch != EOF && ch != '\'') {
+ while (ch != QChar(EOF) && ch != '\'') {
if (ch == '\\')
readChar();
readChar();
@@ -1036,7 +1051,7 @@ QString CppCodeMarker::addMarkUp(const QString &in,
do {
finish = i;
readChar();
- } while (ch != EOF && ch != '\n');
+ } while (ch != QChar(EOF) && ch != '\n');
tag = QStringLiteral("comment");
} else if (ch == '*') {
bool metAster = false;
@@ -1046,7 +1061,7 @@ QString CppCodeMarker::addMarkUp(const QString &in,
readChar();
while (!metAsterSlash) {
- if (ch == EOF)
+ if (ch == QChar(EOF))
break;
if (ch == '*')
diff --git a/src/qdoc/cppcodeparser.cpp b/src/qdoc/cppcodeparser.cpp
index 29e1c0fe6..c81a385a3 100644
--- a/src/qdoc/cppcodeparser.cpp
+++ b/src/qdoc/cppcodeparser.cpp
@@ -46,12 +46,10 @@ QT_BEGIN_NAMESPACE
/* qmake ignore Q_OBJECT */
static bool inMacroCommand_ = false;
-static bool parsingHeaderFile_ = false;
QStringList CppCodeParser::exampleFiles;
QStringList CppCodeParser::exampleDirs;
QSet<QString> CppCodeParser::excludeDirs;
QSet<QString> CppCodeParser::excludeFiles;
-CppCodeParser* CppCodeParser::cppParser_ = 0;
/*!
The constructor initializes some regular expressions
@@ -61,7 +59,6 @@ CppCodeParser::CppCodeParser()
: varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
{
reset();
- cppParser_ = this;
}
/*!
@@ -88,6 +85,7 @@ void CppCodeParser::initializeParser(const Config &config)
nodeTypeMap.insert(COMMAND_NAMESPACE, Node::Namespace);
nodeTypeMap.insert(COMMAND_CLASS, Node::Class);
nodeTypeMap.insert(COMMAND_ENUM, Node::Enum);
+ nodeTypeMap.insert(COMMAND_TYPEALIAS, Node::Typedef);
nodeTypeMap.insert(COMMAND_TYPEDEF, Node::Typedef);
nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property);
nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable);
@@ -140,7 +138,7 @@ QString CppCodeParser::language()
*/
QStringList CppCodeParser::headerFileNameFilter()
{
- return QStringList() << "*.ch" << "*.h" << "*.h++" << "*.hh" << "*.hpp" << "*.hxx";
+ return QStringList();
}
/*!
@@ -149,121 +147,9 @@ QStringList CppCodeParser::headerFileNameFilter()
*/
QStringList CppCodeParser::sourceFileNameFilter()
{
- return QStringList() << "*.c++" << "*.cc" << "*.cpp" << "*.cxx" << "*.mm";
+ return QStringList();
}
-/*!
- Parse the C++ header file identified by \a filePath and add
- the parsed contents to the database. The \a location is used
- for reporting errors.
- */
-void CppCodeParser::parseHeaderFile(const Location& location, const QString& filePath)
-{
- QFile in(filePath);
- currentFile_ = filePath;
- if (!in.open(QIODevice::ReadOnly)) {
- location.error(tr("Cannot open C++ header file '%1'").arg(filePath));
- currentFile_.clear();
- return;
- }
-
- reset();
- Location fileLocation(filePath);
- Tokenizer fileTokenizer(fileLocation, in);
- tokenizer = &fileTokenizer;
- readToken();
- parsingHeaderFile_ = true;
- matchDeclList(qdb_->primaryTreeRoot());
- parsingHeaderFile_ = false;
- if (!fileTokenizer.version().isEmpty())
- qdb_->setVersion(fileTokenizer.version());
- in.close();
-
- if (fileLocation.fileName() == "qiterator.h")
- parseQiteratorDotH(location, filePath);
- currentFile_.clear();
-}
-
-/*!
- Get ready to parse the C++ cpp file identified by \a filePath
- and add its parsed contents to the database. \a location is
- used for reporting errors.
-
- Call matchDocsAndStuff() to do all the parsing and tree building.
- */
-void CppCodeParser::parseSourceFile(const Location& location, const QString& filePath)
-{
- QFile in(filePath);
- currentFile_ = filePath;
- if (!in.open(QIODevice::ReadOnly)) {
- location.error(tr("Cannot open C++ source file '%1' (%2)").arg(filePath).arg(strerror(errno)));
- currentFile_.clear();
- return;
- }
-
- reset();
- Location fileLocation(filePath);
- Tokenizer fileTokenizer(fileLocation, in);
- tokenizer = &fileTokenizer;
- readToken();
-
- /*
- The set of open namespaces is cleared before parsing
- each source file. The word "source" here means cpp file.
- */
- qdb_->clearOpenNamespaces();
-
- matchDocsAndStuff();
- in.close();
- currentFile_.clear();
-}
-
-/*!
- This is called after all the C++ header files have been
- parsed. The most important thing it does is resolve C++
- class inheritance links in the tree. It also initializes
- a bunch of other collections.
- */
-void CppCodeParser::doneParsingHeaderFiles()
-{
- QMapIterator<QString, QString> i(sequentialIteratorClasses);
- while (i.hasNext()) {
- i.next();
- instantiateIteratorMacro(i.key(), i.value(), sequentialIteratorDefinition);
- }
- i = mutableSequentialIteratorClasses;
- while (i.hasNext()) {
- i.next();
- instantiateIteratorMacro(i.key(), i.value(), mutableSequentialIteratorDefinition);
- }
- i = associativeIteratorClasses;
- while (i.hasNext()) {
- i.next();
- instantiateIteratorMacro(i.key(), i.value(), associativeIteratorDefinition);
- }
- i = mutableAssociativeIteratorClasses;
- while (i.hasNext()) {
- i.next();
- instantiateIteratorMacro(i.key(), i.value(), mutableAssociativeIteratorDefinition);
- }
- sequentialIteratorDefinition.clear();
- mutableSequentialIteratorDefinition.clear();
- associativeIteratorDefinition.clear();
- mutableAssociativeIteratorDefinition.clear();
- sequentialIteratorClasses.clear();
- mutableSequentialIteratorClasses.clear();
- associativeIteratorClasses.clear();
- mutableAssociativeIteratorClasses.clear();
-}
-
-/*!
- This is called after all the source files (i.e., not the
- header files) have been parsed. Currently nothing to do.
- */
-void CppCodeParser::doneParsingSourceFiles()
-{
- // contents moved to QdocDatabase::resolveIssues()
-}
static QSet<QString> topicCommands_;
/*!
@@ -286,6 +172,7 @@ const QSet<QString>& CppCodeParser::topicCommands()
<< COMMAND_NAMESPACE
<< COMMAND_PAGE
<< COMMAND_PROPERTY
+ << COMMAND_TYPEALIAS
<< COMMAND_TYPEDEF
<< COMMAND_VARIABLE
<< COMMAND_QMLTYPE
@@ -321,86 +208,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
{
ExtraFuncData extra;
if (command == COMMAND_FN) {
- QStringList parentPath;
- FunctionNode *func = 0;
- FunctionNode *clone = 0;
-
- if (!makeFunctionNode(arg.first, &parentPath, &clone, extra) &&
- !makeFunctionNode("void " + arg.first, &parentPath, &clone, extra)) {
- doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_FN));
- }
- else {
- func = qdb_->findFunctionNode(parentPath, clone);
- if (func == 0) {
- if (parentPath.isEmpty() && !lastPath_.isEmpty())
- func = qdb_->findFunctionNode(lastPath_, clone);
- }
-
- /*
- If the node was not found, then search for it in the
- open C++ namespaces. We don't expect this search to
- be necessary often. Nor do we expect it to succeed
- very often.
- */
- if (func == 0)
- func = qdb_->findNodeInOpenNamespace(parentPath, clone);
-
- if (func == 0) {
- doc.location().warning(tr("Cannot find '%1' in '\\%2' %3")
- .arg(clone->name() + "(...)")
- .arg(COMMAND_FN)
- .arg(arg.first),
- tr("I cannot find any function of that name with the "
- "specified signature. Make sure that the signature "
- "is identical to the declaration, including 'const' "
- "qualifiers."));
- }
- else
- lastPath_ = parentPath;
- if (func) {
- func->borrowParameterNames(clone);
- func->setParentPath(clone->parentPath());
- }
- delete clone;
- }
- return func;
- }
- else if (command == COMMAND_MACRO) {
- QStringList parentPath;
- FunctionNode *func = 0;
-
- extra.root = qdb_->primaryTreeRoot();
- extra.isMacro = true;
- if (makeFunctionNode(arg.first, &parentPath, &func, extra)) {
- if (!parentPath.isEmpty()) {
- doc.startLocation().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO));
- delete func;
- func = 0;
- }
- else {
- func->setMetaness(FunctionNode::MacroWithParams);
- QVector<Parameter> params = func->parameters();
- for (int i = 0; i < params.size(); ++i) {
- Parameter &param = params[i];
- if (param.name().isEmpty() && !param.dataType().isEmpty()
- && param.dataType() != "...")
- param = Parameter("", "", param.dataType());
- }
- func->setParameters(params);
- }
- return func;
- }
- else if (QRegExp("[A-Za-z_][A-Za-z0-9_]+").exactMatch(arg.first)) {
- func = new FunctionNode(qdb_->primaryTreeRoot(), arg.first);
- func->setAccess(Node::Public);
- func->setLocation(doc.startLocation());
- func->setMetaness(FunctionNode::MacroWithoutParams);
- }
- else {
- doc.location().warning(tr("Invalid syntax in '\\%1'").arg(COMMAND_MACRO));
-
- }
- return func;
+ Q_UNREACHABLE();
}
else if (nodeTypeMap.contains(command)) {
/*
@@ -420,8 +228,10 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
if (node == 0)
node = qdb_->findNodeByNameAndType(path, type);
if (node == 0) {
- doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
- .arg(arg.first).arg(command));
+ if (isWorthWarningAbout(doc)) {
+ doc.location().warning(tr("Cannot find '%1' specified with '\\%2' in any header file")
+ .arg(arg.first).arg(command));
+ }
lastPath_ = path;
}
@@ -560,50 +370,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
(command == COMMAND_JSMETHOD) ||
(command == COMMAND_JSATTACHEDSIGNAL) ||
(command == COMMAND_JSATTACHEDMETHOD)) {
- QString module;
- QString name;
- QString type;
- if (splitQmlMethodArg(arg.first, type, module, name)) {
- Aggregate* aggregate = qdb_->findQmlType(module, name);
- if (!aggregate)
- aggregate = qdb_->findQmlBasicType(module, name);
- if (aggregate) {
- bool attached = false;
- Node::NodeType nodeType = Node::QmlMethod;
- if ((command == COMMAND_QMLSIGNAL) ||
- (command == COMMAND_JSSIGNAL))
- nodeType = Node::QmlSignal;
- else if ((command == COMMAND_QMLATTACHEDSIGNAL) ||
- (command == COMMAND_JSATTACHEDSIGNAL)) {
- nodeType = Node::QmlSignal;
- attached = true;
- }
- else if ((command == COMMAND_QMLMETHOD) ||
- (command == COMMAND_JSMETHOD)) {
- // do nothing
- }
- else if ((command == COMMAND_QMLATTACHEDMETHOD) ||
- (command == COMMAND_JSATTACHEDMETHOD))
- attached = true;
- else
- return 0; // never get here.
- FunctionNode* fn = makeFunctionNode(doc,
- arg.first,
- aggregate,
- nodeType,
- attached,
- command);
- if (fn) {
- fn->setLocation(doc.startLocation());
- if ((command == COMMAND_JSSIGNAL) ||
- (command == COMMAND_JSMETHOD) ||
- (command == COMMAND_JSATTACHEDSIGNAL) ||
- (command == COMMAND_JSATTACHEDMETHOD))
- fn->setGenus(Node::JS);
- }
- return fn;
- }
- }
+ Q_UNREACHABLE();
}
return 0;
}
@@ -623,7 +390,8 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
bool CppCodeParser::splitQmlPropertyGroupArg(const QString& arg,
QString& module,
QString& qmlTypeName,
- QString& name)
+ QString& name,
+ const Location& location)
{
QStringList colonSplit = arg.split("::");
if (colonSplit.size() == 3) {
@@ -633,7 +401,7 @@ bool CppCodeParser::splitQmlPropertyGroupArg(const QString& arg,
return true;
}
QString msg = "Unrecognizable QML module/component qualifier for " + arg;
- location().warning(tr(msg.toLatin1().data()));
+ location.warning(tr(msg.toLatin1().data()));
return false;
}
@@ -660,7 +428,8 @@ bool CppCodeParser::splitQmlPropertyArg(const QString& arg,
QString& type,
QString& module,
QString& qmlTypeName,
- QString& name)
+ QString& name,
+ const Location &location)
{
QStringList blankSplit = arg.split(QLatin1Char(' '));
if (blankSplit.size() > 1) {
@@ -679,66 +448,16 @@ bool CppCodeParser::splitQmlPropertyArg(const QString& arg,
return true;
}
QString msg = "Unrecognizable QML module/component qualifier for " + arg;
- location().warning(tr(msg.toLatin1().data()));
+ location.warning(tr(msg.toLatin1().data()));
}
else {
QString msg = "Missing property type for " + arg;
- location().warning(tr(msg.toLatin1().data()));
+ location.warning(tr(msg.toLatin1().data()));
}
return false;
}
/*!
- A QML signal or method argument has the form...
-
- <type> <QML-type>::<name>(<param>, <param>, ...)
- <type> <QML-module>::<QML-type>::<name>(<param>, <param>, ...)
-
- This function splits the \a{arg}ument into one of those
- two forms, sets \a type, \a module, and \a qmlTypeName,
- and returns true. If the argument doesn't match either
- form, an error message is emitted and false is returned.
-
- \note The two QML types \e{Component} and \e{QtObject} never
- have a module qualifier.
- */
-bool CppCodeParser::splitQmlMethodArg(const QString& arg,
- QString& type,
- QString& module,
- QString& qmlTypeName)
-{
- QString name;
- int leftParen = arg.indexOf(QChar('('));
- if (leftParen > 0)
- name = arg.left(leftParen);
- else
- name = arg;
- int firstBlank = name.indexOf(QChar(' '));
- if (firstBlank > 0) {
- type = name.left(firstBlank);
- name = name.right(name.length() - firstBlank - 1);
- }
- else
- type.clear();
-
- QStringList colonSplit(name.split("::"));
- if (colonSplit.size() > 1) {
- if (colonSplit.size() > 2) {
- module = colonSplit[0];
- qmlTypeName = colonSplit[1];
- }
- else {
- module.clear();
- qmlTypeName = colonSplit[0];
- }
- return true;
- }
- QString msg = "Unrecognizable QML module/component qualifier for " + arg;
- location().warning(tr(msg.toLatin1().data()));
- return false;
-}
-
-/*!
Process the topic \a command group found in the \a doc with arguments \a args.
Currently, this function is called only for \e{qmlproperty}
@@ -775,7 +494,7 @@ void CppCodeParser::processQmlProperties(const Doc& doc,
else
qmlPropertyGroupTopic.topic = COMMAND_QMLPROPERTYGROUP;
arg = qmlPropertyGroupTopic.args;
- if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property)) {
+ if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property, doc.location())) {
int i = property.indexOf('.');
if (i != -1) {
property = property.left(i);
@@ -795,7 +514,7 @@ void CppCodeParser::processQmlProperties(const Doc& doc,
if (!qmlPropertyGroupTopic.isEmpty()) {
arg = qmlPropertyGroupTopic.args;
- if (splitQmlPropertyGroupArg(arg, module, qmlTypeName, property)) {
+ if (splitQmlPropertyGroupArg(arg, module, qmlTypeName, property, doc.location())) {
qmlType = qdb_->findQmlType(module, qmlTypeName);
if (qmlType) {
qpgn = new QmlPropertyGroupNode(qmlType, property);
@@ -817,7 +536,7 @@ void CppCodeParser::processQmlProperties(const Doc& doc,
(topic == COMMAND_JSPROPERTY) || (topic == COMMAND_JSATTACHEDPROPERTY)) {
bool attached = ((topic == COMMAND_QMLATTACHEDPROPERTY) ||
(topic == COMMAND_JSATTACHEDPROPERTY));
- if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property)) {
+ if (splitQmlPropertyArg(arg, type, module, qmlTypeName, property, doc.location())) {
Aggregate* aggregate = qdb_->findQmlType(module, qmlTypeName);
if (!aggregate)
aggregate = qdb_->findQmlBasicType(module, qmlTypeName);
@@ -909,11 +628,13 @@ void CppCodeParser::processOtherMetaCommand(const Doc& doc,
FunctionNode *func = (FunctionNode *) node;
const FunctionNode *from = func->reimplementedFrom();
if (from == 0) {
- doc.location().warning(tr("Cannot find base function for '\\%1' in %2()")
- .arg(COMMAND_REIMP).arg(node->name()),
- tr("The function either doesn't exist in any "
- "base class with the same signature or it "
- "exists but isn't virtual."));
+ if (isWorthWarningAbout(doc)) {
+ doc.location().warning(tr("Cannot find base function for '\\%1' in %2()")
+ .arg(COMMAND_REIMP).arg(node->name()),
+ tr("The function either doesn't exist in any "
+ "base class with the same signature or it "
+ "exists but isn't virtual."));
+ }
}
/*
Ideally, we would enable this check to warn whenever
@@ -921,8 +642,9 @@ void CppCodeParser::processOtherMetaCommand(const Doc& doc,
internal if the function is a reimplementation of
another function in a base class.
*/
- else if (from->access() == Node::Private
- || from->parent()->access() == Node::Private) {
+ else if ((from->access() == Node::Private
+ || from->parent()->access() == Node::Private)
+ && isWorthWarningAbout(doc)) {
doc.location().warning(tr("'\\%1' in %2() should be '\\internal' "
"because its base function is private "
"or internal").arg(COMMAND_REIMP).arg(node->name()));
@@ -1179,15 +901,6 @@ bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType)
return matches;
}
-/*
- This function is no longer used.
- */
-bool CppCodeParser::matchTemplateHeader()
-{
- readToken();
- return matchTemplateAngles();
-}
-
bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var, bool qProp)
{
/*
@@ -1212,11 +925,24 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var, bool qProp)
*/
while (match(Tok_const) || match(Tok_volatile))
dataType->append(previousLexeme());
- while (match(Tok_signed) || match(Tok_unsigned) ||
- match(Tok_short) || match(Tok_long) || match(Tok_int64)) {
- dataType->append(previousLexeme());
+ QString pending;
+ while (tok == Tok_signed || tok == Tok_int || tok == Tok_unsigned ||
+ tok == Tok_short || tok == Tok_long || tok == Tok_int64) {
+ if (tok == Tok_signed)
+ pending = lexeme();
+ else {
+ if (tok == Tok_unsigned && !pending.isEmpty())
+ dataType->append(pending);
+ pending.clear();
+ dataType->append(lexeme());
+ }
+ readToken();
virgin = false;
}
+ if (!pending.isEmpty()) {
+ dataType->append(pending);
+ pending.clear();
+ }
while (match(Tok_const) || match(Tok_volatile))
dataType->append(previousLexeme());
@@ -1274,22 +1000,29 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var, bool qProp)
in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
as a single token.
*/
+ dataType->append(" "); // force a space after the type
dataType->append(previousLexeme());
dataType->appendHotspot();
if (var != 0 && match(Tok_Ident))
*var = previousLexeme();
- if (!match(Tok_RightParen) || tok != Tok_LeftParen) {
+ if (!match(Tok_RightParen))
+ return false;
+ dataType->append(previousLexeme());
+ if (!match(Tok_LeftParen))
return false;
- }
dataType->append(previousLexeme());
- int parenDepth0 = tokenizer->parenDepth();
- while (tokenizer->parenDepth() >= parenDepth0 && tok != Tok_Eoi) {
- dataType->append(lexeme());
- readToken();
+ /* parse the parameters. Ignore the parameter name from the type */
+ while (tok != Tok_RightParen && tok != Tok_Eoi) {
+ QString dummy;
+ if (!matchDataType(dataType, &dummy))
+ return false;
+ if (match(Tok_Comma))
+ dataType->append(previousLexeme());
}
- if (match(Tok_RightParen))
- dataType->append(previousLexeme());
+ if (!match(Tok_RightParen))
+ return false;
+ dataType->append(previousLexeme());
}
else {
/*
@@ -1314,6 +1047,24 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var, bool qProp)
if (varComment.exactMatch(previousLexeme()))
*var = varComment.cap(1);
}
+ else if (match(Tok_LeftParen)) {
+ *var = "(";
+ while (tok != Tok_RightParen && tok != Tok_Eoi) {
+ (*var).append(lexeme());
+ readToken();
+ }
+ (*var).append(")");
+ readToken();
+ if (match(Tok_LeftBracket)) {
+ (*var).append("[");
+ while (tok != Tok_RightBracket && tok != Tok_Eoi) {
+ (*var).append(lexeme());
+ readToken();
+ }
+ (*var).append("]");
+ readToken();
+ }
+ }
else if (qProp && (match(Tok_default) || match(Tok_final) || match(Tok_override))) {
// Hack to make 'default', 'final' and 'override' work again in Q_PROPERTY
*var = previousLexeme();
@@ -1369,495 +1120,6 @@ bool CppCodeParser::matchParameter(QVector<Parameter>& pvect, bool& isQPrivateSi
}
/*!
- If the current token is any of several function modifiers,
- return that token value after reading the next token. If it
- is not one of the function modieifer tokens, return -1 but
- don\t read the next token.
- */
-int CppCodeParser::matchFunctionModifier()
-{
- switch (tok) {
- case Tok_friend:
- case Tok_inline:
- case Tok_explicit:
- case Tok_static:
- case Tok_QT_DEPRECATED:
- readToken();
- return tok;
- case Tok_QT_COMPAT:
- case Tok_QT_COMPAT_CONSTRUCTOR:
- case Tok_QT_MOC_COMPAT:
- case Tok_QT3_SUPPORT:
- case Tok_QT3_SUPPORT_CONSTRUCTOR:
- case Tok_QT3_MOC_SUPPORT:
- readToken();
- return Tok_QT_COMPAT;
- default:
- break;
- }
- return -1;
-}
-
-bool CppCodeParser::matchFunctionDecl(Aggregate *parent,
- QStringList *parentPathPtr,
- FunctionNode **funcPtr,
- const QString &templateStuff,
- ExtraFuncData& extra)
-{
- CodeChunk returnType;
- QStringList parentPath;
- QString name;
-
- bool matched_QT_DEPRECATED = false;
- bool matched_friend = false;
- bool matched_static = false;
- bool matched_inline = false;
- bool matched_explicit = false;
- bool matched_compat = false;
-
- int token = tok;
- while (token != -1) {
- switch (token) {
- case Tok_friend:
- matched_friend = true;
- break;
- case Tok_inline:
- matched_inline = true;
- break;
- case Tok_explicit:
- matched_explicit = true;
- break;
- case Tok_static:
- matched_static = true;
- break;
- case Tok_QT_DEPRECATED:
- matched_QT_DEPRECATED = true;
- Q_FALLTHROUGH(); // no break here.
- case Tok_QT_COMPAT:
- matched_compat = true;
- break;
- }
- token = matchFunctionModifier();
- }
-
- FunctionNode::Virtualness virtuality = FunctionNode::NonVirtual;
- if (match(Tok_virtual)) {
- virtuality = FunctionNode::NormalVirtual;
- if (!matched_compat)
- matched_compat = matchCompat();
- }
-
- if (!matchDataType(&returnType)) {
- if (tokenizer->parsingFnOrMacro()
- && (match(Tok_Q_DECLARE_FLAGS) ||
- match(Tok_Q_PROPERTY) ||
- match(Tok_Q_PRIVATE_PROPERTY)))
- returnType = CodeChunk(previousLexeme());
- else {
- return false;
- }
- }
-
- if (returnType.toString() == "QBool")
- returnType = CodeChunk("bool");
-
- if (!matched_compat)
- matched_compat = matchCompat();
-
- if (tok == Tok_operator &&
- (returnType.toString().isEmpty() ||
- returnType.toString().endsWith("::"))) {
- // 'QString::operator const char *()'
- parentPath = returnType.toString().split(sep);
- parentPath.removeAll(QString());
- returnType = CodeChunk();
- readToken();
-
- CodeChunk restOfName;
- if (tok != Tok_Tilde && matchDataType(&restOfName)) {
- name = "operator " + restOfName.toString();
- }
- else {
- name = previousLexeme() + lexeme();
- readToken();
- while (tok != Tok_LeftParen && tok != Tok_Eoi) {
- name += lexeme();
- readToken();
- }
- }
- if (tok != Tok_LeftParen) {
- return false;
- }
- }
- else if (tok == Tok_LeftParen) {
- // constructor or destructor
- parentPath = returnType.toString().split(sep);
- if (!parentPath.isEmpty()) {
- name = parentPath.last();
- parentPath.erase(parentPath.end() - 1);
- }
- returnType = CodeChunk();
- }
- else {
- while (match(Tok_Ident)) {
- name = previousLexeme();
- /*
- This is a hack to let QML module identifiers through.
- */
- matchModuleQualifier(name);
- matchTemplateAngles();
-
- if (match(Tok_Gulbrandsen))
- parentPath.append(name);
- else
- break;
- }
-
- if (tok == Tok_operator) {
- name = lexeme();
- readToken();
- while (tok != Tok_Eoi) {
- name += lexeme();
- readToken();
- if (tok == Tok_LeftParen)
- break;
- }
- }
- if (parent && (tok == Tok_Semicolon ||
- tok == Tok_LeftBracket ||
- tok == Tok_Colon || tok == Tok_Equal)
- && access != Node::Private) {
- if (tok == Tok_LeftBracket) {
- returnType.appendHotspot();
-
- int bracketDepth0 = tokenizer->bracketDepth();
- while ((tokenizer->bracketDepth() >= bracketDepth0 &&
- tok != Tok_Eoi) ||
- tok == Tok_RightBracket) {
- returnType.append(lexeme());
- readToken();
- }
- if (tok != Tok_Semicolon) {
- return false;
- }
- }
- else if (tok == Tok_Colon || tok == Tok_Equal) {
- returnType.appendHotspot();
-
- while (tok != Tok_Semicolon && tok != Tok_Eoi) {
- returnType.append(lexeme());
- readToken();
- }
- if (tok != Tok_Semicolon) {
- return false;
- }
- }
-
- VariableNode *var = new VariableNode(parent, name);
- var->setAccess(access);
- if (parsingHeaderFile_)
- var->setLocation(declLoc());
- else
- var->setLocation(location());
- var->setLeftType(returnType.left());
- var->setRightType(returnType.right());
- if (matched_compat)
- var->setStatus(Node::Compat);
- var->setStatic(matched_static);
- return false;
- }
- if (tok != Tok_LeftParen)
- return false;
- }
- readToken();
-
- // A left paren was seen. Parse the parameters
- bool isQPrivateSignal = false;
- QVector<Parameter> pvect;
- if (tok != Tok_RightParen) {
- do {
- if (!matchParameter(pvect, isQPrivateSignal))
- return false;
- } while (match(Tok_Comma));
- }
- // The parameters must end with a right paren
- if (!match(Tok_RightParen))
- return false;
-
- // look for const
- bool matchedConst = match(Tok_const);
- bool matchFinal = match(Tok_final);
- bool matchOverride = match(Tok_override);
-
- bool isDeleted = false;
- bool isDefaulted = false;
- if (match(Tok_Equal)) {
- if (match(Tok_Number)) // look for 0 indicating pure virtual
- virtuality = FunctionNode::PureVirtual;
- else if (match(Tok_delete))
- isDeleted = true;
- else if (match(Tok_default))
- isDefaulted = true;
- }
- // look for colon indicating ctors which must be skipped
- if (match(Tok_Colon)) {
- while (tok != Tok_LeftBrace && tok != Tok_Eoi)
- readToken();
- }
-
- // If no ';' expect a body, which must be skipped.
- bool body_expected = false;
- bool body_present = false;
- if (!match(Tok_Semicolon) && tok != Tok_Eoi) {
- body_expected = true;
- int nesting = tokenizer->braceDepth();
- if (!match(Tok_LeftBrace))
- return false;
- // skip the body
- while (tokenizer->braceDepth() >= nesting && tok != Tok_Eoi)
- readToken();
- body_present = true;
- match(Tok_RightBrace);
- }
-
- FunctionNode *func = 0;
- bool createFunctionNode = false;
- if (parsingHeaderFile_) {
- if (matched_friend) {
- if (matched_inline) {
- // nothing yet
- }
- if (body_present) {
- if (body_expected) {
- // nothing yet
- }
- createFunctionNode = true;
- if (parent && parent->parent())
- parent = parent->parent();
- else
- return false;
- }
- }
- else
- createFunctionNode = true;
- }
- else
- createFunctionNode = true;
-
- if (createFunctionNode) {
- func = new FunctionNode(extra.type, parent, name, extra.isAttached);
- if (matched_friend)
- access = Node::Public;
- func->setAccess(access);
- if (parsingHeaderFile_)
- func->setLocation(declLoc());
- else
- func->setLocation(location());
- func->setReturnType(returnType.toString());
- func->setParentPath(parentPath);
- func->setTemplateStuff(templateStuff);
- if (matched_compat)
- func->setStatus(Node::Compat);
- if (matched_QT_DEPRECATED)
- func->setStatus(Node::Deprecated);
- if (matched_explicit) { /* What can be done? */ }
- if (!pvect.isEmpty()) {
- func->setParameters(pvect);
- }
- func->setMetaness(metaness_);
- if (parent && (name == parent->name())) {
- FunctionNode::Metaness m = FunctionNode::Ctor;
- if (!pvect.isEmpty()) {
- for (int i=0; i<pvect.size(); i++) {
- const Parameter& p = pvect.at(i);
- if (p.dataType().contains(name)) {
- if (p.dataType().endsWith(QLatin1String("&&"))) {
- m = FunctionNode::MCtor;
- break;
- }
- if (p.dataType().endsWith(QLatin1String("&"))) {
- m = FunctionNode::CCtor;
- break;
- }
- }
- }
- }
- func->setMetaness(m);
- }
- else if (name.startsWith(QLatin1Char('~')))
- func->setMetaness(FunctionNode::Dtor);
- else if (name == QLatin1String("operator=")) {
- FunctionNode::Metaness m = FunctionNode::Plain;
- if (parent && pvect.size() == 1) {
- const Parameter& p = pvect.at(0);
- if (p.dataType().contains(parent->name())) {
- if (p.dataType().endsWith(QLatin1String("&&"))) {
- m = FunctionNode::MAssign;
- }
- else if (p.dataType().endsWith(QLatin1String("&"))) {
- m = FunctionNode::CAssign;
- }
- }
- }
- func->setMetaness(m);
- }
- func->setStatic(matched_static);
- func->setConst(matchedConst);
- func->setVirtualness(virtuality);
- func->setIsDeleted(isDeleted);
- func->setIsDefaulted(isDefaulted);
- func->setFinal(matchFinal);
- func->setOverride(matchOverride);
- if (isQPrivateSignal)
- func->setPrivateSignal();
- }
- if (parentPathPtr != 0)
- *parentPathPtr = parentPath;
- if (funcPtr != 0)
- *funcPtr = func;
- return true;
-}
-
-bool CppCodeParser::matchBaseSpecifier(ClassNode *classe, bool isClass)
-{
- Node::Access access;
-
- switch (tok) {
- case Tok_public:
- access = Node::Public;
- readToken();
- break;
- case Tok_protected:
- access = Node::Protected;
- readToken();
- break;
- case Tok_private:
- access = Node::Private;
- readToken();
- break;
- default:
- access = isClass ? Node::Private : Node::Public;
- }
-
- if (tok == Tok_virtual)
- readToken();
-
- CodeChunk baseClass;
- if (!matchDataType(&baseClass))
- return false;
-
- classe->addUnresolvedBaseClass(access, baseClass.toPath(), baseClass.toString());
- return true;
-}
-
-bool CppCodeParser::matchBaseList(ClassNode *classe, bool isClass)
-{
- for (;;) {
- if (!matchBaseSpecifier(classe, isClass))
- return false;
- if (tok == Tok_LeftBrace)
- return true;
- if (!match(Tok_Comma))
- return false;
- }
-}
-
-/*!
- Parse a C++ class, union, or struct declaration.
-
- This function only handles one level of class nesting, but that is
- sufficient for Qt because there are no cases of class nesting more
- than one level deep.
- */
-bool CppCodeParser::matchClassDecl(Aggregate *parent,
- const QString &templateStuff)
-{
- bool isClass = (tok == Tok_class);
- readToken();
-
- bool compat = matchCompat();
-
- if (tok != Tok_Ident)
- return false;
- while (tok == Tok_Ident)
- readToken();
- if (tok == Tok_Gulbrandsen) {
- Node* n = parent->findChildNode(previousLexeme(),Node::Class);
- if (n) {
- parent = static_cast<Aggregate*>(n);
- if (parent) {
- readToken();
- if (tok != Tok_Ident)
- return false;
- readToken();
- }
- }
- }
-
- const QString className = previousLexeme();
- match(Tok_final); // ignore C++11 final class-virt-specifier
- if (tok != Tok_Colon && tok != Tok_LeftBrace)
- return false;
-
- /*
- So far, so good. We have 'class Foo {' or 'class Foo :'.
- This is enough to recognize a class definition.
- */
- ClassNode *classe = new ClassNode(parent, className);
- classe->setAccess(access);
- classe->setLocation(declLoc());
- if (compat)
- classe->setStatus(Node::Compat);
- if (!physicalModuleName.isEmpty())
- classe->setPhysicalModuleName(physicalModuleName);
- classe->setTemplateStuff(templateStuff);
-
- if (match(Tok_Colon) && !matchBaseList(classe, isClass))
- return false;
- if (!match(Tok_LeftBrace))
- return false;
-
- Node::Access outerAccess = access;
- access = isClass ? Node::Private : Node::Public;
- FunctionNode::Metaness outerMetaness = metaness_;
- metaness_ = FunctionNode::Plain;
-
- bool matches = (matchDeclList(classe) && match(Tok_RightBrace) &&
- match(Tok_Semicolon));
- access = outerAccess;
- metaness_ = outerMetaness;
- return matches;
-}
-
-bool CppCodeParser::matchNamespaceDecl(Aggregate *parent)
-{
- readToken(); // skip 'namespace'
- if (tok != Tok_Ident)
- return false;
- while (tok == Tok_Ident)
- readToken();
- if (tok != Tok_LeftBrace)
- return false;
-
- /*
- So far, so good. We have 'namespace Foo {'.
- */
- QString namespaceName = previousLexeme();
- NamespaceNode* ns = 0;
- if (parent)
- ns = static_cast<NamespaceNode*>(parent->findChildNode(namespaceName, Node::Namespace));
- if (!ns) {
- ns = new NamespaceNode(parent, namespaceName);
- ns->setAccess(access);
- ns->setLocation(declLoc());
- }
-
- readToken(); // skip '{'
- bool matched = matchDeclList(ns);
- return matched && match(Tok_RightBrace);
-}
-
-/*!
Match a C++ \c using clause. Return \c true if the match
is successful. Otherwise false.
@@ -1930,585 +1192,6 @@ bool CppCodeParser::matchUsingDecl(Aggregate* parent)
return true;
}
-bool CppCodeParser::matchEnumItem(Aggregate *parent, EnumNode *enume)
-{
- if (!match(Tok_Ident))
- return false;
-
- QString name = previousLexeme();
- CodeChunk val;
- int parenLevel = 0;
-
- if (match(Tok_Equal)) {
- while (tok != Tok_RightBrace && tok != Tok_Eoi) {
- if (tok == Tok_LeftParen)
- parenLevel++;
- else if (tok == Tok_RightParen)
- parenLevel--;
- else if (tok == Tok_Comma) {
- if (parenLevel <= 0)
- break;
- }
- val.append(lexeme());
- readToken();
- }
- }
-
- if (enume) {
- QString strVal = val.toString();
- if (strVal.isEmpty()) {
- if (enume->items().isEmpty()) {
- strVal = "0";
- }
- else {
- QString last = enume->items().last().value();
- bool ok;
- int n = last.toInt(&ok);
- if (ok) {
- if (last.startsWith(QLatin1Char('0')) && last.size() > 1) {
- if (last.startsWith("0x") || last.startsWith("0X"))
- strVal = last.left(2) + QString::number(n + 1, 16);
- else
- strVal = QLatin1Char('0') + QString::number(n + 1, 8);
- }
- else
- strVal = QString::number(n + 1);
- }
- }
- }
-
- enume->addItem(EnumItem(name, strVal));
- }
- else {
- VariableNode *var = new VariableNode(parent, name);
- var->setAccess(access);
- var->setLocation(location());
- var->setLeftType("const int");
- var->setStatic(true);
- }
- return true;
-}
-
-bool CppCodeParser::matchEnumDecl(Aggregate *parent)
-{
- QString name;
-
- if (!match(Tok_enum))
- return false;
- if (tok == Tok_struct || tok == Tok_class)
- readToken(); // ignore C++11 struct or class attribute
- if (match(Tok_Ident))
- name = previousLexeme();
- if (match(Tok_Colon)) { // ignore C++11 enum-base
- CodeChunk dataType;
- if (!matchDataType(&dataType))
- return false;
- }
- if (tok != Tok_LeftBrace)
- return false;
-
- EnumNode *enume = 0;
-
- if (!name.isEmpty()) {
- enume = new EnumNode(parent, name);
- enume->setAccess(access);
- enume->setLocation(declLoc());
- }
-
- readToken();
-
- if (!matchEnumItem(parent, enume))
- return false;
-
- while (match(Tok_Comma)) {
- if (!matchEnumItem(parent, enume))
- return false;
- }
- return match(Tok_RightBrace) && match(Tok_Semicolon);
-}
-
-bool CppCodeParser::matchTypedefDecl(Aggregate *parent)
-{
- CodeChunk dataType;
- QString name;
-
- if (!match(Tok_typedef))
- return false;
- if (!matchDataType(&dataType, &name))
- return false;
- if (!match(Tok_Semicolon))
- return false;
-
- if (parent && !parent->findChildNode(name, Node::Typedef)) {
- TypedefNode* td = new TypedefNode(parent, name);
- td->setAccess(access);
- td->setLocation(declLoc());
- }
- return true;
-}
-
-bool CppCodeParser::matchProperty(Aggregate *parent)
-{
- int expected_tok = Tok_LeftParen;
- if (match(Tok_Q_PRIVATE_PROPERTY)) {
- expected_tok = Tok_Comma;
- if (!skipTo(Tok_Comma))
- return false;
- }
- else if (!match(Tok_Q_PROPERTY) &&
- !match(Tok_Q_OVERRIDE) &&
- !match(Tok_QDOC_PROPERTY)) {
- return false;
- }
-
- if (!match(expected_tok))
- return false;
-
- QString name;
- CodeChunk dataType;
- if (!matchDataType(&dataType, &name, true))
- return false;
-
- PropertyNode *property = new PropertyNode(parent, name);
- property->setAccess(Node::Public);
- property->setLocation(declLoc());
- property->setDataType(dataType.toString());
-
- while (tok != Tok_RightParen && tok != Tok_Eoi) {
- if (!match(Tok_Ident) && !match(Tok_default) && !match(Tok_final) && !match(Tok_override))
- return false;
- QString key = previousLexeme();
- QString value;
-
- // Keywords with no associated values
- if (key == "CONSTANT") {
- property->setConstant();
- continue;
- }
- else if (key == "FINAL") {
- property->setFinal();
- continue;
- }
-
- if (match(Tok_Ident) || match(Tok_Number)) {
- value = previousLexeme();
- }
- else if (match(Tok_LeftParen)) {
- int depth = 1;
- while (tok != Tok_Eoi) {
- if (tok == Tok_LeftParen) {
- readToken();
- ++depth;
- } else if (tok == Tok_RightParen) {
- readToken();
- if (--depth == 0)
- break;
- } else {
- readToken();
- }
- }
- value = "?";
- }
-
- if (key == "READ")
- qdb_->addPropertyFunction(property, value, PropertyNode::Getter);
- else if (key == "WRITE") {
- qdb_->addPropertyFunction(property, value, PropertyNode::Setter);
- property->setWritable(true);
- }
- else if (key == "STORED")
- property->setStored(value.toLower() == "true");
- else if (key == "DESIGNABLE") {
- QString v = value.toLower();
- if (v == "true")
- property->setDesignable(true);
- else if (v == "false")
- property->setDesignable(false);
- else {
- property->setDesignable(false);
- property->setRuntimeDesFunc(value);
- }
- }
- else if (key == "RESET")
- qdb_->addPropertyFunction(property, value, PropertyNode::Resetter);
- else if (key == "NOTIFY") {
- qdb_->addPropertyFunction(property, value, PropertyNode::Notifier);
- } else if (key == "REVISION") {
- int revision;
- bool ok;
- revision = value.toInt(&ok);
- if (ok)
- property->setRevision(revision);
- else
- location().warning(tr("Invalid revision number: %1").arg(value));
- } else if (key == "SCRIPTABLE") {
- QString v = value.toLower();
- if (v == "true")
- property->setScriptable(true);
- else if (v == "false")
- property->setScriptable(false);
- else {
- property->setScriptable(false);
- property->setRuntimeScrFunc(value);
- }
- }
- }
- match(Tok_RightParen);
- return true;
-}
-
-/*!
- Parse a C++ declaration.
- */
-bool CppCodeParser::matchDeclList(Aggregate *parent)
-{
- ExtraFuncData extra;
- QString templateStuff;
- int braceDepth0 = tokenizer->braceDepth();
- if (tok == Tok_RightBrace) // prevents failure on empty body
- braceDepth0++;
-
- while (tokenizer->braceDepth() >= braceDepth0 && tok != Tok_Eoi) {
- switch (tok) {
- case Tok_Colon:
- readToken();
- break;
- case Tok_class:
- case Tok_struct:
- case Tok_union:
- setDeclLoc();
- matchClassDecl(parent, templateStuff);
- break;
- case Tok_namespace:
- setDeclLoc();
- matchNamespaceDecl(parent);
- break;
- case Tok_using:
- setDeclLoc();
- matchUsingDecl(parent);
- break;
- case Tok_template:
- {
- CodeChunk dataType;
- readToken();
- matchTemplateAngles(&dataType);
- templateStuff = dataType.toString();
- }
- continue;
- case Tok_enum:
- setDeclLoc();
- matchEnumDecl(parent);
- break;
- case Tok_typedef:
- setDeclLoc();
- matchTypedefDecl(parent);
- break;
- case Tok_private:
- readToken();
- access = Node::Private;
- metaness_ = FunctionNode::Plain;
- break;
- case Tok_protected:
- readToken();
- access = Node::Protected;
- metaness_ = FunctionNode::Plain;
- break;
- case Tok_public:
- readToken();
- access = Node::Public;
- metaness_ = FunctionNode::Plain;
- break;
- case Tok_signals:
- case Tok_Q_SIGNALS:
- readToken();
- access = Node::Public;
- metaness_ = FunctionNode::Signal;
- break;
- case Tok_slots:
- case Tok_Q_SLOTS:
- readToken();
- metaness_ = FunctionNode::Slot;
- break;
- case Tok_Q_OBJECT:
- readToken();
- break;
- case Tok_Q_OVERRIDE:
- case Tok_Q_PROPERTY:
- case Tok_Q_PRIVATE_PROPERTY:
- case Tok_QDOC_PROPERTY:
- setDeclLoc();
- if (!matchProperty(parent)) {
- location().warning(tr("Failed to parse token %1 in property declaration").arg(lexeme()));
- skipTo(Tok_RightParen);
- match(Tok_RightParen);
- }
- break;
- case Tok_Q_DECLARE_SEQUENTIAL_ITERATOR:
- readToken();
- if (match(Tok_LeftParen) && match(Tok_Ident))
- sequentialIteratorClasses.insert(previousLexeme(), location().fileName());
- match(Tok_RightParen);
- break;
- case Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR:
- readToken();
- if (match(Tok_LeftParen) && match(Tok_Ident))
- mutableSequentialIteratorClasses.insert(previousLexeme(), location().fileName());
- match(Tok_RightParen);
- break;
- case Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR:
- readToken();
- if (match(Tok_LeftParen) && match(Tok_Ident))
- associativeIteratorClasses.insert(previousLexeme(), location().fileName());
- match(Tok_RightParen);
- break;
- case Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR:
- readToken();
- if (match(Tok_LeftParen) && match(Tok_Ident))
- mutableAssociativeIteratorClasses.insert(previousLexeme(), location().fileName());
- match(Tok_RightParen);
- break;
- case Tok_Q_DECLARE_FLAGS:
- readToken();
- setDeclLoc();
- if (match(Tok_LeftParen) && match(Tok_Ident)) {
- QString flagsType = previousLexeme();
- if (match(Tok_Comma) && match(Tok_Ident)) {
- QString name = previousLexeme();
- TypedefNode *flagsNode = new TypedefNode(parent, flagsType);
- flagsNode->setAccess(access);
- flagsNode->setLocation(declLoc());
- EnumNode* en = static_cast<EnumNode*>(parent->findChildNode(name, Node::Enum));
- if (en)
- en->setFlagsType(flagsNode);
- }
- }
- match(Tok_RightParen);
- break;
- case Tok_QT_MODULE:
- readToken();
- setDeclLoc();
- if (match(Tok_LeftParen) && match(Tok_Ident))
- physicalModuleName = previousLexeme();
- if (!physicalModuleName.startsWith("Qt"))
- physicalModuleName.prepend("Qt");
- match(Tok_RightParen);
- break;
- default:
- if (parsingHeaderFile_)
- setDeclLoc();
- if (!matchFunctionDecl(parent, 0, 0, templateStuff, extra)) {
- while (tok != Tok_Eoi &&
- (tokenizer->braceDepth() > braceDepth0 ||
- (!match(Tok_Semicolon) &&
- tok != Tok_public && tok != Tok_protected &&
- tok != Tok_private))) {
- readToken();
- }
- }
- }
- templateStuff.clear();
- }
- return true;
-}
-
-/*!
- This is called by parseSourceFile() to do the actual parsing
- and tree building.
- */
-bool CppCodeParser::matchDocsAndStuff()
-{
- ExtraFuncData extra;
- const QSet<QString>& topicCommandsAllowed = topicCommands();
- const QSet<QString>& otherMetacommandsAllowed = otherMetaCommands();
- const QSet<QString>& metacommandsAllowed = topicCommandsAllowed + otherMetacommandsAllowed;
-
- while (tok != Tok_Eoi) {
- if (tok == Tok_Doc) {
- /*
- lexeme() returns an entire qdoc comment.
- */
- QString comment = lexeme();
- Location start_loc(location());
- readToken();
-
- Doc::trimCStyleComment(start_loc,comment);
- Location end_loc(location());
-
- /*
- Doc parses the comment.
- */
- Doc doc(start_loc,end_loc,comment,metacommandsAllowed, topicCommandsAllowed);
- QString topic;
- bool isQmlPropertyTopic = false;
- bool isJsPropertyTopic = false;
-
- const TopicList& topics = doc.topicsUsed();
- if (!topics.isEmpty()) {
- topic = topics[0].topic;
- if ((topic == COMMAND_QMLPROPERTY) ||
- (topic == COMMAND_QMLPROPERTYGROUP) ||
- (topic == COMMAND_QMLATTACHEDPROPERTY)) {
- isQmlPropertyTopic = true;
- }
- else if ((topic == COMMAND_JSPROPERTY) ||
- (topic == COMMAND_JSPROPERTYGROUP) ||
- (topic == COMMAND_JSATTACHEDPROPERTY)) {
- isJsPropertyTopic = true;
- }
- }
- NodeList nodes;
- DocList docs;
-
- if (topic.isEmpty()) {
- QStringList parentPath;
- FunctionNode *clone;
- FunctionNode *func = 0;
-
- if (matchFunctionDecl(0, &parentPath, &clone, QString(), extra)) {
- func = qdb_->findFunctionNode(parentPath, clone);
- /*
- If the node was not found, then search for it in the
- open C++ namespaces. We don't expect this search to
- be necessary often. Nor do we expect it to succeed
- very often.
- */
- if (func == 0)
- func = qdb_->findNodeInOpenNamespace(parentPath, clone);
-
- if (func) {
- func->borrowParameterNames(clone);
- nodes.append(func);
- docs.append(doc);
- }
- delete clone;
- }
- else {
- doc.location().warning(tr("Cannot tie this documentation to anything"),
- tr("I found a /*! ... */ comment, but there was no "
- "topic command (e.g., '\\%1', '\\%2') in the "
- "comment and no function definition following "
- "the comment.")
- .arg(COMMAND_FN).arg(COMMAND_PAGE));
- }
- }
- else if (isQmlPropertyTopic || isJsPropertyTopic) {
- Doc nodeDoc = doc;
- processQmlProperties(nodeDoc, nodes, docs, isJsPropertyTopic);
- }
- else {
- ArgList args;
- const QSet<QString>& topicCommandsUsed = topicCommandsAllowed & doc.metaCommandsUsed();
- if (topicCommandsUsed.count() > 0) {
- topic = *topicCommandsUsed.constBegin();
- args = doc.metaCommandArgs(topic);
- }
- if (topicCommandsUsed.count() > 1) {
- QString topics;
- QSet<QString>::ConstIterator t = topicCommandsUsed.constBegin();
- while (t != topicCommandsUsed.constEnd()) {
- topics += " \\" + *t + QLatin1Char(',');
- ++t;
- }
- topics[topics.lastIndexOf(',')] = '.';
- int i = topics.lastIndexOf(',');
- topics[i] = ' ';
- topics.insert(i+1,"and");
- doc.location().warning(tr("Multiple topic commands found in comment: %1").arg(topics));
- }
- ArgList::ConstIterator a = args.constBegin();
- while (a != args.constEnd()) {
- Doc nodeDoc = doc;
- Node *node = processTopicCommand(nodeDoc,topic,*a);
- if (node != 0) {
- nodes.append(node);
- docs.append(nodeDoc);
- }
- ++a;
- }
- }
-
- NodeList::Iterator n = nodes.begin();
- QList<Doc>::Iterator d = docs.begin();
- while (n != nodes.end()) {
- processOtherMetaCommands(*d, *n);
- (*n)->setDoc(*d);
- checkModuleInclusion(*n);
- if ((*n)->isAggregate() && ((Aggregate *)*n)->includes().isEmpty()) {
- Aggregate *m = static_cast<Aggregate *>(*n);
- while (m->parent() && m->physicalModuleName().isEmpty()) {
- m = m->parent();
- }
- if (m == *n)
- ((Aggregate *)*n)->addInclude((*n)->name());
- else
- ((Aggregate *)*n)->setIncludes(m->includes());
- }
- ++d;
- ++n;
- }
- }
- else if (tok == Tok_using) {
- matchUsingDecl(0);
- }
- else {
- QStringList parentPath;
- FunctionNode *clone;
- FunctionNode *fnode = 0;
-
- if (matchFunctionDecl(0, &parentPath, &clone, QString(), extra)) {
- /*
- The location of the definition is more interesting
- than that of the declaration. People equipped with
- a sophisticated text editor can respond to warnings
- concerning undocumented functions very quickly.
-
- Signals are implemented in uninteresting files
- generated by moc.
- */
- fnode = qdb_->findFunctionNode(parentPath, clone);
- if (fnode != 0 && !fnode->isSignal())
- fnode->setLocation(clone->location());
- delete clone;
- }
- else {
- if (tok != Tok_Doc)
- readToken();
- }
- }
- }
- return true;
-}
-
-/*!
- This function uses a Tokenizer to parse the function \a signature
- in an attempt to match it to the signature of a child node of \a root.
- If a match is found, \a funcPtr is set to point to the matching node
- and true is returned.
- */
-bool CppCodeParser::makeFunctionNode(const QString& signature,
- QStringList* parentPathPtr,
- FunctionNode** funcPtr,
- ExtraFuncData& extra)
-{
- Tokenizer* outerTokenizer = tokenizer;
- int outerTok = tok;
-
- QByteArray latin1 = signature.toLatin1();
- Tokenizer stringTokenizer(location(), latin1);
- stringTokenizer.setParsingFnOrMacro(true);
- tokenizer = &stringTokenizer;
- readToken();
-
- inMacroCommand_ = extra.isMacro;
- bool ok = matchFunctionDecl(extra.root, parentPathPtr, funcPtr, QString(), extra);
- inMacroCommand_ = false;
- // potential memory leak with funcPtr
-
- tokenizer = outerTokenizer;
- tok = outerTok;
- return ok;
-}
-
/*!
This function uses a Tokenizer to parse the \a parameters of a
function into the parameter vector \a {pvect}.
@@ -2537,74 +1220,6 @@ bool CppCodeParser::parseParameters(const QString& parameters,
return true;
}
-/*!
- Create a new FunctionNode for a QML method or signal, as
- specified by \a type, as a child of \a parent. \a sig is
- the complete signature, and if \a attached is true, the
- method or signal is "attached". \a qdoctag is the text of
- the \a type.
-
- \a parent is the QML class node. The QML module and QML
- type names have already been consumed to find \a parent.
- What remains in \a sig is the method signature. The method
- must be a child of \a parent.
- */
-FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc,
- const QString& sig,
- Aggregate* parent,
- Node::NodeType type,
- bool attached,
- QString qdoctag)
-{
- QStringList pp;
- FunctionNode* fn = 0;
- ExtraFuncData extra(parent, type, attached);
- if (!makeFunctionNode(sig, &pp, &fn, extra) && !makeFunctionNode("void " + sig, &pp, &fn, extra)) {
- doc.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag));
- }
- return fn;
-}
-
-void CppCodeParser::parseQiteratorDotH(const Location &location, const QString &filePath)
-{
- QFile file(filePath);
- if (!file.open(QFile::ReadOnly))
- return;
-
- QString text = file.readAll();
- text.remove("\r");
- text.remove("\\\n");
- QStringList lines = text.split(QLatin1Char('\n'));
- lines = lines.filter("Q_DECLARE");
- lines.replaceInStrings(QRegExp("#define Q[A-Z_]*\\(C\\)"), QString());
-
- if (lines.size() == 4) {
- sequentialIteratorDefinition = lines[0];
- mutableSequentialIteratorDefinition = lines[1];
- associativeIteratorDefinition = lines[2];
- mutableAssociativeIteratorDefinition = lines[3];
- }
- else {
- location.warning(tr("The qiterator.h hack failed"));
- }
-}
-
-void CppCodeParser::instantiateIteratorMacro(const QString &container,
- const QString &includeFile,
- const QString &macroDef)
-{
- QString resultingCode = macroDef;
- resultingCode.replace(QRegExp("\\bC\\b"), container);
- resultingCode.remove(QRegExp("\\s*##\\s*"));
-
- Location loc(includeFile); // hack to get the include file for free
- QByteArray latin1 = resultingCode.toLatin1();
- Tokenizer stringTokenizer(loc, latin1);
- tokenizer = &stringTokenizer;
- readToken();
- matchDeclList(QDocDatabase::qdocDB()->primaryTreeRoot());
-}
-
void CppCodeParser::createExampleFileNodes(DocumentNode *dn)
{
QString examplePath = dn->name();
diff --git a/src/qdoc/cppcodeparser.h b/src/qdoc/cppcodeparser.h
index c5f90f68d..a8ffba89d 100644
--- a/src/qdoc/cppcodeparser.h
+++ b/src/qdoc/cppcodeparser.h
@@ -59,17 +59,12 @@ class CppCodeParser : public CodeParser
public:
CppCodeParser();
~CppCodeParser();
- static CppCodeParser* cppParser() { return cppParser_; }
void initializeParser(const Config& config) override;
void terminateParser() override;
QString language() override;
QStringList headerFileNameFilter() override;
QStringList sourceFileNameFilter() override;
- void parseHeaderFile(const Location& location, const QString& filePath) override;
- void parseSourceFile(const Location& location, const QString& filePath) override;
- void doneParsingHeaderFiles() override;
- void doneParsingSourceFiles() override;
bool parseParameters(const QString& parameters, QVector<Parameter>& pvect, bool& isQPrivateSignal);
const Location& declLoc() const { return declLoc_; }
void setDeclLoc() { declLoc_ = location(); }
@@ -84,16 +79,14 @@ protected:
bool splitQmlPropertyGroupArg(const QString& arg,
QString& module,
QString& element,
- QString& name);
+ QString& name,
+ const Location& location);
bool splitQmlPropertyArg(const QString& arg,
QString& type,
QString& module,
QString& element,
- QString& name);
- bool splitQmlMethodArg(const QString& arg,
- QString& type,
- QString& module,
- QString& element);
+ QString& name,
+ const Location& location);
virtual void processOtherMetaCommand(const Doc& doc,
const QString& command,
const ArgLocPair& argLocPair,
@@ -113,42 +106,10 @@ protected:
bool matchCompat();
bool matchModuleQualifier(QString& name);
bool matchTemplateAngles(CodeChunk *type = 0);
- bool matchTemplateHeader();
bool matchDataType(CodeChunk *type, QString *var = 0, bool qProp = false);
bool matchParameter(QVector<Parameter>& pvect, bool& isQPrivateSignal);
- bool matchFunctionDecl(Aggregate *parent,
- QStringList *parentPathPtr,
- FunctionNode **funcPtr,
- const QString &templateStuff,
- ExtraFuncData& extra);
- bool matchBaseSpecifier(ClassNode *classe, bool isClass);
- bool matchBaseList(ClassNode *classe, bool isClass);
- bool matchClassDecl(Aggregate *parent,
- const QString &templateStuff = QString());
- bool matchNamespaceDecl(Aggregate *parent);
bool matchUsingDecl(Aggregate* parent);
- bool matchEnumItem(Aggregate *parent, EnumNode *enume);
- bool matchEnumDecl(Aggregate *parent);
- bool matchTypedefDecl(Aggregate *parent);
- bool matchProperty(Aggregate *parent);
- bool matchDeclList(Aggregate *parent);
- bool matchDocsAndStuff();
- bool makeFunctionNode(const QString &synopsis,
- QStringList *parentPathPtr,
- FunctionNode **funcPtr,
- ExtraFuncData& params);
- FunctionNode* makeFunctionNode(const Doc& doc,
- const QString& sig,
- Aggregate* parent,
- Node::NodeType type,
- bool attached,
- QString qdoctag);
- void parseQiteratorDotH(const Location &location, const QString &filePath);
- void instantiateIteratorMacro(const QString &container,
- const QString &includeFile,
- const QString &macroDef);
void createExampleFileNodes(DocumentNode *dn);
- int matchFunctionModifier();
protected:
QMap<QString, Node::NodeType> nodeTypeMap;
@@ -163,20 +124,11 @@ protected:
Location declLoc_;
private:
- QString sequentialIteratorDefinition;
- QString mutableSequentialIteratorDefinition;
- QString associativeIteratorDefinition;
- QString mutableAssociativeIteratorDefinition;
- QMap<QString, QString> sequentialIteratorClasses;
- QMap<QString, QString> mutableSequentialIteratorClasses;
- QMap<QString, QString> associativeIteratorClasses;
- QMap<QString, QString> mutableAssociativeIteratorClasses;
static QStringList exampleFiles;
static QStringList exampleDirs;
static QSet<QString> excludeDirs;
static QSet<QString> excludeFiles;
- static CppCodeParser* cppParser_;
QString exampleNameFilter;
QString exampleImageFilter;
};
@@ -205,6 +157,7 @@ protected:
#define COMMAND_REIMP Doc::alias("reimp")
#define COMMAND_RELATES Doc::alias("relates")
#define COMMAND_STARTPAGE Doc::alias("startpage")
+#define COMMAND_TYPEALIAS Doc::alias("typealias")
#define COMMAND_TYPEDEF Doc::alias("typedef")
#define COMMAND_VARIABLE Doc::alias("variable")
#define COMMAND_QMLABSTRACT Doc::alias("qmlabstract")
diff --git a/src/qdoc/doc.cpp b/src/qdoc/doc.cpp
index 3300da593..5f9ca669a 100644
--- a/src/qdoc/doc.cpp
+++ b/src/qdoc/doc.cpp
@@ -72,14 +72,12 @@ enum {
CMD_BRIEF,
CMD_C,
CMD_CAPTION,
- CMD_CHAPTER,
CMD_CODE,
CMD_CODELINE,
CMD_DIV,
CMD_DOTS,
CMD_E,
CMD_ELSE,
- CMD_ENDCHAPTER,
CMD_ENDCODE,
CMD_ENDDIV,
CMD_ENDFOOTNOTE,
@@ -89,7 +87,6 @@ enum {
CMD_ENDLIST,
CMD_ENDMAPREF,
CMD_ENDOMIT,
- CMD_ENDPART,
CMD_ENDQUOTATION,
CMD_ENDRAW,
CMD_ENDSECTION1,
@@ -127,7 +124,6 @@ enum {
CMD_OMIT,
CMD_OMITVALUE,
CMD_OVERLOAD,
- CMD_PART,
CMD_PRINTLINE,
CMD_PRINTTO,
CMD_PRINTUNTIL,
@@ -188,14 +184,12 @@ static struct {
{ "brief", CMD_BRIEF, 0 },
{ "c", CMD_C, 0 },
{ "caption", CMD_CAPTION, 0 },
- { "chapter", CMD_CHAPTER, 0 },
{ "code", CMD_CODE, 0 },
{ "codeline", CMD_CODELINE, 0},
{ "div", CMD_DIV, 0 },
{ "dots", CMD_DOTS, 0 },
{ "e", CMD_E, 0 },
{ "else", CMD_ELSE, 0 },
- { "endchapter", CMD_ENDCHAPTER, 0 },
{ "endcode", CMD_ENDCODE, 0 },
{ "enddiv", CMD_ENDDIV, 0 },
{ "endfootnote", CMD_ENDFOOTNOTE, 0 },
@@ -205,7 +199,6 @@ static struct {
{ "endlist", CMD_ENDLIST, 0 },
{ "endmapref", CMD_ENDMAPREF, 0 },
{ "endomit", CMD_ENDOMIT, 0 },
- { "endpart", CMD_ENDPART, 0 },
{ "endquotation", CMD_ENDQUOTATION, 0 },
{ "endraw", CMD_ENDRAW, 0 },
{ "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now
@@ -243,7 +236,6 @@ static struct {
{ "omit", CMD_OMIT, 0 },
{ "omitvalue", CMD_OMITVALUE, 0 },
{ "overload", CMD_OVERLOAD, 0 },
- { "part", CMD_PART, 0 },
{ "printline", CMD_PRINTLINE, 0 },
{ "printto", CMD_PRINTTO, 0 },
{ "printuntil", CMD_PRINTUNTIL, 0 },
@@ -501,8 +493,10 @@ private:
QStack<int> openedInputs;
- QString in;
+ QString input_;
int pos;
+ int backslashPos;
+ int endPos;
int len;
Location cachedLoc;
int cachedPos;
@@ -554,9 +548,9 @@ void DocParser::parse(const QString& source,
const QSet<QString>& metaCommandSet,
const QSet<QString>& possibleTopics)
{
- in = source;
+ input_ = source;
pos = 0;
- len = in.length();
+ len = input_.length();
cachedLoc = docPrivate->start_loc;
cachedPos = 0;
priv = docPrivate;
@@ -583,15 +577,16 @@ void DocParser::parse(const QString& source,
int numPreprocessorSkipping = 0;
while (pos < len) {
- QChar ch = in.at(pos);
+ QChar ch = input_.at(pos);
switch (ch.unicode()) {
case '\\':
{
QString cmdStr;
+ backslashPos = pos;
pos++;
while (pos < len) {
- ch = in.at(pos);
+ ch = input_.at(pos);
if (ch.isLetterOrNumber()) {
cmdStr += ch;
pos++;
@@ -600,15 +595,16 @@ void DocParser::parse(const QString& source,
break;
}
}
+ endPos = pos;
if (cmdStr.isEmpty()) {
if (pos < len) {
enterPara();
- if (in.at(pos).isSpace()) {
+ if (input_.at(pos).isSpace()) {
skipAllSpaces();
appendChar(QLatin1Char(' '));
}
else {
- appendChar(in.at(pos++));
+ appendChar(input_.at(pos++));
}
}
}
@@ -656,9 +652,6 @@ void DocParser::parse(const QString& source,
leavePara();
enterPara(Atom::CaptionLeft, Atom::CaptionRight);
break;
- case CMD_CHAPTER:
- startSection(Doc::Chapter, cmd);
- break;
case CMD_CODE:
leavePara();
append(Atom::Code, getCode(CMD_CODE, 0));
@@ -731,9 +724,6 @@ void DocParser::parse(const QString& source,
location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE)));
}
break;
- case CMD_ENDCHAPTER:
- endSection(Doc::Chapter, cmd);
- break;
case CMD_ENDCODE:
closeCommand(cmd);
break;
@@ -800,9 +790,6 @@ void DocParser::parse(const QString& source,
case CMD_ENDOMIT:
closeCommand(cmd);
break;
- case CMD_ENDPART:
- endSection(Doc::Part, cmd);
- break;
case CMD_ENDQUOTATION:
if (closeCommand(cmd)) {
leavePara();
@@ -1098,9 +1085,6 @@ void DocParser::parse(const QString& source,
if (!priv->omitEnumItemList.contains(p1))
priv->omitEnumItemList.append(p1);
break;
- case CMD_PART:
- startSection(Doc::Part, cmd);
- break;
case CMD_PRINTLINE:
leavePara();
{
@@ -1429,10 +1413,12 @@ void DocParser::parse(const QString& source,
"syntax definitions"));
}
else {
- location().push(macro.defaultDefLocation.filePath());
- in.insert(pos, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams));
- len = in.length();
- openedInputs.push(pos + macro.defaultDef.length());
+ QString expanded = expandMacroToString(cmdStr,
+ macro.defaultDef,
+ macro.numParams);
+ input_.replace(backslashPos, endPos - backslashPos, expanded);
+ len = input_.length();
+ pos = backslashPos;
}
}
}
@@ -1488,8 +1474,8 @@ void DocParser::parse(const QString& source,
case '/':
{
if (pos + 2 < len)
- if (in.at(pos + 1) == '/')
- if (in.at(pos + 2) == '!') {
+ if (input_.at(pos + 1) == '/')
+ if (input_.at(pos + 2) == '!') {
pos += 2;
getRestOfLine();
break;
@@ -1543,7 +1529,7 @@ void DocParser::parse(const QString& source,
int numStrangeSymbols = 0;
while (pos < len) {
- unsigned char latin1Ch = in.at(pos).toLatin1();
+ unsigned char latin1Ch = input_.at(pos).toLatin1();
if (islower(latin1Ch)) {
++numLowercase;
++pos;
@@ -1566,14 +1552,14 @@ void DocParser::parse(const QString& source,
++pos;
}
else if (latin1Ch == ':' && pos < len - 1
- && in.at(pos + 1) == QLatin1Char(':')) {
+ && input_.at(pos + 1) == QLatin1Char(':')) {
++numStrangeSymbols;
pos += 2;
}
else if (latin1Ch == '(') {
if (pos > startPos) {
if (pos < len - 1 &&
- in.at(pos + 1) == QLatin1Char(')')) {
+ input_.at(pos + 1) == QLatin1Char(')')) {
++numStrangeSymbols;
pos += 2;
break;
@@ -1600,7 +1586,7 @@ void DocParser::parse(const QString& source,
}
}
else {
- QString word = in.mid(startPos, pos - startPos);
+ QString word = input_.mid(startPos, pos - startPos);
// is word a C++ symbol or an English word?
if ((numInternalUppercase >= 1 && numLowercase >= 2)
|| numStrangeSymbols > 0) {
@@ -1651,7 +1637,7 @@ Location &DocParser::location()
cachedPos = openedInputs.pop();
}
while (cachedPos < pos)
- cachedLoc.advance(in.at(cachedPos++));
+ cachedLoc.advance(input_.at(cachedPos++));
return cachedLoc;
}
@@ -1727,8 +1713,8 @@ void DocParser::include(const QString& fileName, const QString& identifier)
inFile.close();
if (identifier.isEmpty()) {
- in.insert(pos, includedStuff);
- len = in.length();
+ input_.insert(pos, includedStuff);
+ len = input_.length();
openedInputs.push(pos + includedStuff.length());
}
else {
@@ -1771,8 +1757,8 @@ void DocParser::include(const QString& fileName, const QString& identifier)
.arg(userFriendlyFilePath));
}
else {
- in.insert(pos, result);
- len = in.length();
+ input_.insert(pos, result);
+ len = input_.length();
openedInputs.push(pos + result.length());
}
}
@@ -1918,14 +1904,14 @@ void DocParser::parseAlso()
{
leavePara();
skipSpacesOnLine();
- while (pos < len && in[pos] != '\n') {
+ while (pos < len && input_[pos] != '\n') {
QString target;
QString str;
- if (in[pos] == '{') {
+ if (input_[pos] == '{') {
target = getArgument();
skipSpacesOnLine();
- if (in[pos] == '{') {
+ if (input_[pos] == '{') {
str = getArgument();
// hack for C++ to support links like \l{QString::}{count()}
@@ -1937,12 +1923,12 @@ void DocParser::parseAlso()
}
#ifdef QDOC2_COMPAT
}
- else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") {
+ else if (input_[pos] == '\\' && input_.mid(pos, 5) == "\\link") {
pos += 6;
target = getArgument();
- int endPos = in.indexOf("\\endlink", pos);
+ int endPos = input_.indexOf("\\endlink", pos);
if (endPos != -1) {
- str = in.mid(pos, endPos - pos).trimmed();
+ str = input_.mid(pos, endPos - pos).trimmed();
pos = endPos + 8;
}
#endif
@@ -1960,11 +1946,11 @@ void DocParser::parseAlso()
priv->addAlso(also);
skipSpacesOnLine();
- if (pos < len && in[pos] == ',') {
+ if (pos < len && input_[pos] == ',') {
pos++;
skipSpacesOrOneEndl();
}
- else if (in[pos] != '\n') {
+ else if (input_[pos] != '\n') {
location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
}
}
@@ -2239,13 +2225,7 @@ Doc::Sections DocParser::getSectioningUnit()
{
QString name = getOptionalArgument();
- if (name == "part") {
- return Doc::Part;
- }
- else if (name == "chapter") {
- return Doc::Chapter;
- }
- else if (name == "section1") {
+ if (name == "section1") {
return Doc::Section1;
}
else if (name == "section2") {
@@ -2279,10 +2259,10 @@ QString DocParser::getBracedArgument(bool verbatim)
{
QString arg;
int delimDepth = 0;
- if (pos < (int) in.length() && in[pos] == '{') {
+ if (pos < (int) input_.length() && input_[pos] == '{') {
pos++;
- while (pos < (int) in.length() && delimDepth >= 0) {
- switch (in[pos].unicode()) {
+ while (pos < (int) input_.length() && delimDepth >= 0) {
+ switch (input_[pos].unicode()) {
case '{':
delimDepth++;
arg += QLatin1Char('{');
@@ -2296,16 +2276,16 @@ QString DocParser::getBracedArgument(bool verbatim)
break;
case '\\':
if (verbatim) {
- arg += in[pos];
+ arg += input_[pos];
pos++;
}
else {
pos++;
- if (pos < (int) in.length()) {
- if (in[pos].isLetterOrNumber())
+ if (pos < (int) input_.length()) {
+ if (input_[pos].isLetterOrNumber())
break;
- arg += in[pos];
- if (in[pos].isSpace()) {
+ arg += input_[pos];
+ if (input_[pos].isSpace()) {
skipAllSpaces();
}
else {
@@ -2315,16 +2295,17 @@ QString DocParser::getBracedArgument(bool verbatim)
}
break;
default:
- if (in[pos].isSpace() && !verbatim)
+ if (input_[pos].isSpace() && !verbatim)
arg += QChar(' ');
else
- arg += in[pos];
+ arg += input_[pos];
pos++;
}
}
if (delimDepth > 0)
location().warning(tr("Missing '}'"));
}
+ endPos = pos;
return arg;
}
@@ -2347,16 +2328,17 @@ QString DocParser::getArgument(bool verbatim)
int delimDepth = 0;
int startPos = pos;
+ endPos = pos;
QString arg = getBracedArgument(verbatim);
if (arg.isEmpty()) {
- while ((pos < in.length()) &&
- ((delimDepth > 0) || ((delimDepth == 0) && !in[pos].isSpace()))) {
- switch (in[pos].unicode()) {
+ while ((pos < input_.length()) &&
+ ((delimDepth > 0) || ((delimDepth == 0) && !input_[pos].isSpace()))) {
+ switch (input_[pos].unicode()) {
case '(':
case '[':
case '{':
delimDepth++;
- arg += in[pos];
+ arg += input_[pos];
pos++;
break;
case ')':
@@ -2364,22 +2346,22 @@ QString DocParser::getArgument(bool verbatim)
case '}':
delimDepth--;
if (pos == startPos || delimDepth >= 0) {
- arg += in[pos];
+ arg += input_[pos];
pos++;
}
break;
case '\\':
if (verbatim) {
- arg += in[pos];
+ arg += input_[pos];
pos++;
}
else {
pos++;
- if (pos < (int) in.length()) {
- if (in[pos].isLetterOrNumber())
+ if (pos < (int) input_.length()) {
+ if (input_[pos].isLetterOrNumber())
break;
- arg += in[pos];
- if (in[pos].isSpace()) {
+ arg += input_[pos];
+ if (input_[pos].isSpace()) {
skipAllSpaces();
}
else {
@@ -2389,17 +2371,18 @@ QString DocParser::getArgument(bool verbatim)
}
break;
default:
- arg += in[pos];
+ arg += input_[pos];
pos++;
}
}
+ endPos = pos;
if ((arg.length() > 1) &&
- (QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
+ (QString(".,:;!?").indexOf(input_[pos - 1]) != -1) &&
!arg.endsWith("...")) {
arg.truncate(arg.length() - 1);
pos--;
}
- if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
+ if (arg.length() > 2 && input_.mid(pos - 2, 2) == "'s") {
arg.truncate(arg.length() - 2);
pos -= 2;
}
@@ -2418,10 +2401,10 @@ QString DocParser::getBracketedArgument()
QString arg;
int delimDepth = 0;
skipSpacesOrOneEndl();
- if (pos < in.length() && in[pos] == '[') {
+ if (pos < input_.length() && input_[pos] == '[') {
pos++;
- while (pos < in.length() && delimDepth >= 0) {
- switch (in[pos].unicode()) {
+ while (pos < input_.length() && delimDepth >= 0) {
+ switch (input_[pos].unicode()) {
case '[':
delimDepth++;
arg += QLatin1Char('[');
@@ -2434,11 +2417,11 @@ QString DocParser::getBracketedArgument()
pos++;
break;
case '\\':
- arg += in[pos];
+ arg += input_[pos];
pos++;
break;
default:
- arg += in[pos];
+ arg += input_[pos];
pos++;
}
}
@@ -2451,8 +2434,8 @@ QString DocParser::getBracketedArgument()
QString DocParser::getOptionalArgument()
{
skipSpacesOrOneEndl();
- if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
- in[pos + 1].isLetterOrNumber()) {
+ if (pos + 1 < (int) input_.length() && input_[pos] == '\\' &&
+ input_[pos + 1].isLetterOrNumber()) {
return QString();
}
else {
@@ -2471,13 +2454,13 @@ QString DocParser::getRestOfLine()
do {
int begin = pos;
- while (pos < in.size() && in[pos] != '\n') {
- if (in[pos] == '\\' && !trailingSlash) {
+ while (pos < input_.size() && input_[pos] != '\n') {
+ if (input_[pos] == '\\' && !trailingSlash) {
trailingSlash = true;
++pos;
- while ((pos < in.size()) &&
- in[pos].isSpace() &&
- (in[pos] != '\n'))
+ while ((pos < input_.size()) &&
+ input_[pos].isSpace() &&
+ (input_[pos] != '\n'))
++pos;
}
else {
@@ -2488,15 +2471,15 @@ QString DocParser::getRestOfLine()
if (!t.isEmpty())
t += QLatin1Char(' ');
- t += in.mid(begin, pos - begin).simplified();
+ t += input_.mid(begin, pos - begin).simplified();
if (trailingSlash) {
t.chop(1);
t = t.simplified();
}
- if (pos < in.size())
+ if (pos < input_.size())
++pos;
- } while (pos < in.size() && trailingSlash);
+ } while (pos < input_.size() && trailingSlash);
return t;
}
@@ -2513,20 +2496,20 @@ QString DocParser::getMetaCommandArgument(const QString &cmdStr)
int begin = pos;
int parenDepth = 0;
- while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
- if (in.at(pos) == '(')
+ while (pos < input_.size() && (input_[pos] != '\n' || parenDepth > 0)) {
+ if (input_.at(pos) == '(')
++parenDepth;
- else if (in.at(pos) == ')')
+ else if (input_.at(pos) == ')')
--parenDepth;
++pos;
}
- if (pos == in.size() && parenDepth > 0) {
+ if (pos == input_.size() && parenDepth > 0) {
pos = begin;
location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
}
- QString t = in.mid(begin, pos - begin).simplified();
+ QString t = input_.mid(begin, pos - begin).simplified();
skipSpacesOnLine();
return t;
}
@@ -2536,14 +2519,14 @@ QString DocParser::getUntilEnd(int cmd)
int endCmd = endCmdFor(cmd);
QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
QString t;
- int end = rx.indexIn(in, pos);
+ int end = rx.indexIn(input_, pos);
if (end == -1) {
location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
- pos = in.length();
+ pos = input_.length();
}
else {
- t = in.mid(pos, end - pos);
+ t = input_.mid(pos, end - pos);
pos = end + rx.matchedLength();
}
return t;
@@ -2572,8 +2555,8 @@ bool DocParser::isBlankLine()
{
int i = pos;
- while (i < len && in[i].isSpace()) {
- if (in[i] == '\n')
+ while (i < len && input_[i].isSpace()) {
+ if (input_[i] == '\n')
return true;
i++;
}
@@ -2585,13 +2568,13 @@ bool DocParser::isLeftBraceAhead()
int numEndl = 0;
int i = pos;
- while (i < len && in[i].isSpace() && numEndl < 2) {
+ while (i < len && input_[i].isSpace() && numEndl < 2) {
// ### bug with '\\'
- if (in[i] == '\n')
+ if (input_[i] == '\n')
numEndl++;
i++;
}
- return numEndl < 2 && i < len && in[i] == '{';
+ return numEndl < 2 && i < len && input_[i] == '{';
}
bool DocParser::isLeftBracketAhead()
@@ -2599,13 +2582,13 @@ bool DocParser::isLeftBracketAhead()
int numEndl = 0;
int i = pos;
- while (i < len && in[i].isSpace() && numEndl < 2) {
+ while (i < len && input_[i].isSpace() && numEndl < 2) {
// ### bug with '\\'
- if (in[i] == '\n')
+ if (input_[i] == '\n')
numEndl++;
i++;
}
- return numEndl < 2 && i < len && in[i] == '[';
+ return numEndl < 2 && i < len && input_[i] == '[';
}
/*!
@@ -2613,9 +2596,9 @@ bool DocParser::isLeftBracketAhead()
*/
void DocParser::skipSpacesOnLine()
{
- while ((pos < in.length()) &&
- in[pos].isSpace() &&
- (in[pos].unicode() != '\n'))
+ while ((pos < input_.length()) &&
+ input_[pos].isSpace() &&
+ (input_[pos].unicode() != '\n'))
++pos;
}
@@ -2625,8 +2608,8 @@ void DocParser::skipSpacesOnLine()
void DocParser::skipSpacesOrOneEndl()
{
int firstEndl = -1;
- while (pos < (int) in.length() && in[pos].isSpace()) {
- QChar ch = in[pos];
+ while (pos < (int) input_.length() && input_[pos].isSpace()) {
+ QChar ch = input_[pos];
if (ch == '\n') {
if (firstEndl == -1) {
firstEndl = pos;
@@ -2642,7 +2625,7 @@ void DocParser::skipSpacesOrOneEndl()
void DocParser::skipAllSpaces()
{
- while (pos < len && in[pos].isSpace())
+ while (pos < len && input_[pos].isSpace())
pos++;
}
@@ -2651,10 +2634,10 @@ void DocParser::skipToNextPreprocessorCommand()
QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + QLatin1Char('|') +
cmdName(CMD_ELSE) + QLatin1Char('|') +
cmdName(CMD_ENDIF) + ")\\b");
- int end = rx.indexIn(in, pos + 1); // ### + 1 necessary?
+ int end = rx.indexIn(input_, pos + 1); // ### + 1 necessary?
if (end == -1)
- pos = in.length();
+ pos = input_.length();
else
pos = end;
}
@@ -2664,8 +2647,6 @@ int DocParser::endCmdFor(int cmd)
switch (cmd) {
case CMD_BADCODE:
return CMD_ENDCODE;
- case CMD_CHAPTER:
- return CMD_ENDCHAPTER;
case CMD_CODE:
return CMD_ENDCODE;
case CMD_DIV:
@@ -2690,8 +2671,6 @@ int DocParser::endCmdFor(int cmd)
return CMD_NEWCODE;
case CMD_OMIT:
return CMD_ENDOMIT;
- case CMD_PART:
- return CMD_ENDPART;
case CMD_QUOTATION:
return CMD_ENDQUOTATION;
case CMD_RAW:
diff --git a/src/qdoc/doc/qdoc-manual-cmdindex.qdoc b/src/qdoc/doc/qdoc-manual-cmdindex.qdoc
index feac8816a..85f5f75d9 100644
--- a/src/qdoc/doc/qdoc-manual-cmdindex.qdoc
+++ b/src/qdoc/doc/qdoc-manual-cmdindex.qdoc
@@ -46,7 +46,6 @@
\li \l {brief-command} {\\brief}
\li \l {c-command} {\\c}
\li \l {caption-command} {\\caption}
- \li \l {chapter-command} {\\chapter}
\li \l {class-command} {\\class}
\li \l {code-command} {\\code}
\li \l {codeline-command} {\\codeline}
@@ -101,7 +100,6 @@
\li \l {omitvalue-command} {\\omitvalue}
\li \l {overload-command} {\\overload}
\li \l {page-command} {\\page}
- \li \l {part-command} {\\part}
\li \l {preliminary-command} {\\preliminary}
\li \l {previouspage-command} {\\previouspage}
\li \l {printline-command} {\\printline}
diff --git a/src/qdoc/doc/qdoc-manual-markupcmds.qdoc b/src/qdoc/doc/qdoc-manual-markupcmds.qdoc
index 2360f6630..3366c52c5 100644
--- a/src/qdoc/doc/qdoc-manual-markupcmds.qdoc
+++ b/src/qdoc/doc/qdoc-manual-markupcmds.qdoc
@@ -45,7 +45,6 @@
\li \l {brief-command} {\\brief}
\li \l {c-command} {\\c}
\li \l {caption-command} {\\caption}
- \li \l {chapter-command} {\\chapter}
\li \l {code-command} {\\code}
\li \l {codeline-command} {\\codeline}
\li \l {div-command} {\\div}
@@ -74,7 +73,6 @@
\li \l {note-command} {\\note}
\li \l {oldcode-command} {\\oldcode}
\li \l {omit-command} {\\omit}
- \li \l {part-command} {\\part}
\li \l {printline-command} {\\printline}
\li \l {printto-command} {\\printto}
\li \l {printuntil-command} {\\printuntil}
@@ -730,17 +728,13 @@
\title Document Structure
The document structuring commands are for dividing your document
- into sections. QDoc supports six kinds of sections: \c \part, \c
- \chapter, \c \section1, \c \section2, \c \section3, and \c
- \section4. The \c \section1..4 commands are the most useful. They
+ into sections. QDoc supports four levels of section: \c \section1,
+ \c \section2, \c \section3, and \c \section4. The section commands
correspond to the traditional section, subsection, etc used in
outlining.
- \target part-command
- \section1 \\part
-
- The \\part command is intended for use in a large document, like a
- book.
+ \target section-commands
+ \section1 Section commands
In general a document structuring command considers everything
that follows it until the first line break as its argument. The
@@ -748,88 +742,77 @@
spanned over several lines, make sure that each line (except the
last one) is ended with a backslash.
- In total, there are six levels of sections in QDoc: \c \part, \c
- \chapter, \c \section1, \c \section2, \c \section3 and \c
- \section4. \c \section1 to \c \section4 correspond to the
- traditional section, subsection, subsubsection and
- subsubsubsection.
+ In total, there are four levels for sections in QDoc: \c \section1,
+ \c \section2, \c \section3 and \c \section4. These correspond to the
+ traditional section, subsection, subsubsection and subsubsubsection.
There is a strict ordering of the section units:
\code
- part
- |
- chapter
- |
- section1
- |
- section2
- |
- section3
- |
- section4
+ section1
+ |
+ section2
+ |
+ section3
+ |
+ section4
\endcode
- For example, a \c section1 unit can only appear as the top level
- section or inside a \c chapter unit. Skipping a section unit, for
- example from \c part to \c section1, is not allowed.
-
- You can \e begin with either of the three: \c part, \c chapter or
- \c section1.
+ When sections are used, the first section command should be \c section1.
\code
/ *!
- \part Basic Qt
+ \section1 Basic Qt
- This is the first part.
+ This is the first section.
- \chapter Getting Started
+ \section2 Getting Started
- This is the first part's first chapter.
+ This is the first subsection.
- \section1 Hello Qt
+ \section3 Hello Qt
- This is the first chapter's first section.
+ This is the first subsubsection.
- \section1 Making Connections
+ \section3 Making Connections
- This is the first chapter's second section.
+ This is the second subsubsection.
- \section1 Using the Reference Documentation
+ \section3 Using the Reference Documentation
- This is the first chapter's third section.
+ This is the third subsubsection.
- \chapter Creating Dialogs
+ \section2 Creating Dialogs
- This is the first part's second chapter.
+ This is the second subsection.
- \section1 Subclassing QDialog
+ \section3 Subclassing QDialog
- This is the second chapter's first section.
+ This is the first subsubsection.
...
- \part Intermediate Qt
+ \section1 Intermediate Qt
- This is the second part.
+ This is the second section.
- \chapter Layout Management
+ \section2 Layout Management
- This is the second part's first chapter.
+ This is the second section's first subsection.
- \section1 Basic Layouts
+ \section3 Basic Layouts
- This is the first chapter's first section.
+ This is the first subsubsection.
...
* /
@@ -842,54 +825,54 @@
<a name="Basic Qt">
<h1>Basic Qt</h1>
</a>
- <p>This is the first part.</p>
+ <p>This is the first section.</p>
<a name="Getting started">
<h2>Getting Started</h2>
</a>
- This is the first part's first chapter.</p>
+ This is the first subsection.</p>
<a name="Hello Qt">
<h3>Hello Qt</h3>
</a>
- <p>This is the first chapter's first section.</p>
+ <p>This is the first subsubsection.</p>
<a name="Making Connections">
<h3>Making Connections</h3>
</a>
- <p>This is the first chapter's second section.</p>
+ <p>This is the second subsubsection.</p>
<a name="Using the Reference Documentation">
<h3>Using the Reference Documentation</h3>
</a>
- <p>This is the first chapter's third section.</p>
+ <p>This is the third subsubsection.</p>
<a name="Creating Dialogs">
<h2>Creating Dialogs</h2>
</a>
- <p>This is the first part's second chapter.</p>
+ <p>This is the second subsection.</p>
<a name="Subclassing QDialog">
<h3>Subclassing QDialog</h3>
</a>
- <p>This is the second chapter's first section.</p>
+ <p>This is the first subsubsection.</p>
...
<a name="Intermediate Qt">
<h1>Intermediate Qt</h1>
</a>
- <p>This is the second part.</p>
+ <p>This is the second section.</p>
<a name="Layout Management">
<h2>Layout Management</h2>
</a>
- <p>This is the second part's first chapter.</p>
+ <p>This is the second section's first subsection.</p>
<a name="Basic Layouts">
<h3>Basic Layouts</h3>
</a>
- <p>This is the first chapter's first section.</p>
+ <p>This is the first subsubsection.</p>
...
@@ -900,21 +883,12 @@
heading appears in the automatically generated table of contents
that normally appears in the upper right-hand corner of the page.
- \target chapter-command
- \section1 \\chapter
-
- The \\chapter command is intended for use in
- larger documents, and divides the document into chapters.
-
- See \l{part} {\\part} for an explanation of the various
- section units, command argument, and rendering.
-
\target sectionOne-command
\section1 \\section1
The \\section1 command starts a new section.
- See \l{part} {\\part} for an explanation of the various
+ See \l{section-commands} {Section commands} for an explanation of the various
section units, command argument, and rendering.
\target sectionTwo-command
@@ -922,7 +896,7 @@
The \\section2 command starts a new section.
- See \l{part} {\\part} for an explanation of the various
+ See \l{section-commands} {Section commands} for an explanation of the various
section units, command argument, and rendering.
\target sectionThree-command
@@ -930,7 +904,7 @@
The \\section3 command starts a new section.
- See \l{part} {\\part} for an explanation of the various
+ See \l{section-commands} {Section commands} for an explanation of the various
section units, command argument, and rendering.
\target sectionFour-command
@@ -938,7 +912,7 @@
The \\section4 command starts a new section.
- See \l{part} {\\part} for an explanation of the various
+ See \l{section-commands} {Section commands} for an explanation of the various
section units, command argument, and rendering.
*/
@@ -1874,8 +1848,7 @@
\l {title-command} {\\title} command.
\li \c {\l {Introduction to QDoc}}- The text from one of the
- \l{part-command} {\\part}, \l{chapter} {\\chapter}, or \l
- {sectionOne-command} {\\section} commands.
+ \l{section-commands} {Section commands}.
\li \c {\l fontmatching} - The argument of a \l {target-command}
{\\target} command.
diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp
index 940ea7216..2336e9af8 100644
--- a/src/qdoc/generator.cpp
+++ b/src/qdoc/generator.cpp
@@ -894,15 +894,8 @@ void Generator::generateBody(const Node *node, CodeMarker *marker)
QSet<QString> definedParams;
QVector<Parameter>::ConstIterator p = func->parameters().constBegin();
while (p != func->parameters().constEnd()) {
- if ((*p).name().isEmpty() && (*p).dataType() != QLatin1String("...")
- && (*p).dataType() != QLatin1String("void")
- && func->name() != QLatin1String("operator++")
- && func->name() != QLatin1String("operator--")) {
- node->doc().location().warning(tr("Missing parameter name"));
- }
- else {
+ if (!(*p).name().isEmpty())
definedParams.insert((*p).name());
- }
++p;
}
@@ -1638,8 +1631,7 @@ void Generator::generateOverloadedSignal(const Node* node, CodeMarker* marker)
for (int i = 0; i < func->parameters().size(); ++i) {
if (i != 0)
code += ", ";
- const Parameter &p = func->parameters().at(i);
- code += p.dataType() + p.rightType();
+ code += func->parameters().at(i).dataType();
}
code += QLatin1Char(')');
@@ -1654,7 +1646,7 @@ void Generator::generateOverloadedSignal(const Node* node, CodeMarker* marker)
code += p.dataType();
if (code[code.size()-1].isLetterOrNumber())
code += QLatin1Char(' ');
- code += p.name() + p.rightType();
+ code += p.name();
}
code += "){ /* ... */ });";
@@ -2154,7 +2146,7 @@ void Generator::initializeTextOutput()
void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList)
{
- if (node->type() == Node::Function) {
+ if (node->isFunction() && !node->isMacro()) {
const FunctionNode *func = static_cast<const FunctionNode *>(node);
if (func->overloadNumber() == 0) {
QString alternateName;
diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp
index aa7943287..a32e32caa 100644
--- a/src/qdoc/htmlgenerator.cpp
+++ b/src/qdoc/htmlgenerator.cpp
@@ -3993,26 +3993,42 @@ void HtmlGenerator::generateDetailedMember(const Node *node,
#endif
generateExtractionMark(node, MemberMark);
generateKeywordAnchors(node);
- QString nodeRef = refForNode(node);
- if (node->isEnumType() && (etn = static_cast<const EnumNode *>(node))->flagsType()) {
-#ifdef GENERATE_MAC_REFS
- generateMacRef(etn->flagsType(), marker);
-#endif
- out() << "<h3 class=\"flags\" id=\"" << nodeRef << "\">";
- out() << "<a name=\"" + nodeRef + "\"></a>";
- generateSynopsis(etn, relative, marker, CodeMarker::Detailed);
- out() << "<br/>";
- generateSynopsis(etn->flagsType(),
- relative,
- marker,
- CodeMarker::Detailed);
- out() << "</h3>\n";
+ QString nodeRef = 0;
+ if (node->isSharedCommentNode()) {
+ const SharedCommentNode *scn = reinterpret_cast<const SharedCommentNode*>(node);
+ const QVector<Node*>& collective = scn->collective();
+ out() << "<div class=\"fngroup\">\n";
+ foreach (const Node* n, collective) {
+ if (n->isFunction()) {
+ nodeRef = refForNode(n);
+ out() << "<h3 class=\"fn fngroupitem\" id=\"" << nodeRef << "\">";
+ out() << "<a name=\"" + nodeRef + "\"></a>";
+ generateSynopsis(n, relative, marker, CodeMarker::Detailed);
+ out() << "</h3>";
+ }
+ }
+ out() << "</div>";
+ out() << divNavTop << '\n';
}
else {
- out() << "<h3 class=\"fn\" id=\"" << nodeRef << "\">";
- out() << "<a name=\"" + nodeRef + "\"></a>";
- generateSynopsis(node, relative, marker, CodeMarker::Detailed);
- out() << "</h3>" << divNavTop << '\n';
+ nodeRef = refForNode(node);
+ if (node->isEnumType() && (etn = static_cast<const EnumNode *>(node))->flagsType()) {
+#ifdef GENERATE_MAC_REFS
+ generateMacRef(etn->flagsType(), marker);
+#endif
+ out() << "<h3 class=\"flags\" id=\"" << nodeRef << "\">";
+ out() << "<a name=\"" + nodeRef + "\"></a>";
+ generateSynopsis(etn, relative, marker, CodeMarker::Detailed);
+ out() << "<br/>";
+ generateSynopsis(etn->flagsType(), relative, marker, CodeMarker::Detailed);
+ out() << "</h3>\n";
+ }
+ else {
+ out() << "<h3 class=\"fn\" id=\"" << nodeRef << "\">";
+ out() << "<a name=\"" + nodeRef + "\"></a>";
+ generateSynopsis(node, relative, marker, CodeMarker::Detailed);
+ out() << "</h3>" << divNavTop << '\n';
+ }
}
generateStatus(node, marker);
diff --git a/src/qdoc/location.cpp b/src/qdoc/location.cpp
index b60233386..51fb45b54 100644
--- a/src/qdoc/location.cpp
+++ b/src/qdoc/location.cpp
@@ -406,11 +406,20 @@ void Location::emitMessage(MessageType type,
if (!details.isEmpty())
result += "\n[" + details + QLatin1Char(']');
result.replace("\n", "\n ");
- if (type == Error)
- result.prepend(tr(": error: "));
- else if (type == Warning) {
- result.prepend(tr(": warning: "));
- ++warningCount;
+ if (isEmpty()) {
+ if (type == Error)
+ result.prepend(tr(": error: "));
+ else if (type == Warning) {
+ result.prepend(tr(": warning: "));
+ ++warningCount;
+ }
+ } else {
+ if (type == Error)
+ result.prepend(tr(": (qdoc) error: "));
+ else if (type == Warning) {
+ result.prepend(tr(": (qdoc) warning: "));
+ ++warningCount;
+ }
}
if (type != Report)
result.prepend(toString());
diff --git a/src/qdoc/main.cpp b/src/qdoc/main.cpp
index 519952280..4dd307541 100644
--- a/src/qdoc/main.cpp
+++ b/src/qdoc/main.cpp
@@ -26,6 +26,16 @@
**
****************************************************************************/
+#ifdef HEINOUS_SYSINC_HACK // There must be no #include before this !
+#define setlocale locale_file_name_for_clang_qdoc() { \
+ static char data[] = __FILE__; \
+ return data; \
+ } \
+ extern char *setlocale
+#include <locale.h>
+#undef setlocale
+#endif // HEINOUS_SYSINC_HACK
+
#include <qglobal.h>
#include <qhashfunctions.h>
#include <stdlib.h>
@@ -46,6 +56,7 @@
#include "jscodemarker.h"
#include "qmlcodemarker.h"
#include "qmlcodeparser.h"
+#include "clangcodeparser.h"
#include <qdatetime.h>
#include <qdebug.h>
#include "qtranslator.h"
@@ -54,7 +65,7 @@
#endif
#include "qcommandlineoption.h"
#include "qcommandlineparser.h"
-
+#include <qhashfunctions.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -73,6 +84,7 @@ static bool noLinkErrors = false;
static bool autolinkErrors = false;
static bool obsoleteLinks = false;
static QStringList defines;
+static QStringList includesPaths;
static QStringList dependModules;
static QStringList indexDirs;
static QString currentDir;
@@ -82,6 +94,7 @@ static QHash<QString,QString> defaults;
typedef QPair<QString, QTranslator*> Translator;
static QList<Translator> translators;
#endif
+static ClangCodeParser* clangParser_ = 0;
/*!
Read some XML indexes containing definitions from other
@@ -245,12 +258,14 @@ static void processQdocconfFile(const QString &fileName)
Location::initialize(config);
config.load(fileName);
QString project = config.getString(CONFIG_PROJECT);
- //qDebug() << "Start project:" << project;
+ QString moduleHeader = config.getString(CONFIG_MODULEHEADER);
/*
Add the defines to the configuration variables.
*/
QStringList defs = defines + config.getStringList(CONFIG_DEFINES);
config.setStringList(CONFIG_DEFINES,defs);
+ QStringList incs = includesPaths + config.getStringList(CONFIG_INCLUDEPATHS);
+ config.setStringList(CONFIG_INCLUDEPATHS, incs);
Location::terminate();
currentDir = QFileInfo(fileName).path();
@@ -266,6 +281,7 @@ static void processQdocconfFile(const QString &fileName)
phase += "prepare phase ";
else if (Generator::generating())
phase += "generate phase ";
+
QString msg = "Running qdoc for " + config.getString(CONFIG_PROJECT) + phase;
Location::logToStdErr(msg);
@@ -357,6 +373,10 @@ static void processQdocconfFile(const QString &fileName)
qdb->newPrimaryTree(project);
else
qdb->setPrimaryTree(project);
+ if (!moduleHeader.isEmpty())
+ clangParser_->setModuleHeader(moduleHeader);
+ else
+ clangParser_->setModuleHeader(project);
dependModules = config.getStringList(CONFIG_DEPENDS);
dependModules.removeDuplicates();
@@ -433,7 +453,6 @@ static void processQdocconfFile(const QString &fileName)
Parse each header file in the set using the appropriate parser and add it
to the big tree.
*/
- QSet<CodeParser *> usedParsers;
Generator::debug("Parsing header files");
int parsed = 0;
@@ -444,16 +463,13 @@ static void processQdocconfFile(const QString &fileName)
++parsed;
Generator::debug(QString("Parsing " + h.key()));
codeParser->parseHeaderFile(config.location(), h.key());
- usedParsers.insert(codeParser);
}
++h;
}
- foreach (CodeParser *codeParser, usedParsers)
- codeParser->doneParsingHeaderFiles();
-
- usedParsers.clear();
- qdb->resolveInheritance();
+ clangParser_->precompileHeaders();
+ // Moved into ClangCodeParser after building PCH
+ //qdb->resolveInheritance();
/*
Parse each source text file in the set using the appropriate parser and
@@ -468,19 +484,12 @@ static void processQdocconfFile(const QString &fileName)
++parsed;
Generator::debug(QString("Parsing " + s.key()));
codeParser->parseSourceFile(config.location(), s.key());
- usedParsers.insert(codeParser);
}
++s;
}
Generator::debug(QString("Parsing done."));
/*
- Currently these doneParsingSourceFiles() calls do nothing.
- */
- foreach (CodeParser *codeParser, usedParsers)
- codeParser->doneParsingSourceFiles();
-
- /*
Now the primary tree has been built from all the header and
source files. Resolve all the class names, function names,
targets, URLs, links, and other stuff that needs resolving.
@@ -546,6 +555,7 @@ private:
QCommandLineOption noLinkErrorsOption, autoLinkErrorsOption, debugOption;
QCommandLineOption prepareOption, generateOption, logProgressOption;
QCommandLineOption singleExecOption, writeQaPagesOption;
+ QCommandLineOption includePathOption, includePathSystemOption, frameworkOption;
};
QDocCommandLineParser::QDocCommandLineParser()
@@ -568,7 +578,10 @@ QDocCommandLineParser::QDocCommandLineParser()
generateOption(QStringList() << QStringLiteral("generate")),
logProgressOption(QStringList() << QStringLiteral("log-progress")),
singleExecOption(QStringList() << QStringLiteral("single-exec")),
- writeQaPagesOption(QStringList() << QStringLiteral("write-qa-pages"))
+ writeQaPagesOption(QStringList() << QStringLiteral("write-qa-pages")),
+ includePathOption("I", "Add dir to the include path for header files.", "path"),
+ includePathSystemOption("isystem", "Add dir to the system include path for header files.", "path"),
+ frameworkOption("F", "Add macOS framework to the include path for header files.", "framework")
{
setApplicationDescription(QCoreApplication::translate("qdoc", "Qt documentation generator"));
addHelpOption();
@@ -640,7 +653,31 @@ QDocCommandLineParser::QDocCommandLineParser()
writeQaPagesOption.setDescription(QCoreApplication::translate("qdoc", "Write QA pages."));
addOption(writeQaPagesOption);
+
+ includePathOption.setFlags(QCommandLineOption::ShortOptionStyle);
+ addOption(includePathOption);
+
+ addOption(includePathSystemOption);
+
+ frameworkOption.setFlags(QCommandLineOption::ShortOptionStyle);
+ addOption(frameworkOption);
+}
+
+#ifdef HEINOUS_SYSINC_HACK
+/*!
+ Return the system include directory used when compiling this file.
+ */
+static QByteArray getSystemIncludePath()
+{
+ const char *raw = locale_file_name_for_clang_qdoc();
+ const char *slash = strrchr(raw, '/');
+ if (slash == NULL)
+ slash = strrchr(raw, '\\');
+ if (slash == NULL)
+ return QByteArray();
+ return QByteArray(raw, slash - raw);
}
+#endif // HEINOUS_SYSINC_HACK
void QDocCommandLineParser::process(const QCoreApplication &app)
{
@@ -685,6 +722,21 @@ void QDocCommandLineParser::process(const QCoreApplication &app)
if (isSet(logProgressOption))
Location::startLoggingProgress();
+ QDir currentDir = QDir::current();
+ const auto paths = values(includePathOption);
+ for (const auto &i : paths)
+ includesPaths << "-I" << currentDir.absoluteFilePath(i);
+ auto paths2 = values(includePathSystemOption);
+#ifdef HEINOUS_SYSINC_HACK
+ if (paths2.isEmpty())
+ paths2 << QString(getSystemIncludePath());
+#endif // HEINOUS_SYSINC_HACK
+ for (const auto &i : paths2)
+ includesPaths << "-isystem" << currentDir.absoluteFilePath(i);
+ const auto paths3 = values(frameworkOption);
+ for (const auto &i : paths3)
+ includesPaths << "-F" << currentDir.absoluteFilePath(i);
+
/*
The default indent for code is 0.
The default value for false is 0.
@@ -722,7 +774,8 @@ int main(int argc, char **argv)
Create code parsers for the languages to be parsed,
and create a tree for C++.
*/
- CppCodeParser cppParser;
+ ClangCodeParser clangParser;
+ clangParser_ = &clangParser;
QmlCodeParser qmlParser;
PureDocParser docParser;
diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp
index 9ec92555d..2c8e3eae8 100644
--- a/src/qdoc/node.cpp
+++ b/src/qdoc/node.cpp
@@ -35,6 +35,7 @@
#include <qdebug.h>
#include "generator.h"
#include "tokenizer.h"
+#include "puredocparser.h"
QT_BEGIN_NAMESPACE
@@ -69,6 +70,7 @@ void Node::initialize()
goals_.insert("qmlmethod", Node::QmlMethod);
goals_.insert("qmlbasictype", Node::QmlBasicType);
goals_.insert("enum", Node::Enum);
+ goals_.insert("typealias", Node::Typedef);
goals_.insert("typedef", Node::Typedef);
goals_.insert("namespace", Node::Namespace);
}
@@ -184,11 +186,12 @@ void Node::removeRelates()
/*!
Returns this node's name member. Appends "()" to the returned
- name, if this node is a function node.
+ name if this node is a function node, but not if it is a macro
+ because macro names normally appear without parentheses.
*/
QString Node::plainName() const
{
- if (type() == Node::Function)
+ if (isFunction() && !isMacro())
return name_ + QLatin1String("()");
return name_;
}
@@ -455,6 +458,8 @@ QString Node::nodeTypeString(unsigned char t)
return "QML signal handler";
case QmlMethod:
return "QML method";
+ case SharedComment:
+ return "shared comment";
default:
break;
}
@@ -699,33 +704,6 @@ Node::ThreadSafeness Node::inheritedThreadSafeness() const
return (ThreadSafeness) safeness_;
}
-#if 0
-/*!
- Returns the sanitized file name without the path.
- If the file is an html file, the html suffix
- is removed. Why?
- */
-QString Node::fileBase() const
-{
- QString base = name();
- if (base.endsWith(".html"))
- base.chop(5);
- base.replace(QRegExp("[^A-Za-z0-9]+"), " ");
- base = base.trimmed();
- base.replace(QLatin1Char(' '), QLatin1Char('-'));
- return base.toLower();
-}
-/*!
- Returns this node's Universally Unique IDentifier as a
- QString. Creates the UUID first, if it has not been created.
- */
-QString Node::guid() const
-{
- if (uuid_.isEmpty())
- uuid_ = idForNode();
- return uuid_;
-}
-#endif
/*!
If this node is a QML or JS type node, return a pointer to
@@ -941,7 +919,7 @@ FunctionNode *Aggregate::findFunctionNode(const QString& name, const QString& pa
bool isQPrivateSignal = false; // Not used in the search
QVector<Parameter> testParams;
if (!params.isEmpty()) {
- CppCodeParser* cppParser = CppCodeParser::cppParser();
+ CppCodeParser* cppParser = PureDocParser::pureDocParser();
cppParser->parseParameters(params, testParams, isQPrivateSignal);
}
NodeList funcs = secondaryFunctionMap_.value(name);
@@ -1262,14 +1240,15 @@ bool Aggregate::isSameSignature(const FunctionNode *f1, const FunctionNode *f2)
return false;
if (f1->isConst() != f2->isConst())
return false;
+ if (f1->isRef() != f2->isRef())
+ return false;
+ if (f1->isRefRef() != f2->isRefRef())
+ return false;
QVector<Parameter>::ConstIterator p1 = f1->parameters().constBegin();
QVector<Parameter>::ConstIterator p2 = f2->parameters().constBegin();
while (p2 != f2->parameters().constEnd()) {
if ((*p1).hasType() && (*p2).hasType()) {
- if ((*p1).rightType() != (*p2).rightType())
- return false;
-
QString t1 = p1->dataType();
QString t2 = p2->dataType();
@@ -1568,6 +1547,7 @@ LeafNode::LeafNode(NodeType type, Aggregate *parent, const QString& name)
case QmlSignalHandler:
case QmlMethod:
case QmlBasicType:
+ case SharedComment:
setPageType(ApiPage);
break;
default:
@@ -1595,6 +1575,7 @@ LeafNode::LeafNode(Aggregate* parent, NodeType type, const QString& name)
case QmlSignal:
case QmlSignalHandler:
case QmlMethod:
+ case SharedComment:
setPageType(ApiPage);
break;
default:
@@ -1690,6 +1671,8 @@ void ClassNode::fixBaseClasses()
// Remove private and duplicate base classes.
while (i < bases_.size()) {
ClassNode* bc = bases_.at(i).node_;
+ if (!bc)
+ bc = QDocDatabase::qdocDB()->findClassNode(bases_.at(i).path_);
if (bc && (bc->access() == Node::Private || found.contains(bc))) {
RelatedClass rc = bases_.at(i);
bases_.removeAt(i);
@@ -1964,6 +1947,20 @@ void TypedefNode::setAssociatedEnum(const EnumNode *enume)
}
/*!
+ \class TypeAliasNode
+ */
+
+/*!
+ Constructs a TypeAliasNode for the \a aliasedType with the
+ specified \a name and \a parent.
+ */
+TypeAliasNode::TypeAliasNode(Aggregate *parent, const QString& name, const QString& aliasedType)
+ : TypedefNode(parent, name), aliasedType_(aliasedType)
+{
+ // nothing.
+}
+
+/*!
\class Parameter
\brief The class Parameter contains one parameter.
@@ -1972,17 +1969,11 @@ void TypedefNode::setAssociatedEnum(const EnumNode *enume)
*/
/*!
- Constructs this parameter from the left and right types
- \a dataType and rightType, the parameter \a name, and the
- \a defaultValue. In practice, \a rightType is not used,
- and I don't know what is was meant for.
+ Constructs this parameter from the \a dataType, the \a name,
+ and the \a defaultValue.
*/
-Parameter::Parameter(const QString& dataType,
- const QString& rightType,
- const QString& name,
- const QString& defaultValue)
+Parameter::Parameter(const QString& dataType, const QString& name, const QString& defaultValue)
: dataType_(dataType),
- rightType_(rightType),
name_(name),
defaultValue_(defaultValue)
{
@@ -1994,7 +1985,6 @@ Parameter::Parameter(const QString& dataType,
*/
Parameter::Parameter(const Parameter& p)
: dataType_(p.dataType_),
- rightType_(p.rightType_),
name_(p.name_),
defaultValue_(p.defaultValue_)
{
@@ -2007,7 +1997,6 @@ Parameter::Parameter(const Parameter& p)
Parameter& Parameter::operator=(const Parameter& p)
{
dataType_ = p.dataType_;
- rightType_ = p.rightType_;
name_ = p.name_;
defaultValue_ = p.defaultValue_;
return *this;
@@ -2020,7 +2009,7 @@ Parameter& Parameter::operator=(const Parameter& p)
*/
QString Parameter::reconstruct(bool value) const
{
- QString p = dataType_ + rightType_;
+ QString p = dataType_;
if (!p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) && !p.endsWith(QChar(' ')))
p += QLatin1Char(' ');
p += name_;
@@ -2054,7 +2043,11 @@ FunctionNode::FunctionNode(Aggregate *parent, const QString& name)
isDefaulted_(false),
isFinal_(false),
isOverride_(false),
- reimplementedFrom_(0)
+ isImplicit_(false),
+ isRef_(false),
+ isRefRef_(false),
+ reimplementedFrom_(0),
+ collective_(0)
{
setGenus(Node::CPP);
}
@@ -2081,7 +2074,11 @@ FunctionNode::FunctionNode(NodeType type, Aggregate *parent, const QString& name
isDefaulted_(false),
isFinal_(false),
isOverride_(false),
- reimplementedFrom_(0)
+ isImplicit_(false),
+ isRef_(false),
+ isRefRef_(false),
+ reimplementedFrom_(0),
+ collective_(0)
{
setGenus(Node::QML);
if (type == QmlMethod || type == QmlSignal) {
@@ -2235,7 +2232,31 @@ void FunctionNode::addParameter(const Parameter& parameter)
}
/*!
+ Split the parameters \a t and store them in this function's
+ Parameter vector.
*/
+void FunctionNode::setParameters(const QString &t)
+{
+ clearParams();
+ if (!t.isEmpty()) {
+ QStringList commaSplit = t.split(',');
+ foreach (QString s, commaSplit) {
+ QStringList blankSplit = s.split(' ');
+ QString pName = blankSplit.last();
+ blankSplit.removeLast();
+ QString pType = blankSplit.join(' ');
+ int i = 0;
+ while (i < pName.length() && !pName.at(i).isLetter())
+ i++;
+ if (i > 0) {
+ pType += QChar(' ') + pName.left(i);
+ pName = pName.mid(i);
+ }
+ addParameter(Parameter(pType, pName));
+ }
+ }
+}
+
void FunctionNode::borrowParameterNames(const FunctionNode *source)
{
QVector<Parameter>::Iterator t = parameters_.begin();
@@ -2249,16 +2270,6 @@ void FunctionNode::borrowParameterNames(const FunctionNode *source)
}
/*!
- If this function is a reimplementation, \a from points
- to the FunctionNode of the function being reimplemented.
- */
-void FunctionNode::setReimplementedFrom(FunctionNode *f)
-{
- reimplementedFrom_ = f;
- f->reimplementedBy_.append(this);
-}
-
-/*!
Adds the "associated" property \a p to this function node.
The function might be the setter or getter for a property,
for example.
@@ -2311,7 +2322,7 @@ QString FunctionNode::rawParameters(bool names, bool values) const
{
QString raw;
foreach (const Parameter &parameter, parameters()) {
- raw += parameter.dataType() + parameter.rightType();
+ raw += parameter.dataType();
if (names)
raw += parameter.name();
if (values)
@@ -2345,17 +2356,30 @@ QString FunctionNode::signature(bool values, bool noReturnType) const
QString s;
if (!noReturnType && !returnType().isEmpty())
s = returnType() + QLatin1Char(' ');
- s += name() + QLatin1Char('(');
- QStringList reconstructedParameters = reconstructParameters(values);
- int p = reconstructedParameters.size();
- if (p > 0) {
- for (int i=0; i<p; i++) {
- s += reconstructedParameters[i];
- if (i < (p-1))
- s += ", ";
+ s += name();
+ if (!isMacroWithoutParams()) {
+ s += QLatin1Char('(');
+ QStringList reconstructedParameters = reconstructParameters(values);
+ int p = reconstructedParameters.size();
+ if (p > 0) {
+ for (int i=0; i<p; i++) {
+ s += reconstructedParameters[i];
+ if (i < (p-1))
+ s += ", ";
+ }
}
- }
- s += QLatin1Char(')');
+ s += QLatin1Char(')');
+ if (isMacro())
+ return s;
+ }
+ if (isConst())
+ s += " const";
+ if (isRef())
+ s += " &";
+ else if (isRefRef())
+ s += " &&";
+ if (isImplicit())
+ s += " = default";
return s;
}
@@ -2382,6 +2406,36 @@ void FunctionNode::debug() const
}
/*!
+ Compares this FunctionNode to the FunctionNode pointed to
+ by \a fn. Returns true if they describe the same function.
+ */
+bool FunctionNode::compare(const FunctionNode *fn) const
+{
+ if (!fn)
+ return false;
+ if (type() != fn->type())
+ return false;
+ if (parent() != fn->parent())
+ return false;
+ if (returnType_ != fn->returnType())
+ return false;
+ if (isConst() != fn->isConst())
+ return false;
+ if (isAttached() != fn->isAttached())
+ return false;
+ const QVector<Parameter>& p = fn->parameters();
+ if (parameters().size() != p.size())
+ return false;
+ if (!p.isEmpty()) {
+ for (int i = 0; i < p.size(); ++i) {
+ if (parameters()[i].dataType() != p[i].dataType())
+ return false;
+ }
+ }
+ return true;
+}
+
+/*!
\class PropertyNode
This class describes one instance of using the Q_PROPERTY macro.
@@ -2850,236 +2904,6 @@ QString Node::cleanId(const QString &str)
return clean;
}
-#if 0
-/*!
- Creates a string that can be used as a UUID for the node,
- depending on the type and subtype of the node. Uniquenss
- is not guaranteed, but it is expected that strings created
- here will be unique within an XML document. Hence, the
- returned string can be used as the value of an \e id
- attribute.
- */
-QString Node::idForNode() const
-{
- const FunctionNode* func;
- const TypedefNode* tdn;
- QString str;
-
- switch (type()) {
- case Node::Namespace:
- str = "namespace-" + fullDocumentName();
- break;
- case Node::Class:
- str = "class-" + fullDocumentName();
- break;
- case Node::Enum:
- str = "enum-" + name();
- break;
- case Node::Typedef:
- tdn = static_cast<const TypedefNode*>(this);
- if (tdn->associatedEnum()) {
- return tdn->associatedEnum()->idForNode();
- }
- else {
- str = "typedef-" + name();
- }
- break;
- case Node::Function:
- func = static_cast<const FunctionNode*>(this);
- if (func->associatedProperty()) {
- return func->associatedProperty()->idForNode();
- }
- else {
- if (func->name().startsWith("operator")) {
- str.clear();
- /*
- The test below should probably apply to all
- functions, but for now, overloaded operators
- are the only ones that produce duplicate id
- attributes in the DITA XML files.
- */
- if (relatesTo_)
- str = "nonmember-";
- QString op = func->name().mid(8);
- if (!op.isEmpty()) {
- int i = 0;
- while (i<op.size() && op.at(i) == ' ')
- ++i;
- if (i>0 && i<op.size()) {
- op = op.mid(i);
- }
- if (!op.isEmpty()) {
- i = 0;
- while (i < op.size()) {
- const QChar c = op.at(i);
- const uint u = c.unicode();
- if ((u >= 'a' && u <= 'z') ||
- (u >= 'A' && u <= 'Z') ||
- (u >= '0' && u <= '9'))
- break;
- ++i;
- }
- str += "operator-";
- if (i>0) {
- QString tail = op.mid(i);
- op = op.left(i);
- if (operators_.contains(op)) {
- str += operators_.value(op);
- if (!tail.isEmpty())
- str += QLatin1Char('-') + tail;
- }
- else
- qDebug() << "qdoc internal error: Operator missing from operators_ map:" << op;
- }
- else {
- str += op;
- }
- }
- }
- }
- else if (parent_) {
- if (parent_->isClass())
- str = "class-member-" + func->name();
- else if (parent_->isNamespace())
- str = "namespace-member-" + func->name();
- else if (parent_->isQmlType())
- str = "qml-method-" + parent_->name().toLower() + "-" + func->name();
- else if (parent_->isJsType())
- str = "js-method-" + parent_->name().toLower() + "-" + func->name();
- else if (parent_->type() == Document) {
- qDebug() << "qdoc internal error: Node subtype not handled:"
- << parent_->docSubtype() << func->name();
- }
- else
- qDebug() << "qdoc internal error: Node type not handled:"
- << parent_->type() << func->name();
-
- }
- if (func->overloadNumber() != 0)
- str += QLatin1Char('-') + QString::number(func->overloadNumber());
- }
- break;
- case Node::QmlType:
- if (genus() == QML)
- str = "qml-class-" + name();
- else
- str = "js-type-" + name();
- break;
- case Node::QmlBasicType:
- if (genus() == QML)
- str = "qml-basic-type-" + name();
- else
- str = "js-basic-type-" + name();
- break;
- case Node::Document:
- {
- switch (docSubtype()) {
- case Node::Page:
- case Node::HeaderFile:
- str = title();
- if (str.isEmpty()) {
- str = name();
- if (str.endsWith(".html"))
- str.remove(str.size()-5,5);
- }
- str.replace(QLatin1Char('/'), QLatin1Char('-'));
- break;
- case Node::File:
- str = name();
- str.replace(QLatin1Char('/'), QLatin1Char('-'));
- break;
- case Node::Example:
- str = name();
- str.replace(QLatin1Char('/'), QLatin1Char('-'));
- break;
- default:
- qDebug() << "ERROR: A case was not handled in Node::idForNode():"
- << "docSubtype():" << docSubtype() << "type():" << type();
- break;
- }
- }
- break;
- case Node::Group:
- case Node::Module:
- str = title();
- if (str.isEmpty()) {
- str = name();
- if (str.endsWith(".html"))
- str.remove(str.size()-5,5);
- }
- str.replace(QLatin1Char('/'), QLatin1Char('-'));
- break;
- case Node::QmlModule:
- if (genus() == QML)
- str = "qml-module-" + name();
- else
- str = "js-module-" + name();
- break;
- case Node::QmlProperty:
- if (genus() == QML)
- str = "qml-";
- else
- str = "js-";
- if (isAttached())
- str += "attached-property-" + name();
- else
- str += "property-" + name();
- break;
- case Node::QmlPropertyGroup:
- {
- Node* n = const_cast<Node*>(this);
- if (genus() == QML)
- str = "qml-propertygroup-" + n->name();
- else
- str = "js-propertygroup-" + n->name();
- }
- break;
- case Node::Property:
- str = "property-" + name();
- break;
- case Node::QmlSignal:
- if (genus() == QML)
- str = "qml-signal-" + name();
- else
- str = "js-signal-" + name();
- break;
- case Node::QmlSignalHandler:
- if (genus() == QML)
- str = "qml-signal-handler-" + name();
- else
- str = "js-signal-handler-" + name();
- break;
- case Node::QmlMethod:
- func = static_cast<const FunctionNode*>(this);
- if (genus() == QML)
- str = "qml-method-";
- else
- str = "js-method-";
- str += parent_->name().toLower() + "-" + func->name();
- if (func->overloadNumber() != 0)
- str += QLatin1Char('-') + QString::number(func->overloadNumber());
- break;
- case Node::Variable:
- str = "var-" + name();
- break;
- default:
- qDebug() << "ERROR: A case was not handled in Node::idForNode():"
- << "type():" << type() << "docSubtype():" << docSubtype();
- break;
- }
- if (str.isEmpty()) {
- qDebug() << "ERROR: A link text was empty in Node::idForNode():"
- << "type():" << type() << "docSubtype():" << docSubtype()
- << "name():" << name()
- << "title():" << title();
- }
- else {
- str = cleanId(str);
- }
- return str;
-}
-#endif
-
/*!
Prints the inner node's list of children.
For debugging only.
diff --git a/src/qdoc/node.h b/src/qdoc/node.h
index 15fda389b..c550eff96 100644
--- a/src/qdoc/node.h
+++ b/src/qdoc/node.h
@@ -52,6 +52,7 @@ class FunctionNode;
class PropertyNode;
class CollectionNode;
class QmlPropertyNode;
+class SharedCommentNode;
typedef QList<Node*> NodeList;
typedef QList<PropertyNode*> PropNodeList;
@@ -87,6 +88,7 @@ public:
QmlSignalHandler,
QmlMethod,
QmlBasicType,
+ SharedComment,
LastType
};
@@ -205,6 +207,7 @@ public:
virtual bool isJsBasicType() const { return false; }
virtual bool isEnumType() const { return false; }
virtual bool isTypedef() const { return false; }
+ virtual bool isTypeAlias() const { return false; }
virtual bool isExample() const { return false; }
virtual bool isExampleFile() const { return false; }
virtual bool isHeaderFile() const { return false; }
@@ -216,6 +219,7 @@ public:
virtual bool isQtQuickNode() const { return false; }
virtual bool isAbstract() const { return false; }
virtual bool isProperty() const { return false; }
+ virtual bool isVariable() const { return false; }
virtual bool isQmlProperty() const { return false; }
virtual bool isJsProperty() const { return false; }
virtual bool isQmlPropertyGroup() const { return false; }
@@ -232,6 +236,10 @@ public:
virtual bool isReadOnly() const { return false; }
virtual bool isDefault() const { return false; }
virtual bool isExternalPage() const { return false; }
+ virtual bool isImplicit() const { return false; }
+ virtual bool isInCollective() const { return false; }
+ virtual bool isSharedCommentNode() const { return false; }
+ virtual bool isMacro() const { return false; }
virtual void addMember(Node* ) { }
virtual bool hasMembers() const { return false; }
virtual bool hasNamespaces() const { return false; }
@@ -257,6 +265,7 @@ public:
virtual Tree* tree() const;
virtual void findChildren(const QString& , NodeList& nodes) const { nodes.clear(); }
virtual void setNoAutoList(bool ) { }
+ virtual void setCollectiveNode(SharedCommentNode* ) { }
bool isIndexNode() const { return indexNodeFlag_; }
NodeType type() const { return (NodeType) nodeType_; }
virtual DocSubtype docSubtype() const { return NoSubtype; }
@@ -273,6 +282,7 @@ public:
virtual void setObsoleteLink(const QString& ) { };
virtual void setQtVariable(const QString& ) { }
virtual QString qtVariable() const { return QString(); }
+ virtual bool hasTag(const QString& ) const { return false; }
const QMap<LinkType, QPair<QString,QString> >& links() const { return linkMap_; }
void setLink(LinkType linkType, const QString &link, const QString &desc);
@@ -322,7 +332,6 @@ public:
virtual void setOutputSubdirectory(const QString& t) { outSubDir_ = t; }
QString fullDocumentName() const;
static QString cleanId(const QString &str);
- //QString idForNode() const;
static FlagValue toFlagValue(bool b);
static bool fromFlagValue(FlagValue fv, bool defaultValue);
@@ -824,6 +833,19 @@ private:
const EnumNode* associatedEnum_;
};
+class TypeAliasNode : public TypedefNode
+{
+ public:
+ TypeAliasNode(Aggregate* parent, const QString& name, const QString& aliasedType);
+ virtual ~TypeAliasNode() { }
+
+ virtual bool isTypeAlias() const { return true; }
+ QString aliasedType() { return aliasedType_; }
+
+ private:
+ QString aliasedType_;
+};
+
inline void EnumNode::setFlagsType(TypedefNode* t)
{
flagsType_ = t;
@@ -835,7 +857,6 @@ class Parameter
public:
Parameter() {}
Parameter(const QString& dataType,
- const QString& rightType = QString(),
const QString& name = QString(),
const QString& defaultValue = QString());
Parameter(const Parameter& p);
@@ -844,21 +865,35 @@ public:
void setName(const QString& name) { name_ = name; }
- bool hasType() const { return dataType_.length() + rightType_.length() > 0; }
+ bool hasType() const { return dataType_.length() > 0; }
const QString& dataType() const { return dataType_; }
- const QString& rightType() const { return rightType_; }
const QString& name() const { return name_; }
const QString& defaultValue() const { return defaultValue_; }
+ void setDefaultValue(const QString& defaultValue) { defaultValue_ = defaultValue; }
QString reconstruct(bool value = false) const;
public:
QString dataType_;
- QString rightType_; // mws says remove this 04/08/2015
QString name_;
QString defaultValue_;
};
+class SharedCommentNode : public LeafNode
+{
+ public:
+ SharedCommentNode(Aggregate* parent, int count)
+ : LeafNode(Node::SharedComment, parent, QString()) { collective_.reserve(count); }
+ virtual ~SharedCommentNode() { collective_.clear(); }
+
+ virtual bool isSharedCommentNode() const Q_DECL_OVERRIDE { return true; }
+ void append(Node* n) { collective_.append(n); }
+ const QVector<Node*>& collective() const { return collective_; }
+
+ private:
+ QVector<Node*> collective_;
+};
+
//friend class QTypeInfo<Parameter>;
//Q_DECLARE_TYPEINFO(Parameter, Q_MOVABLE_TYPE);
@@ -901,8 +936,9 @@ public:
void setReimplemented(bool b);
void addParameter(const Parameter& parameter);
inline void setParameters(const QVector<Parameter>& parameters);
+ void setParameters(const QString &t);
void borrowParameterNames(const FunctionNode* source);
- void setReimplementedFrom(FunctionNode* from);
+ void setReimplementedFrom(FunctionNode* from) { reimplementedFrom_ = from; }
const QString& returnType() const { return returnType_; }
QString metaness() const;
@@ -915,7 +951,9 @@ public:
bool isSomeCtor() const { return isCtor() || isCCtor() || isMCtor(); }
bool isMacroWithParams() const { return (metaness_ == MacroWithParams); }
bool isMacroWithoutParams() const { return (metaness_ == MacroWithoutParams); }
- bool isMacro() const { return (isMacroWithParams() || isMacroWithoutParams()); }
+ bool isMacro() const Q_DECL_OVERRIDE {
+ return (isMacroWithParams() || isMacroWithoutParams());
+ }
bool isSignal() const { return (metaness_ == Signal); }
bool isSlot() const { return (metaness_ == Slot); }
bool isCtor() const { return (metaness_ == Ctor); }
@@ -924,6 +962,9 @@ public:
bool isMCtor() const { return (metaness_ == MCtor); }
bool isCAssign() const { return (metaness_ == CAssign); }
bool isMAssign() const { return (metaness_ == MAssign); }
+ bool isSpecialMemberFunction() const {
+ return (isDtor() || isCCtor() || isMCtor() || isCAssign() || isMAssign());
+ }
bool isNonvirtual() const { return (virtualness_ == NonVirtual); }
bool isVirtual() const { return (virtualness_ == NormalVirtual); }
bool isPureVirtual() const { return (virtualness_ == PureVirtual); }
@@ -945,12 +986,12 @@ public:
bool isJsMethod() const override {
return (type() == Node::QmlMethod) && (genus() == Node::JS);
}
+ QVector<Parameter> &parameters() { return parameters_; }
const QVector<Parameter>& parameters() const { return parameters_; }
void clearParams() { parameters_.clear(); }
QStringList parameterNames() const;
QString rawParameters(bool names = false, bool values = false) const;
const FunctionNode* reimplementedFrom() const { return reimplementedFrom_; }
- const QList<FunctionNode*> &reimplementedBy() const { return reimplementedBy_; }
const PropNodeList& associatedProperties() const { return associatedProperties_; }
const QStringList& parentPath() const { return parentPath_; }
bool hasAssociatedProperties() const { return !associatedProperties_.isEmpty(); }
@@ -990,6 +1031,24 @@ public:
void setOverride(bool b) { isOverride_ = b; }
bool isOverride() const { return isOverride_; }
+ void setImplicit(bool b) { isImplicit_ = b; }
+ bool isImplicit() const Q_DECL_OVERRIDE { return isImplicit_; }
+
+ void setRef(bool b) { isRef_ = b; }
+ bool isRef() const { return isRef_; }
+
+ void setRefRef(bool b) { isRefRef_ = b; }
+ bool isRefRef() const { return isRefRef_; }
+
+ bool isInCollective() const Q_DECL_OVERRIDE { return (collective_ != 0); }
+ const SharedCommentNode* collective() const { return collective_; }
+ void setCollectiveNode(SharedCommentNode* t) Q_DECL_OVERRIDE { collective_ = t; }
+
+ virtual bool hasTag(const QString& t) const Q_DECL_OVERRIDE { return (tag_ == t); }
+ void setTag(const QString& t) { tag_ = t; }
+ const QString &tag() const { return tag_; }
+ bool compare(const FunctionNode *fn) const;
+
private:
void addAssociatedProperty(PropertyNode* property);
@@ -1010,11 +1069,15 @@ private:
bool isDefaulted_ : 1;
bool isFinal_ : 1;
bool isOverride_ : 1;
+ bool isImplicit_ : 1;
+ bool isRef_ : 1;
+ bool isRefRef_ : 1;
unsigned char overloadNumber_;
QVector<Parameter> parameters_;
const FunctionNode* reimplementedFrom_;
PropNodeList associatedProperties_;
- QList<FunctionNode*> reimplementedBy_;
+ SharedCommentNode* collective_;
+ QString tag_;
};
class PropertyNode : public LeafNode
@@ -1115,17 +1178,18 @@ public:
VariableNode(Aggregate* parent, const QString &name);
virtual ~VariableNode() { }
- void setLeftType(const QString &leftType) { lrftType_ = leftType; }
+ void setLeftType(const QString &leftType) { leftType_ = leftType; }
void setRightType(const QString &rightType) { rightType_ = rightType; }
void setStatic(bool b) { static_ = b; }
- const QString &leftType() const { return lrftType_; }
+ const QString &leftType() const { return leftType_; }
const QString &rightType() const { return rightType_; }
- QString dataType() const { return lrftType_ + rightType_; }
+ QString dataType() const { return leftType_ + rightType_; }
bool isStatic() const { return static_; }
+ virtual bool isVariable() const { return true; }
private:
- QString lrftType_;
+ QString leftType_;
QString rightType_;
bool static_;
};
diff --git a/src/qdoc/puredocparser.cpp b/src/qdoc/puredocparser.cpp
index 16a6e831f..50c6a7bba 100644
--- a/src/qdoc/puredocparser.cpp
+++ b/src/qdoc/puredocparser.cpp
@@ -42,11 +42,14 @@
QT_BEGIN_NAMESPACE
+PureDocParser *PureDocParser::pureParser_ = 0;
+
/*!
Constructs the pure doc parser.
*/
PureDocParser::PureDocParser()
{
+ pureParser_ = this;
}
/*!
@@ -54,6 +57,7 @@ PureDocParser::PureDocParser()
*/
PureDocParser::~PureDocParser()
{
+ pureParser_ = 0;
}
/*!
@@ -180,7 +184,23 @@ bool PureDocParser::processQdocComments()
ArgList::ConstIterator a = args.cbegin();
while (a != args.cend()) {
Doc nodeDoc = doc;
- Node* node = processTopicCommand(nodeDoc,topic,*a);
+ Node *node = 0;
+ if (topic == COMMAND_FN) {
+ node = parserForLanguage("Clang")->parseFnArg(doc.location(), a->first);
+ } else if (topic == COMMAND_MACRO) {
+ node = parserForLanguage("Clang")->parseMacroArg(doc.location(), a->first);
+ } else if (topic == COMMAND_QMLSIGNAL ||
+ topic == COMMAND_QMLMETHOD ||
+ topic == COMMAND_QMLATTACHEDSIGNAL ||
+ topic == COMMAND_QMLATTACHEDMETHOD ||
+ topic == COMMAND_JSSIGNAL ||
+ topic == COMMAND_JSMETHOD ||
+ topic == COMMAND_JSATTACHEDSIGNAL ||
+ topic == COMMAND_JSATTACHEDMETHOD) {
+ node = parseOtherFuncArg(topic, doc.location(), a->first);
+ } else {
+ node = processTopicCommand(nodeDoc, topic, *a);
+ }
if (node != 0) {
nodes.append(node);
docs.append(nodeDoc);
diff --git a/src/qdoc/puredocparser.h b/src/qdoc/puredocparser.h
index f03a75d97..fdd4f50c6 100644
--- a/src/qdoc/puredocparser.h
+++ b/src/qdoc/puredocparser.h
@@ -55,8 +55,11 @@ public:
QStringList sourceFileNameFilter() override;
void parseSourceFile(const Location& location, const QString& filePath) override;
+ static PureDocParser *pureDocParser() { return pureParser_; }
+
private:
bool processQdocComments();
+ static PureDocParser *pureParser_;
};
QT_END_NAMESPACE
diff --git a/src/qdoc/qdoc.pro b/src/qdoc/qdoc.pro
index 9963fdf08..1dafeee37 100644
--- a/src/qdoc/qdoc.pro
+++ b/src/qdoc/qdoc.pro
@@ -1,3 +1,4 @@
+
!force_bootstrap {
requires(qtConfig(xmlstreamwriter))
}
@@ -10,6 +11,11 @@ qtHaveModule(qmldevtools-private) {
DEFINES += QT_NO_DECLARATIVE
}
+LIBS += $$CLANG_LIBS
+INCLUDEPATH += $$CLANG_INCLUDEPATH
+!disable_external_rpath: QMAKE_RPATHDIR += $$CLANG_LIBDIR
+DEFINES += $$shell_quote(CLANG_RESOURCE_DIR=\"$${CLANG_LIBDIR}/clang/$${CLANG_VERSION}/include\")
+
DEFINES += \
QDOC2_COMPAT
@@ -19,9 +25,13 @@ INCLUDEPATH += $$QT_SOURCE_TREE/src/tools/qdoc \
# Increase the stack size on MSVC to 4M to avoid a stack overflow
win32-msvc*:{
QMAKE_LFLAGS += /STACK:4194304
+} else {
+ # (MSVC objects to defining a function in the locale.h dllimport context)
+ DEFINES += HEINOUS_SYSINC_HACK
}
HEADERS += atom.h \
+ clangcodeparser.h \
codechunk.h \
codemarker.h \
codeparser.h \
@@ -49,6 +59,7 @@ HEADERS += atom.h \
webxmlgenerator.h
SOURCES += atom.cpp \
+ clangcodeparser.cpp \
codechunk.cpp \
codemarker.cpp \
codeparser.cpp \
diff --git a/src/qdoc/qdocdatabase.h b/src/qdoc/qdocdatabase.h
index 7ff840b3b..9b3e7628b 100644
--- a/src/qdoc/qdocdatabase.h
+++ b/src/qdoc/qdocdatabase.h
@@ -348,6 +348,8 @@ class QDocDatabase
const CollectionNode* getCollectionNode(const QString& name, Node::Genus genus) {
return forest_.getCollectionNode(name, genus);
}
+ Node *findFunctionNodeForTag(QString tag) { return primaryTree()->findFunctionNodeForTag(tag); }
+ Node* findMacroNode(const QString &t) { return primaryTree()->findMacroNode(t); }
private:
const Node* findNodeForTarget(QStringList& targetPath,
diff --git a/src/qdoc/qdocindexfiles.cpp b/src/qdoc/qdocindexfiles.cpp
index 492fc0897..c97ed2f46 100644
--- a/src/qdoc/qdocindexfiles.cpp
+++ b/src/qdoc/qdocindexfiles.cpp
@@ -494,6 +494,11 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
functionNode->setIsDefaulted(attributes.value(QLatin1String("default")) == QLatin1String("true"));
functionNode->setFinal(attributes.value(QLatin1String("final")) == QLatin1String("true"));
functionNode->setOverride(attributes.value(QLatin1String("override")) == QLatin1String("true"));
+ int refness = attributes.value(QLatin1String("refness")).toUInt();
+ if (refness == 1)
+ functionNode->setRef(true);
+ else if (refness == 2)
+ functionNode->setRefRef(true);
if (attributes.value(QLatin1String("overload")) == QLatin1String("true")) {
functionNode->setOverloadFlag(true);
functionNode->setOverloadNumber(attributes.value(QLatin1String("overload-number")).toUInt());
@@ -518,8 +523,7 @@ void QDocIndexFiles::readIndexSection(QXmlStreamReader& reader,
if (reader.name() == QLatin1String("parameter")) {
// Do not use the default value for the parameter; it is not
// required, and has been known to cause problems.
- Parameter parameter(childAttributes.value(QLatin1String("left")).toString(),
- childAttributes.value(QLatin1String("right")).toString(),
+ Parameter parameter(childAttributes.value(QLatin1String("type")).toString(),
childAttributes.value(QLatin1String("name")).toString(),
QString()); // childAttributes.value(QLatin1String("default"))
functionNode->addParameter(parameter);
@@ -1020,6 +1024,8 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
ClassNode* n = related.node_;
if (n)
baseStrings.insert(n->fullName());
+ else if (!related.path_.isEmpty())
+ baseStrings.insert(related.path_.join(QLatin1String("::")));
}
if (!baseStrings.isEmpty())
{
@@ -1195,6 +1201,10 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
writer.writeAttribute("default", functionNode->isDefaulted() ? "true" : "false");
writer.writeAttribute("final", functionNode->isFinal() ? "true" : "false");
writer.writeAttribute("override", functionNode->isOverride() ? "true" : "false");
+ if (functionNode->isRef())
+ writer.writeAttribute("refness", QString::number(1));
+ else if (functionNode->isRefRef())
+ writer.writeAttribute("refness", QString::number(2));
if (functionNode->isOverload())
writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber()));
if (functionNode->relates()) {
@@ -1234,8 +1244,7 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
for (int i = 0; i < functionNode->parameters().size(); ++i) {
Parameter parameter = functionNode->parameters()[i];
writer.writeStartElement("parameter");
- writer.writeAttribute("left", parameter.dataType());
- writer.writeAttribute("right", parameter.rightType());
+ writer.writeAttribute("type", parameter.dataType());
writer.writeAttribute("name", parameter.name());
writer.writeAttribute("default", parameter.defaultValue());
writer.writeEndElement(); // parameter
diff --git a/src/qdoc/qmlcodeparser.cpp b/src/qdoc/qmlcodeparser.cpp
index 31da874fe..31775bb1b 100644
--- a/src/qdoc/qmlcodeparser.cpp
+++ b/src/qdoc/qmlcodeparser.cpp
@@ -204,14 +204,6 @@ void QmlCodeParser::parseSourceFile(const Location& location, const QString& fil
#endif
}
-/*!
- Performs cleanup after qdoc is done parsing all the QML files.
- Currently, no cleanup is required.
- */
-void QmlCodeParser::doneParsingSourceFiles()
-{
-}
-
static QSet<QString> topicCommands_;
/*!
Returns the set of strings representing the topic commands.
diff --git a/src/qdoc/qmlcodeparser.h b/src/qdoc/qmlcodeparser.h
index 0a63a019d..3788175f3 100644
--- a/src/qdoc/qmlcodeparser.h
+++ b/src/qdoc/qmlcodeparser.h
@@ -61,7 +61,6 @@ public:
QString language() override;
QStringList sourceFileNameFilter() override;
void parseSourceFile(const Location& location, const QString& filePath) override;
- void doneParsingSourceFiles() override;
#ifndef QT_NO_DECLARATIVE
/* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
diff --git a/src/qdoc/qmlvisitor.cpp b/src/qdoc/qmlvisitor.cpp
index ac64348d6..9ebf422cf 100644
--- a/src/qdoc/qmlvisitor.cpp
+++ b/src/qdoc/qmlvisitor.cpp
@@ -382,7 +382,7 @@ bool QmlSignatureParser::matchParameter()
readToken();
}
}
- func_->addParameter(Parameter(dataType.toString(), "", name, defaultValue.toString()));
+ func_->addParameter(Parameter(dataType.toString(), name, defaultValue.toString()));
return true;
}
diff --git a/src/qdoc/tree.cpp b/src/qdoc/tree.cpp
index 0354386ee..9669f3991 100644
--- a/src/qdoc/tree.cpp
+++ b/src/qdoc/tree.cpp
@@ -1523,4 +1523,53 @@ TargetList* Tree::getTargetList(const QString& module)
return targetListMap_->value(module);
}
+/*!
+ Search this tree recursively from \a parent to find a function
+ node with the specified \a tag. If no function node is found
+ with the required \a tag, return 0.
+ */
+Node* Tree::findFunctionNodeForTag(const QString &tag, Aggregate* parent)
+{
+ if (!parent)
+ parent = root();
+ const NodeList& children = parent->childNodes();
+ for (Node *n : children) {
+ if (n && n->isFunction() && n->hasTag(tag))
+ return n;
+ }
+ for (Node *n : children) {
+ if (n && n->isAggregate()) {
+ Aggregate* a = static_cast<Aggregate*>(n);
+ n = findFunctionNodeForTag(tag, a);
+ if (n)
+ return n;
+ }
+ }
+ return 0;
+}
+
+/*!
+ There should only be one macro node for macro name \a t.
+ The macro node is not built until the \macro command is seen.
+ */
+Node *Tree::findMacroNode(const QString &t, const Aggregate *parent)
+{
+ if (!parent)
+ parent = root();
+ const NodeList &children = parent->childNodes();
+ for (Node *n : children) {
+ if (n && (n->isMacro() || n->isFunction()) && n->name() == t)
+ return n;
+ }
+ for (Node *n : children) {
+ if (n && n->isAggregate()) {
+ Aggregate *a = static_cast<Aggregate*>(n);
+ n = findMacroNode(t, a);
+ if (n)
+ return n;
+ }
+ }
+ return 0;
+}
+
QT_END_NAMESPACE
diff --git a/src/qdoc/tree.h b/src/qdoc/tree.h
index 0cb07aff7..1aac6d664 100644
--- a/src/qdoc/tree.h
+++ b/src/qdoc/tree.h
@@ -216,6 +216,8 @@ class Tree
bool broken);
TargetList* getTargetList(const QString& module);
QStringList getTargetListKeys() { return targetListMap_->keys(); }
+ Node* findFunctionNodeForTag(const QString &tag, Aggregate* parent = 0);
+ Node *findMacroNode(const QString &t, const Aggregate *parent = 0);
public:
const QString& camelCaseModuleName() const { return camelCaseModuleName_; }
diff --git a/src/src.pro b/src/src.pro
index 41064a5d5..ed370052c 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -13,13 +13,14 @@ qtHaveModule(widgets) {
}
SUBDIRS += linguist \
- qdoc \
qtattributionsscanner
qtConfig(library) {
!android|android_app: SUBDIRS += qtplugininfo
}
+config_clang: SUBDIRS += qdoc
+
if(!android|android_app):!uikit: SUBDIRS += qtpaths
mac {