diff options
| author | Friedemann Kleint <friedemann.kleint@qt.io> | 2023-05-10 13:50:56 +0200 |
|---|---|---|
| committer | Cristián Maureira-Fredes <cristian.maureira-fredes@qt.io> | 2025-11-24 12:06:06 +0100 |
| commit | 845630ad239c4b37ff37e49ef5bb969a8946744b (patch) | |
| tree | 5c65f2e270511459d346c14f0ad8d01f5e6e39a0 /sources/shiboken6/ApiExtractor/qtdocparser.cpp | |
| parent | 3cf2077a1b060bbea3419ccde23c5da6485a2e24 (diff) | |
Move the shiboken-generator source around
THIS COMMIT WAS GENERATED BY A SCRIPT
Task-number: PYSIDE-962
Task-number: PYSIDE-1587
Change-Id: I58b05c3d05606efb6303193f2d7f907a0ab5741b
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'sources/shiboken6/ApiExtractor/qtdocparser.cpp')
| -rw-r--r-- | sources/shiboken6/ApiExtractor/qtdocparser.cpp | 555 |
1 files changed, 0 insertions, 555 deletions
diff --git a/sources/shiboken6/ApiExtractor/qtdocparser.cpp b/sources/shiboken6/ApiExtractor/qtdocparser.cpp deleted file mode 100644 index 6b9f1b0e4..000000000 --- a/sources/shiboken6/ApiExtractor/qtdocparser.cpp +++ /dev/null @@ -1,555 +0,0 @@ -// 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 "qtdocparser.h" -#include "classdocumentation.h" -#include "abstractmetaargument.h" -#include "abstractmetaenum.h" -#include "abstractmetafunction.h" -#include "abstractmetalang.h" -#include "abstractmetatype.h" -#include "documentation.h" -#include "exception.h" -#include "modifications.h" -#include "messages.h" -#include "propertyspec.h" -#include "reporthandler.h" -#include "flagstypeentry.h" -#include "complextypeentry.h" -#include "functiontypeentry.h" -#include "enumtypeentry.h" -#include "typesystemtypeentry.h" -#include "typedatabase.h" - -#include "qtcompat.h" - -#include <QtCore/qdir.h> -#include <QtCore/qfile.h> -#include <QtCore/qhash.h> -#include <QtCore/qurl.h> - -#include <algorithm> -#include <iterator> -#include <optional> -#include <utility> - -using namespace Qt::StringLiterals; - -enum : std::uint8_t { debugFunctionSearch = 0 }; - -constexpr auto briefStartElement = "<brief>"_L1; -constexpr auto briefEndElement = "</brief>"_L1; -constexpr auto webxmlSuffix = ".webxml"_L1; - -// Return the package of a type "PySide6.QtGui.QPainter" -> "PySide6.QtGui" -static QStringView packageFromPythonType(QStringView pythonType) -{ - qsizetype pos = pythonType.startsWith(u"PySide6.") ? 8 : 0; - auto dot = pythonType.indexOf(u'.', pos); - return dot != -1 ? pythonType.sliced(0, dot) : pythonType; -} - -// Return the qdoc dir "PySide6.QtGui.QPainter" -> "qtgui/webxml" (QTBUG-119500) -static QString qdocModuleDirFromPackage(QStringView package) -{ - if (package.startsWith(u"PySide6.")) - package = package.sliced(8); - return package.toString().toLower() + "/webxml"_L1; -} - -// Populate a cache of package to WebXML dir -static QHash<QString, QString> getPackageToModuleDir() -{ - QHash<QString, QString> result; - const auto &typeSystemEntries = TypeDatabase::instance()->typeSystemEntries(); - for (const auto &te : typeSystemEntries) { - const QString &package = te->name(); - const QString &docPackage = te->hasDocTargetLangPackage() - ? te->docTargetLangPackage() : package; - result.insert(package, qdocModuleDirFromPackage(docPackage)); - } - return result; -} - -QString QtDocParser::qdocModuleDir(const QString &pythonType) -{ - static const QHash<QString, QString> packageToModuleDir = getPackageToModuleDir(); - - const QStringView package = packageFromPythonType(pythonType); - const auto it = packageToModuleDir.constFind(package); - if (it == packageToModuleDir.cend()) { - const QString known = packageToModuleDir.keys().join(", "_L1); - qCWarning(lcShibokenDoc, "Type from unknown package: \"%s\" (known: %s).", - qPrintable(pythonType), qPrintable(known)); - return qdocModuleDirFromPackage(package); - } - return it.value(); -} - -static QString xmlFileBaseName(const AbstractMetaClassPtr &metaClass) -{ - QString className = metaClass->qualifiedCppName().toLower(); - className.replace("::"_L1, "-"_L1); - return className; -} - -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) -{ - switch (metaType.typeUsagePattern()) { - case AbstractMetaType::FlagsPattern: { - // Modify qualified name "QFlags<Qt::AlignmentFlag>" with name "Alignment" - // to "Qt::Alignment" as seen by qdoc. - const auto flagsEntry = std::static_pointer_cast<const FlagsTypeEntry>(metaType.typeEntry()); - QString name = flagsEntry->qualifiedCppName(); - if (name.endsWith(u'>') && name.startsWith(u"QFlags<")) { - const auto lastColon = name.lastIndexOf(u':'); - if (lastColon != -1) { - name.replace(lastColon + 1, name.size() - lastColon - 1, metaType.name()); - name.remove(0, 7); - } else { - name = metaType.name(); // QFlags<> of enum in global namespace - } - } - str << name; - } - break; - case AbstractMetaType::ContainerPattern: { // QVector<int> - str << metaType.typeEntry()->qualifiedCppName() << '<'; - const auto &instantiations = metaType.instantiations(); - for (qsizetype i = 0, size = instantiations.size(); i < size; ++i) { - if (i) - str << ", "; - const auto &instantiation = instantiations.at(i); - formatPreQualifications(str, instantiation); - str << instantiation.typeEntry()->qualifiedCppName(); - formatPostQualifications(str, instantiation); - } - str << '>'; - } - break; - default: // Fully qualify enums (Qt::AlignmentFlag), nested classes, etc. - str << metaType.typeEntry()->qualifiedCppName(); - break; - } -} - -static QString formatFunctionArgTypeQuery(const AbstractMetaType &metaType) -{ - QString result; - QTextStream str(&result);formatPreQualifications(str, metaType); - formatFunctionUnqualifiedArgTypeQuery(str, metaType); - formatPostQualifications(str, metaType); - return result; -} - -QtDocParser::FunctionDocumentationOpt - QtDocParser::functionDocumentation(const QString &sourceFileName, - const ClassDocumentation &classDocumentation, - const AbstractMetaClassCPtr &metaClass, - const AbstractMetaFunctionCPtr &func, QString *errorMessage) -{ - errorMessage->clear(); - - FunctionDocumentationOpt orig = queryFunctionDocumentation(sourceFileName, classDocumentation, metaClass, - func, errorMessage); - if (!orig.has_value() || orig.value().description.isEmpty()) - return orig; - - const auto funcModifs = DocParser::getXpathDocModifications(func, metaClass); - if (funcModifs.isEmpty()) - return orig; - - FunctionDocumentation modified = orig.value(); - modified.description = applyDocModifications(funcModifs, orig->description); - return modified; -} - -QtDocParser::FunctionDocumentationOpt - QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, - const ClassDocumentation &classDocumentation, - const AbstractMetaClassCPtr &metaClass, - const AbstractMetaFunctionCPtr &func, QString *errorMessage) -{ - // Search candidates by name and const-ness - FunctionDocumentationList candidates = - classDocumentation.findFunctionCandidates(func->name(), func->isConstant()); - if (candidates.isEmpty()) { - *errorMessage = msgCannotFindDocumentation(sourceFileName, func.get()) - + u" (no matches)"_s; - return std::nullopt; - } - - // Try an exact query - FunctionDocumentationQuery fq; - fq.name = func->name(); - fq.constant = func->isConstant(); - for (const auto &arg : func->arguments()) - fq.parameters.append(formatFunctionArgTypeQuery(arg.type())); - - const auto funcFlags = func->flags(); - // Re-add arguments removed by the metabuilder to binary operator functions - if (funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorLeadingClassArgumentRemoved) - || funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorTrailingClassArgumentRemoved)) { - QString classType = metaClass->qualifiedCppName(); - if (!funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorClassArgumentByValue)) { - classType.prepend(u"const "_s); - classType.append(u" &"_s); - } - if (funcFlags.testFlag(AbstractMetaFunction::Flag::OperatorLeadingClassArgumentRemoved)) - fq.parameters.prepend(classType); - else - fq.parameters.append(classType); - } - - const qsizetype index = ClassDocumentation::indexOfFunction(candidates, fq); - - if (debugFunctionSearch) { - qDebug() << __FUNCTION__ << metaClass->name() << fq << funcFlags << "returns" - << index << "\n " << candidates.value(index) << "\n " << candidates; - } - - if (index != -1) - return candidates.at(index); - - // Fallback: Try matching by argument count - const auto parameterCount = func->arguments().size(); - auto pend = std::remove_if(candidates.begin(), candidates.end(), - [parameterCount](const FunctionDocumentation &fd) { - return fd.parameters.size() != parameterCount; }); - candidates.erase(pend, candidates.end()); - if (candidates.size() == 1) { - const auto &match = candidates.constFirst(); - QTextStream(errorMessage) << msgFallbackForDocumentation(sourceFileName, func.get()) - << "\n Falling back to \"" << match.signature - << "\" obtained by matching the argument count only."; - return candidates.constFirst(); - } - - QTextStream(errorMessage) << msgCannotFindDocumentation(sourceFileName, func.get()) - << " (" << candidates.size() << " candidates matching the argument count)"; - return std::nullopt; -} - -// Extract the <brief>/detailed sections from a WebXML (class) documentation (from <description>) -static std::pair<QString, QString> extractBrief(QString value) -{ - std::pair<QString, QString> result; - const auto briefStart = value.indexOf(briefStartElement); - if (briefStart > 0) { - const auto briefEnd = value.indexOf(briefEndElement, - briefStart + briefStartElement.size()); - if (briefEnd > briefStart) { - const auto briefLength = briefEnd + briefEndElement.size() - briefStart; - if (briefLength > briefStartElement.size() + briefEndElement.size()) - result.first = value.sliced(briefStart, briefLength); - value.remove(briefStart, briefLength); - // Remove any space/newlines between the <brief/> element and its - // surrounding XML elements. - auto lastElement = value.lastIndexOf(u'>', briefStart); - if (lastElement != -1) { - ++lastElement; - const auto nextElement = value.indexOf(u'<', briefStart); - if (nextElement > lastElement) - value.remove(lastElement, nextElement - lastElement); - } - } - } - - if (value != "<description></description>"_L1) - result.second = value; - - return result; -} - -// Apply the documentation parsed from WebXML to a AbstractMetaFunction and complete argument -// names missing from parsed headers using the WebXML names (exact match only). -static void applyDocumentation(const FunctionDocumentation &funcDoc, - const QString &sourceFileName, - const AbstractMetaFunctionPtr &func) -{ - const Documentation documentation(funcDoc.description, {}, sourceFileName); - func->setDocumentation(documentation); - - if (const auto argCount = func->arguments().size(); argCount == funcDoc.parameterNames.size()) { - for (qsizetype a = 0; a < argCount; ++a) { - if (!func->arguments().at(a).hasName() && !funcDoc.parameterNames.at(a).isEmpty()) - func->setArgumentName(a, funcDoc.parameterNames.at(a)); - } - } -} - -// Find the webxml file for global functions/enums -// by the doc-file typesystem attribute or via include file. -static QString findGlobalWebXmLFile(const QString &documentationDataDirectory, - const QString &package, - const QString &docFile, - const Include &include) -{ - QString result; - const QString root = documentationDataDirectory + u'/' - + QtDocParser::qdocModuleDir(package) + u'/'; - if (!docFile.isEmpty()) { - result = root + docFile; - if (!result.endsWith(webxmlSuffix)) - result += webxmlSuffix; - return QFileInfo::exists(result) ? result : QString{}; - } - if (include.name().isEmpty()) - return {}; - // qdoc "\headerfile <QtLogging>" directive produces "qtlogging.webxml" - result = root + QFileInfo(include.name()).baseName() + webxmlSuffix; - if (QFileInfo::exists(result)) - return result; - // qdoc "\headerfile <qdrawutil.h>" produces "qdrawutil-h.webxml" - result.insert(result.size() - webxmlSuffix.size(), "-h"_L1); - return QFileInfo::exists(result) ? result : QString{}; -} - -void QtDocParser::fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f) -{ - auto te = f->typeEntry(); - if (te == nullptr) - return; - - const QString sourceFileName = - findGlobalWebXmLFile(documentationDataDirectory(), te->targetLangPackage(), te->docFile(), te->include()); - if (sourceFileName.isEmpty()) - return; - - QString errorMessage; - auto classDocumentationO = parseWebXml({sourceFileName}, &errorMessage); - if (!classDocumentationO.has_value()) { - qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); - return; - } - - const auto funcDocOpt = functionDocumentation(sourceFileName, classDocumentationO.value(), - {}, f, &errorMessage); - if (funcDocOpt.has_value()) - applyDocumentation(funcDocOpt.value(), sourceFileName, f); - else if (!errorMessage.isEmpty()) - qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); -} - -void QtDocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &e) -{ - auto te = e.typeEntry(); - const QString sourceFileName = - findGlobalWebXmLFile(documentationDataDirectory(), te->targetLangPackage(), te->docFile(), te->include()); - if (sourceFileName.isEmpty()) - return; - - QString errorMessage; - auto classDocumentationO = parseWebXml({sourceFileName}, &errorMessage); - if (!classDocumentationO.has_value()) { - qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); - return; - } - if (!extractEnumDocumentation(classDocumentationO.value(), sourceFileName, e)) { - qCWarning(lcShibokenDoc, "%s", - qPrintable(msgCannotFindDocumentation(sourceFileName, {}, e, {}))); - } -} - -QString QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass) -{ - if (!metaClass) - return {}; - - auto context = metaClass->enclosingClass(); - while (context) { - if (!context->enclosingClass()) - break; - context = context->enclosingClass(); - } - - // Find qdoc files of a class. - QStringList allCandidates; - const auto typeEntry = metaClass->typeEntry(); - const QString docDir = documentationDataDirectory() + u'/' - + QtDocParser::qdocModuleDir(typeEntry->targetLangPackage()) + u'/'; - const QString baseName = xmlFileBaseName(metaClass); - allCandidates.append(docDir + baseName + webxmlSuffix); - const QString &docFile = typeEntry->docFile(); - if (!docFile.isEmpty()) - allCandidates.append(docDir + docFile + webxmlSuffix); - allCandidates.append(docDir + baseName + ".xml"_L1); - QStringList candidates; - std::copy_if(allCandidates.cbegin(), allCandidates.cend(), std::back_inserter(candidates), - qOverload<const QString &>(QFileInfo::exists)); - - if (candidates.isEmpty()) { - qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindQDocFile(metaClass, allCandidates))); - return {}; - } - - QString errorMessage; - const auto classDocumentationO = parseWebXml(candidates, &errorMessage); - if (!classDocumentationO.has_value()) { - qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); - return {}; - } - - const auto &classDocumentation = classDocumentationO.value(); - const QString &sourceFileName = candidates.constFirst(); - for (const auto &p : classDocumentation.properties) { - Documentation doc(p.description, p.brief, sourceFileName); - metaClass->setPropertyDocumentation(p.name, doc); - } - - QString docString = applyDocModifications(DocParser::getXpathDocModifications(metaClass), - classDocumentation.description); - - if (docString.isEmpty()) { - QString className = metaClass->name(); - qCWarning(lcShibokenDoc, "%s", - qPrintable(msgCannotFindDocumentation(sourceFileName, "class", className, {}))); - } - const auto descriptionPair = extractBrief(docString); - - Documentation doc; - doc.setSourceFile(sourceFileName); - doc.setValue(descriptionPair.first, DocumentationType::Brief); - doc.setValue(descriptionPair.second, DocumentationType::Detailed); - metaClass->setDocumentation(doc); - - //Functions Documentation - const auto &funcs = DocParser::documentableFunctions(metaClass); - for (const auto &func : funcs) { - const auto funcDocOpt = functionDocumentation(sourceFileName, classDocumentation, - metaClass, func, &errorMessage); - if (funcDocOpt.has_value()) { - applyDocumentation(funcDocOpt.value(), sourceFileName, - std::const_pointer_cast<AbstractMetaFunction>(func)); - } else if (!errorMessage.isEmpty()) { - qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); - } - } -#if 0 - // Fields - const AbstractMetaFieldList &fields = metaClass->fields(); - for (AbstractMetaField *field : fields) { - if (field->isPrivate()) - return; - - QString query = "/doxygen/compounddef/sectiondef/memberdef/name[text()=\"" + field->name() + "\"]/.."; - Documentation doc = getDocumentation(DocModificationList(), xquery, query); - field->setDocumentation(doc); - } -#endif - // Enums - for (AbstractMetaEnum &meta_enum : metaClass->enums()) { - if (!extractEnumDocumentation(classDocumentation, sourceFileName, meta_enum)) { - qCWarning(lcShibokenDoc, "%s", - qPrintable(msgCannotFindDocumentation(sourceFileName, metaClass, meta_enum, {}))); - } - } - - return sourceFileName; -} - -bool QtDocParser::extractEnumDocumentation(const ClassDocumentation &classDocumentation, - const QString &sourceFileName, - AbstractMetaEnum &meta_enum) -{ - const auto index = classDocumentation.indexOfEnum(meta_enum.name()); - if (index == -1) - return false; - QString doc = classDocumentation.enums.at(index).description; - const auto firstPara = doc.indexOf(u"<para>"); - if (firstPara != -1) { - const QString baseClass = QtDocParser::enumBaseClass(meta_enum); - if (baseClass != "Enum"_L1) { - const QString note = "(inherits <teletype>enum."_L1 + baseClass - + "</teletype>) "_L1; - doc.insert(firstPara + 6, note); - } - } - // Fully qualify enums: "<term>QFrame::NoFrame" -> "<term>QFrame::FrameShape::NoFrame" - const QString classQualifier = "<term>"_L1 + classDocumentation.name + "::"_L1; - doc.replace(classQualifier, classQualifier + meta_enum.name() + "::"_L1); - doc.replace("::None</term>"_L1, "::None\\_</term>"_L1); - Documentation enumDoc(doc, {}, sourceFileName); - meta_enum.setDocumentation(enumDoc); - return true; -} - -static QString qmlReferenceLink(const QFileInfo &qmlModuleFi) -{ - return "https://doc.qt.io/qt-"_L1 + QString::number(QT_VERSION_MAJOR) - + u'/' + qmlModuleFi.baseName() + ".html"_L1; -} - -// Find a webxml file containing QML types. Note: These files are empty; -// we need to point to the web docs. -static std::optional<QFileInfo> qmlModuleFile(const QString &dirPath, - const QString &lowerModuleName) -{ - static constexpr auto postFix = "-qmlmodule.webxml"_L1; - const QFileInfo moduleFile(dirPath + u'/' + lowerModuleName + postFix); - if (moduleFile.exists()) - return moduleFile; - // Some file names are irregular, fall back to using a filter - const QFileInfoList qmlModuleFiles = - QDir(dirPath).entryInfoList({u'*' + postFix}, QDir::Files); - if (!qmlModuleFiles.isEmpty()) - return qmlModuleFiles.constFirst(); - return std::nullopt; -} - -ModuleDocumentation QtDocParser::retrieveModuleDocumentation(const QString &name) -{ - // TODO: This method of acquiring the module name supposes that the target language uses - // dots as module separators in package names. Improve this. - QString completeModuleName = name; - if (completeModuleName.endsWith("QtQuickControls2"_L1)) - completeModuleName.chop(1); - const QString moduleName = completeModuleName.sliced(name.lastIndexOf(u'.') + 1); - const QString lowerModuleName = moduleName.toLower(); - - const QString dirPath = documentationDataDirectory() + u'/' + qdocModuleDir(completeModuleName); - const QString sourceFile = dirPath + u'/' + lowerModuleName + "-index.webxml"_L1; - if (!QFile::exists(sourceFile)) { - qCWarning(lcShibokenDoc).noquote().nospace() - << "Can't find qdoc file for module " << name << ", tried: " - << QDir::toNativeSeparators(sourceFile); - return {}; - } - - QString errorMessage; - QString docString = webXmlModuleDescription(sourceFile, &errorMessage); - if (!errorMessage.isEmpty()) { - qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); - return {}; - } - if (docString.isEmpty()) { - qCWarning(lcShibokenDoc, "%s", - qPrintable(msgCannotFindDocumentation(sourceFile, "module", name))); - return {}; - } - - ModuleDocumentation result{Documentation{docString, {}, sourceFile}, {}}; - - // If a QML module info file exists, insert a link to the Qt docs. - // Note: These files are empty; we need to point to the web docs. - if (const auto qmlModuleFileO = qmlModuleFile(dirPath, lowerModuleName)) - result.qmlTypesUrl = qmlReferenceLink(qmlModuleFileO.value()); - return result; -} |
