summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mkspecs/features/qt_find_clang.prf100
-rw-r--r--qttools.pro1
-rw-r--r--src/qdoc/clangcodeparser.cpp1034
-rw-r--r--src/qdoc/clangcodeparser.h66
-rw-r--r--src/qdoc/codeparser.cpp5
-rw-r--r--src/qdoc/codeparser.h2
-rw-r--r--src/qdoc/config.cpp1
-rw-r--r--src/qdoc/config.h2
-rw-r--r--src/qdoc/cppcodeparser.cpp883
-rw-r--r--src/qdoc/cppcodeparser.h40
-rw-r--r--src/qdoc/main.cpp44
-rw-r--r--src/qdoc/node.cpp3
-rw-r--r--src/qdoc/node.h1
-rw-r--r--src/qdoc/puredocparser.cpp4
-rw-r--r--src/qdoc/puredocparser.h3
-rw-r--r--src/qdoc/qdoc.pro8
-rw-r--r--src/qdoc/qmlcodeparser.cpp8
-rw-r--r--src/qdoc/qmlcodeparser.h1
-rw-r--r--src/src.pro3
19 files changed, 1277 insertions, 932 deletions
diff --git a/mkspecs/features/qt_find_clang.prf b/mkspecs/features/qt_find_clang.prf
new file mode 100644
index 000000000..14a2e80bf
--- /dev/null
+++ b/mkspecs/features/qt_find_clang.prf
@@ -0,0 +1,100 @@
+config_clang_done: return()
+
+defineReplace(extractVersion) { return($$replace(1, ^(\\d+\\.\\d+\\.\\d+)(svn)?$, \\1)) }
+defineReplace(extractMajorVersion) { return($$replace(1, ^(\\d+)\\.\\d+\\.\\d+(svn)?$, \\1)) }
+defineReplace(extractMinorVersion) { return($$replace(1, ^\\d+\\.(\\d+)\\.\\d+(svn)?$, \\1)) }
+defineReplace(extractPatchVersion) { return($$replace(1, ^\\d+\\.\\d+\\.(\\d+)(svn)?$, \\1)) }
+
+defineTest(versionIsAtLeast) {
+ actual_major_version = $$extractMajorVersion($$1)
+ actual_minor_version = $$extractMinorVersion($$1)
+ actual_patch_version = $$extractPatchVersion($$1)
+ required_min_major_version = $$extractMajorVersion($$2)
+ required_min_minor_version = $$extractMinorVersion($$2)
+ required_min_patch_version = $$extractPatchVersion($$2)
+
+ isEqual(actual_major_version, $$required_min_major_version) {
+ isEqual(actual_minor_version, $$required_min_minor_version) {
+ isEqual(actual_patch_version, $$required_min_patch_version): return(true)
+ greaterThan(actual_patch_version, $$required_min_patch_version): return(true)
+ }
+ greaterThan(actual_minor_version, $$required_min_minor_version): return(true)
+ }
+ greaterThan(actual_major_version, $$required_min_major_version): return(true)
+
+ return(false)
+}
+
+defineReplace(findLLVMVersionFromLibDir) {
+ libdir = $$1
+ version_dirs = $$files($$libdir/clang/*)
+ for (version_dir, version_dirs) {
+ fileName = $$basename(version_dir)
+ version = $$find(fileName, ^(\\d+\\.\\d+\\.\\d+)$)
+ !isEmpty(version) {
+ isEmpty(candidateVersion): candidateVersion = $$version
+ else: versionIsAtLeast($$version, $$candidateVersion): candidateVersion = $$version
+ }
+ }
+ return($$candidateVersion)
+}
+
+for(_, $$list(_)) { # just a way to break easily
+ isEmpty(CLANG_INSTALL_DIR): CLANG_INSTALL_DIR = $$(CLANG_INSTALL_DIR)
+ CLANG_INSTALL_DIR = $$clean_path($$CLANG_INSTALL_DIR)
+ clangInstallDir = $$CLANG_INSTALL_DIR
+ isEmpty(CLANG_INSTALL_DIR) {
+ win32 {
+ log("Set the CLANG_INSTALL_DIR environment variable to configure clang location.$$escape_expand(\\n)")
+ break()
+ }
+ clangInstallDir = /usr
+ }
+
+ # note: llvm_config only exits on unix
+ llvm_config = $$clangInstallDir/bin/llvm-config
+ exists($$llvm_config) {
+ CLANG_LIBDIR = $$system("$$llvm_config --libdir 2>/dev/null")
+ CLANG_INCLUDEPATH = $$system("$$llvm_config --includedir 2>/dev/null")
+ output = $$system("$$llvm_config --version 2>/dev/null")
+ CLANG_VERSION = $$extractVersion($$output)
+ } else {
+ CLANG_LIBDIR = $$clangInstallDir/lib
+ CLANG_INCLUDEPATH = $$clangInstallDir/include
+ CLANG_VERSION = $$findLLVMVersionFromLibDir($$CLANG_LIBDIR)
+ }
+ isEmpty(CLANG_VERSION) {
+ !isEmpty(CLANG_INSTALL_DIR): \
+ error("Cannot determine clang version at $${CLANG_INSTALL_DIR}.")
+ log("Set the CLANG_INSTALL_DIR environment variable to configure clang location.$$escape_expand(\\n)")
+ break()
+ }
+
+ LIBCLANG_MAIN_HEADER = $$CLANG_INCLUDEPATH/clang-c/Index.h
+ !exists($$LIBCLANG_MAIN_HEADER) {
+ !isEmpty(CLANG_INSTALL_DIR): \
+ error("Cannot find libclang's main header file, candidate: $${LIBCLANG_MAIN_HEADER}.")
+ log("Set the CLANG_INSTALL_DIR environment variable to configure clang location.$$escape_expand(\\n)")
+ break()
+ }
+
+ !contains(QMAKE_DEFAULT_LIBDIRS, $$CLANG_LIBDIR): CLANG_LIBS = -L$${CLANG_LIBDIR}
+ win32: \
+ CLANG_LIBS += -llibclang -ladvapi32 -lshell32
+ else: \
+ CLANG_LIBS += -lclang
+
+ !versionIsAtLeast($$CLANG_VERSION, "3.6.2") {
+ log("LLVM/Clang version >= 3.6.2 required, version provided: $$CLANG_VERSION.$$escape_expand(\\n)")
+ log("Clang was found in $$CLANG_INSTALL_DIR. Set the CLANG_INSTALL_DIR environment variable to override.$$escape_expand(\\n)")
+ break()
+ }
+
+ cache(CLANG_LIBS)
+ cache(CLANG_INCLUDEPATH)
+ cache(CLANG_LIBDIR)
+ cache(CLANG_VERSION)
+ cache(CONFIG, add, $$list(config_clang))
+}
+
+cache(CONFIG, add, $$list(config_clang_done))
diff --git a/qttools.pro b/qttools.pro
index 58c33f27c..b87dcea59 100644
--- a/qttools.pro
+++ b/qttools.pro
@@ -1 +1,2 @@
+load(qt_find_clang)
load(qt_parts)
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp
new file mode 100644
index 000000000..ba270a613
--- /dev/null
+++ b/src/qdoc/clangcodeparser.cpp
@@ -0,0 +1,1034 @@
+/****************************************************************************
+**
+** 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 <qregularexpression.h>
+#include <qtemporarydir.h>
+#include "generator.h"
+
+#include <clang-c/Index.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Call clang_visitChildren on the given cursor with the lambda as a callback
+ T can be any functor that is callable with a CXCursor parametter and returns a CXChildVisitResult
+ (in other word compatible with function<CXChildVisitResult(CXCursor)>
+ */
+template <typename T> bool visitChildrenLambda(CXCursor cursor, T &&lambda)
+{
+ auto 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);
+ if (t1 != t2) {
+ QString parentScope = parent->name() + QLatin1String("::");
+ if (t1.remove(parentScope) != t2.remove(parentScope)) {
+ 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;
+ }
+}
+
+class ClangVisitor {
+public:
+ ClangVisitor(QDocDatabase *qdb, const QSet<QString> &allHeaders)
+ : qdb_(qdb), parent_(qdb->primaryTreeRoot()), allHeaders_(allHeaders)
+ {}
+
+ 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;
+ }
+
+ Node *nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocation nextCommentLoc);
+private:
+ /*! \class 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);
+ void parseProperty(const QString &spelling, const Location &loc);
+ void readParameterNamesAndAttributes(FunctionNode* fn, 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;
+}
+
+CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation loc)
+{
+ auto kind = clang_getCursorKind(cursor);
+ switch (kind) {
+ case CXCursor_StructDecl:
+ case CXCursor_UnionDecl:
+ 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));
+
+ if (parent_ && parent_->findChildNode(className, Node::Class)) {
+ return CXChildVisit_Continue;
+ }
+
+ ClassNode *classe = new ClassNode(parent_, 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);
+ if (!baseNode || !baseNode->isClass())
+ return CXChildVisit_Continue;
+ auto classe = static_cast<ClassNode*>(parent_);
+ 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);
+ 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);
+ // 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) {
+ auto typeName = fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i)));
+ pvect.append(Parameter(adjustTypeName(typeName)));
+ }
+ 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;
+ }
+ case CXCursor_EnumDecl: {
+ if (findNodeForCursor(qdb_, cursor)) // Was already parsed, propably in another translation unit
+ return CXChildVisit_Continue;
+ auto en = new EnumNode(parent_, fromCXString(clang_getCursorSpelling(cursor)));
+ 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)
+{
+ const QLatin1String metaKeyword("READ|WRITE|CONSTANT|FINAL|REVISION|MEMBER|RESET|SCRIPTABLE|STORED|WRITE|DESIGNABLE|EDITABLE|NOTIFY|USER");
+ static QRegularExpression typeNameRx(QLatin1String("^[^(]*\\((?<type>.*?)\\s*(?<name>[a-zA-Z0-9_]+)\\s+(")
+ + metaKeyword + QLatin1String(")\\s"));
+ auto match = typeNameRx.match(spelling);
+ if (!match.hasMatch()) {
+ qWarning() << "ERROR PARSING " << spelling;
+ return;
+ }
+ auto type = match.captured(QStringLiteral("type"));
+ auto name = match.captured(QStringLiteral("name"));
+ auto *property = new PropertyNode(parent_, name);
+ property->setAccess(Node::Public);
+ property->setLocation(loc);
+ property->setDataType(type);
+
+ static QRegularExpression nextKeyword(QLatin1String("\\s(?<key>") + metaKeyword
+ + QLatin1String(")\\s+(?<value>.*?)(\\s*\\)$|\\s+(") + metaKeyword + QLatin1String("))"));
+ int pos = match.capturedEnd(QStringLiteral("name"));
+ while ((match = nextKeyword.match(spelling, pos)).hasMatch()) {
+ pos = match.capturedEnd(QStringLiteral("value"));
+ auto key = match.capturedRef(QStringLiteral("key"));
+ auto value = match.captured(QStringLiteral("value"));
+ // Keywords with no associated values
+ if (key == "CONSTANT") {
+ property->setConstant();
+ } else if (key == "FINAL") {
+ property->setFinal();
+ } else 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.
+}
+
+/*!
+
+ */
+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();
+}
+
+/*!
+ */
+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());
+}
+
+
+/*!
+ 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)
+{
+ 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
+ };
+ std::vector<const char *> args(std::begin(defaultArgs), std::end(defaultArgs));
+
+ auto moreArgs = includePaths_;
+ if (moreArgs.isEmpty()) {
+ // Try to guess the include paths if none were given.
+ auto forest = qdb_->searchOrder();
+ QByteArray installDocDir = Config::installDir.toUtf8();
+ QByteArray version = qdb_->version().toUtf8();
+ moreArgs += "-I" + installDocDir + "/../include";
+ moreArgs += "-I" + filePath.toUtf8() + "/../";
+ moreArgs += "-I" + filePath.toUtf8() + "/../../";
+ for (const auto &s : forest) {
+ QByteArray module = s->camelCaseModuleName().toUtf8();
+ moreArgs += "-I" + installDocDir + "/../include/" + module;
+ moreArgs += "-I" + installDocDir + "/../include/" + module + "/" + version;
+ moreArgs += "-I" + installDocDir + "/../include/" + module + "/" + version + "/" + module;
+ }
+ }
+
+ for (const auto &p : qAsConst(moreArgs))
+ args.push_back(p.constData());
+
+ auto flags = CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies;
+ CXIndex index = clang_createIndex(1, 1);
+
+ if (!pchFileDir_) {
+ pchFileDir_.reset(new QTemporaryDir(QDir::tempPath() + QLatin1String("/qdoc_pch")));
+ if (pchFileDir_->isValid()) {
+ const QByteArray module = qdb_->primaryTreeRoot()->tree()->camelCaseModuleName().toUtf8();
+ QByteArray header;
+ // 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()) {
+ QByteArray installDocDir = Config::installDir.toUtf8();
+ const QByteArray candidate = installDocDir + "/../include/" + module + "/" + module;
+ if (QFile::exists(QString::fromUtf8(candidate)))
+ header = candidate;
+ }
+ if (header.isEmpty()) {
+ qWarning() << "Could not find the module header in the include path for module"
+ << module << " (include paths: "<< includePaths_ << ")";
+ } else {
+ args.push_back("-xc++");
+ CXTranslationUnit tu;
+ CXErrorCode err = clang_parseTranslationUnit2(
+ index, header.constData(), args.data(), 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() << "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() << "Could not create PCH file for " << header << " error code:" << err;
+ }
+ args.pop_back(); // remove the "-xc++";
+ }
+ }
+ }
+ if (!pchName_.isEmpty() && !filePath.endsWith(".mm")) {
+ args.push_back("-include-pch");
+ args.push_back(pchName_.constData());
+ }
+
+ CXTranslationUnit tu;
+ CXErrorCode err = clang_parseTranslationUnit2(index, filePath.toLocal8Bit(), args.data(),
+ args.size(), nullptr, 0, flags, &tu);
+ if (err || !tu) {
+ qWarning() << "Could not parse " << 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 {
+ 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();
+ 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;
+ }
+ }
+
+ clang_disposeTokens(tu, tokens, numTokens);
+ clang_disposeTranslationUnit(tu);
+ clang_disposeIndex(index);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qdoc/clangcodeparser.h b/src/qdoc/clangcodeparser.h
new file mode 100644
index 000000000..e0359e400
--- /dev/null
+++ b/src/qdoc/clangcodeparser.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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;
+private:
+ QSet<QString> allHeaders_;
+ QVector<QByteArray> includePaths_;
+ QScopedPointer<QTemporaryDir> pchFileDir_;
+ QByteArray pchName_;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qdoc/codeparser.cpp b/src/qdoc/codeparser.cpp
index 2b8e28f91..83d2ddc4d 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.
diff --git a/src/qdoc/codeparser.h b/src/qdoc/codeparser.h
index cd24987c6..0ff7014a7 100644
--- a/src/qdoc/codeparser.h
+++ b/src/qdoc/codeparser.h
@@ -53,8 +53,6 @@ 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;
bool isParsingH() const;
bool isParsingCpp() const;
diff --git a/src/qdoc/config.cpp b/src/qdoc/config.cpp
index 46d7c5bf1..76c615046 100644
--- a/src/qdoc/config.cpp
+++ b/src/qdoc/config.cpp
@@ -75,6 +75,7 @@ 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");
diff --git a/src/qdoc/config.h b/src/qdoc/config.h
index 298cd3f63..588e0e0a9 100644
--- a/src/qdoc/config.h
+++ b/src/qdoc/config.h
@@ -188,6 +188,7 @@ struct ConfigStrings
static QString IGNORETOKENS;
static QString IMAGEDIRS;
static QString IMAGES;
+ static QString INCLUDEPATHS;
static QString INDEXES;
static QString LANDINGPAGE;
static QString LANGUAGE;
@@ -268,6 +269,7 @@ 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
diff --git a/src/qdoc/cppcodeparser.cpp b/src/qdoc/cppcodeparser.cpp
index 7298d1ec2..7ec6ed805 100644
--- a/src/qdoc/cppcodeparser.cpp
+++ b/src/qdoc/cppcodeparser.cpp
@@ -51,7 +51,6 @@ 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 +60,6 @@ CppCodeParser::CppCodeParser()
: varComment("/\\*\\s*([a-zA-Z_0-9]+)\\s*\\*/"), sep("(?:<[^>]+>)?::")
{
reset();
- cppParser_ = this;
}
/*!
@@ -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_;
/*!
@@ -563,7 +449,7 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc,
QString module;
QString name;
QString type;
- if (splitQmlMethodArg(arg.first, type, module, name)) {
+ if (splitQmlMethodArg(arg.first, type, module, name, doc.location())) {
Aggregate* aggregate = qdb_->findQmlType(module, name);
if (!aggregate)
aggregate = qdb_->findQmlBasicType(module, name);
@@ -623,7 +509,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 +520,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 +547,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,11 +567,11 @@ 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;
}
@@ -705,7 +593,8 @@ bool CppCodeParser::splitQmlPropertyArg(const QString& arg,
bool CppCodeParser::splitQmlMethodArg(const QString& arg,
QString& type,
QString& module,
- QString& qmlTypeName)
+ QString& qmlTypeName,
+ const Location& location)
{
QString name;
int leftParen = arg.indexOf(QChar('('));
@@ -734,7 +623,7 @@ bool CppCodeParser::splitQmlMethodArg(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;
}
@@ -775,7 +664,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 +684,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 +706,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);
@@ -1179,15 +1068,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)
{
/*
@@ -1718,145 +1598,6 @@ bool CppCodeParser::matchFunctionDecl(Aggregate *parent,
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,555 +1671,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.
@@ -2494,7 +1686,8 @@ bool CppCodeParser::makeFunctionNode(const QString& signature,
int outerTok = tok;
QByteArray latin1 = signature.toLatin1();
- Tokenizer stringTokenizer(location(), latin1);
+ Location tmpLoc(signature); // FIXME give a proper location with a filename.
+ Tokenizer stringTokenizer(tokenizer ? location() : tmpLoc, latin1);
stringTokenizer.setParsingFnOrMacro(true);
tokenizer = &stringTokenizer;
readToken();
@@ -2565,46 +1758,6 @@ FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc,
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 c49da77f2..79e5f47b6 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_; }
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 doneParsingHeaderFiles() Q_DECL_OVERRIDE;
- virtual void doneParsingSourceFiles() Q_DECL_OVERRIDE;
bool parseParameters(const QString& parameters, QVector<Parameter>& pvect, bool& isQPrivateSignal);
const Location& declLoc() const { return declLoc_; }
void setDeclLoc() { declLoc_ = location(); }
@@ -84,16 +79,19 @@ 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);
+ QString& name,
+ const Location& location);
bool splitQmlMethodArg(const QString& arg,
QString& type,
QString& module,
- QString& element);
+ QString& element,
+ const Location& location);
virtual void processOtherMetaCommand(const Doc& doc,
const QString& command,
const ArgLocPair& argLocPair,
@@ -113,7 +111,6 @@ 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,
@@ -121,18 +118,7 @@ protected:
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,
@@ -143,13 +129,8 @@ protected:
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;
Tokenizer *tokenizer;
@@ -163,20 +144,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;
};
diff --git a/src/qdoc/main.cpp b/src/qdoc/main.cpp
index e5fe2412f..8cc5a704a 100644
--- a/src/qdoc/main.cpp
+++ b/src/qdoc/main.cpp
@@ -45,6 +45,7 @@
#include "jscodemarker.h"
#include "qmlcodemarker.h"
#include "qmlcodeparser.h"
+#include "clangcodeparser.h"
#include <qdatetime.h>
#include <qdebug.h>
#include "qtranslator.h"
@@ -72,6 +73,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;
@@ -243,6 +245,8 @@ static void processQdocconfFile(const QString &fileName)
*/
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();
@@ -425,7 +429,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;
@@ -436,15 +439,10 @@ 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();
/*
@@ -460,19 +458,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.
@@ -537,6 +528,7 @@ private:
QCommandLineOption noLinkErrorsOption, autoLinkErrorsOption, debugOption;
QCommandLineOption prepareOption, generateOption, logProgressOption;
QCommandLineOption singleExecOption, writeQaPagesOption;
+ QCommandLineOption includePathOption, includePathSystemOption, frameworkOption;
};
QDocCommandLineParser::QDocCommandLineParser()
@@ -559,7 +551,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();
@@ -631,6 +626,14 @@ 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);
}
void QDocCommandLineParser::process(const QCoreApplication &app)
@@ -676,6 +679,17 @@ 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);
+ const auto paths2 = values(includePathSystemOption);
+ 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.
@@ -713,7 +727,7 @@ 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;
QmlCodeParser qmlParser;
PureDocParser docParser;
diff --git a/src/qdoc/node.cpp b/src/qdoc/node.cpp
index 9919317f2..f1af55ca6 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
@@ -930,7 +931,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);
diff --git a/src/qdoc/node.h b/src/qdoc/node.h
index 33cdd3c1a..f5507293c 100644
--- a/src/qdoc/node.h
+++ b/src/qdoc/node.h
@@ -849,6 +849,7 @@ public:
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;
diff --git a/src/qdoc/puredocparser.cpp b/src/qdoc/puredocparser.cpp
index 16a6e831f..636c54d26 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;
}
/*!
diff --git a/src/qdoc/puredocparser.h b/src/qdoc/puredocparser.h
index ec8d6e389..59a8a850a 100644
--- a/src/qdoc/puredocparser.h
+++ b/src/qdoc/puredocparser.h
@@ -55,8 +55,11 @@ public:
virtual QStringList sourceFileNameFilter() Q_DECL_OVERRIDE;
virtual void parseSourceFile(const Location& location, const QString& filePath) Q_DECL_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 a49ddc555..36a14b9d8 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
@@ -22,6 +28,7 @@ win32-msvc*:{
}
HEADERS += atom.h \
+ clangcodeparser.h \
codechunk.h \
codemarker.h \
codeparser.h \
@@ -47,6 +54,7 @@ HEADERS += atom.h \
tokenizer.h \
tree.h
SOURCES += atom.cpp \
+ clangcodeparser.cpp \
codechunk.cpp \
codemarker.cpp \
codeparser.cpp \
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 1b317a1c1..231f163b7 100644
--- a/src/qdoc/qmlcodeparser.h
+++ b/src/qdoc/qmlcodeparser.h
@@ -61,7 +61,6 @@ public:
virtual QString language() Q_DECL_OVERRIDE;
virtual QStringList sourceFileNameFilter() Q_DECL_OVERRIDE;
virtual void parseSourceFile(const Location& location, const QString& filePath) Q_DECL_OVERRIDE;
- virtual void doneParsingSourceFiles() Q_DECL_OVERRIDE;
#ifndef QT_NO_DECLARATIVE
/* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */
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 {