qdoc: Improve resolving related non-members and their overload numbers
authorTopi Reinio <topi.reinio@digia.com>
Wed, 12 Aug 2015 09:00:19 +0000 (11:00 +0200)
committerTopi Reiniö <topi.reinio@digia.com>
Wed, 19 Aug 2015 11:30:23 +0000 (11:30 +0000)
There were several problems related to resolving related non-member
(RNM) functions for classes. This commit does the following changes:

  - Overload numbers for RNMs are now calculated at the time the
    \relates command is processed, instead of a separate step.
  - If a \relates refers to an entity outside the module boundary,
    write the argument passed to it as-is into the index file.
  - Delay the destruction of QDocIndexFiles singleton, to resolve
    the RNMs read from the index files prior to generating docs.
  - Remove the redundant call to normalizeOverloads() for single-
    exec mode as unnecessary.

These changes ensure that all RNMs are listed in the documentation
for the node that they belong to.

A remaining issue is that if a function relates to a class outside
the module boundary, that function documentation will be empty
because the doc content is not stored into the index file (for
obvious reasons). Single-exec mode does not have this problem.

Change-Id: I33f038120728932cd9fd70da28d9090023068bd6
Task-number: QTBUG-47589
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
src/tools/qdoc/cppcodeparser.cpp
src/tools/qdoc/node.cpp
src/tools/qdoc/node.h
src/tools/qdoc/qdocdatabase.cpp
src/tools/qdoc/qdocindexfiles.cpp
src/tools/qdoc/qdocindexfiles.h

index 0f707772563b49b3b15a4b7831d8fc51c7eba063..46a62d629fd0c9457b14c782b4a6165c52ea551e 100644 (file)
@@ -924,8 +924,14 @@ void CppCodeParser::processOtherMetaCommand(const Doc& doc,
     else if (command == COMMAND_RELATES) {
         QStringList path = arg.split("::");
         Node* n = qdb_->findRelatesNode(path);
-        if (!n)
-            doc.location().warning(tr("Cannot find '%1' in '\\%2'").arg(arg).arg(COMMAND_RELATES));
+        if (!n) {
+            // Store just a string to write to the index file
+            if (Generator::preparing())
+                node->setRelates(arg);
+            else
+                doc.location().warning(tr("Cannot find '%1' in '\\%2'").arg(arg).arg(COMMAND_RELATES));
+
+        }
         else
             node->setRelates(static_cast<Aggregate*>(n));
     }
index c803acbf605bc5540b3a0d36bd2f30a72ad9eb46..c879d1d9b417431c1fe54e828376dfc88c756949 100644 (file)
@@ -106,7 +106,24 @@ Node::~Node()
 {
     if (parent_)
         parent_->removeChild(this);
+
     if (relatesTo_)
+        removeRelates();
+}
+
+/*!
+    Removes this node from the aggregate's list of related
+    nodes, or if this node has created a dummy "relates"
+    aggregate, deletes it.
+*/
+void Node::removeRelates()
+{
+    if (!relatesTo_)
+        return;
+
+    if (relatesTo_->isDocumentNode() && !relatesTo_->parent())
+        delete relatesTo_;
+    else
         relatesTo_->removeRelated(this);
 }
 
@@ -463,11 +480,19 @@ bool Node::fromFlagValue(FlagValue fv, bool defaultValue)
  */
 void Node::setRelates(Aggregate *pseudoParent)
 {
-    if (relatesTo_) {
-        relatesTo_->removeRelated(this);
-    }
+    removeRelates();
     relatesTo_ = pseudoParent;
-    pseudoParent->related_.append(this);
+    pseudoParent->addRelated(this);
+}
+
+/*!
+  Sets the (unresolved) entity \a name that this node relates to.
+ */
+void Node::setRelates(const QString& name)
+{
+    removeRelates();
+    // Create a dummy aggregate for writing the name into the index
+    relatesTo_ = new DocumentNode(0, name, Node::NoSubtype, Node::NoPageType);
 }
 
 /*!
@@ -934,8 +959,9 @@ void Aggregate::makeUndocumentedChildrenInternal()
 }
 
 /*!
-  This is where we should set the overload numbers, including
-  the related non-members.
+  This is where we set the overload numbers for function nodes.
+  \note Overload numbers for related non-members are handled
+  separately.
  */
 void Aggregate::normalizeOverloads()
 {
@@ -1006,23 +1032,6 @@ void Aggregate::normalizeOverloads()
         }
         ++p;
     }
