diff options
-rw-r--r-- | sources/shiboken6/ApiExtractor/doxygenparser.cpp | 2 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/messages.cpp | 25 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/messages.h | 9 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/qtdocparser.cpp | 175 |
4 files changed, 154 insertions, 57 deletions
diff --git a/sources/shiboken6/ApiExtractor/doxygenparser.cpp b/sources/shiboken6/ApiExtractor/doxygenparser.cpp index ed589dd09..0a1703d31 100644 --- a/sources/shiboken6/ApiExtractor/doxygenparser.cpp +++ b/sources/shiboken6/ApiExtractor/doxygenparser.cpp @@ -172,7 +172,7 @@ void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass) QString doc = getDocumentation(xquery, funcQuery, DocModificationList()); if (doc.isEmpty()) { qCWarning(lcShibokenDoc, "%s", - qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, func.data(), + qPrintable(msgCannotFindDocumentation(doxyFilePath, func.data(), funcQuery))); } else { funcDoc.setValue(doc, tag.first); diff --git a/sources/shiboken6/ApiExtractor/messages.cpp b/sources/shiboken6/ApiExtractor/messages.cpp index d53c27fbb..920c5690b 100644 --- a/sources/shiboken6/ApiExtractor/messages.cpp +++ b/sources/shiboken6/ApiExtractor/messages.cpp @@ -540,14 +540,31 @@ QString msgCannotFindDocumentation(const QString &fileName, return result; } +QString msgFallbackForDocumentation(const QString &fileName, + const char *what, const QString &name, + const QString &query) +{ + QString result; + QTextStream(&result) << "Fallback used while trying to find documentation for " << what + << ' ' << name << " in:\n " << QDir::toNativeSeparators(fileName) + << "\n using query:\n " << query; + return result; +} + QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, const AbstractMetaFunction *function, const QString &query) { - const QString name = metaClass->name() + QLatin1String("::") - + function->minimalSignature(); - return msgCannotFindDocumentation(fileName, "function", name, query); + return msgCannotFindDocumentation(fileName, "function", + function->classQualifiedSignature(), query); +} + +QString msgFallbackForDocumentation(const QString &fileName, + const AbstractMetaFunction *function, + const QString &query) +{ + return msgFallbackForDocumentation(fileName, "function", + function->classQualifiedSignature(), query); } QString msgCannotFindDocumentation(const QString &fileName, diff --git a/sources/shiboken6/ApiExtractor/messages.h b/sources/shiboken6/ApiExtractor/messages.h index 008556f2b..b66221792 100644 --- a/sources/shiboken6/ApiExtractor/messages.h +++ b/sources/shiboken6/ApiExtractor/messages.h @@ -169,11 +169,18 @@ QString msgCannotFindDocumentation(const QString &fileName, const char *what, const QString &name, const QString &query); +QString msgFallbackForDocumentation(const QString &fileName, + const char *what, const QString &name, + const QString &query); + QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, const AbstractMetaFunction *function, const QString &query); +QString msgFallbackForDocumentation(const QString &fileName, + const AbstractMetaFunction *function, + const QString &query); + QString msgCannotFindDocumentation(const QString &fileName, const AbstractMetaClass *metaClass, const AbstractMetaEnum &e, diff --git a/sources/shiboken6/ApiExtractor/qtdocparser.cpp b/sources/shiboken6/ApiExtractor/qtdocparser.cpp index c2fe4841e..fe9609b73 100644 --- a/sources/shiboken6/ApiExtractor/qtdocparser.cpp +++ b/sources/shiboken6/ApiExtractor/qtdocparser.cpp @@ -54,6 +54,30 @@ Documentation QtDocParser::retrieveModuleDocumentation() return retrieveModuleDocumentation(packageName()); } +enum FunctionMatchFlags +{ + MatchArgumentCount = 0x1, + MatchArgumentType = 0x2, + MatchArgumentFuzzyType = 0x4, // Match a "const &" using contains() + DescriptionOnly = 0x8 +}; + +static void formatPreQualifications(QTextStream &str, const AbstractMetaType &type) +{ + if (type.isConstant()) + str << "const " ; +} + +static void formatPostQualifications(QTextStream &str, const AbstractMetaType &type) +{ + if (type.referenceType() == LValueReference) + str << " &"; + else if (type.referenceType() == RValueReference) + str << " &&"; + else if (type.indirections()) + str << ' ' << QByteArray(type.indirections(), '*'); +} + static void formatFunctionUnqualifiedArgTypeQuery(QTextStream &str, const AbstractMetaType &metaType) { @@ -81,7 +105,10 @@ static void formatFunctionUnqualifiedArgTypeQuery(QTextStream &str, for (int i = 0, size = instantiations.size(); i < size; ++i) { if (i) str << ", "; - str << instantiations.at(i).typeEntry()->qualifiedCppName(); + const auto &instantiation = instantiations.at(i); + formatPreQualifications(str, instantiation); + str << instantiation.typeEntry()->qualifiedCppName(); + formatPostQualifications(str, instantiation); } str << '>'; } @@ -92,52 +119,45 @@ static void formatFunctionUnqualifiedArgTypeQuery(QTextStream &str, } } -static void formatFunctionArgTypeQuery(QTextStream &str, const AbstractMetaType &metaType) +static inline void formatFunctionArgTypeQuery(QTextStream &str, const AbstractMetaType &metaType) { - if (metaType.isConstant()) - str << "const " ; - + formatPreQualifications(str, metaType); formatFunctionUnqualifiedArgTypeQuery(str, metaType); - - if (metaType.referenceType() == LValueReference) - str << " &"; - else if (metaType.referenceType() == RValueReference) - str << " &&"; - else if (metaType.indirections()) - str << ' ' << QByteArray(metaType.indirections(), '*'); + formatPostQualifications(str, metaType); } static void formatFunctionArgTypeQuery(QTextStream &str, qsizetype n, - const AbstractMetaArgument &arg) + const AbstractMetaType &metaType) { // Fixme: Use arguments.at(i)->type()->originalTypeDescription() // instead to get unresolved typedefs? - const AbstractMetaType &metaType = arg.type(); - str << "/parameter[" << (n + 1) << "]["; - - // If there is any qualifier like '*', '&', we search by the type as a - // contained word to avoid space mismatches and apparently an issue in - // libxml/xslt that does not match '&' in attributes. - // This should be "matches(type, "^(.*\W)?<type>(\W.*)?$")"), but - // libxslt only supports XPath 1.0. Also note, "\b" is not supported - if (metaType.referenceType() != NoReference || metaType.indirections() != 0) { - str << "contains(@type, \""; - formatFunctionUnqualifiedArgTypeQuery(str, metaType); - str << " \")"; // ending with space - } else { - str << "@type=\""; - formatFunctionArgTypeQuery(str, metaType); - str << "\""; - } - str << "]/.."; + str << "/parameter[" << (n + 1) << "][@type=\""; + formatFunctionArgTypeQuery(str, metaType); + str << "\"]/.."; } -enum FunctionMatchFlags +// If there is any qualifier like '*', '&', we search by the type as a +// contained word to avoid space mismatches and apparently an issue in +// libxml/xslt that does not match '&' in attributes. +// This should be "matches(type, "^(.*\W)?<type>(\W.*)?$")"), but +// libxslt only supports XPath 1.0. Also note, "\b" is not supported +static void formatFunctionFuzzyArgTypeQuery(QTextStream &str, qsizetype n, + const AbstractMetaType &metaType) { - MatchArgumentCount = 0x1, - MatchArgumentType = 0x2, - DescriptionOnly = 0x4 -}; + str << "/parameter[" << (n + 1) << "][contains(@type, \""; + formatFunctionUnqualifiedArgTypeQuery(str, metaType); + str << " \")]/.."; // ending with space +} + +static bool tryFuzzyMatching(const AbstractMetaType &metaType) +{ + return metaType.referenceType() != NoReference || metaType.indirections() != 0; +} + +static bool tryFuzzyArgumentMatching(const AbstractMetaArgument &arg) +{ + return tryFuzzyMatching(arg.type()); +} static QString functionXQuery(const QString &classQuery, const AbstractMetaFunctionCPtr &func, @@ -152,9 +172,15 @@ static QString functionXQuery(const QString &classQuery, if (matchFlags & MatchArgumentCount) str << " and count(parameter)=" << arguments.size(); str << ']'; - if (!arguments.isEmpty() && (matchFlags & MatchArgumentType)) { - for (qsizetype i = 0, size = arguments.size(); i < size; ++i) - formatFunctionArgTypeQuery(str, i, arguments.at(i)); + if (!arguments.isEmpty() + && (matchFlags & (MatchArgumentType | MatchArgumentFuzzyType)) != 0) { + for (qsizetype i = 0, size = arguments.size(); i < size; ++i) { + const auto &type = arguments.at(i).type(); + if ((matchFlags & MatchArgumentFuzzyType) != 0 && tryFuzzyMatching(type)) + formatFunctionFuzzyArgTypeQuery(str, i, type); + else + formatFunctionArgTypeQuery(str, i, type); + } } if (matchFlags & DescriptionOnly) str << "/description"; @@ -178,19 +204,32 @@ static QStringList signaturesFromWebXml(QString w) return result; } -static QString msgArgumentCountMatch(const AbstractMetaFunction *func, - const QStringList &matches) +static QString msgArgumentMatch(const QString &query, const QStringList &matches) { QString result; QTextStream str(&result); - str << "\n Note: Querying for the argument count==" - << func->arguments().size() << " only yields " << matches.size() - << " matches"; + str << "\n Note: Querying for " << query << " yields "; + if (matches.isEmpty()) + str << "no"; + else + str << matches.size(); + str << " matches"; if (!matches.isEmpty()) str << ": \"" << matches.join(QLatin1String("\", \"")) << '"'; return result; } +static inline QString msgArgumentFuzzyTypeMatch(const QStringList &matches) +{ + return msgArgumentMatch(u"arguments using fuzzy types"_qs, matches); +} + +static inline QString msgArgumentCountMatch(const AbstractMetaArgumentList &args, + const QStringList &matches) +{ + return msgArgumentMatch(u"the argument count=="_qs + QString::number(args.size()), matches); +} + QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, const AbstractMetaClass* metaClass, const QString &classQuery, @@ -199,6 +238,8 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, const XQueryPtr &xquery, QString *errorMessage) { + errorMessage->clear(); + DocModificationList funcModifs; for (const DocModification &funcModif : signedModifs) { if (funcModif.signature() == func->minimalSignature()) @@ -213,8 +254,7 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, const QString properyDocumentation = getDocumentation(xquery, propertyQuery, funcModifs); if (properyDocumentation.isEmpty()) { *errorMessage = - msgCannotFindDocumentation(sourceFileName, metaClass, func.data(), - propertyQuery); + msgCannotFindDocumentation(sourceFileName, func.data(), propertyQuery); } return properyDocumentation; } @@ -224,11 +264,41 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, const QString result = getDocumentation(xquery, fullQuery, funcModifs); if (!result.isEmpty()) return result; - *errorMessage = msgCannotFindDocumentation(sourceFileName, metaClass, func.data(), fullQuery); - if (func->arguments().isEmpty()) // No arguments, can't be helped + const auto &arguments = func->arguments(); + if (arguments.isEmpty()) { // No arguments, can't be helped + *errorMessage = msgCannotFindDocumentation(sourceFileName, func.data(), fullQuery); return result; - // Test whether some mismatch in argument types occurred by checking for - // the argument count only. Include the outer <function> element. + } + + // If there are any "const &" or similar parameters, try fuzzy matching. + // Include the outer <function> element for checking. + if (std::any_of(arguments.cbegin(), arguments.cend(), tryFuzzyArgumentMatching)) { + const unsigned flags = MatchArgumentCount | MatchArgumentFuzzyType; + QString fuzzyArgumentQuery = functionXQuery(classQuery, func, flags); + QStringList signatures = + signaturesFromWebXml(getDocumentation(xquery, fuzzyArgumentQuery, funcModifs)); + if (signatures.size() == 1) { + // One match was found. Repeat the query restricted to the <description> + // element and use the result with a warning. + errorMessage->prepend(msgFallbackForDocumentation(sourceFileName, func.data(), + fullQuery)); + errorMessage->append(u"\n Falling back to \""_qs + signatures.constFirst() + + u"\" obtained by matching fuzzy argument types."_qs); + fuzzyArgumentQuery = functionXQuery(classQuery, func, flags | DescriptionOnly); + return getDocumentation(xquery, fuzzyArgumentQuery, funcModifs); + } + + *errorMessage += msgArgumentFuzzyTypeMatch(signatures); + + if (signatures.size() > 1) { // Ambiguous, no point in trying argument count + errorMessage->prepend(msgCannotFindDocumentation(sourceFileName, func.data(), + fullQuery)); + return result; + } + } + + // Finally, test whether some mismatch in argument types occurred by checking for + // the argument count only. QString countOnlyQuery = functionXQuery(classQuery, func, MatchArgumentCount); QStringList signatures = signaturesFromWebXml(getDocumentation(xquery, countOnlyQuery, funcModifs)); @@ -236,11 +306,14 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, // One match was found. Repeat the query restricted to the <description> // element and use the result with a warning. countOnlyQuery = functionXQuery(classQuery, func, MatchArgumentCount | DescriptionOnly); + errorMessage->prepend(msgFallbackForDocumentation(sourceFileName, func.data(), fullQuery)); errorMessage->append(QLatin1String("\n Falling back to \"") + signatures.constFirst() + QLatin1String("\" obtained by matching the argument count only.")); return getDocumentation(xquery, countOnlyQuery, funcModifs); } - *errorMessage += msgArgumentCountMatch(func.data(), signatures); + + errorMessage->prepend(msgCannotFindDocumentation(sourceFileName, func.data(), fullQuery)); + *errorMessage += msgArgumentCountMatch(arguments, signatures); return result; } |