aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp')
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerdiagnosticmodel.cpp215
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) {