summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTopi Reinio <topi.reinio@qt.io>2020-09-17 15:00:04 +0200
committerTopi Reinio <topi.reinio@qt.io>2020-09-18 09:23:42 +0200
commit35d963c599471e7c5bb11d06850be7d05830e7cd (patch)
treebfc7346697664610ccef5f4bb3ca6a6c84176dd4
parentd99f7d0be90dd9b10efeaa7604d861f0253edc9c (diff)
qdoc: ClangCodeParser: Improve function argument matching
In certain cases, Clang returns a parameter type for a function argument that differs in the header declation and source definition. This can happen if the type has an alias in a different namespace, both namespaces are visible to the source file, and both the type and the alias are included in the pre-compiled header. Resolve these cases by recording the canonical spelling of the header type into Parameter, and comparing it to the canonical spelling from the source file. Remove findFunctionNodeForCursor() as redundant. This makes the fix apply both for /*! */ comments preceding function bodies, as well as separate \fn commands. Fixes: QTBUG-86665 Change-Id: I47566f90adb956095537115ff8e8fcd9c0adffbe Reviewed-by: Paul Wicking <paul.wicking@qt.io>
-rw-r--r--src/qdoc/clangcodeparser.cpp111
-rw-r--r--src/qdoc/parameters.h5
-rw-r--r--tests/auto/qdoc/generatedoutput/expected_output/space.html45
-rw-r--r--tests/auto/qdoc/generatedoutput/testdata/configs/usingdirective.qdocconf3
-rw-r--r--tests/auto/qdoc/generatedoutput/testdata/usingdirective/UsingDirective2
-rw-r--r--tests/auto/qdoc/generatedoutput/testdata/usingdirective/alias.h7
-rw-r--r--tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.cpp18
-rw-r--r--tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.h7
-rw-r--r--tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp6
9 files changed, 119 insertions, 85 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp
index 3f3b88bdb..9f9dba7e0 100644
--- a/src/qdoc/clangcodeparser.cpp
+++ b/src/qdoc/clangcodeparser.cpp
@@ -339,19 +339,29 @@ static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur)
continue;
bool different = false;
for (int i = 0; i < actualArg; ++i) {
+ CXType argType = clang_getArgType(funcType, i);
if (args.size() <= i)
- args.append(fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i))));
- QString t1 = parameters.at(i).type();
- QString t2 = args.at(i);
- auto p2 = parent;
- while (p2 && t1 != t2) {
- QString parentScope = p2->name() + QLatin1String("::");
- t1 = t1.remove(parentScope);
- t2 = t2.remove(parentScope);
- p2 = p2->parent();
+ args.append(fromCXString(clang_getTypeSpelling(argType)));
+ QString recordedType = parameters.at(i).type();
+ QString typeSpelling = args.at(i);
+ auto p = parent;
+ while (p && recordedType != typeSpelling) {
+ QString parentScope = p->name() + QLatin1String("::");
+ recordedType.remove(parentScope);
+ typeSpelling.remove(parentScope);
+ p = p->parent();
}
- if (t1 != t2) {
- different = true;
+ different = recordedType != typeSpelling;
+
+ // Retry with a canonical type spelling
+ if (different && (argType.kind == CXType_Typedef || argType.kind == CXType_Elaborated)) {
+ QStringView canonicalType = parameters.at(i).canonicalType();
+ if (!canonicalType.isEmpty()) {
+ different = canonicalType !=
+ fromCXString(clang_getTypeSpelling(clang_getCanonicalType(argType)));
+ }
+ }
+ if (different) {
break;
}
}
@@ -372,79 +382,6 @@ static Node *findNodeForCursor(QDocDatabase *qdb, CXCursor cur)
}
}
-/*!
- Find the function node from the QDocDatabase \a qdb that
- corrseponds to the declaration represented by the cursor
- \a cur, if it exists.
- */
-static Node *findFunctionNodeForCursor(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 == nullptr || !p->isAggregate())
- return nullptr;
- auto parent = static_cast<Aggregate *>(p);
-
- switch (kind) {
- case CXCursor_FunctionDecl:
- case CXCursor_FunctionTemplate:
- case CXCursor_CXXMethod:
- case CXCursor_Constructor:
- case CXCursor_Destructor:
- case CXCursor_ConversionFunction: {
- NodeVector 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(Node::CPP))
- continue;
- auto fn = static_cast<FunctionNode *>(candidate);
- const Parameters &parameters = fn->parameters();
- if (parameters.count() != (numArg + isVariadic))
- continue;
- if (fn->isConst() != bool(clang_CXXMethod_isConst(cur)))
- continue;
- if (isVariadic && parameters.last().type() != QLatin1String("..."))
- continue;
- bool different = false;
- for (int i = 0; i < numArg; ++i) {
- if (args.size() <= i)
- args.append(fromCXString(clang_getTypeSpelling(clang_getArgType(funcType, i))));
- QString t1 = parameters.at(i).type();
- QString t2 = args.at(i);
- auto p2 = parent;
- while (p2 && t1 != t2) {
- QString parentScope = p2->name() + QLatin1String("::");
- t1 = t1.remove(parentScope);
- t2 = t2.remove(parentScope);
- p2 = p2->parent();
- }
- if (t1 != t2) {
- different = true;
- break;
- }
- }
- if (!different)
- return fn;
- }
- break;
- }
- default:
- break;
- }
- return nullptr;
-}
-
class ClangVisitor
{
public:
@@ -619,7 +556,7 @@ CXChildVisitResult ClangVisitor::visitFnSignature(CXCursor cursor, CXSourceLocat
*fnNode = nullptr;
ignoreSignature = true;
} else {
- *fnNode = findFunctionNodeForCursor(qdb_, cursor);
+ *fnNode = findNodeForCursor(qdb_, cursor);
if (*fnNode && (*fnNode)->isFunction(Node::CPP)) {
FunctionNode *fn = static_cast<FunctionNode *>(*fnNode);
readParameterNamesAndAttributes(fn, cursor);
@@ -825,6 +762,10 @@ CXChildVisitResult ClangVisitor::visitHeader(CXCursor cursor, CXSourceLocation l
fn->setMetaness(FunctionNode::CAssign);
}
parameters.append(adjustTypeName(fromCXString(clang_getTypeSpelling(argType))));
+ if (argType.kind == CXType_Typedef || argType.kind == CXType_Elaborated) {
+ parameters.last().setCanonicalType(fromCXString(
+ clang_getTypeSpelling(clang_getCanonicalType(argType))));
+ }
}
if (parameters.count() > 0) {
if (parameters.last().type().endsWith(QLatin1String("::QPrivateSignal"))) {
diff --git a/src/qdoc/parameters.h b/src/qdoc/parameters.h
index f7999ecae..effb49467 100644
--- a/src/qdoc/parameters.h
+++ b/src/qdoc/parameters.h
@@ -65,7 +65,11 @@ public:
QString signature(bool includeValue = false) const;
+ const QString &canonicalType() const { return canonicalType_; }
+ void setCanonicalType(const QString &t) { canonicalType_ = t; }
+
public:
+ QString canonicalType_ {};
QString type_;
QString name_;
QString defaultValue_;
@@ -92,6 +96,7 @@ public:
int count() const { return parameters_.size(); }
void reserve(int count) { parameters_.reserve(count); }
const Parameter &at(int i) const { return parameters_.at(i); }
+ Parameter &last() { return parameters_.last(); }
const Parameter &last() const { return parameters_.last(); }
inline Parameter &operator[](int index) { return parameters_[index]; }
void append(const QString &type, const QString &name, const QString &value);
diff --git a/tests/auto/qdoc/generatedoutput/expected_output/space.html b/tests/auto/qdoc/generatedoutput/expected_output/space.html
new file mode 100644
index 000000000..b40932b2f
--- /dev/null
+++ b/tests/auto/qdoc/generatedoutput/expected_output/space.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+<!-- space.cpp -->
+ <title>Space Namespace | UsingDirective</title>
+</head>
+<body>
+<div class="sidebar">
+<div class="toc">
+<h3><a name="toc">Contents</a></h3>
+<ul>
+<li class="level1"><a href="#functions">Functions</a></li>
+<li class="level1"><a href="#details">Detailed Description</a></li>
+</ul>
+</div>
+<div class="sidebar-content" id="sidebar-content"></div></div>
+<h1 class="title">Space Namespace</h1>
+<!-- $$$Space-brief -->
+<p>A namespace..&#x2e;in space. <a href="#details">More...</a></p>
+<!-- @@@Space -->
+<div class="table"><table class="alignedsummary">
+<tr><td class="memItemLeft rightAlign topAlign"> Header:</td><td class="memItemRight bottomAlign"> <span class="preprocessor">#include &lt;Space&gt;</span>
+</td></tr></table></div><ul>
+</ul>
+<a name="functions"></a>
+<h2 id="functions">Functions</h2>
+<div class="table"><table class="alignedsummary">
+<tr><td class="memItemLeft rightAlign topAlign"> void </td><td class="memItemRight bottomAlign"><b><a href="space.html#spaceFun">spaceFun</a></b>(Space::spacename <i>space</i>)</td></tr>
+</table></div>
+<a name="details"></a>
+<!-- $$$Space-description -->
+<div class="descr">
+<h2 id="details">Detailed Description</h2>
+</div>
+<!-- @@@Space -->
+<div class="func">
+<h2>Function Documentation</h2>
+<!-- $$$spaceFun[overload1]$$$spaceFunSpace::spacename -->
+<h3 class="fn" id="spaceFun"><a name="spaceFun"></a><span class="type">void</span> <span class="name">spaceFun</span>(<span class="type">Space::spacename</span> <i>space</i>)</h3>
+<p>A <i>space</i> function.</p>
+<!-- @@@spaceFun -->
+</div>
+</body>
+</html>
diff --git a/tests/auto/qdoc/generatedoutput/testdata/configs/usingdirective.qdocconf b/tests/auto/qdoc/generatedoutput/testdata/configs/usingdirective.qdocconf
new file mode 100644
index 000000000..d5a925b58
--- /dev/null
+++ b/tests/auto/qdoc/generatedoutput/testdata/configs/usingdirective.qdocconf
@@ -0,0 +1,3 @@
+include(config.qdocconf)
+project = UsingDirective
+{includepaths,headerdirs,sourcedirs} = ../usingdirective
diff --git a/tests/auto/qdoc/generatedoutput/testdata/usingdirective/UsingDirective b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/UsingDirective
new file mode 100644
index 000000000..422d01e91
--- /dev/null
+++ b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/UsingDirective
@@ -0,0 +1,2 @@
+#include "alias.h"
+#include "space.h"
diff --git a/tests/auto/qdoc/generatedoutput/testdata/usingdirective/alias.h b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/alias.h
new file mode 100644
index 000000000..1fb9ee471
--- /dev/null
+++ b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/alias.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "space.h"
+
+namespace Alias {
+ using spacename = Space::spacename;
+}
diff --git a/tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.cpp b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.cpp
new file mode 100644
index 000000000..4a4962550
--- /dev/null
+++ b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.cpp
@@ -0,0 +1,18 @@
+#include "space.h"
+
+/*!
+ \namespace Space
+ \inmodule UsingDirective
+ \brief A namespace...in space.
+*/
+
+using namespace Alias;
+using namespace Space;
+
+/*!
+ \relates Space
+ A \a space function.
+*/
+void spaceFun(spacename space)
+{
+}
diff --git a/tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.h b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.h
new file mode 100644
index 000000000..9ac01fba6
--- /dev/null
+++ b/tests/auto/qdoc/generatedoutput/testdata/usingdirective/space.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Space {
+ typedef int spacename;
+}
+
+void spaceFun(Space::spacename space);
diff --git a/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp b/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp
index 4b83fec64..d95d9b975 100644
--- a/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp
+++ b/tests/auto/qdoc/generatedoutput/tst_generatedoutput.cpp
@@ -74,6 +74,7 @@ private slots:
void noAutoList();
void nestedMacro();
void headerFile();
+ void usingDirective();
private:
QScopedPointer<QTemporaryDir> m_outputDir;
@@ -453,6 +454,11 @@ void tst_generatedOutput::headerFile()
"headerfile-docbook/headers.xml");
}
+void tst_generatedOutput::usingDirective()
+{
+ testAndCompare("testdata/configs/usingdirective.qdocconf", "space.html");
+}
+
int main(int argc, char *argv[])
{
tst_generatedOutput tc;