File indexing completed on 2024-05-12 04:38:01

0001 /*
0002     SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "dumpdotgraph.h"
0008 
0009 #include "ducontext.h"
0010 #include "topducontext.h"
0011 #include "declaration.h"
0012 #include "duchainpointer.h"
0013 #include "parsingenvironment.h"
0014 #include "identifier.h"
0015 #include "functiondefinition.h"
0016 
0017 namespace KDevelop {
0018 QString shortLabel(KDevelop::DUContext* context)
0019 {
0020     return QStringLiteral("q%1").arg(( quint64 )context);
0021 }
0022 
0023 QString shortLabel(KDevelop::Declaration* declaration)
0024 {
0025     return QStringLiteral("q%1").arg(( quint64 )declaration);
0026 }
0027 
0028 QString rangeToString(const KTextEditor::Range& r)
0029 {
0030     return QStringLiteral("%1:%2->%3:%4").arg(r.start().line()).arg(r.start().column()).arg(r.end().line()).arg(
0031         r.end().column());
0032 }
0033 
0034 class DumpDotGraphPrivate
0035 {
0036 public:
0037 
0038     QString dotGraphInternal(KDevelop::DUContext* contex, bool isMaster, bool shortened);
0039 
0040     void addDeclaration(QTextStream& stream, Declaration* decl);
0041 
0042     QMap<QUrl, QString> m_hadVersions;
0043     QMap<KDevelop::DUChainBase*, bool> m_hadObjects;
0044     TopDUContext* m_topContext;
0045 };
0046 
0047 QString DumpDotGraph::dotGraph(KDevelop::DUContext* context, bool shortened)
0048 {
0049     Q_D(DumpDotGraph);
0050 
0051     d->m_hadObjects.clear();
0052     d->m_hadVersions.clear();
0053     d->m_topContext = context->topContext(); ///@todo maybe get this as a parameter
0054     return d->dotGraphInternal(context, true, shortened);
0055 }
0056 
0057 DumpDotGraph::DumpDotGraph()
0058     : d_ptr(new DumpDotGraphPrivate())
0059 {
0060 }
0061 
0062 DumpDotGraph::~DumpDotGraph() = default;
0063 
0064 void DumpDotGraphPrivate::addDeclaration(QTextStream& stream, Declaration* dec)
0065 {
0066     if (m_hadObjects.contains(dec))
0067         return;
0068 
0069     m_hadObjects[dec] = true;
0070 
0071     Declaration* declarationForDefinition = nullptr;
0072     if (dynamic_cast<FunctionDefinition*>(dec))
0073         declarationForDefinition = static_cast<FunctionDefinition*>(dec)->declaration(m_topContext);
0074 
0075     if (!declarationForDefinition) {
0076         //Declaration
0077         stream << shortLabel(dec) <<
0078             "[shape=box, label=\"" <<
0079             dec->toString() << " [" <<
0080             dec->qualifiedIdentifier().toString() << "]" << " " <<
0081             rangeToString(dec->range().castToSimpleRange()) << "\"];\n";
0082         stream << shortLabel(dec->context()) << " -> " << shortLabel(dec) << "[color=green];\n";
0083         if (dec->internalContext())
0084             stream << shortLabel(dec) << " -> " << shortLabel(dec->internalContext()) <<
0085                 "[label=\"internal\", color=blue];\n";
0086     } else {
0087         //Definition
0088         stream << shortLabel(dec) <<  "[shape=regular,color=yellow,label=\"" << declarationForDefinition->toString() <<
0089             " " << rangeToString(dec->range().castToSimpleRange()) <<  "\"];\n";
0090         stream << shortLabel(dec->context()) << " -> " << shortLabel(dec) << ";\n";
0091 
0092         stream << shortLabel(dec) << " -> " << shortLabel(declarationForDefinition) <<
0093             "[label=\"defines\",color=green];\n";
0094         addDeclaration(stream, declarationForDefinition);
0095 
0096         if (dec->internalContext())
0097             stream << shortLabel(dec) << " -> " << shortLabel(dec->internalContext()) <<
0098                 "[label=\"internal\", color=blue];\n";
0099     }
0100 }
0101 
0102 QString DumpDotGraphPrivate::dotGraphInternal(KDevelop::DUContext* context, bool isMaster, bool shortened)
0103 {
0104     if (m_hadObjects.contains(context))
0105         return QString();
0106 
0107     m_hadObjects[context] = true;
0108 
0109     QTextStream stream;
0110     QString ret;
0111     stream.setString(&ret, QIODevice::WriteOnly);
0112 
0113     if (isMaster)
0114         stream << "Digraph chain {\n";
0115 
0116     QString shape = QStringLiteral("parallelogram");
0117     //QString shape = "box";
0118     QString label = QStringLiteral("unknown");
0119 
0120     if (dynamic_cast<TopDUContext*>(context)) {
0121         auto* topCtx = static_cast<TopDUContext*>(context);
0122         if (topCtx->parsingEnvironmentFile()) {
0123             QUrl url = topCtx->parsingEnvironmentFile()->url().toUrl();
0124             const QString fileName = url.fileName();
0125             QString file = url.toDisplayString(QUrl::PreferLocalFile);
0126             if (topCtx->parsingEnvironmentFile() && topCtx->parsingEnvironmentFile()->isProxyContext())
0127                 url.setPath(url.path() + QLatin1String("/_[proxy]_"));
0128 
0129             //Find the context this one is derived from. If there is one, connect it with a line, and shorten the url.
0130             if (m_hadVersions.contains(url)) {
0131                 stream << shortLabel(context) << " -> " << m_hadVersions[url] << "[color=blue,label=\"version\"];\n";
0132                 file = fileName;
0133             } else {
0134                 m_hadVersions[url] = shortLabel(context);
0135             }
0136 
0137             label = file;
0138 
0139             if (topCtx->importers().count() != 0)
0140                 label += QStringLiteral(" imported by %1").arg(topCtx->importers().count());
0141         } else {
0142             label = QStringLiteral("unknown file");
0143         }
0144         if (topCtx->parsingEnvironmentFile() && topCtx->parsingEnvironmentFile()->isProxyContext())
0145             label = QLatin1String("Proxy-context ") + label;
0146     } else {
0147         label = /*"context " + */ context->localScopeIdentifier().toString();
0148         label += QLatin1Char(' ') + rangeToString(context->range().castToSimpleRange());
0149     }
0150 
0151     //label = QStringLiteral("%1 ").arg((size_t)context) + label;
0152 
0153     if (isMaster && !dynamic_cast<TopDUContext*>(context)) {
0154         //Also draw contexts that import this one
0155         const auto importers = context->importers();
0156         for (DUContext* ctx : importers) {
0157             stream << dotGraphInternal(ctx, false, true);
0158         }
0159     }
0160     const auto importedParentContexts = context->importedParentContexts();
0161     for (const DUContext::Import& parent : importedParentContexts) {
0162         if (parent.context(m_topContext)) {
0163             stream << dotGraphInternal(parent.context(m_topContext), false, true);
0164             QString label = QStringLiteral("imports");
0165             if ((!dynamic_cast<TopDUContext*>(parent.context(m_topContext)) || !dynamic_cast<TopDUContext*>(context)) &&
0166                 !(parent.context(m_topContext)->url() == context->url())) {
0167                 label += QLatin1String(" from ") + parent.context(m_topContext)->url().toUrl().fileName()
0168                          + QLatin1String(" to ") + context->url().toUrl().fileName();
0169             }
0170 
0171             stream << shortLabel(context) << QLatin1String(" -> ") << shortLabel(parent.context(m_topContext)) <<
0172                 QLatin1String("[style=dotted,label=\"") << label  << QLatin1String("\"];\n");
0173         }
0174     }
0175 
0176     const auto childContexts = context->childContexts();
0177     if (!childContexts.isEmpty()) {
0178         label += QStringLiteral(", %1 C.").arg(childContexts.count());
0179     }
0180 
0181     if (!shortened) {
0182         for (DUContext* child : childContexts) {
0183             stream << dotGraphInternal(child, false, false);
0184             stream << shortLabel(context) << QLatin1String(" -> ") << shortLabel(child) << QLatin1String(
0185                 "[style=dotted,color=green];\n");
0186         }
0187     }
0188 
0189     const auto localDeclarations = context->localDeclarations();
0190     if (!localDeclarations.isEmpty()) {
0191         label += QStringLiteral(", %1 D.").arg(localDeclarations.count());
0192     }
0193 
0194     if (!shortened) {
0195         for (Declaration* dec : localDeclarations) {
0196             addDeclaration(stream, dec);
0197         }
0198     }
0199 
0200     if (context->owner()) {
0201         addDeclaration(stream, context->owner());
0202     }
0203 
0204     stream << shortLabel(context) << "[shape=" << shape << ",label=\"" << label << "\"" <<
0205     (isMaster ? QLatin1String("color=red") : QLatin1String("color=blue")) << "];\n";
0206 
0207     if (isMaster)
0208         stream << "}\n";
0209 
0210     return ret;
0211 }
0212 }