diff options
Diffstat (limited to 'plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp')
-rw-r--r-- | plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp | 215 |
1 files changed, 186 insertions, 29 deletions
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp index 0cc4dfb9a0..c96e9c7528 100644 --- a/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp +++ b/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp @@ -18,6 +18,7 @@ #include "clangstaticanalyzerdiagnosticmodel.h" +#include "clangstaticanalyzerdiagnosticview.h" #include "clangstaticanalyzerprojectsettingsmanager.h" #include "clangstaticanalyzerutils.h" @@ -28,32 +29,53 @@ #include <QCoreApplication> #include <QFileInfo> +#include <cmath> + namespace ClangStaticAnalyzer { namespace Internal { -ClangStaticAnalyzerDiagnosticModel::ClangStaticAnalyzerDiagnosticModel(QObject *parent) - : QAbstractListModel(parent) +class DiagnosticItem : public Utils::TreeItem { -} +public: + DiagnosticItem(const Diagnostic &diag); -void ClangStaticAnalyzerDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics) + Diagnostic diagnostic() const { return m_diagnostic; } + +private: + QVariant data(int column, int role) const override; + + const Diagnostic m_diagnostic; +}; + +class ExplainingStepItem : public Utils::TreeItem { - beginInsertRows(QModelIndex(), m_diagnostics.size(), - m_diagnostics.size() + diagnostics.size() - 1 ); - m_diagnostics += diagnostics; - endInsertRows(); +public: + ExplainingStepItem(const ExplainingStep &step); + +private: + QVariant data(int column, int role) const override; + + const ExplainingStep m_step; +}; + +ClangStaticAnalyzerDiagnosticModel::ClangStaticAnalyzerDiagnosticModel(QObject *parent) + : Utils::TreeModel(parent) +{ + setHeader(QStringList() << tr("Issue") << tr("Location")); } -void ClangStaticAnalyzerDiagnosticModel::clear() +void ClangStaticAnalyzerDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics) { - beginResetModel(); - m_diagnostics.clear(); - endResetModel(); + foreach (const Diagnostic &d, diagnostics) + rootItem()->appendChild(new DiagnosticItem(d)); } -int ClangStaticAnalyzerDiagnosticModel::rowCount(const QModelIndex &parent) const +QList<Diagnostic> ClangStaticAnalyzerDiagnosticModel::diagnostics() const { - return parent.isValid() ? 0 : m_diagnostics.count(); + QList<Diagnostic> diags; + foreach (const Utils::TreeItem * const item, rootItem()->children()) + diags << static_cast<const DiagnosticItem *>(item)->diagnostic(); + return diags; } static QString createDiagnosticToolTipString(const Diagnostic &diagnostic) @@ -100,28 +122,162 @@ static QString createDiagnosticToolTipString(const Diagnostic &diagnostic) return html; } -QVariant ClangStaticAnalyzerDiagnosticModel::data(const QModelIndex &index, int role) const +static QString createExplainingStepToolTipString(const ExplainingStep &step) { - if (!index.isValid()) - return QVariant(); + if (step.message == step.extendedMessage) + return createFullLocationString(step.location); + + typedef QPair<QString, QString> StringPair; + QList<StringPair> lines; + + if (!step.message.isEmpty()) { + lines << qMakePair( + QCoreApplication::translate("ClangStaticAnalyzer::ExplainingStep", "Message:"), + step.message.toHtmlEscaped()); + } + if (!step.extendedMessage.isEmpty()) { + lines << qMakePair( + QCoreApplication::translate("ClangStaticAnalyzer::ExplainingStep", "Extended Message:"), + step.extendedMessage.toHtmlEscaped()); + } + + lines << qMakePair( + QCoreApplication::translate("ClangStaticAnalyzer::ExplainingStep", "Location:"), + createFullLocationString(step.location)); + + QString html = QLatin1String("<html>" + "<head>" + "<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n" + "<body><dl>"); + + foreach (const StringPair &pair, lines) { + html += QLatin1String("<dt>"); + html += pair.first; + html += QLatin1String("</dt><dd>"); + html += pair.second; + html += QLatin1String("</dd>\n"); + } + html += QLatin1String("</dl></body></html>"); + return html; +} + +static QString createLocationString(const Analyzer::DiagnosticLocation &location) +{ + const QString filePath = location.filePath; + const QString lineNumber = QString::number(location.line); + const QString fileAndLine = filePath + QLatin1Char(':') + lineNumber; + return QLatin1String("in ") + fileAndLine; +} + +static QString createExplainingStepNumberString(int number) +{ + const int fieldWidth = 2; + return QString::fromLatin1("%1:").arg(number, fieldWidth); +} + +static QString createExplainingStepString(const ExplainingStep &explainingStep, int number) +{ + return createExplainingStepNumberString(number) + + QLatin1Char(' ') + + explainingStep.extendedMessage + + QLatin1Char(' ') + + createLocationString(explainingStep.location); +} + +static QString fullText(const Diagnostic &diagnostic) +{ + // Summary. + QString text = diagnostic.category + QLatin1String(": ") + diagnostic.type; + if (diagnostic.type != diagnostic.description) + text += QLatin1String(": ") + diagnostic.description; + text += QLatin1Char('\n'); + + // Explaining steps. + int explainingStepNumber = 1; + foreach (const ExplainingStep &explainingStep, diagnostic.explainingSteps) { + text += createExplainingStepString(explainingStep, explainingStepNumber++) + + QLatin1Char('\n'); + } + + text.chop(1); // Trailing newline. + return text; +} + - if (index.parent().isValid()) +DiagnosticItem::DiagnosticItem(const Diagnostic &diag) : m_diagnostic(diag) +{ + // Don't show explaining steps if they add no information. + if (diag.explainingSteps.count() == 1) { + const ExplainingStep &step = diag.explainingSteps.first(); + if (step.message == diag.description && step.location == diag.location) + return; + } + + foreach (const ExplainingStep &s, diag.explainingSteps) + appendChild(new ExplainingStepItem(s)); +} + +QVariant locationData(int role, const Analyzer::DiagnosticLocation &location) +{ + switch (role) { + case Analyzer::DetailedErrorView::LocationRole: + return QVariant::fromValue(location); + case Qt::ToolTipRole: + return location.filePath.isEmpty() ? QVariant() : QVariant(location.filePath); + default: return QVariant(); + } +} + +QVariant DiagnosticItem::data(int column, int role) const +{ + if (column == Analyzer::DetailedErrorView::LocationColumn) + return locationData(role, m_diagnostic.location); - const int row = index.row(); - if (row < 0 || row >= m_diagnostics.size()) + // DiagnosticColumn + switch (role) { + case Analyzer::DetailedErrorView::FullTextRole: + return fullText(m_diagnostic); + case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole: + return QVariant::fromValue(m_diagnostic); + case Qt::DisplayRole: + return m_diagnostic.description; + case Qt::ToolTipRole: + return createDiagnosticToolTipString(m_diagnostic); + default: return QVariant(); + } +} - const Diagnostic diagnostic = m_diagnostics.at(row); +ExplainingStepItem::ExplainingStepItem(const ExplainingStep &step) : m_step(step) +{ +} - if (role == Qt::DisplayRole) - return QString(QLatin1String("Some specific diagnostic")); // TODO: Remove? - else if (role == Qt::ToolTipRole) - return createDiagnosticToolTipString(diagnostic); - else if (role == Qt::UserRole) - return QVariant::fromValue<Diagnostic>(diagnostic); +QVariant ExplainingStepItem::data(int column, int role) const +{ + if (column == Analyzer::DetailedErrorView::LocationColumn) + return locationData(role, m_step.location); - return QVariant(); + // DiagnosticColumn + switch (role) { + case Analyzer::DetailedErrorView::FullTextRole: + return fullText(static_cast<DiagnosticItem *>(parent())->diagnostic()); + case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole: + return QVariant::fromValue(static_cast<DiagnosticItem *>(parent())->diagnostic()); + case Qt::DisplayRole: { + const int row = parent()->children().indexOf(const_cast<ExplainingStepItem *>(this)) + 1; + const int padding = static_cast<int>(std::log10(parent()->rowCount())) + - static_cast<int>(std::log10(row)); + return QString::fromLatin1("%1%2: %3") + .arg(QString(padding, QLatin1Char(' '))) + .arg(row) + .arg(m_step.message); + } + case Qt::ToolTipRole: + return createExplainingStepToolTipString(m_step); + default: + return QVariant(); + } } @@ -165,7 +321,8 @@ void ClangStaticAnalyzerDiagnosticFilterModel::addSuppressedDiagnostic( bool ClangStaticAnalyzerDiagnosticFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { - Q_UNUSED(sourceParent); + if (sourceParent.isValid()) + return true; const Diagnostic diag = static_cast<ClangStaticAnalyzerDiagnosticModel *>(sourceModel()) ->diagnostics().at(sourceRow); foreach (const SuppressedDiagnostic &d, m_suppressedDiagnostics) { |