forked from qt-creator/qt-creator
Analyzer: Re-design the diagnostics view.
The old one had a number of problems, mainly due to the awkward
delegate that was used for presenting the data. For instance:
- Only one diagnostic at a time could be looked at
in detail.
- Once it had been opened, it was not possible to close
such a detailed view again, other than by opening a new one.
We now use a tree view for showing the diagnostics, so users
can show and hide details about as many diagnostics as they
wish. That also gets us sensible item selection capabilities,
so features like suppressing several diagnostics at once can
be implemented in the future.
Change-Id: I840fdbfeca4d936ce600c8f6dde58b2ab93b0d00
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
@@ -14,6 +14,7 @@ SOURCES += \
|
|||||||
analyzerrunconfigwidget.cpp \
|
analyzerrunconfigwidget.cpp \
|
||||||
analyzerutils.cpp \
|
analyzerutils.cpp \
|
||||||
detailederrorview.cpp \
|
detailederrorview.cpp \
|
||||||
|
diagnosticlocation.cpp \
|
||||||
startremotedialog.cpp
|
startremotedialog.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
@@ -27,6 +28,7 @@ HEADERS += \
|
|||||||
analyzerrunconfigwidget.h \
|
analyzerrunconfigwidget.h \
|
||||||
analyzerutils.h \
|
analyzerutils.h \
|
||||||
detailederrorview.h \
|
detailederrorview.h \
|
||||||
|
diagnosticlocation.h \
|
||||||
startremotedialog.h
|
startremotedialog.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ QtcPlugin {
|
|||||||
"analyzerutils.h",
|
"analyzerutils.h",
|
||||||
"detailederrorview.cpp",
|
"detailederrorview.cpp",
|
||||||
"detailederrorview.h",
|
"detailederrorview.h",
|
||||||
|
"diagnosticlocation.cpp",
|
||||||
|
"diagnosticlocation.h",
|
||||||
"ianalyzertool.cpp",
|
"ianalyzertool.cpp",
|
||||||
"ianalyzertool.h",
|
"ianalyzertool.h",
|
||||||
"startremotedialog.cpp",
|
"startremotedialog.cpp",
|
||||||
|
|||||||
@@ -30,247 +30,132 @@
|
|||||||
|
|
||||||
#include "detailederrorview.h"
|
#include "detailederrorview.h"
|
||||||
|
|
||||||
|
#include "diagnosticlocation.h"
|
||||||
|
|
||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QAbstractTextDocumentLayout>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QContextMenuEvent>
|
#include <QContextMenuEvent>
|
||||||
#include <QFontMetrics>
|
#include <QFileInfo>
|
||||||
|
#include <QHeaderView>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QScrollBar>
|
#include <QSharedPointer>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
namespace Analyzer {
|
namespace Analyzer {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
DetailedErrorDelegate::DetailedErrorDelegate(QListView *parent)
|
class DetailedErrorDelegate : public QStyledItemDelegate
|
||||||
: QStyledItemDelegate(parent),
|
|
||||||
m_detailsWidget(0)
|
|
||||||
{
|
{
|
||||||
connect(parent->verticalScrollBar(), &QScrollBar::valueChanged,
|
Q_OBJECT
|
||||||
this, &DetailedErrorDelegate::onVerticalScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize DetailedErrorDelegate::sizeHint(const QStyleOptionViewItem &opt,
|
public:
|
||||||
const QModelIndex &index) const
|
DetailedErrorDelegate(QTreeView *parent) : QStyledItemDelegate(parent) { }
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return QStyledItemDelegate::sizeHint(opt, index);
|
|
||||||
|
|
||||||
const QListView *view = qobject_cast<const QListView *>(parent());
|
private:
|
||||||
const int viewportWidth = view->viewport()->width();
|
QString actualText(const QModelIndex &index) const
|
||||||
const bool isSelected = view->selectionModel()->currentIndex() == index;
|
{
|
||||||
const int dy = 2 * s_itemMargin;
|
const auto location = index.model()->data(index, DetailedErrorView::LocationRole)
|
||||||
|
.value<DiagnosticLocation>();
|
||||||
if (!isSelected) {
|
return location.isValid()
|
||||||
QFontMetrics fm(opt.font);
|
? QString::fromLatin1("<a href=\"file://%1\">%2:%3")
|
||||||
return QSize(viewportWidth, fm.height() + dy);
|
.arg(location.filePath, QFileInfo(location.filePath).fileName())
|
||||||
|
.arg(location.line)
|
||||||
|
: QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_detailsWidget && m_detailsIndex != index) {
|
using DocConstPtr = QSharedPointer<const QTextDocument>;
|
||||||
m_detailsWidget->deleteLater();
|
DocConstPtr document(const QStyleOptionViewItem &option) const
|
||||||
m_detailsWidget = 0;
|
{
|
||||||
|
const auto doc = QSharedPointer<QTextDocument>::create();
|
||||||
|
doc->setHtml(option.text);
|
||||||
|
doc->setTextWidth(option.rect.width());
|
||||||
|
doc->setDocumentMargin(0);
|
||||||
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_detailsWidget) {
|
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
|
||||||
m_detailsWidget = createDetailsWidget(opt.font, index, view->viewport());
|
{
|
||||||
QTC_ASSERT(m_detailsWidget->parent() == view->viewport(),
|
QStyleOptionViewItem opt = option;
|
||||||
m_detailsWidget->setParent(view->viewport()));
|
opt.text = actualText(index);
|
||||||
m_detailsIndex = index;
|
initStyleOption(&opt, index);
|
||||||
} else {
|
|
||||||
QTC_ASSERT(m_detailsIndex == index, /**/);
|
const DocConstPtr doc = document(opt);
|
||||||
|
return QSize(doc->idealWidth(), doc->size().height());
|
||||||
}
|
}
|
||||||
const int widthExcludingMargins = viewportWidth - 2 * s_itemMargin;
|
|
||||||
m_detailsWidget->setFixedWidth(widthExcludingMargins);
|
|
||||||
|
|
||||||
m_detailsWidgetHeight = m_detailsWidget->heightForWidth(widthExcludingMargins);
|
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||||
// HACK: it's a bug in QLabel(?) that we have to force the widget to have the size it said
|
const QModelIndex &index) const override
|
||||||
// it would have.
|
{
|
||||||
m_detailsWidget->setFixedHeight(m_detailsWidgetHeight);
|
QStyleOptionViewItem opt = option;
|
||||||
return QSize(viewportWidth, dy + m_detailsWidget->heightForWidth(widthExcludingMargins));
|
initStyleOption(&opt, index);
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &basicOption,
|
QStyle *style = opt.widget? opt.widget->style() : QApplication::style();
|
||||||
const QModelIndex &index) const
|
|
||||||
{
|
|
||||||
QStyleOptionViewItemV4 opt(basicOption);
|
|
||||||
initStyleOption(&opt, index);
|
|
||||||
|
|
||||||
const QListView *const view = qobject_cast<const QListView *>(parent());
|
// Painting item without text
|
||||||
const bool isSelected = view->selectionModel()->currentIndex() == index;
|
opt.text.clear();
|
||||||
|
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
|
||||||
|
opt.text = actualText(index);
|
||||||
|
|
||||||
QFontMetrics fm(opt.font);
|
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||||
QPoint pos = opt.rect.topLeft();
|
|
||||||
|
|
||||||
painter->save();
|
// Highlighting text if item is selected
|
||||||
|
if (opt.state & QStyle::State_Selected) {
|
||||||
const QColor bgColor = isSelected
|
ctx.palette.setColor(QPalette::Text, opt.palette.color(QPalette::Active,
|
||||||
? opt.palette.highlight().color()
|
QPalette::HighlightedText));
|
||||||
: opt.palette.background().color();
|
|
||||||
painter->setBrush(bgColor);
|
|
||||||
|
|
||||||
// clear background
|
|
||||||
painter->setPen(Qt::NoPen);
|
|
||||||
painter->drawRect(opt.rect);
|
|
||||||
|
|
||||||
pos.rx() += s_itemMargin;
|
|
||||||
pos.ry() += s_itemMargin;
|
|
||||||
|
|
||||||
if (isSelected) {
|
|
||||||
// only show detailed widget and let it handle everything
|
|
||||||
QTC_ASSERT(m_detailsIndex == index, /**/);
|
|
||||||
QTC_ASSERT(m_detailsWidget, return); // should have been set in sizeHint()
|
|
||||||
m_detailsWidget->move(pos);
|
|
||||||
// when scrolling quickly, the widget can get stuck in a visible part of the scroll area
|
|
||||||
// even though it should not be visible. therefore we hide it every time the scroll value
|
|
||||||
// changes and un-hide it when the item with details widget is paint()ed, i.e. visible.
|
|
||||||
m_detailsWidget->show();
|
|
||||||
|
|
||||||
const int viewportWidth = view->viewport()->width();
|
|
||||||
const int widthExcludingMargins = viewportWidth - 2 * s_itemMargin;
|
|
||||||
QTC_ASSERT(m_detailsWidget->width() == widthExcludingMargins, /**/);
|
|
||||||
QTC_ASSERT(m_detailsWidgetHeight == m_detailsWidget->height(), /**/);
|
|
||||||
} else {
|
|
||||||
// the reference coordinate for text drawing is the text baseline; move it inside the view rect.
|
|
||||||
pos.ry() += fm.ascent();
|
|
||||||
|
|
||||||
const QColor textColor = opt.palette.text().color();
|
|
||||||
painter->setPen(textColor);
|
|
||||||
// draw only text + location
|
|
||||||
|
|
||||||
const SummaryLineInfo info = summaryInfo(index);
|
|
||||||
const QString errorText = info.errorText;
|
|
||||||
painter->drawText(pos, errorText);
|
|
||||||
|
|
||||||
const int whatWidth = QFontMetrics(opt.font).width(errorText);
|
|
||||||
const int space = 10;
|
|
||||||
const int widthLeft = opt.rect.width() - (pos.x() + whatWidth + space + s_itemMargin);
|
|
||||||
if (widthLeft > 0) {
|
|
||||||
QFont monospace = opt.font;
|
|
||||||
monospace.setFamily(QLatin1String("monospace"));
|
|
||||||
QFontMetrics metrics(monospace);
|
|
||||||
QColor nameColor = textColor;
|
|
||||||
nameColor.setAlphaF(0.7);
|
|
||||||
|
|
||||||
painter->setFont(monospace);
|
|
||||||
painter->setPen(nameColor);
|
|
||||||
|
|
||||||
QPoint namePos = pos;
|
|
||||||
namePos.rx() += whatWidth + space;
|
|
||||||
painter->drawText(namePos, metrics.elidedText(info.errorLocation, Qt::ElideLeft,
|
|
||||||
widthLeft));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt);
|
||||||
|
painter->save();
|
||||||
|
painter->translate(textRect.topLeft());
|
||||||
|
painter->setClipRect(textRect.translated(-textRect.topLeft()));
|
||||||
|
document(opt)->documentLayout()->draw(painter, ctx);
|
||||||
|
painter->restore();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Separator lines (like Issues pane)
|
} // namespace Internal
|
||||||
painter->setPen(QColor::fromRgb(150,150,150));
|
|
||||||
painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
|
|
||||||
|
|
||||||
painter->restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorDelegate::onCurrentSelectionChanged(const QModelIndex &now,
|
|
||||||
const QModelIndex &previous)
|
|
||||||
{
|
|
||||||
if (m_detailsWidget) {
|
|
||||||
m_detailsWidget->deleteLater();
|
|
||||||
m_detailsWidget = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_detailsIndex = QModelIndex();
|
|
||||||
if (now.isValid())
|
|
||||||
emit sizeHintChanged(now);
|
|
||||||
if (previous.isValid())
|
|
||||||
emit sizeHintChanged(previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorDelegate::onLayoutChanged()
|
|
||||||
{
|
|
||||||
if (m_detailsWidget) {
|
|
||||||
m_detailsWidget->deleteLater();
|
|
||||||
m_detailsWidget = 0;
|
|
||||||
m_detailsIndex = QModelIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorDelegate::onViewResized()
|
|
||||||
{
|
|
||||||
const QListView *view = qobject_cast<const QListView *>(parent());
|
|
||||||
if (m_detailsWidget)
|
|
||||||
emit sizeHintChanged(view->selectionModel()->currentIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorDelegate::onVerticalScroll()
|
|
||||||
{
|
|
||||||
if (m_detailsWidget)
|
|
||||||
m_detailsWidget->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expects "file://some/path[:line[:column]]" - the line/column part is optional
|
|
||||||
void DetailedErrorDelegate::openLinkInEditor(const QString &link)
|
|
||||||
{
|
|
||||||
const QString linkWithoutPrefix = link.mid(int(strlen("file://")));
|
|
||||||
const QChar separator = QLatin1Char(':');
|
|
||||||
const int lineColon = linkWithoutPrefix.indexOf(separator, /*after drive letter + colon =*/ 2);
|
|
||||||
const QString path = linkWithoutPrefix.left(lineColon);
|
|
||||||
const QString lineColumn = linkWithoutPrefix.mid(lineColon + 1);
|
|
||||||
const int line = lineColumn.section(separator, 0, 0).toInt();
|
|
||||||
const int column = lineColumn.section(separator, 1, 1).toInt();
|
|
||||||
Core::EditorManager::openEditorAt(path, qMax(line, 0), qMax(column, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorDelegate::copyToClipboard()
|
|
||||||
{
|
|
||||||
QApplication::clipboard()->setText(textualRepresentation());
|
|
||||||
}
|
|
||||||
|
|
||||||
DetailedErrorView::DetailedErrorView(QWidget *parent) :
|
DetailedErrorView::DetailedErrorView(QWidget *parent) :
|
||||||
QListView(parent),
|
QTreeView(parent),
|
||||||
m_copyAction(0)
|
m_copyAction(new QAction(this))
|
||||||
{
|
{
|
||||||
}
|
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
setItemDelegateForColumn(LocationColumn, new Internal::DetailedErrorDelegate(this));
|
||||||
|
|
||||||
DetailedErrorView::~DetailedErrorView()
|
|
||||||
{
|
|
||||||
itemDelegate()->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorView::setItemDelegate(QAbstractItemDelegate *delegate)
|
|
||||||
{
|
|
||||||
QListView::setItemDelegate(delegate);
|
|
||||||
|
|
||||||
DetailedErrorDelegate *myDelegate = qobject_cast<DetailedErrorDelegate *>(itemDelegate());
|
|
||||||
connect(this, &DetailedErrorView::resized, myDelegate, &DetailedErrorDelegate::onViewResized);
|
|
||||||
|
|
||||||
m_copyAction = new QAction(this);
|
|
||||||
m_copyAction->setText(tr("Copy"));
|
m_copyAction->setText(tr("Copy"));
|
||||||
m_copyAction->setIcon(QIcon(QLatin1String(Core::Constants::ICON_COPY)));
|
m_copyAction->setIcon(QIcon(QLatin1String(Core::Constants::ICON_COPY)));
|
||||||
m_copyAction->setShortcut(QKeySequence::Copy);
|
m_copyAction->setShortcut(QKeySequence::Copy);
|
||||||
m_copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
m_copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||||
connect(m_copyAction, &QAction::triggered, myDelegate, &DetailedErrorDelegate::copyToClipboard);
|
connect(m_copyAction, &QAction::triggered, [this] {
|
||||||
|
const QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||||
|
QTC_ASSERT(selectedRows.count() == 1, return);
|
||||||
|
QApplication::clipboard()->setText(model()->data(selectedRows.first(),
|
||||||
|
FullTextRole).toString());
|
||||||
|
});
|
||||||
|
connect(this, &QAbstractItemView::clicked, [](const QModelIndex &index) {
|
||||||
|
if (index.column() == LocationColumn) {
|
||||||
|
const auto loc = index.model()
|
||||||
|
->data(index, Analyzer::DetailedErrorView::LocationRole)
|
||||||
|
.value<DiagnosticLocation>();
|
||||||
|
if (loc.isValid())
|
||||||
|
Core::EditorManager::openEditorAt(loc.filePath, loc.line, loc.column - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
addAction(m_copyAction);
|
addAction(m_copyAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailedErrorView::setModel(QAbstractItemModel *model)
|
DetailedErrorView::~DetailedErrorView()
|
||||||
{
|
{
|
||||||
QListView::setModel(model);
|
|
||||||
|
|
||||||
DetailedErrorDelegate *delegate = qobject_cast<DetailedErrorDelegate *>(itemDelegate());
|
|
||||||
QTC_ASSERT(delegate, return);
|
|
||||||
|
|
||||||
connect(selectionModel(), &QItemSelectionModel::currentChanged,
|
|
||||||
delegate, &DetailedErrorDelegate::onCurrentSelectionChanged);
|
|
||||||
connect(model, &QAbstractItemModel::layoutChanged,
|
|
||||||
delegate, &DetailedErrorDelegate::onLayoutChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorView::resizeEvent(QResizeEvent *e)
|
|
||||||
{
|
|
||||||
emit resized();
|
|
||||||
QListView::resizeEvent(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailedErrorView::contextMenuEvent(QContextMenuEvent *e)
|
void DetailedErrorView::contextMenuEvent(QContextMenuEvent *e)
|
||||||
@@ -288,19 +173,6 @@ void DetailedErrorView::contextMenuEvent(QContextMenuEvent *e)
|
|||||||
menu.exec(e->globalPos());
|
menu.exec(e->globalPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailedErrorView::updateGeometries()
|
|
||||||
{
|
|
||||||
if (model()) {
|
|
||||||
QModelIndex index = model()->index(0, modelColumn(), rootIndex());
|
|
||||||
QStyleOptionViewItem option = viewOptions();
|
|
||||||
// delegate for row / column
|
|
||||||
QSize step = itemDelegate()->sizeHint(option, index);
|
|
||||||
horizontalScrollBar()->setSingleStep(step.width() + spacing());
|
|
||||||
verticalScrollBar()->setSingleStep(step.height() + spacing());
|
|
||||||
}
|
|
||||||
QListView::updateGeometries();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DetailedErrorView::goNext()
|
void DetailedErrorView::goNext()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(rowCount(), return);
|
QTC_ASSERT(rowCount(), return);
|
||||||
@@ -346,3 +218,5 @@ void DetailedErrorView::setCurrentRow(int row)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Analyzer
|
} // namespace Analyzer
|
||||||
|
|
||||||
|
#include "detailederrorview.moc"
|
||||||
|
|||||||
@@ -33,59 +33,12 @@
|
|||||||
|
|
||||||
#include "analyzerbase_global.h"
|
#include "analyzerbase_global.h"
|
||||||
|
|
||||||
#include <QListView>
|
#include <QTreeView>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
namespace Analyzer {
|
namespace Analyzer {
|
||||||
|
|
||||||
// Provides the details widget for the DetailedErrorView
|
class ANALYZER_EXPORT DetailedErrorView : public QTreeView
|
||||||
class ANALYZER_EXPORT DetailedErrorDelegate : public QStyledItemDelegate
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct SummaryLineInfo {
|
|
||||||
QString errorText;
|
|
||||||
QString errorLocation;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// This delegate can only work on one view at a time, parent. parent will also be the parent
|
|
||||||
/// in the QObject parent-child system.
|
|
||||||
explicit DetailedErrorDelegate(QListView *parent);
|
|
||||||
|
|
||||||
virtual SummaryLineInfo summaryInfo(const QModelIndex &index) const = 0;
|
|
||||||
void copyToClipboard();
|
|
||||||
|
|
||||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
|
||||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
|
||||||
const QModelIndex &index) const;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void onCurrentSelectionChanged(const QModelIndex &now, const QModelIndex &previous);
|
|
||||||
void onViewResized();
|
|
||||||
void onLayoutChanged();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onVerticalScroll();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void openLinkInEditor(const QString &link);
|
|
||||||
mutable QPersistentModelIndex m_detailsIndex;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// the constness of this function is a necessary lie because it is called from paint() const.
|
|
||||||
virtual QWidget *createDetailsWidget(const QFont &font, const QModelIndex &errorIndex,
|
|
||||||
QWidget *parent) const = 0;
|
|
||||||
virtual QString textualRepresentation() const = 0;
|
|
||||||
|
|
||||||
static const int s_itemMargin = 2;
|
|
||||||
mutable QWidget *m_detailsWidget;
|
|
||||||
mutable int m_detailsWidgetHeight;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A QListView that displays additional details for the currently selected item
|
|
||||||
class ANALYZER_EXPORT DetailedErrorView : public QListView
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -93,21 +46,18 @@ public:
|
|||||||
DetailedErrorView(QWidget *parent = 0);
|
DetailedErrorView(QWidget *parent = 0);
|
||||||
~DetailedErrorView();
|
~DetailedErrorView();
|
||||||
|
|
||||||
// Reimplemented to connect delegate to connection model after it has
|
|
||||||
// been set by superclass implementation.
|
|
||||||
void setModel(QAbstractItemModel *model);
|
|
||||||
|
|
||||||
void setItemDelegate(QAbstractItemDelegate *delegate); // Takes ownership
|
|
||||||
|
|
||||||
void goNext();
|
void goNext();
|
||||||
void goBack();
|
void goBack();
|
||||||
|
|
||||||
signals:
|
enum ItemRole {
|
||||||
void resized();
|
LocationRole = Qt::UserRole,
|
||||||
|
FullTextRole
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
enum Column {
|
||||||
void updateGeometries();
|
DiagnosticColumn,
|
||||||
void resizeEvent(QResizeEvent *e);
|
LocationColumn,
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
@@ -119,7 +69,7 @@ private:
|
|||||||
QList<QAction *> commonActions() const;
|
QList<QAction *> commonActions() const;
|
||||||
virtual QList<QAction *> customActions() const;
|
virtual QList<QAction *> customActions() const;
|
||||||
|
|
||||||
QAction *m_copyAction;
|
QAction * const m_copyAction;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Analyzer
|
} // namespace Analyzer
|
||||||
|
|||||||
65
src/plugins/analyzerbase/diagnosticlocation.cpp
Normal file
65
src/plugins/analyzerbase/diagnosticlocation.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2015 The Qt Company Ltd.
|
||||||
|
** Contact: http://www.qt.io/licensing
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms and
|
||||||
|
** conditions see http://www.qt.io/terms-conditions. For further information
|
||||||
|
** use the contact form at http://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, The Qt Company gives you certain additional
|
||||||
|
** rights. These rights are described in The Qt Company LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "diagnosticlocation.h"
|
||||||
|
|
||||||
|
namespace Analyzer {
|
||||||
|
|
||||||
|
DiagnosticLocation::DiagnosticLocation() : line(0), column(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DiagnosticLocation::DiagnosticLocation(const QString &filePath, int line, int column)
|
||||||
|
: filePath(filePath), line(line), column(column)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiagnosticLocation::isValid() const
|
||||||
|
{
|
||||||
|
return !filePath.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const DiagnosticLocation &first, const DiagnosticLocation &second)
|
||||||
|
{
|
||||||
|
return first.filePath == second.filePath
|
||||||
|
&& first.line == second.line
|
||||||
|
&& first.column == second.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const DiagnosticLocation &location)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "Location(" << location.filePath << ", "
|
||||||
|
<< location.line << ", "
|
||||||
|
<< location.column << ')';
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Analyzer
|
||||||
|
|
||||||
64
src/plugins/analyzerbase/diagnosticlocation.h
Normal file
64
src/plugins/analyzerbase/diagnosticlocation.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2015 The Qt Company Ltd.
|
||||||
|
** Contact: http://www.qt.io/licensing
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms and
|
||||||
|
** conditions see http://www.qt.io/terms-conditions. For further information
|
||||||
|
** use the contact form at http://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, The Qt Company gives you certain additional
|
||||||
|
** rights. These rights are described in The Qt Company LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef ANALYZERDIAGNOSTIC_H
|
||||||
|
#define ANALYZERDIAGNOSTIC_H
|
||||||
|
|
||||||
|
#include "analyzerbase_global.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace Analyzer {
|
||||||
|
|
||||||
|
class ANALYZER_EXPORT DiagnosticLocation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DiagnosticLocation();
|
||||||
|
DiagnosticLocation(const QString &filePath, int line, int column);
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
QString filePath;
|
||||||
|
|
||||||
|
// Both values start at 1.
|
||||||
|
int line;
|
||||||
|
int column;
|
||||||
|
};
|
||||||
|
|
||||||
|
ANALYZER_EXPORT bool operator==(const DiagnosticLocation &first, const DiagnosticLocation &second);
|
||||||
|
ANALYZER_EXPORT QDebug operator<<(QDebug dbg, const DiagnosticLocation &location);
|
||||||
|
|
||||||
|
} // namespace Analyzer
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(Analyzer::DiagnosticLocation)
|
||||||
|
|
||||||
|
#endif // Include guard.
|
||||||
|
|
||||||
@@ -47,245 +47,17 @@
|
|||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QLabel>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include <QStyledItemDelegate>
|
|
||||||
#include <QTextDocument>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
using namespace Valgrind::XmlProtocol;
|
using namespace Valgrind::XmlProtocol;
|
||||||
|
|
||||||
namespace Valgrind {
|
namespace Valgrind {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class MemcheckErrorDelegate : public Analyzer::DetailedErrorDelegate
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MemcheckErrorDelegate(QListView *parent);
|
|
||||||
|
|
||||||
SummaryLineInfo summaryInfo(const QModelIndex &index) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QWidget *createDetailsWidget(const QFont &font, const QModelIndex &errorIndex,
|
|
||||||
QWidget *parent) const;
|
|
||||||
QString textualRepresentation() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
static QString makeFrameName(const Frame &frame, const QString &relativeTo,
|
|
||||||
bool link = true, const QString &linkAttr = QString())
|
|
||||||
{
|
|
||||||
const QString d = frame.directory();
|
|
||||||
const QString f = frame.fileName();
|
|
||||||
const QString fn = frame.functionName();
|
|
||||||
const QString fullPath = frame.filePath();
|
|
||||||
|
|
||||||
QString path;
|
|
||||||
if (!d.isEmpty() && !f.isEmpty())
|
|
||||||
path = fullPath;
|
|
||||||
else
|
|
||||||
path = frame.object();
|
|
||||||
|
|
||||||
if (QFile::exists(path))
|
|
||||||
path = QFileInfo(path).canonicalFilePath();
|
|
||||||
|
|
||||||
if (path.startsWith(relativeTo))
|
|
||||||
path.remove(0, relativeTo.length());
|
|
||||||
|
|
||||||
if (frame.line() != -1)
|
|
||||||
path += QLatin1Char(':') + QString::number(frame.line());
|
|
||||||
|
|
||||||
// Since valgrind only runs on POSIX systems, converting path separators
|
|
||||||
// will ruin the paths on Windows. Leave it untouched.
|
|
||||||
path = path.toHtmlEscaped();
|
|
||||||
|
|
||||||
if (link && !f.isEmpty() && QFile::exists(fullPath)) {
|
|
||||||
// make a hyperlink label
|
|
||||||
path = QString::fromLatin1("<a href=\"file://%1:%2\" %4>%3</a>")
|
|
||||||
.arg(fullPath).arg(frame.line()).arg(path).arg(linkAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fn.isEmpty())
|
|
||||||
return QCoreApplication::translate("Valgrind::Internal", "%1 in %2").arg(fn.toHtmlEscaped(), path);
|
|
||||||
if (!path.isEmpty())
|
|
||||||
return path;
|
|
||||||
return QString::fromLatin1("0x%1").arg(frame.instructionPointer(), 0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString relativeToPath()
|
|
||||||
{
|
|
||||||
// The project for which we insert the snippet.
|
|
||||||
const ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
|
||||||
|
|
||||||
QString relativeTo(project ? project->projectDirectory().toString() : QDir::homePath());
|
|
||||||
const QChar slash = QLatin1Char('/');
|
|
||||||
if (!relativeTo.endsWith(slash))
|
|
||||||
relativeTo.append(slash);
|
|
||||||
|
|
||||||
return relativeTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString errorLocation(const QModelIndex &index, const Error &error,
|
|
||||||
bool link = false, bool absolutePath = false,
|
|
||||||
const QString &linkAttr = QString())
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return QString();
|
|
||||||
const ErrorListModel *model = 0;
|
|
||||||
const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel *>(index.model());
|
|
||||||
while (!model && proxy) {
|
|
||||||
model = qobject_cast<const ErrorListModel *>(proxy->sourceModel());
|
|
||||||
proxy = qobject_cast<const QAbstractProxyModel *>(proxy->sourceModel());
|
|
||||||
}
|
|
||||||
QTC_ASSERT(model, return QString());
|
|
||||||
|
|
||||||
const QString relativePath = absolutePath ? QString() : relativeToPath();
|
|
||||||
return QCoreApplication::translate("Valgrind::Internal", "in %1").
|
|
||||||
arg(makeFrameName(model->findRelevantFrame(error), relativePath,
|
|
||||||
link, linkAttr));
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget *MemcheckErrorDelegate::createDetailsWidget(const QFont & font,
|
|
||||||
const QModelIndex &errorIndex,
|
|
||||||
QWidget *parent) const
|
|
||||||
{
|
|
||||||
QWidget *widget = new QWidget(parent);
|
|
||||||
QTC_ASSERT(errorIndex.isValid(), return widget);
|
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout;
|
|
||||||
// code + white-space:pre so the padding (see below) works properly
|
|
||||||
// don't include frameName here as it should wrap if required and pre-line is not supported
|
|
||||||
// by Qt yet it seems
|
|
||||||
const QString displayTextTemplate = QString::fromLatin1("<code style='white-space:pre'>%1:</code> %2");
|
|
||||||
const QString relativeTo = relativeToPath();
|
|
||||||
const Error error = errorIndex.data(ErrorListModel::ErrorRole).value<Error>();
|
|
||||||
|
|
||||||
QLabel *errorLabel = new QLabel();
|
|
||||||
errorLabel->setWordWrap(true);
|
|
||||||
errorLabel->setContentsMargins(0, 0, 0, 0);
|
|
||||||
errorLabel->setMargin(0);
|
|
||||||
errorLabel->setIndent(0);
|
|
||||||
QPalette p = errorLabel->palette();
|
|
||||||
QColor lc = p.color(QPalette::Text);
|
|
||||||
QString linkStyle = QString::fromLatin1("style=\"color:rgba(%1, %2, %3, %4);\"")
|
|
||||||
.arg(lc.red()).arg(lc.green()).arg(lc.blue()).arg(int(0.7 * 255));
|
|
||||||
p.setBrush(QPalette::Text, p.highlightedText());
|
|
||||||
errorLabel->setPalette(p);
|
|
||||||
errorLabel->setText(QString::fromLatin1("%1 <span %4>%2</span>")
|
|
||||||
.arg(error.what(),
|
|
||||||
errorLocation(errorIndex, error, /*link=*/ true,
|
|
||||||
/*absolutePath=*/ false, linkStyle),
|
|
||||||
linkStyle));
|
|
||||||
connect(errorLabel, &QLabel::linkActivated, this, &MemcheckErrorDelegate::openLinkInEditor);
|
|
||||||
layout->addWidget(errorLabel);
|
|
||||||
|
|
||||||
const QVector<Stack> stacks = error.stacks();
|
|
||||||
for (int i = 0; i < stacks.count(); ++i) {
|
|
||||||
const Stack &stack = stacks.at(i);
|
|
||||||
// auxwhat for additional stacks
|
|
||||||
if (i > 0) {
|
|
||||||
QLabel *stackLabel = new QLabel(stack.auxWhat());
|
|
||||||
stackLabel->setWordWrap(true);
|
|
||||||
stackLabel->setContentsMargins(0, 0, 0, 0);
|
|
||||||
stackLabel->setMargin(0);
|
|
||||||
stackLabel->setIndent(0);
|
|
||||||
QPalette p = stackLabel->palette();
|
|
||||||
p.setBrush(QPalette::Text, p.highlightedText());
|
|
||||||
stackLabel->setPalette(p);
|
|
||||||
layout->addWidget(stackLabel);
|
|
||||||
}
|
|
||||||
int frameNr = 1;
|
|
||||||
foreach (const Frame &frame, stack.frames()) {
|
|
||||||
QString frameName = makeFrameName(frame, relativeTo);
|
|
||||||
QTC_ASSERT(!frameName.isEmpty(), /**/);
|
|
||||||
|
|
||||||
QLabel *frameLabel = new QLabel(widget);
|
|
||||||
frameLabel->setAutoFillBackground(true);
|
|
||||||
if (frameNr % 2 == 0) {
|
|
||||||
// alternating rows
|
|
||||||
QPalette p = frameLabel->palette();
|
|
||||||
p.setBrush(QPalette::Base, p.alternateBase());
|
|
||||||
frameLabel->setPalette(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
QFont fixedPitchFont = font;
|
|
||||||
fixedPitchFont.setFixedPitch(true);
|
|
||||||
frameLabel->setFont(fixedPitchFont);
|
|
||||||
connect(frameLabel, &QLabel::linkActivated, this, &MemcheckErrorDelegate::openLinkInEditor);
|
|
||||||
// pad frameNr to 2 chars since only 50 frames max are supported by valgrind
|
|
||||||
const QString displayText = displayTextTemplate
|
|
||||||
.arg(frameNr++, 2).arg(frameName);
|
|
||||||
frameLabel->setText(displayText);
|
|
||||||
|
|
||||||
frameLabel->setToolTip(toolTipForFrame(frame));
|
|
||||||
frameLabel->setWordWrap(true);
|
|
||||||
frameLabel->setContentsMargins(0, 0, 0, 0);
|
|
||||||
frameLabel->setMargin(0);
|
|
||||||
frameLabel->setIndent(10);
|
|
||||||
layout->addWidget(frameLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
layout->setSpacing(0);
|
|
||||||
widget->setLayout(layout);
|
|
||||||
return widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemcheckErrorDelegate::MemcheckErrorDelegate(QListView *parent)
|
|
||||||
: Analyzer::DetailedErrorDelegate(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Analyzer::DetailedErrorDelegate::SummaryLineInfo MemcheckErrorDelegate::summaryInfo(
|
|
||||||
const QModelIndex &index) const
|
|
||||||
{
|
|
||||||
const Error error = index.data(ErrorListModel::ErrorRole).value<Error>();
|
|
||||||
SummaryLineInfo info;
|
|
||||||
info.errorText = error.what();
|
|
||||||
info.errorLocation = errorLocation(index, error);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MemcheckErrorDelegate::textualRepresentation() const
|
|
||||||
{
|
|
||||||
QTC_ASSERT(m_detailsIndex.isValid(), return QString());
|
|
||||||
|
|
||||||
QString content;
|
|
||||||
QTextStream stream(&content);
|
|
||||||
const Error error = m_detailsIndex.data(ErrorListModel::ErrorRole).value<Error>();
|
|
||||||
|
|
||||||
stream << error.what() << "\n";
|
|
||||||
stream << " "
|
|
||||||
<< errorLocation(m_detailsIndex, error, /*link=*/ false, /*absolutePath=*/ true)
|
|
||||||
<< "\n";
|
|
||||||
|
|
||||||
foreach (const Stack &stack, error.stacks()) {
|
|
||||||
if (!stack.auxWhat().isEmpty())
|
|
||||||
stream << stack.auxWhat();
|
|
||||||
int i = 1;
|
|
||||||
foreach (const Frame &frame, stack.frames())
|
|
||||||
stream << " " << i++ << ": " << makeFrameName(frame, QString(), false) << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.flush();
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemcheckErrorView::MemcheckErrorView(QWidget *parent)
|
MemcheckErrorView::MemcheckErrorView(QWidget *parent)
|
||||||
: Analyzer::DetailedErrorView(parent),
|
: Analyzer::DetailedErrorView(parent),
|
||||||
m_settings(0)
|
m_settings(0)
|
||||||
{
|
{
|
||||||
MemcheckErrorDelegate *delegate = new MemcheckErrorDelegate(this);
|
|
||||||
setItemDelegate(delegate);
|
|
||||||
|
|
||||||
m_suppressAction = new QAction(this);
|
m_suppressAction = new QAction(this);
|
||||||
m_suppressAction->setText(tr("Suppress Error"));
|
m_suppressAction->setText(tr("Suppress Error"));
|
||||||
m_suppressAction->setIcon(QIcon(QLatin1String(":/valgrind/images/eye_crossed.png")));
|
m_suppressAction->setIcon(QIcon(QLatin1String(":/valgrind/images/eye_crossed.png")));
|
||||||
@@ -343,5 +115,3 @@ QList<QAction *> MemcheckErrorView::customActions() const
|
|||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Valgrind
|
} // namespace Valgrind
|
||||||
|
|
||||||
#include "memcheckerrorview.moc"
|
|
||||||
|
|||||||
@@ -35,29 +35,74 @@
|
|||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
#include "modelhelpers.h"
|
#include "modelhelpers.h"
|
||||||
|
|
||||||
|
#include <analyzerbase/diagnosticlocation.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace Valgrind {
|
namespace Valgrind {
|
||||||
namespace XmlProtocol {
|
namespace XmlProtocol {
|
||||||
|
|
||||||
class ErrorListModel::Private
|
class ErrorListModelPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QVector<Error> errors;
|
QVariant errorData(const QModelIndex &index, int role) const;
|
||||||
QVariant errorData(int row, int column, int role) const;
|
|
||||||
QSharedPointer<const ErrorListModel::RelevantFrameFinder> relevantFrameFinder;
|
QSharedPointer<const ErrorListModel::RelevantFrameFinder> relevantFrameFinder;
|
||||||
Frame findRelevantFrame(const Error &error) const;
|
Frame findRelevantFrame(const Error &error) const;
|
||||||
QString formatAbsoluteFilePath(const Error &error) const;
|
QString errorLocation(const Error &error) const;
|
||||||
QString formatLocation(const Error &error) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorListModel::ErrorListModel(QObject *parent)
|
class ErrorItem : public Utils::TreeItem
|
||||||
: QAbstractItemModel(parent)
|
|
||||||
, d(new Private)
|
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
ErrorItem(const ErrorListModelPrivate *modelPrivate, const Error &error);
|
||||||
|
|
||||||
|
const ErrorListModelPrivate *modelPrivate() const { return m_modelPrivate; }
|
||||||
|
Error error() const { return m_error; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant data(int column, int role) const override;
|
||||||
|
|
||||||
|
const ErrorListModelPrivate * const m_modelPrivate;
|
||||||
|
const Error m_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StackItem : public Utils::TreeItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StackItem(const Stack &stack);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant data(int column, int role) const override;
|
||||||
|
|
||||||
|
const ErrorItem *getErrorItem() const;
|
||||||
|
|
||||||
|
const Stack m_stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrameItem : public Utils::TreeItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FrameItem(const Frame &frame);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant data(int column, int role) const override;
|
||||||
|
|
||||||
|
const ErrorItem *getErrorItem() const;
|
||||||
|
|
||||||
|
const Frame m_frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ErrorListModel::ErrorListModel(QObject *parent)
|
||||||
|
: Utils::TreeModel(parent)
|
||||||
|
, d(new ErrorListModelPrivate)
|
||||||
|
{
|
||||||
|
setHeader(QStringList() << tr("Issue") << tr("Location"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorListModel::~ErrorListModel()
|
ErrorListModel::~ErrorListModel()
|
||||||
@@ -75,27 +120,7 @@ void ErrorListModel::setRelevantFrameFinder(const QSharedPointer<const RelevantF
|
|||||||
d->relevantFrameFinder = finder;
|
d->relevantFrameFinder = finder;
|
||||||
}
|
}
|
||||||
|
|
||||||
Frame ErrorListModel::findRelevantFrame(const Error &error) const
|
Frame ErrorListModelPrivate::findRelevantFrame(const Error &error) const
|
||||||
{
|
|
||||||
return d->findRelevantFrame(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex ErrorListModel::index(int row, int column, const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
if (parent.isValid()) {
|
|
||||||
QTC_ASSERT(parent.model() == this, qt_noop());
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
return createIndex(row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex ErrorListModel::parent(const QModelIndex &child) const
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!child.isValid() || child.model() == this, return QModelIndex());
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
Frame ErrorListModel::Private::findRelevantFrame(const Error &error) const
|
|
||||||
{
|
{
|
||||||
if (relevantFrameFinder)
|
if (relevantFrameFinder)
|
||||||
return relevantFrameFinder->findRelevant(error);
|
return relevantFrameFinder->findRelevant(error);
|
||||||
@@ -109,181 +134,194 @@ Frame ErrorListModel::Private::findRelevantFrame(const Error &error) const
|
|||||||
return Frame();
|
return Frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ErrorListModel::Private::formatAbsoluteFilePath(const Error &error) const
|
static QString makeFrameName(const Frame &frame, bool withLocation)
|
||||||
{
|
{
|
||||||
return findRelevantFrame(error).filePath();
|
const QString d = frame.directory();
|
||||||
}
|
const QString f = frame.fileName();
|
||||||
|
const QString fn = frame.functionName();
|
||||||
|
const QString fullPath = frame.filePath();
|
||||||
|
|
||||||
QString ErrorListModel::Private::formatLocation(const Error &error) const
|
QString path;
|
||||||
{
|
if (!d.isEmpty() && !f.isEmpty())
|
||||||
const Frame frame = findRelevantFrame(error);
|
path = fullPath;
|
||||||
const QString file = frame.fileName();
|
else
|
||||||
if (!frame.functionName().isEmpty())
|
path = frame.object();
|
||||||
return frame.functionName();
|
|
||||||
if (!file.isEmpty()) {
|
if (QFile::exists(path))
|
||||||
const qint64 line = frame.line();
|
path = QFileInfo(path).canonicalFilePath();
|
||||||
if (line > 0)
|
|
||||||
return QString::fromLatin1("%1:%2").arg(file, QString::number(frame.line()));
|
if (frame.line() != -1)
|
||||||
return file;
|
path += QLatin1Char(':') + QString::number(frame.line());
|
||||||
|
|
||||||
|
if (!fn.isEmpty()) {
|
||||||
|
const QString location = withLocation || path == frame.object()
|
||||||
|
? QString::fromLatin1(" in %2").arg(path) : QString();
|
||||||
|
return QCoreApplication::translate("Valgrind::Internal", "%1%2").arg(fn, location);
|
||||||
}
|
}
|
||||||
return frame.object();
|
if (!path.isEmpty())
|
||||||
|
return path;
|
||||||
|
return QString::fromLatin1("0x%1").arg(frame.instructionPointer(), 0, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ErrorListModel::Private::errorData(int row, int column, int role) const
|
QString ErrorListModelPrivate::errorLocation(const Error &error) const
|
||||||
{
|
{
|
||||||
// A dummy entry.
|
return QCoreApplication::translate("Valgrind::Internal", "in %1").
|
||||||
if (row == 0 && errors.isEmpty()) {
|
arg(makeFrameName(findRelevantFrame(error), true));
|
||||||
if (role == Qt::DisplayRole)
|
|
||||||
return tr("No errors found");
|
|
||||||
if (role == ErrorRole)
|
|
||||||
return tr("No errors found");
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row < 0 || row >= errors.size())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
const Error &error = errors.at(row);
|
|
||||||
|
|
||||||
if (error.stacks().count())
|
|
||||||
switch (role) {
|
|
||||||
case Qt::DisplayRole: {
|
|
||||||
switch (column) {
|
|
||||||
case WhatColumn:
|
|
||||||
return error.what();
|
|
||||||
case LocationColumn:
|
|
||||||
return formatLocation(error);
|
|
||||||
case AbsoluteFilePathColumn:
|
|
||||||
return formatAbsoluteFilePath(error);
|
|
||||||
case LineColumn: {
|
|
||||||
const qint64 line = findRelevantFrame(error).line();
|
|
||||||
return line > 0 ? line : QVariant();
|
|
||||||
}
|
|
||||||
case UniqueColumn:
|
|
||||||
return error.unique();
|
|
||||||
case TidColumn:
|
|
||||||
return error.tid();
|
|
||||||
case KindColumn:
|
|
||||||
return error.kind();
|
|
||||||
case LeakedBlocksColumn:
|
|
||||||
return error.leakedBlocks();
|
|
||||||
case LeakedBytesColumn:
|
|
||||||
return error.leakedBytes();
|
|
||||||
case HelgrindThreadIdColumn:
|
|
||||||
return error.helgrindThreadId();
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Qt::ToolTipRole:
|
|
||||||
return toolTipForFrame(findRelevantFrame(error));
|
|
||||||
case ErrorRole:
|
|
||||||
return QVariant::fromValue<Error>(error);
|
|
||||||
case AbsoluteFilePathRole:
|
|
||||||
return formatAbsoluteFilePath(error);
|
|
||||||
case FileRole:
|
|
||||||
return findRelevantFrame(error).fileName();
|
|
||||||
case LineRole: {
|
|
||||||
const qint64 line = findRelevantFrame(error).line();
|
|
||||||
return line > 0 ? line : QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ErrorListModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
if (!index.parent().isValid())
|
|
||||||
return d->errorData(index.row(), index.column(), role);
|
|
||||||
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ErrorListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
||||||
{
|
|
||||||
if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
switch (section) {
|
|
||||||
case WhatColumn:
|
|
||||||
return tr("What");
|
|
||||||
case LocationColumn:
|
|
||||||
return tr("Location");
|
|
||||||
case AbsoluteFilePathColumn:
|
|
||||||
return tr("File");
|
|
||||||
case LineColumn:
|
|
||||||
return tr("Line");
|
|
||||||
case UniqueColumn:
|
|
||||||
return tr("Unique");
|
|
||||||
case TidColumn:
|
|
||||||
return tr("Thread ID");
|
|
||||||
case KindColumn:
|
|
||||||
return tr("Kind");
|
|
||||||
case LeakedBlocksColumn:
|
|
||||||
return tr("Leaked Blocks");
|
|
||||||
case LeakedBytesColumn:
|
|
||||||
return tr("Leaked Bytes");
|
|
||||||
case HelgrindThreadIdColumn:
|
|
||||||
return tr("Helgrind Thread ID");
|
|
||||||
}
|
|
||||||
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ErrorListModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
if (parent.isValid())
|
|
||||||
return 0;
|
|
||||||
return d->errors.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ErrorListModel::columnCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0);
|
|
||||||
return ColumnCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ErrorListModel::removeRows(int row, int count, const QModelIndex &parent)
|
|
||||||
{
|
|
||||||
QTC_ASSERT(!parent.isValid() || parent.model() == this, return false);
|
|
||||||
|
|
||||||
if (row < 0 || row + count > d->errors.size() || parent.isValid())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
beginRemoveRows(parent, row, row + count);
|
|
||||||
d->errors.remove(row, count);
|
|
||||||
endRemoveRows();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorListModel::addError(const Error &error)
|
void ErrorListModel::addError(const Error &error)
|
||||||
{
|
{
|
||||||
beginInsertRows(QModelIndex(), d->errors.size(), d->errors.size());
|
rootItem()->appendChild(new ErrorItem(d, error));
|
||||||
d->errors.push_back(error);
|
|
||||||
endInsertRows();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error ErrorListModel::error(const QModelIndex &index) const
|
|
||||||
|
ErrorItem::ErrorItem(const ErrorListModelPrivate *modelPrivate, const Error &error)
|
||||||
|
: m_modelPrivate(modelPrivate), m_error(error)
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
QTC_ASSERT(!m_error.stacks().isEmpty(), return);
|
||||||
return Error();
|
|
||||||
|
|
||||||
QTC_ASSERT(index.model() == this, return Error());
|
// If there's more than one stack, we simply map the real tree structure.
|
||||||
|
// Otherwise, we skip the stack level, which has no useful information and would
|
||||||
const int r = index.row();
|
// just annoy the user.
|
||||||
if (r < 0 || r >= d->errors.size())
|
// The same goes for the frame level.
|
||||||
return Error();
|
if (m_error.stacks().count() > 1) {
|
||||||
return d->errors.at(r);
|
foreach (const Stack &s, m_error.stacks())
|
||||||
|
appendChild(new StackItem(s));
|
||||||
|
} else if (m_error.stacks().first().frames().count() > 1) {
|
||||||
|
foreach (const Frame &f, m_error.stacks().first().frames())
|
||||||
|
appendChild(new FrameItem(f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorListModel::clear()
|
static QVariant location(const Frame &frame, int role)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
switch (role) {
|
||||||
d->errors.clear();
|
case Analyzer::DetailedErrorView::LocationRole:
|
||||||
endResetModel();
|
return QVariant::fromValue(Analyzer::DiagnosticLocation(frame.filePath(), frame.line(), 0));
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
return frame.filePath().isEmpty() ? QVariant() : QVariant(frame.filePath());
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ErrorItem::data(int column, int role) const
|
||||||
|
{
|
||||||
|
if (column == Analyzer::DetailedErrorView::LocationColumn) {
|
||||||
|
const Frame frame = m_modelPrivate->findRelevantFrame(m_error);
|
||||||
|
return location(frame, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiagnosticColumn
|
||||||
|
switch (role) {
|
||||||
|
case Analyzer::DetailedErrorView::FullTextRole: {
|
||||||
|
QString content;
|
||||||
|
QTextStream stream(&content);
|
||||||
|
|
||||||
|
stream << m_error.what() << "\n";
|
||||||
|
stream << " "
|
||||||
|
<< m_modelPrivate->errorLocation(m_error)
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
|
foreach (const Stack &stack, m_error.stacks()) {
|
||||||
|
if (!stack.auxWhat().isEmpty())
|
||||||
|
stream << stack.auxWhat();
|
||||||
|
int i = 1;
|
||||||
|
foreach (const Frame &frame, stack.frames())
|
||||||
|
stream << " " << i++ << ": " << makeFrameName(frame, true) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.flush();
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
case ErrorListModel::ErrorRole:
|
||||||
|
return QVariant::fromValue<Error>(m_error);
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
// If and only if there is exactly one frame, we have omitted creating a child item for it
|
||||||
|
// (see the constructor) and display the function name in the error item instead.
|
||||||
|
if (m_error.stacks().count() != 1 || m_error.stacks().first().frames().count() != 1
|
||||||
|
|| m_error.stacks().first().frames().first().functionName().isEmpty()) {
|
||||||
|
return m_error.what();
|
||||||
|
}
|
||||||
|
return ErrorListModel::tr("%1 in function %2")
|
||||||
|
.arg(m_error.what(), m_error.stacks().first().frames().first().functionName());
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
return toolTipForFrame(m_modelPrivate->findRelevantFrame(m_error));
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StackItem::StackItem(const Stack &stack) : m_stack(stack)
|
||||||
|
{
|
||||||
|
foreach (const Frame &f, m_stack.frames())
|
||||||
|
appendChild(new FrameItem(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant StackItem::data(int column, int role) const
|
||||||
|
{
|
||||||
|
const ErrorItem * const errorItem = getErrorItem();
|
||||||
|
if (column == Analyzer::DetailedErrorView::LocationColumn)
|
||||||
|
return location(errorItem->modelPrivate()->findRelevantFrame(errorItem->error()), role);
|
||||||
|
|
||||||
|
// DiagnosticColumn
|
||||||
|
switch (role) {
|
||||||
|
case ErrorListModel::ErrorRole:
|
||||||
|
return QVariant::fromValue(errorItem->error());
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
return m_stack.auxWhat().isEmpty() ? errorItem->error().what() : m_stack.auxWhat();
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
return toolTipForFrame(errorItem->modelPrivate()->findRelevantFrame(errorItem->error()));
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorItem *StackItem::getErrorItem() const
|
||||||
|
{
|
||||||
|
return static_cast<ErrorItem *>(parent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FrameItem::FrameItem(const Frame &frame) : m_frame(frame)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant FrameItem::data(int column, int role) const
|
||||||
|
{
|
||||||
|
if (column == Analyzer::DetailedErrorView::LocationColumn)
|
||||||
|
return location(m_frame, role);
|
||||||
|
|
||||||
|
// DiagnosticColumn
|
||||||
|
switch (role) {
|
||||||
|
case ErrorListModel::ErrorRole:
|
||||||
|
return QVariant::fromValue(getErrorItem()->error());
|
||||||
|
case Qt::DisplayRole: {
|
||||||
|
const int row = parent()->children().indexOf(const_cast<FrameItem *>(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(makeFrameName(m_frame, false));
|
||||||
|
}
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
return toolTipForFrame(m_frame);
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorItem *FrameItem::getErrorItem() const
|
||||||
|
{
|
||||||
|
for (const TreeItem *parentItem = parent(); parentItem; parentItem = parentItem->parent()) {
|
||||||
|
const ErrorItem * const errorItem = dynamic_cast<const ErrorItem *>(parentItem);
|
||||||
|
if (errorItem)
|
||||||
|
return errorItem;
|
||||||
|
}
|
||||||
|
QTC_CHECK(false);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace XmlProtocol
|
} // namespace XmlProtocol
|
||||||
|
|||||||
@@ -32,39 +32,25 @@
|
|||||||
#ifndef LIBVALGRIND_PROTOCOL_ERRORLISTMODEL_H
|
#ifndef LIBVALGRIND_PROTOCOL_ERRORLISTMODEL_H
|
||||||
#define LIBVALGRIND_PROTOCOL_ERRORLISTMODEL_H
|
#define LIBVALGRIND_PROTOCOL_ERRORLISTMODEL_H
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <analyzerbase/detailederrorview.h>
|
||||||
|
#include <utils/treemodel.h>
|
||||||
|
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
namespace Valgrind {
|
namespace Valgrind {
|
||||||
namespace XmlProtocol {
|
namespace XmlProtocol {
|
||||||
|
|
||||||
class Error;
|
class Error;
|
||||||
|
class ErrorListModelPrivate;
|
||||||
class Frame;
|
class Frame;
|
||||||
|
|
||||||
class ErrorListModel : public QAbstractItemModel
|
class ErrorListModel : public Utils::TreeModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Column {
|
|
||||||
WhatColumn = 0,
|
|
||||||
LocationColumn,
|
|
||||||
AbsoluteFilePathColumn,
|
|
||||||
LineColumn,
|
|
||||||
UniqueColumn,
|
|
||||||
TidColumn,
|
|
||||||
KindColumn,
|
|
||||||
LeakedBlocksColumn,
|
|
||||||
LeakedBytesColumn,
|
|
||||||
HelgrindThreadIdColumn,
|
|
||||||
ColumnCount
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
ErrorRole = Qt::UserRole,
|
ErrorRole = Analyzer::DetailedErrorView::FullTextRole + 1,
|
||||||
AbsoluteFilePathRole,
|
|
||||||
FileRole,
|
|
||||||
LineRole
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class RelevantFrameFinder
|
class RelevantFrameFinder
|
||||||
@@ -80,26 +66,11 @@ public:
|
|||||||
QSharedPointer<const RelevantFrameFinder> relevantFrameFinder() const;
|
QSharedPointer<const RelevantFrameFinder> relevantFrameFinder() const;
|
||||||
void setRelevantFrameFinder(const QSharedPointer<const RelevantFrameFinder> &finder);
|
void setRelevantFrameFinder(const QSharedPointer<const RelevantFrameFinder> &finder);
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const;
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
|
|
||||||
QModelIndex parent(const QModelIndex &child) const;
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
|
||||||
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
|
||||||
|
|
||||||
Error error(const QModelIndex &index) const;
|
|
||||||
|
|
||||||
Frame findRelevantFrame(const Error &error) const;
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void addError(const Valgrind::XmlProtocol::Error &error);
|
void addError(const Valgrind::XmlProtocol::Error &error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Private;
|
ErrorListModelPrivate *const d;
|
||||||
Private *const d;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace XmlProtocol
|
} // namespace XmlProtocol
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QTC_LIB_DEPENDS += utils ssh
|
QTC_LIB_DEPENDS += utils ssh
|
||||||
QTC_PLUGIN_DEPENDS += projectexplorer
|
QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer
|
||||||
include(../../qttest.pri)
|
include(../../qttest.pri)
|
||||||
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
||||||
TARGET = tst_callgrindparsertests
|
TARGET = tst_callgrindparsertests
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QTC_LIB_DEPENDS += utils ssh
|
QTC_LIB_DEPENDS += utils ssh
|
||||||
QTC_PLUGIN_DEPENDS += projectexplorer
|
QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer
|
||||||
include(../../../../qtcreator.pri)
|
include(../../../../qtcreator.pri)
|
||||||
include(../../qttestrpath.pri)
|
include(../../qttestrpath.pri)
|
||||||
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QTC_LIB_DEPENDS += utils ssh
|
QTC_LIB_DEPENDS += utils ssh
|
||||||
QTC_PLUGIN_DEPENDS += projectexplorer
|
QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer
|
||||||
include(../../qttest.pri)
|
include(../../qttest.pri)
|
||||||
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
QTC_LIB_DEPENDS += utils ssh
|
QTC_LIB_DEPENDS += utils ssh
|
||||||
QTC_PLUGIN_DEPENDS += projectexplorer
|
QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer
|
||||||
include(../../qttest.pri)
|
include(../../qttest.pri)
|
||||||
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import qbs
|
import qbs
|
||||||
|
|
||||||
QtcAutotest {
|
QtcAutotest {
|
||||||
|
Depends { name: "AnalyzerBase" }
|
||||||
Depends { name: "QtcSsh" }
|
Depends { name: "QtcSsh" }
|
||||||
Depends { name: "Utils" }
|
Depends { name: "Utils" }
|
||||||
Depends { name: "ProjectExplorer" }
|
Depends { name: "ProjectExplorer" }
|
||||||
|
|||||||
Reference in New Issue
Block a user