// Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "doxygenparser.h" #include "abstractmetaargument.h" #include "abstractmetalang.h" #include "abstractmetafield.h" #include "abstractmetafunction.h" #include "abstractmetaenum.h" #include "abstractmetatype.h" #include "documentation.h" #include "messages.h" #include "modifications.h" #include "propertyspec.h" #include "reporthandler.h" #include "complextypeentry.h" #include "xmlutils.h" #include "qtcompat.h" #include #include using namespace Qt::StringLiterals; static QString getSectionKindAttr(const AbstractMetaFunctionCPtr &func) { if (func->isSignal()) return u"signal"_s; QString kind = func->isPublic() ? u"public"_s : u"protected"_s; if (func->isStatic()) kind += u"-static"_s; else if (func->isSlot()) kind += u"-slot"_s; return kind; } QString DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass) { if (!metaClass) return {}; QString doxyFileSuffix; if (metaClass->enclosingClass()) { doxyFileSuffix += metaClass->enclosingClass()->name(); doxyFileSuffix += u"_1_1"_s; // FIXME: Check why _1_1!! } doxyFileSuffix += metaClass->name(); doxyFileSuffix += u".xml"_s; static constexpr QLatin1StringView prefixes[] = { "class"_L1, "struct"_L1, "namespace"_L1 }; bool isProperty = false; QString doxyFilePath; for (const auto &prefix : prefixes) { doxyFilePath = documentationDataDirectory() + u'/' + prefix + doxyFileSuffix; if (QFile::exists(doxyFilePath)) break; doxyFilePath.clear(); } if (doxyFilePath.isEmpty()) { qCWarning(lcShibokenDoc).noquote().nospace() << "Can't find doxygen file for class " << metaClass->name() << ", tried: " << QDir::toNativeSeparators(documentationDataDirectory()) << "/{struct|class|namespace}"<< doxyFileSuffix; return {}; } QString errorMessage; XQueryPtr xquery = XQuery::create(doxyFilePath, &errorMessage); if (!xquery) { qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); return {}; } static const QList> docTags = { { DocumentationType::Brief, u"briefdescription"_s }, { DocumentationType::Detailed, u"detaileddescription"_s } }; // Get class documentation Documentation classDoc; classDoc.setSourceFile(doxyFilePath); for (const auto &tag : docTags) { const QString classQuery = u"/doxygen/compounddef/"_s + tag.second; QString doc = getDocumentation(xquery, classQuery, metaClass->typeEntry()->docModifications()); if (doc.isEmpty()) qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindDocumentation(doxyFilePath, "class", metaClass->name(), classQuery))); else classDoc.setValue(doc, tag.first); } metaClass->setDocumentation(classDoc); //Functions Documentation const auto &funcs = DocParser::documentableFunctions(metaClass); for (const auto &func : funcs) { QString query = u"/doxygen/compounddef/sectiondef"_s; // properties if (func->isPropertyReader() || func->isPropertyWriter() || func->isPropertyResetter()) { const auto prop = metaClass->propertySpecs().at(func->propertySpecIndex()); query += u"[@kind=\"property\"]/memberdef/name[text()=\""_s + prop.name() + u"\"]"_s; isProperty = true; } else { // normal methods QString kind = getSectionKindAttr(func); query += u"[@kind=\""_s + kind + u"-func\"]/memberdef/name[text()=\""_s + func->originalName() + u"\"]"_s; if (func->arguments().isEmpty()) { QString args = func->isConstant() ? u"() const"_s : u"()"_s; query += u"/../argsstring[text()=\""_s + args + u"\"]"_s; } else { int i = 1; const AbstractMetaArgumentList &arguments = func->arguments(); for (const AbstractMetaArgument &arg : arguments) { if (!arg.type().isPrimitive()) { query += u"/../param["_s + QString::number(i) + u"]/type/ref[text()=\""_s + arg.type().cppSignature().toHtmlEscaped() + u"\"]/../.."_s; } else { query += u"/../param["_s + QString::number(i) + u"]/type[text(), \""_s + arg.type().cppSignature().toHtmlEscaped() + u"\"]/.."_s; } ++i; } } } Documentation funcDoc; funcDoc.setSourceFile(doxyFilePath); for (const auto &tag : docTags) { QString funcQuery(query); if (!isProperty) { funcQuery += u"/../"_s + tag.second; } else { funcQuery = u'(' + funcQuery; funcQuery += u"/../"_s + tag.second + u")[1]"_s; } QString doc = getDocumentation(xquery, funcQuery, DocParser::getXpathDocModifications(func, metaClass)); if (doc.isEmpty()) { qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindDocumentation(doxyFilePath, func.get(), funcQuery))); } else { funcDoc.setValue(doc, tag.first); } } std::const_pointer_cast(func)->setDocumentation(funcDoc); isProperty = false; } //Fields for (AbstractMetaField &field : metaClass->fields()) { if (field.isPrivate()) continue; Documentation fieldDoc; fieldDoc.setSourceFile(doxyFilePath); for (const auto &tag : docTags) { QString query = u"/doxygen/compounddef/sectiondef/memberdef/name[text()=\""_s + field.name() + u"\"]/../"_s + tag.second; QString doc = getDocumentation(xquery, query, DocModificationList()); if (doc.isEmpty()) { qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, field, query))); } else { fieldDoc.setValue(doc, tag.first); } } field.setDocumentation(fieldDoc); } //Enums for (AbstractMetaEnum &meta_enum : metaClass->enums()) { QString query = u"/doxygen/compounddef/sectiondef/memberdef[@kind=\"enum\"]/name[text()=\""_s + meta_enum.name() + u"\"]/.."_s; QString doc = getDocumentation(xquery, query, DocModificationList()); if (doc.isEmpty()) { qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, meta_enum, query))); } meta_enum.setDocumentation(Documentation(doc, {}, doxyFilePath)); } return doxyFilePath; } ModuleDocumentation DoxygenParser::retrieveModuleDocumentation(const QString &name) { QString sourceFile = documentationDataDirectory() + u"/indexpage.xml"_s; if (!QFile::exists(sourceFile)) { qCWarning(lcShibokenDoc).noquote().nospace() << "Can't find doxygen XML file for module " << name << ", tried: " << QDir::toNativeSeparators(sourceFile); return {}; } QString errorMessage; XQueryPtr xquery = XQuery::create(sourceFile, &errorMessage); if (!xquery) { qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage)); return {}; } // Module documentation QString query = u"/doxygen/compounddef/detaileddescription"_s; const QString doc = getDocumentation(xquery, query, DocModificationList()); return {Documentation(doc, {}, sourceFile), {}}; }