qdoc: Improve resolving related non-members and their overload numbers

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>
This commit is contained in:
Topi Reinio 2015-08-12 11:00:19 +02:00 committed by Topi Reiniö
parent 53762b102b
commit d558100aa7
6 changed files with 112 additions and 33 deletions

View 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));
}

View 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);
}
}
}
/*!

View 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;

View 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();
}
/*!

View 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();
}

View 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);