From 99120ca3cf2a22f89fd7ec46ff483882fc3a2bbe Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Thu, 26 Feb 2015 10:34:47 +0100 Subject: qdoc: Correct parsing of the using clause MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qdoc could only parse the using clause where the 'using' keyword was followed by 'namespace'. Now it can parse using clauses with or without 'namespace'. Change-Id: Ic4aad025c00b3bda2bc1cbd52d0ba8dbbad653e5 Task-number: QTBUG-44553 Reviewed-by: Alex Blasche Reviewed-by: Topi Reiniƶ --- src/tools/qdoc/cppcodeparser.cpp | 85 +++++++++++++++++++++++++++++----------- src/tools/qdoc/cppcodeparser.h | 2 +- src/tools/qdoc/node.cpp | 11 ++++++ src/tools/qdoc/node.h | 16 ++++++++ src/tools/qdoc/qdocdatabase.cpp | 1 + src/tools/qdoc/tree.cpp | 26 +++++++++++- src/tools/qdoc/tree.h | 1 + 7 files changed, 118 insertions(+), 24 deletions(-) (limited to 'src/tools') diff --git a/src/tools/qdoc/cppcodeparser.cpp b/src/tools/qdoc/cppcodeparser.cpp index 405be6f500..6cb86a7515 100644 --- a/src/tools/qdoc/cppcodeparser.cpp +++ b/src/tools/qdoc/cppcodeparser.cpp @@ -1692,35 +1692,76 @@ bool CppCodeParser::matchNamespaceDecl(InnerNode *parent) return matched && match(Tok_RightBrace); } -bool CppCodeParser::matchUsingDecl() +/*! + Match a C++ \c using clause. Return \c true if the match + is successful. Otherwise false. + + If the \c using clause is for a namespace, an open namespace + insertOpenNamespace(name); + if (usingNamespace) { + // 'using namespace Foo;'. + qdb_->insertOpenNamespace(name); + } + else if (parent && parent->isClass()) { + ClassNode* cn = static_cast(parent); + cn->addUnresolvedUsingClause(name); + } return true; } @@ -1961,7 +2002,7 @@ bool CppCodeParser::matchDeclList(InnerNode *parent) matchNamespaceDecl(parent); break; case Tok_using: - matchUsingDecl(); + matchUsingDecl(parent); break; case Tok_template: { @@ -2221,7 +2262,7 @@ bool CppCodeParser::matchDocsAndStuff() } } else if (tok == Tok_using) { - matchUsingDecl(); + matchUsingDecl(0); } else { QStringList parentPath; diff --git a/src/tools/qdoc/cppcodeparser.h b/src/tools/qdoc/cppcodeparser.h index dc5c8ca8bd..f9ddcab88c 100644 --- a/src/tools/qdoc/cppcodeparser.h +++ b/src/tools/qdoc/cppcodeparser.h @@ -127,7 +127,7 @@ protected: bool matchClassDecl(InnerNode *parent, const QString &templateStuff = QString()); bool matchNamespaceDecl(InnerNode *parent); - bool matchUsingDecl(); + bool matchUsingDecl(InnerNode* parent); bool matchEnumItem(InnerNode *parent, EnumNode *enume); bool matchEnumDecl(InnerNode *parent); bool matchTypedefDecl(InnerNode *parent); diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index f65b4ec942..e14ca4af3e 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -1414,6 +1414,17 @@ void ClassNode::addUnresolvedBaseClass(Access access, bases_.append(RelatedClass(access, path, signature)); } +/*! + Add an unresolved \c using clause to this class node's list + of \c using clauses. The unresolved \c using clause will be + resolved before the generate phase of qdoc. In an unresolved + \c using clause, the pointer to the function node is 0. + */ +void ClassNode::addUnresolvedUsingClause(const QString& signature) +{ + usingClauses_.append(UsingClause(signature)); +} + /*! */ void ClassNode::fixBaseClasses() diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index 8e25aac9c1..b6717b3b1b 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -479,6 +479,18 @@ struct RelatedClass QString signature_; }; +struct UsingClause +{ + UsingClause() { } + UsingClause(const QString& signature) : node_(0), signature_(signature) { } + const QString& signature() const { return signature_; } + const Node* node() { return node_; } + void setNode(const Node* n) { node_ = n; } + + const Node* node_; + QString signature_; +}; + class ClassNode : public InnerNode { public: @@ -493,16 +505,19 @@ public: void addResolvedBaseClass(Access access, ClassNode* node); void addDerivedClass(Access access, ClassNode* node); void addUnresolvedBaseClass(Access access, const QStringList& path, const QString& signature); + void addUnresolvedUsingClause(const QString& signature); void fixBaseClasses(); void fixPropertyUsingBaseClasses(PropertyNode* pn); QList& baseClasses() { return bases_; } QList& derivedClasses() { return derived_; } QList& ignoredBaseClasses() { return ignoredBases_; } + QList& usingClauses() { return usingClauses_; } const QList &baseClasses() const { return bases_; } const QList &derivedClasses() const { return derived_; } const QList &ignoredBaseClasses() const { return ignoredBases_; } + const QList& usingClauses() const { return usingClauses_; } QString serviceName() const { return sname; } void setServiceName(const QString& value) { sname = value; } @@ -517,6 +532,7 @@ private: QList bases_; QList derived_; QList ignoredBases_; + QList usingClauses_; bool abstract_; bool wrapper_; QString sname; diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index 2f7c2eaeb7..75295613f7 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -1331,6 +1331,7 @@ void QDocDatabase::resolveStuff() resolveQmlInheritance(primaryTreeRoot()); //primaryTree()->resolveTargets(primaryTreeRoot()); primaryTree()->resolveCppToQmlLinks(); + primaryTree()->resolveUsingClauses(); } /*! diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index ed4b4e3979..6393ad4e6f 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -498,6 +498,29 @@ void Tree::resolveCppToQmlLinks() } } +/*! + For each C++ class node, resolve any \c using clauses + that appeared in the class declaration. + */ +void Tree::resolveUsingClauses() +{ + foreach (Node* child, root_.childNodes()) { + if (child->isClass()) { + ClassNode* cn = static_cast(child); + QList& usingClauses = cn->usingClauses(); + QList::iterator uc = usingClauses.begin(); + while (uc != usingClauses.end()) { + if (!(*uc).node()) { + const Node* n = qdb_->findFunctionNode((*uc).signature(), cn, Node::CPP); + if (n) + (*uc).setNode(n); + } + ++uc; + } + } + } +} + /*! */ void Tree::fixInheritance(NamespaceNode* rootNode) @@ -1427,7 +1450,8 @@ void Tree::insertQmlType(const QString& key, QmlTypeNode* n) const Node* Tree::findFunctionNode(const QString& target, const Node* relative, Node::Genus genus) { QString t = target; - t.chop(2); + if (t.endsWith("()")) + t.chop(2); QStringList path = t.split("::"); const FunctionNode* fn = findFunctionNode(path, relative, SearchBaseClasses, genus); if (fn && fn->metaness() != FunctionNode::MacroWithoutParams) diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h index 2e6f766770..67063ef86a 100644 --- a/src/tools/qdoc/tree.h +++ b/src/tools/qdoc/tree.h @@ -153,6 +153,7 @@ class Tree void resolveInheritanceHelper(int pass, ClassNode* cn); void resolveProperties(); void resolveCppToQmlLinks(); + void resolveUsingClauses(); void fixInheritance(NamespaceNode *rootNode = 0); NamespaceNode *root() { return &root_; } -- cgit v1.2.3 From 01d5c773894604fa840247f23a3388cb5cf599d9 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Mon, 9 Feb 2015 14:01:14 +0100 Subject: qdoc: Fix generation of Doxygen tag files This change addresses two issues that affect generating .tags files in QDoc: - Paths written to the .tags file are now always relative to output subdirectory, i.e. the same folder where the file is created in. - Opening the file for writing no longer fails silently. If the 'tagfile' configuration variable specifies no path, or the path does not exist, the file is written to the output directory by default. Change-Id: I23ac529dbfa67575ae96da1c3123ea487cd664f5 Task-number: QTBUG-44366 (cherry picked from commit 5113fdeb7d50979c6acbe2c9c5b26ad134614873) Reviewed-by: Martin Smith --- src/tools/qdoc/qdoctagfiles.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src/tools') diff --git a/src/tools/qdoc/qdoctagfiles.cpp b/src/tools/qdoc/qdoctagfiles.cpp index 199c83174e..778df33048 100644 --- a/src/tools/qdoc/qdoctagfiles.cpp +++ b/src/tools/qdoc/qdoctagfiles.cpp @@ -149,7 +149,7 @@ void QDocTagFiles::generateTagFileCompounds(QXmlStreamWriter& writer, const Inne if (node->type() == Node::Class) { writer.writeTextElement("name", node->fullDocumentName()); - writer.writeTextElement("filename", gen_->fullDocumentLocation(node,Generator::useOutputSubdirs())); + writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false)); // Classes contain information about their base classes. const ClassNode* classNode = static_cast(node); @@ -169,7 +169,7 @@ void QDocTagFiles::generateTagFileCompounds(QXmlStreamWriter& writer, const Inne } else { writer.writeTextElement("name", node->fullDocumentName()); - writer.writeTextElement("filename", gen_->fullDocumentLocation(node,Generator::useOutputSubdirs())); + writer.writeTextElement("filename", gen_->fullDocumentLocation(node, false)); // Recurse to write all members. generateTagFileMembers(writer, static_cast(node)); @@ -285,7 +285,7 @@ void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const InnerN writer.writeTextElement("type", "virtual " + functionNode->returnType()); writer.writeTextElement("name", objName); - QStringList pieces = gen_->fullDocumentLocation(node,Generator::useOutputSubdirs()).split(QLatin1Char('#')); + QStringList pieces = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#')); writer.writeTextElement("anchorfile", pieces[0]); writer.writeTextElement("anchor", pieces[1]); QString signature = functionNode->signature(); @@ -303,7 +303,7 @@ void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const InnerN const PropertyNode* propertyNode = static_cast(node); writer.writeAttribute("type", propertyNode->dataType()); writer.writeTextElement("name", objName); - QStringList pieces = gen_->fullDocumentLocation(node,Generator::useOutputSubdirs()).split(QLatin1Char('#')); + QStringList pieces = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#')); writer.writeTextElement("anchorfile", pieces[0]); writer.writeTextElement("anchor", pieces[1]); writer.writeTextElement("arglist", QString()); @@ -314,7 +314,7 @@ void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const InnerN { const EnumNode* enumNode = static_cast(node); writer.writeTextElement("name", objName); - QStringList pieces = gen_->fullDocumentLocation(node).split(QLatin1Char('#')); + QStringList pieces = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#')); writer.writeTextElement("anchor", pieces[1]); writer.writeTextElement("arglist", QString()); writer.writeEndElement(); // member @@ -337,7 +337,7 @@ void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const InnerN else writer.writeAttribute("type", QString()); writer.writeTextElement("name", objName); - QStringList pieces = gen_->fullDocumentLocation(node,Generator::useOutputSubdirs()).split(QLatin1Char('#')); + QStringList pieces = gen_->fullDocumentLocation(node, false).split(QLatin1Char('#')); writer.writeTextElement("anchorfile", pieces[0]); writer.writeTextElement("anchor", pieces[1]); writer.writeTextElement("arglist", QString()); @@ -358,8 +358,19 @@ void QDocTagFiles::generateTagFileMembers(QXmlStreamWriter& writer, const InnerN void QDocTagFiles::generateTagFile(const QString& fileName, Generator* g) { QFile file(fileName); - if (!file.open(QFile::WriteOnly | QFile::Text)) - return ; + QFileInfo fileInfo(fileName); + + // If no path was specified or it doesn't exist, + // default to the output directory + if (fileInfo.fileName() == fileName || !fileInfo.dir().exists()) + file.setFileName(gen_->outputDir() + QLatin1Char('/') + + fileInfo.fileName()); + + if (!file.open(QFile::WriteOnly | QFile::Text)) { + Location::null.warning( + QString("Failed to open %1 for writing.").arg(file.fileName())); + return; + } gen_ = g; QXmlStreamWriter writer(&file); -- cgit v1.2.3