summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorOlivier Goffart <ogoffart@woboq.com>2016-03-03 13:12:47 +0100
committerMartin Smith <martin.smith@qt.io>2017-08-10 07:31:47 +0000
commitb032aee53c8fb6807485186f276ca25b86ac1251 (patch)
treea23ca6f0a1e2d13b2629299a49234035b1fb3863 /src
parent3c74e2594595f06b3e07f441b254ee7f57b2c770 (diff)
Use clang as a parser in qdoc
The file qt_find_clang.prf is inspired by qtcreator's clang_installation.pri. The code from the while loop in ClangVisitor::parseProperty contains code moved from CppCodeParser::matchProperty. The code in the for loop of ClangCodeParser::parseSourceFile (from the "Doc parses the comment"), is mostly moved from CppCodeParser::matchDocsAndStuff. In CppCodeParser, most of the code is removed since clang is used for parsing. We just need to leave enough to parse the declaration in the comments which still use the old parser (\fn, ...) Known issues: - When the parameter name is a comment, it is lost. (e.g. QObject::eventFilter(QObject * /* watched */, QEvent * /* event */) - I can't compute default parameters when they are expanded from a macro. (e.g. QObject::tr) - Instances of #ifndef Q_QDOC need to be reviewed Change-Id: I92d4ca4fc52810d9d3de433147a9953eea3a1802 Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
Diffstat (limited to 'src')
-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
17 files changed, 1176 insertions, 932 deletions
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 {