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

0001 /*
0002     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "problem.h"
0008 
0009 #include "duchainregister.h"
0010 #include "topducontextdynamicdata.h"
0011 #include "topducontext.h"
0012 #include "topducontextdata.h"
0013 #include "duchain.h"
0014 #include "duchainlock.h"
0015 
0016 #include <editor/documentrange.h>
0017 
0018 #include <interfaces/iassistant.h>
0019 
0020 #include <KLocalizedString>
0021 
0022 namespace KDevelop {
0023 REGISTER_DUCHAIN_ITEM(Problem);
0024 DEFINE_LIST_MEMBER_HASH(ProblemData, diagnostics, LocalIndexedProblem)
0025 }
0026 
0027 using namespace KDevelop;
0028 
0029 LocalIndexedProblem::LocalIndexedProblem(const ProblemPointer& problem, const TopDUContext* top)
0030     : m_index(problem->m_indexInTopContext)
0031 {
0032     ENSURE_CHAIN_READ_LOCKED
0033     // ensure child problems are properly serialized before we serialize the parent problem
0034     // see below, the diagnostic size is kept in sync by the mutable API of Problem
0035     // the const cast is ugly but we don't really "change" the state as observed from the outside
0036     auto& serialized = const_cast<Problem*>(problem.data())->d_func_dynamic()->diagnosticsList();
0037     serialized.clear();
0038     serialized.reserve(problem->m_diagnostics.size());
0039     for (const ProblemPointer& child : qAsConst(problem->m_diagnostics)) {
0040         serialized << LocalIndexedProblem(child, top);
0041     }
0042 
0043     if (!m_index) {
0044         m_index = top->m_dynamicData->allocateProblemIndex(problem);
0045     }
0046 }
0047 
0048 ProblemPointer LocalIndexedProblem::data(const TopDUContext* top) const
0049 {
0050     if (!m_index) {
0051         return {};
0052     }
0053     return top->m_dynamicData->problemForIndex(m_index);
0054 }
0055 
0056 Problem::Problem()
0057     : DUChainBase(*new ProblemData)
0058 {
0059     d_func_dynamic()->setClassId(this);
0060 }
0061 
0062 Problem::Problem(ProblemData& data)
0063     : DUChainBase(data)
0064 {
0065 }
0066 
0067 Problem::~Problem()
0068 {
0069 }
0070 
0071 TopDUContext* Problem::topContext() const
0072 {
0073     return m_topContext.data();
0074 }
0075 
0076 IndexedString Problem::url() const
0077 {
0078     return d_func()->url;
0079 }
0080 
0081 DocumentRange Problem::finalLocation() const
0082 {
0083     return DocumentRange(d_func()->url, d_func()->m_range.castToSimpleRange());
0084 }
0085 
0086 void Problem::setFinalLocation(const DocumentRange& location)
0087 {
0088     setRange(RangeInRevision::castFromSimpleRange(location));
0089     d_func_dynamic()->url = location.document;
0090 }
0091 
0092 IProblem::FinalLocationMode Problem::finalLocationMode() const
0093 {
0094     return d_func()->finalLocationMode;
0095 }
0096 
0097 void Problem::setFinalLocationMode(IProblem::FinalLocationMode mode)
0098 {
0099     d_func_dynamic()->finalLocationMode = mode;
0100 }
0101 
0102 void Problem::clearDiagnostics()
0103 {
0104     m_diagnostics.clear();
0105     // keep serialization in sync, see also LocalIndexedProblem ctor above
0106     d_func_dynamic()->diagnosticsList().clear();
0107 }
0108 
0109 QVector<IProblem::Ptr> Problem::diagnostics() const
0110 {
0111     QVector<IProblem::Ptr> vector;
0112 
0113     for (const auto& ptr : qAsConst(m_diagnostics)) {
0114         vector.push_back(ptr);
0115     }
0116 
0117     return vector;
0118 }
0119 
0120 void Problem::setDiagnostics(const QVector<IProblem::Ptr>& diagnostics)
0121 {
0122     clearDiagnostics();
0123 
0124     for (const IProblem::Ptr& problem : diagnostics) {
0125         addDiagnostic(problem);
0126     }
0127 }
0128 
0129 void Problem::addDiagnostic(const IProblem::Ptr& diagnostic)
0130 {
0131     auto* problem = dynamic_cast<Problem*>(diagnostic.data());
0132     Q_ASSERT(problem != nullptr);
0133 
0134     ProblemPointer ptr(problem);
0135 
0136     m_diagnostics << ptr;
0137 }
0138 
0139 QString Problem::description() const
0140 {
0141     return d_func()->description.str();
0142 }
0143 
0144 void Problem::setDescription(const QString& description)
0145 {
0146     d_func_dynamic()->description = IndexedString(description);
0147 }
0148 
0149 QString Problem::explanation() const
0150 {
0151     return d_func()->explanation.str();
0152 }
0153 
0154 void Problem::setExplanation(const QString& explanation)
0155 {
0156     d_func_dynamic()->explanation = IndexedString(explanation);
0157 }
0158 
0159 IProblem::Source Problem::source() const
0160 {
0161     return d_func()->source;
0162 }
0163 
0164 void Problem::setSource(IProblem::Source source)
0165 {
0166     d_func_dynamic()->source = source;
0167 }
0168 
0169 QExplicitlySharedDataPointer<IAssistant> Problem::solutionAssistant() const
0170 {
0171     return {};
0172 }
0173 
0174 IProblem::Severity Problem::severity() const
0175 {
0176     return d_func()->severity;
0177 }
0178 
0179 void Problem::setSeverity(Severity severity)
0180 {
0181     d_func_dynamic()->severity = severity;
0182 }
0183 
0184 QString Problem::severityString() const
0185 {
0186     switch (severity()) {
0187     case IProblem::NoSeverity:
0188         return {};
0189     case IProblem::Error:
0190         return i18n("Error");
0191     case IProblem::Warning:
0192         return i18n("Warning");
0193     case IProblem::Hint:
0194         return i18n("Hint");
0195     }
0196     return QString();
0197 }
0198 
0199 QString Problem::sourceString() const
0200 {
0201     switch (source()) {
0202     case IProblem::Disk:
0203         return i18n("Disk");
0204     case IProblem::Preprocessor:
0205         return i18n("Preprocessor");
0206     case IProblem::Lexer:
0207         return i18n("Lexer");
0208     case IProblem::Parser:
0209         return i18n("Parser");
0210     case IProblem::DUChainBuilder:
0211         return i18n("Definition-Use Chain");
0212     case IProblem::SemanticAnalysis:
0213         return i18n("Semantic analysis");
0214     case IProblem::ToDo:
0215         return i18n("To-do");
0216     case IProblem::Unknown:
0217     default:
0218         return i18n("Unknown");
0219     }
0220 }
0221 
0222 QString Problem::toString() const
0223 {
0224     return i18nc("<severity>: <description> in <url>:[<range>]: <explanation> (found by <source>)",
0225                  "%1: %2 in %3:[(%4,%5),(%6,%7)]: %8 (found by %9)",
0226                  severityString(),
0227                  description(),
0228                  url().str(),
0229                  range().start.line,
0230                  range().start.column,
0231                  range().end.line,
0232                  range().end.column,
0233                  (explanation().isEmpty() ? i18n("<no explanation>") : explanation()),
0234                  sourceString());
0235 }
0236 
0237 void Problem::rebuildDynamicData(DUContext* parent, uint ownIndex)
0238 {
0239     auto top = dynamic_cast<TopDUContext*>(parent);
0240     Q_ASSERT(top);
0241 
0242     m_topContext = top;
0243     m_indexInTopContext = ownIndex;
0244 
0245     // deserialize child diagnostics here, as the top-context might get unloaded
0246     // but we still want to keep the child-diagnostics in-tact, as one would assume
0247     // a shared-ptr works.
0248     const auto data = d_func();
0249     m_diagnostics.reserve(data->diagnosticsSize());
0250     for (uint i = 0; i < data->diagnosticsSize(); ++i) {
0251         m_diagnostics << ProblemPointer(data->diagnostics()[i].data(top));
0252     }
0253 
0254     DUChainBase::rebuildDynamicData(parent, ownIndex);
0255 }
0256 
0257 QDebug operator<<(QDebug s, const Problem& problem)
0258 {
0259     s.nospace() << problem.toString();
0260     return s.space();
0261 }
0262 
0263 QDebug operator<<(QDebug s, const ProblemPointer& problem)
0264 {
0265     if (!problem) {
0266         s.nospace() << "<invalid problem>";
0267     } else {
0268         s.nospace() << problem->toString();
0269     }
0270     return s.space();
0271 }