-    /*
-      Add the related non-members here.
-     */
-    if (!related_.isEmpty()) {
-        foreach (Node* n, related_) {
-            if (n->isFunction()) {
-                FunctionNode* fn = static_cast<FunctionNode*>(n);
-                QMap<QString, Node *>::Iterator p = primaryFunctionMap_.find(fn->name());
-                if (p != primaryFunctionMap_.end()) {
-                    secondaryFunctionMap_[fn->name()].append(fn);
-                    fn->setOverloadNumber(secondaryFunctionMap_[fn->name()].size());
-                }
-                else
-                    fn->setOverloadNumber(0);
-            }
-        }
-    }
     /*
       Recursive part.
      */
@@ -1322,10 +1331,49 @@ QString Node::physicalModuleName() const
 }
 
 /*!
+  Removes a node from the list of nodes related to this one.
+  If it is a function node, also remove from the primary/
+  secondary function maps.
  */
 void Aggregate::removeRelated(Node *pseudoChild)
 {
     related_.removeAll(pseudoChild);
+
+    if (pseudoChild->isFunction()) {
+        QMap<QString, Node *>::Iterator p = primaryFunctionMap_.find(pseudoChild->name());
+        while (p != primaryFunctionMap_.end()) {
+            if (p.value() == pseudoChild) {
+                primaryFunctionMap_.erase(p);
+                break;
+            }
+            ++p;
+        }
+        NodeList& overloads = secondaryFunctionMap_[pseudoChild->name()];
+        overloads.removeAll(pseudoChild);
+    }
+}
+
+/*!
+  Adds \a pseudoChild to the list of nodes related to this one. Resolve a correct
+  overload number for a related non-member function.
+ */
+void Aggregate::addRelated(Node *pseudoChild)
+{
+    related_.append(pseudoChild);
+
+    if (pseudoChild->isFunction()) {
+        FunctionNode* fn = static_cast<FunctionNode*>(pseudoChild);
+        if (primaryFunctionMap_.contains(pseudoChild->name())) {
+            secondaryFunctionMap_[pseudoChild->name()].append(pseudoChild);
+            fn->setOverloadNumber(secondaryFunctionMap_[pseudoChild->name()].size());
+            fn->setOverloadFlag(true);
+        }
+        else {
+            primaryFunctionMap_.insert(pseudoChild->name(), pseudoChild);
+            fn->setOverloadNumber(0);
+            fn->setOverloadFlag(false);
+        }
+    }
 }
 
 /*!
index 596a71b6d5e2aee83d57a148fe61c7e45a671fa6..6262cee0abbd7915d593515430dda4c6b68ba4f6 100644 (file)
@@ -178,6 +178,7 @@ public:
     void setThreadSafeness(ThreadSafeness t) { safeness_ = (unsigned char) t; }
     void setSince(const QString &since);
     void setRelates(Aggregate* pseudoParent);
+    void setRelates(const QString &name);
     void setPhysicalModuleName(const QString &name) { physicalModuleName_ = name; }
     void setUrl(const QString& url) { url_ = url; }
     void setTemplateStuff(const QString &t) { templateStuff_ = t; }
@@ -336,6 +337,7 @@ public:
 
 protected:
     Node(NodeType type, Aggregate* parent, const QString& name);
+    void removeRelates();
 
 private:
 
@@ -421,6 +423,7 @@ private:
 
     static bool isSameSignature(const FunctionNode* f1, const FunctionNode* f2);
     void removeRelated(Node* pseudoChild);
+    void addRelated(Node* pseudoChild);
 
     QString outputFileName_;
     QStringList pageKeywds;
index 7f2e64b00c74e2b9f9caac0cb3bbf621d14ff6a1..97f1929fb4077afbd676b05a72ba748496f4874f 100644 (file)
@@ -1295,6 +1295,10 @@ void QDocDatabase::resolveIssues() {
     resolveQmlInheritance(primaryTreeRoot());
     primaryTree()->resolveTargets(primaryTreeRoot());
     primaryTree()->resolveCppToQmlLinks();
+    if (!Generator::singleExec()) {
+        QDocIndexFiles::qdocIndexFiles()->resolveRelates();
+        QDocIndexFiles::destroyQDocIndexFiles();
+    }
 }
 
 void QDocDatabase::resolveStuff()
@@ -1305,7 +1309,6 @@ void QDocDatabase::resolveStuff()
     primaryTree()->resolveCppToQmlLinks();
     primaryTree()->resolveUsingClauses();
     resolveNamespaces();
-    primaryTreeRoot()->normalizeOverloads();
 }
 
 /*!
@@ -1495,7 +1498,6 @@ void QDocDatabase::readIndexes(const QStringList& t)
             qDebug() << "This index file is already in memory:" << f;
     }
     QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles);
-    QDocIndexFiles::destroyQDocIndexFiles();
 }
 
 /*!
index 8db901bbc78c2d8d3837a21c92501ee1bc54cf36..fc262d9834615deea7ea8ae81e6075b3254d1ca9 100644 (file)
@@ -100,6 +100,7 @@ void QDocIndexFiles::destroyQDocIndexFiles()
  */
 void QDocIndexFiles::readIndexes(const QStringList& indexFiles)
 {
+    relatedList_.clear();
     foreach (const QString& indexFile, indexFiles) {
         QString msg = "Loading index file: " + indexFile;
         Location::logToStdErr(msg);
@@ -146,9 +147,7 @@ void QDocIndexFiles::readIndexFile(const QString& path)
         indexUrl = installDir.relativeFilePath(path).section('/', 0, -2);
     }
     project_ = attrs.value(QLatin1String("project")).toString();
-
     basesList_.clear();
-    relatedList_.clear();
 
     NamespaceNode* root = qdb_->newIndexTree(project_);
 
@@ -730,17 +729,37 @@ void QDocIndexFiles::resolveIndex()
                 pair.first->addUnresolvedBaseClass(Node::Public, basePath, QString());
         }
     }
