// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "docparser.h" #include "abstractmetaargument.h" #include "abstractmetaenum.h" #include "abstractmetafunction.h" #include "abstractmetalang.h" #include "abstractmetatype.h" #include "messages.h" #include "modifications.h" #include "reporthandler.h" #include "enumtypeentry.h" #include "complextypeentry.h" #include "xmlutils.h" #include #include #include #include #include "qtcompat.h" #include #ifdef HAVE_LIBXSLT # include # include #endif #include using namespace Qt::StringLiterals; static inline bool isXpathDocModification(const DocModification &mod) { return mod.mode() == TypeSystem::DocModificationXPathReplace; } static inline bool isNotXpathDocModification(const DocModification &mod) { return mod.mode() != TypeSystem::DocModificationXPathReplace; } static void removeXpathDocModifications(DocModificationList *l) { l->erase(std::remove_if(l->begin(), l->end(), isXpathDocModification), l->end()); } static void removeNonXpathDocModifications(DocModificationList *l) { l->erase(std::remove_if(l->begin(), l->end(), isNotXpathDocModification), l->end()); } DocParser::DocParser() = default; DocParser::~DocParser() = default; void DocParser::fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &) { } void DocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &) { } QString DocParser::getDocumentation(const XQueryPtr &xquery, const QString& query, const DocModificationList& mods) { QString doc = execXQuery(xquery, query); return applyDocModifications(mods, doc.trimmed()); } QString DocParser::execXQuery(const XQueryPtr &xquery, const QString& query) { QString errorMessage; const QString result = xquery->evaluate(query, &errorMessage); if (!errorMessage.isEmpty()) qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); return result; } static bool usesRValueReference(const AbstractMetaArgument &a) { return a.type().referenceType() == RValueReference; } bool DocParser::skipForQuery(const AbstractMetaFunctionCPtr &func) { // Skip private functions and copies created by AbstractMetaClass::fixFunctions() // Note: Functions inherited from templates will cause warnings about missing // documentation, but they should at least be listed. if (!func || func->isPrivate() || func->attributes().testFlag(AbstractMetaFunction::AddedMethod) || func->isModifiedRemoved() || func->declaringClass() != func->ownerClass() || func->isConversionOperator()) { return true; } switch (func->functionType()) { case AbstractMetaFunction::MoveConstructorFunction: case AbstractMetaFunction::AssignmentOperatorFunction: case AbstractMetaFunction::MoveAssignmentOperatorFunction: return true; default: break; } return std::any_of(func->arguments().cbegin(), func->arguments().cend(), usesRValueReference); } DocModificationList DocParser::getDocModifications(const AbstractMetaClassCPtr &cppClass) { auto result = cppClass->typeEntry()->docModifications(); removeXpathDocModifications(&result); return result; } static void filterBySignature(const AbstractMetaFunctionCPtr &func, DocModificationList *l) { if (!l->isEmpty()) { const QString minimalSignature = func->minimalSignature(); const auto filter = [&minimalSignature](const DocModification &mod) { return mod.signature() != minimalSignature; }; l->erase(std::remove_if(l->begin(), l->end(), filter), l->end()); } } DocModificationList DocParser::getDocModifications(const AbstractMetaFunctionCPtr &func, const AbstractMetaClassCPtr &cppClass) { DocModificationList result; if (func->isUserAdded()) { result = func->addedFunctionDocModifications(); removeXpathDocModifications(&result); } else if (cppClass != nullptr) { result = cppClass->typeEntry()->functionDocModifications(); removeXpathDocModifications(&result); filterBySignature(func, &result); } return result; } DocModificationList DocParser::getXpathDocModifications(const AbstractMetaClassCPtr &cppClass) { auto result = cppClass->typeEntry()->docModifications(); removeNonXpathDocModifications(&result); return result; } DocModificationList DocParser::getXpathDocModifications(const AbstractMetaFunctionCPtr &func, const AbstractMetaClassCPtr &cppClass) { DocModificationList result; if (func->isUserAdded()) { result = func->addedFunctionDocModifications(); removeNonXpathDocModifications(&result); } else if (cppClass != nullptr) { result = cppClass->typeEntry()->functionDocModifications(); removeNonXpathDocModifications(&result); filterBySignature(func, &result); } return result; } QString DocParser::enumBaseClass(const AbstractMetaEnum &e) { switch (e.typeEntry()->pythonEnumType()) { case TypeSystem::PythonEnumType::IntEnum: return u"IntEnum"_s; case TypeSystem::PythonEnumType::Flag: return u"Flag"_s; case TypeSystem::PythonEnumType::IntFlag: return u"IntFlag"_s; default: break; } return e.typeEntry()->flags() != nullptr ? u"Flag"_s : u"Enum"_s; } AbstractMetaFunctionCList DocParser::documentableFunctions(const AbstractMetaClassCPtr &metaClass) { auto result = metaClass->functionsInTargetLang(); for (auto i = result.size() - 1; i >= 0; --i) { if (DocParser::skipForQuery(result.at(i)) || result.at(i)->isUserAdded()) result.removeAt(i); } result.append(metaClass->cppSignalFunctions()); return result; } QString DocParser::applyDocModifications(const DocModificationList& xpathMods, const QString& xml) { const char xslPrefix[] = R"( )"; if (xpathMods.isEmpty() || xml.isEmpty()) return xml; QString xsl = QLatin1StringView(xslPrefix); for (const DocModification &mod : xpathMods) { Q_ASSERT(isXpathDocModification(mod)); QString xpath = mod.xpath(); xpath.replace(u'"', u"""_s); xsl += ""_L1 + mod.code() + "\n"_L1; } QString errorMessage; const QString result = xsl_transform(xml, xsl, &errorMessage); if (!errorMessage.isEmpty()) qCWarning(lcShibokenDoc, "%s", qPrintable(msgXpathDocModificationError(xpathMods, errorMessage))); if (result == xml) { const QString message = u"Query did not result in any modifications to \""_s + xml + u'"'; qCWarning(lcShibokenDoc, "%s", qPrintable(msgXpathDocModificationError(xpathMods, message))); } return result; }