diff options
author | Topi Reinio <topi.reinio@qt.io> | 2020-09-17 15:00:04 +0200 |
---|---|---|
committer | Topi Reinio <topi.reinio@qt.io> | 2020-09-18 09:23:42 +0200 |
commit | 35d963c599471e7c5bb11d06850be7d05830e7cd (patch) | |
tree | bfc7346697664610ccef5f4bb3ca6a6c84176dd4 | |
parent | d99f7d0be90dd9b10efeaa7604d861f0253edc9c (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>
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 ¶meters = 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...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 <Space></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; |