+    // No longer needed.
+    basesList_.clear();
+}
+
+/*
+    Goes though the list of nodes that are related to other aggregates
+    that were read from all index files, and tries to find the aggregate
+    nodes from the database. Calls the node's setRelates() for each
+    aggregate that is found in the local module (primary tree).
+
+    This function is meant to be called before starting the doc generation,
+    after all the index files are read.
+ */
+void QDocIndexFiles::resolveRelates()
+{
+    if (relatedList_.isEmpty())
+        return;
+
+    // Restrict searching only to the local (primary) tree
+    QVector<Tree*> searchOrder = qdb_->searchOrder();
+    qdb_->setLocalSearch();
 
     QPair<FunctionNode*,QString> relatedPair;
     foreach (relatedPair, relatedList_) {
         QStringList path = relatedPair.second.split("::");
         Node* n = qdb_->findRelatesNode(path);
         if (n)
-            relatedPair.first->setRelates(static_cast<ClassNode*>(n));
+            relatedPair.first->setRelates(static_cast<Aggregate*>(n));
     }
-
-    // No longer needed.
-    basesList_.clear();
+    // Restore original search order
+    qdb_->setSearchOrder(searchOrder);
     relatedList_.clear();
 }
 
index 1b9b6afc40a0fc81de116191ee88735d5b9edac6..67a7e7226e078eb05883ed4404d1e1ffcbf38d90 100644 (file)
@@ -65,6 +65,7 @@ class QDocIndexFiles
     void readIndexFile(const QString& path);
     void readIndexSection(QXmlStreamReader &reader, Node* current, const QString& indexUrl);
     void resolveIndex();
+    void resolveRelates();
     bool generateIndexSection(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false);
     void generateIndexSections(QXmlStreamWriter& writer, Node* node, bool generateInternalNodes = false);