diff --git a/src/plugins/analyzerbase/analyzerbase.pro b/src/plugins/analyzerbase/analyzerbase.pro index a7013745789e9b1979c9c5831a8406eba1fe4094..04bfc7c8df2d8ad0b7de7a7ebb76b1d95c719c8f 100644 --- a/src/plugins/analyzerbase/analyzerbase.pro +++ b/src/plugins/analyzerbase/analyzerbase.pro @@ -14,6 +14,7 @@ SOURCES += \ analyzerrunconfigwidget.cpp \ analyzerutils.cpp \ detailederrorview.cpp \ + diagnosticlocation.cpp \ startremotedialog.cpp HEADERS += \ @@ -27,6 +28,7 @@ HEADERS += \ analyzerrunconfigwidget.h \ analyzerutils.h \ detailederrorview.h \ + diagnosticlocation.h \ startremotedialog.h RESOURCES += \ diff --git a/src/plugins/analyzerbase/analyzerbase.qbs b/src/plugins/analyzerbase/analyzerbase.qbs index 259e5f8ddf9a0d17fdea0f67b0bcf12206ffdf94..6cb2aa7dbd98377da735ed2d5dac3340c5baf72c 100644 --- a/src/plugins/analyzerbase/analyzerbase.qbs +++ b/src/plugins/analyzerbase/analyzerbase.qbs @@ -29,6 +29,8 @@ QtcPlugin { "analyzerutils.h", "detailederrorview.cpp", "detailederrorview.h", + "diagnosticlocation.cpp", + "diagnosticlocation.h", "ianalyzertool.cpp", "ianalyzertool.h", "startremotedialog.cpp", diff --git a/src/plugins/analyzerbase/detailederrorview.cpp b/src/plugins/analyzerbase/detailederrorview.cpp index 1c31a7ff4028b7454d40d8d912205b3d728705c8..884c6edfb93cca9c6a4998e3a417be717177a275 100644 --- a/src/plugins/analyzerbase/detailederrorview.cpp +++ b/src/plugins/analyzerbase/detailederrorview.cpp @@ -30,247 +30,132 @@ #include "detailederrorview.h" +#include "diagnosticlocation.h" + #include #include #include +#include #include #include #include #include -#include +#include +#include #include #include -#include +#include +#include namespace Analyzer { +namespace Internal { -DetailedErrorDelegate::DetailedErrorDelegate(QListView *parent) - : QStyledItemDelegate(parent), - m_detailsWidget(0) -{ - connect(parent->verticalScrollBar(), &QScrollBar::valueChanged, - this, &DetailedErrorDelegate::onVerticalScroll); -} - -QSize DetailedErrorDelegate::sizeHint(const QStyleOptionViewItem &opt, - const QModelIndex &index) const +class DetailedErrorDelegate : public QStyledItemDelegate { - if (!index.isValid()) - return QStyledItemDelegate::sizeHint(opt, index); + Q_OBJECT - const QListView *view = qobject_cast(parent()); - const int viewportWidth = view->viewport()->width(); - const bool isSelected = view->selectionModel()->currentIndex() == index; - const int dy = 2 * s_itemMargin; +public: + DetailedErrorDelegate(QTreeView *parent) : QStyledItemDelegate(parent) { } - if (!isSelected) { - QFontMetrics fm(opt.font); - return QSize(viewportWidth, fm.height() + dy); +private: + QString actualText(const QModelIndex &index) const + { + const auto location = index.model()->data(index, DetailedErrorView::LocationRole) + .value(); + return location.isValid() + ? QString::fromLatin1("%2:%3") + .arg(location.filePath, QFileInfo(location.filePath).fileName()) + .arg(location.line) + : QString(); } - if (m_detailsWidget && m_detailsIndex != index) { - m_detailsWidget->deleteLater(); - m_detailsWidget = 0; + using DocConstPtr = QSharedPointer; + DocConstPtr document(const QStyleOptionViewItem &option) const + { + const auto doc = QSharedPointer::create(); + doc->setHtml(option.text); + doc->setTextWidth(option.rect.width()); + doc->setDocumentMargin(0); + return doc; } - if (!m_detailsWidget) { - m_detailsWidget = createDetailsWidget(opt.font, index, view->viewport()); - QTC_ASSERT(m_detailsWidget->parent() == view->viewport(), - m_detailsWidget->setParent(view->viewport())); - m_detailsIndex = index; - } else { - QTC_ASSERT(m_detailsIndex == index, /**/); - } - const int widthExcludingMargins = viewportWidth - 2 * s_itemMargin; - m_detailsWidget->setFixedWidth(widthExcludingMargins); - - m_detailsWidgetHeight = m_detailsWidget->heightForWidth(widthExcludingMargins); - // HACK: it's a bug in QLabel(?) that we have to force the widget to have the size it said - // it would have. - m_detailsWidget->setFixedHeight(m_detailsWidgetHeight); - return QSize(viewportWidth, dy + m_detailsWidget->heightForWidth(widthExcludingMargins)); -} + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override + { + QStyleOptionViewItem opt = option; + opt.text = actualText(index); + initStyleOption(&opt, index); -void DetailedErrorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &basicOption, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 opt(basicOption); - initStyleOption(&opt, index); - - const QListView *const view = qobject_cast(parent()); - const bool isSelected = view->selectionModel()->currentIndex() == index; - - QFontMetrics fm(opt.font); - QPoint pos = opt.rect.topLeft(); - - painter->save(); - - const QColor bgColor = isSelected - ? opt.palette.highlight().color() - : 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)); - } + const DocConstPtr doc = document(opt); + return QSize(doc->idealWidth(), doc->size().height()); } - // Separator lines (like Issues pane) - painter->setPen(QColor::fromRgb(150,150,150)); - painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override + { + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); - painter->restore(); -} + QStyle *style = opt.widget? opt.widget->style() : QApplication::style(); -void DetailedErrorDelegate::onCurrentSelectionChanged(const QModelIndex &now, - const QModelIndex &previous) -{ - if (m_detailsWidget) { - m_detailsWidget->deleteLater(); - m_detailsWidget = 0; - } + // Painting item without text + opt.text.clear(); + style->drawControl(QStyle::CE_ItemViewItem, &opt, painter); + opt.text = actualText(index); - m_detailsIndex = QModelIndex(); - if (now.isValid()) - emit sizeHintChanged(now); - if (previous.isValid()) - emit sizeHintChanged(previous); -} + QAbstractTextDocumentLayout::PaintContext ctx; -void DetailedErrorDelegate::onLayoutChanged() -{ - if (m_detailsWidget) { - m_detailsWidget->deleteLater(); - m_detailsWidget = 0; - m_detailsIndex = QModelIndex(); - } -} + // Highlighting text if item is selected + if (opt.state & QStyle::State_Selected) { + ctx.palette.setColor(QPalette::Text, opt.palette.color(QPalette::Active, + QPalette::HighlightedText)); + } -void DetailedErrorDelegate::onViewResized() -{ - const QListView *view = qobject_cast(parent()); - if (m_detailsWidget) - emit sizeHintChanged(view->selectionModel()->currentIndex()); -} + 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(); + } +}; -void DetailedErrorDelegate::onVerticalScroll() -{ - if (m_detailsWidget) - m_detailsWidget->hide(); -} +} // namespace Internal -// 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) : - QListView(parent), - m_copyAction(0) -{ -} - -DetailedErrorView::~DetailedErrorView() + QTreeView(parent), + m_copyAction(new QAction(this)) { - itemDelegate()->deleteLater(); -} - -void DetailedErrorView::setItemDelegate(QAbstractItemDelegate *delegate) -{ - QListView::setItemDelegate(delegate); + header()->setSectionResizeMode(QHeaderView::ResizeToContents); + setItemDelegateForColumn(LocationColumn, new Internal::DetailedErrorDelegate(this)); - DetailedErrorDelegate *myDelegate = qobject_cast(itemDelegate()); - connect(this, &DetailedErrorView::resized, myDelegate, &DetailedErrorDelegate::onViewResized); - - m_copyAction = new QAction(this); m_copyAction->setText(tr("Copy")); m_copyAction->setIcon(QIcon(QLatin1String(Core::Constants::ICON_COPY))); m_copyAction->setShortcut(QKeySequence::Copy); m_copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); - connect(m_copyAction, &QAction::triggered, myDelegate, &DetailedErrorDelegate::copyToClipboard); - addAction(m_copyAction); -} - -void DetailedErrorView::setModel(QAbstractItemModel *model) -{ - QListView::setModel(model); - - DetailedErrorDelegate *delegate = qobject_cast(itemDelegate()); - QTC_ASSERT(delegate, return); + 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(); + if (loc.isValid()) + Core::EditorManager::openEditorAt(loc.filePath, loc.line, loc.column - 1); + } + }); - connect(selectionModel(), &QItemSelectionModel::currentChanged, - delegate, &DetailedErrorDelegate::onCurrentSelectionChanged); - connect(model, &QAbstractItemModel::layoutChanged, - delegate, &DetailedErrorDelegate::onLayoutChanged); + addAction(m_copyAction); } -void DetailedErrorView::resizeEvent(QResizeEvent *e) +DetailedErrorView::~DetailedErrorView() { - emit resized(); - QListView::resizeEvent(e); } void DetailedErrorView::contextMenuEvent(QContextMenuEvent *e) @@ -288,19 +173,6 @@ void DetailedErrorView::contextMenuEvent(QContextMenuEvent *e) 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() { QTC_ASSERT(rowCount(), return); @@ -346,3 +218,5 @@ void DetailedErrorView::setCurrentRow(int row) } } // namespace Analyzer + +#include "detailederrorview.moc" diff --git a/src/plugins/analyzerbase/detailederrorview.h b/src/plugins/analyzerbase/detailederrorview.h index 2942a9cdc4899d43a700662c9bf8c67815b00600..fc28cde3ea88962e6718d1b4b47efa9f56d2702a 100644 --- a/src/plugins/analyzerbase/detailederrorview.h +++ b/src/plugins/analyzerbase/detailederrorview.h @@ -33,59 +33,12 @@ #include "analyzerbase_global.h" -#include +#include #include namespace Analyzer { -// Provides the details widget for the DetailedErrorView -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 +class ANALYZER_EXPORT DetailedErrorView : public QTreeView { Q_OBJECT @@ -93,21 +46,18 @@ public: DetailedErrorView(QWidget *parent = 0); ~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 goBack(); -signals: - void resized(); + enum ItemRole { + LocationRole = Qt::UserRole, + FullTextRole + }; -protected: - void updateGeometries(); - void resizeEvent(QResizeEvent *e); + enum Column { + DiagnosticColumn, + LocationColumn, + }; private: void contextMenuEvent(QContextMenuEvent *e) override; @@ -119,7 +69,7 @@ private: QList commonActions() const; virtual QList customActions() const; - QAction *m_copyAction; + QAction * const m_copyAction; }; } // namespace Analyzer diff --git a/src/plugins/analyzerbase/diagnosticlocation.cpp b/src/plugins/analyzerbase/diagnosticlocation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f50c2d44388f527881cb1b0c31731735433e006b --- /dev/null +++ b/src/plugins/analyzerbase/diagnosticlocation.cpp @@ -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 + diff --git a/src/plugins/analyzerbase/diagnosticlocation.h b/src/plugins/analyzerbase/diagnosticlocation.h new file mode 100644 index 0000000000000000000000000000000000000000..0e0fd15779f80e718f4ec6d986244940113a64f3 --- /dev/null +++ b/src/plugins/analyzerbase/diagnosticlocation.h @@ -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 +#include +#include + +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. + diff --git a/src/plugins/valgrind/memcheckerrorview.cpp b/src/plugins/valgrind/memcheckerrorview.cpp index 05348a5a519ce72e225f0531586653512260718e..3ffcee27e9bb3d1f848cbf748818bbc5a4078329 100644 --- a/src/plugins/valgrind/memcheckerrorview.cpp +++ b/src/plugins/valgrind/memcheckerrorview.cpp @@ -47,245 +47,17 @@ #include #include -#include -#include - #include -#include -#include -#include -#include -#include -#include -#include using namespace Valgrind::XmlProtocol; namespace Valgrind { 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("%3") - .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(index.model()); - while (!model && proxy) { - model = qobject_cast(proxy->sourceModel()); - proxy = qobject_cast(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("%1: %2"); - const QString relativeTo = relativeToPath(); - const Error error = errorIndex.data(ErrorListModel::ErrorRole).value(); - - 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  %2") - .arg(error.what(), - errorLocation(errorIndex, error, /*link=*/ true, - /*absolutePath=*/ false, linkStyle), - linkStyle)); - connect(errorLabel, &QLabel::linkActivated, this, &MemcheckErrorDelegate::openLinkInEditor); - layout->addWidget(errorLabel); - - const QVector 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(); - 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(); - - 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) : Analyzer::DetailedErrorView(parent), m_settings(0) { - MemcheckErrorDelegate *delegate = new MemcheckErrorDelegate(this); - setItemDelegate(delegate); - m_suppressAction = new QAction(this); m_suppressAction->setText(tr("Suppress Error")); m_suppressAction->setIcon(QIcon(QLatin1String(":/valgrind/images/eye_crossed.png"))); @@ -343,5 +115,3 @@ QList MemcheckErrorView::customActions() const } // namespace Internal } // namespace Valgrind - -#include "memcheckerrorview.moc" diff --git a/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp b/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp index 6c23993097b326279e6be8301bda570f98196f54..31692f0624105f4f8d1ce51e4754456325502213 100644 --- a/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp +++ b/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp @@ -35,29 +35,74 @@ #include "stack.h" #include "modelhelpers.h" +#include #include +#include #include #include +#include + namespace Valgrind { namespace XmlProtocol { -class ErrorListModel::Private +class ErrorListModelPrivate { public: - QVector errors; - QVariant errorData(int row, int column, int role) const; + QVariant errorData(const QModelIndex &index, int role) const; QSharedPointer relevantFrameFinder; Frame findRelevantFrame(const Error &error) const; - QString formatAbsoluteFilePath(const Error &error) const; - QString formatLocation(const Error &error) const; + QString errorLocation(const Error &error) const; +}; + +class ErrorItem : public Utils::TreeItem +{ +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) - : QAbstractItemModel(parent) - , d(new Private) + : Utils::TreeModel(parent) + , d(new ErrorListModelPrivate) { + setHeader(QStringList() << tr("Issue") << tr("Location")); } ErrorListModel::~ErrorListModel() @@ -75,27 +120,7 @@ void ErrorListModel::setRelevantFrameFinder(const QSharedPointerrelevantFrameFinder = finder; } -Frame ErrorListModel::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 +Frame ErrorListModelPrivate::findRelevantFrame(const Error &error) const { if (relevantFrameFinder) return relevantFrameFinder->findRelevant(error); @@ -109,181 +134,194 @@ Frame ErrorListModel::Private::findRelevantFrame(const Error &error) const 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 path; + if (!d.isEmpty() && !f.isEmpty()) + path = fullPath; + else + path = frame.object(); + + if (QFile::exists(path)) + path = QFileInfo(path).canonicalFilePath(); + + if (frame.line() != -1) + 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); + } + if (!path.isEmpty()) + return path; + return QString::fromLatin1("0x%1").arg(frame.instructionPointer(), 0, 16); } -QString ErrorListModel::Private::formatLocation(const Error &error) const +QString ErrorListModelPrivate::errorLocation(const Error &error) const { - const Frame frame = findRelevantFrame(error); - const QString file = frame.fileName(); - if (!frame.functionName().isEmpty()) - return frame.functionName(); - if (!file.isEmpty()) { - const qint64 line = frame.line(); - if (line > 0) - return QString::fromLatin1("%1:%2").arg(file, QString::number(frame.line())); - return file; - } - return frame.object(); + return QCoreApplication::translate("Valgrind::Internal", "in %1"). + arg(makeFrameName(findRelevantFrame(error), true)); } -QVariant ErrorListModel::Private::errorData(int row, int column, int role) const +void ErrorListModel::addError(const Error &error) { - // A dummy entry. - if (row == 0 && errors.isEmpty()) { - if (role == Qt::DisplayRole) - return tr("No errors found"); - if (role == ErrorRole) - return tr("No errors found"); - return QVariant(); - } + rootItem()->appendChild(new ErrorItem(d, error)); +} - if (row < 0 || row >= errors.size()) - return QVariant(); - const Error &error = errors.at(row); +ErrorItem::ErrorItem(const ErrorListModelPrivate *modelPrivate, const Error &error) + : m_modelPrivate(modelPrivate), m_error(error) +{ + QTC_ASSERT(!m_error.stacks().isEmpty(), return); + + // 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 + // just annoy the user. + // The same goes for the frame level. + if (m_error.stacks().count() > 1) { + 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)); + } +} - if (error.stacks().count()) +static QVariant location(const Frame &frame, int role) +{ 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 Analyzer::DetailedErrorView::LocationRole: + return QVariant::fromValue(Analyzer::DiagnosticLocation(frame.filePath(), frame.line(), 0)); case Qt::ToolTipRole: - return toolTipForFrame(findRelevantFrame(error)); - case ErrorRole: - return QVariant::fromValue(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 frame.filePath().isEmpty() ? QVariant() : QVariant(frame.filePath()); + default: + return QVariant(); } - return QVariant(); } -QVariant ErrorListModel::data(const QModelIndex &index, int role) const +QVariant ErrorItem::data(int column, int role) const { - if (!index.isValid()) - return QVariant(); - - if (!index.parent().isValid()) - return d->errorData(index.row(), index.column(), role); + if (column == Analyzer::DetailedErrorView::LocationColumn) { + const Frame frame = m_modelPrivate->findRelevantFrame(m_error); + return location(frame, role); + } - return QVariant(); -} + // 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"; + } -QVariant ErrorListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + stream.flush(); + return content; + } + case ErrorListModel::ErrorRole: + return QVariant::fromValue(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(); - - 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 +StackItem::StackItem(const Stack &stack) : m_stack(stack) { - QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0); - return ColumnCount; + foreach (const Frame &f, m_stack.frames()) + appendChild(new FrameItem(f)); } -bool ErrorListModel::removeRows(int row, int count, const QModelIndex &parent) +QVariant StackItem::data(int column, int role) const { - QTC_ASSERT(!parent.isValid() || parent.model() == this, return false); - - if (row < 0 || row + count > d->errors.size() || parent.isValid()) - return false; + const ErrorItem * const errorItem = getErrorItem(); + if (column == Analyzer::DetailedErrorView::LocationColumn) + return location(errorItem->modelPrivate()->findRelevantFrame(errorItem->error()), role); - beginRemoveRows(parent, row, row + count); - d->errors.remove(row, count); - endRemoveRows(); - return true; + // 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(); + } } -void ErrorListModel::addError(const Error &error) +const ErrorItem *StackItem::getErrorItem() const { - beginInsertRows(QModelIndex(), d->errors.size(), d->errors.size()); - d->errors.push_back(error); - endInsertRows(); + return static_cast(parent()); } -Error ErrorListModel::error(const QModelIndex &index) const + +FrameItem::FrameItem(const Frame &frame) : m_frame(frame) { - if (!index.isValid()) - return Error(); +} - QTC_ASSERT(index.model() == this, return Error()); +QVariant FrameItem::data(int column, int role) const +{ + if (column == Analyzer::DetailedErrorView::LocationColumn) + return location(m_frame, role); - const int r = index.row(); - if (r < 0 || r >= d->errors.size()) - return Error(); - return d->errors.at(r); + // DiagnosticColumn + switch (role) { + case ErrorListModel::ErrorRole: + return QVariant::fromValue(getErrorItem()->error()); + case Qt::DisplayRole: { + const int row = parent()->children().indexOf(const_cast(this)) + 1; + const int padding = static_cast(std::log10(parent()->rowCount())) + - static_cast(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(); + } } -void ErrorListModel::clear() +const ErrorItem *FrameItem::getErrorItem() const { - beginResetModel(); - d->errors.clear(); - endResetModel(); + for (const TreeItem *parentItem = parent(); parentItem; parentItem = parentItem->parent()) { + const ErrorItem * const errorItem = dynamic_cast(parentItem); + if (errorItem) + return errorItem; + } + QTC_CHECK(false); + return nullptr; } } // namespace XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/errorlistmodel.h b/src/plugins/valgrind/xmlprotocol/errorlistmodel.h index 6888362771ce8cc1dc2083be861204bb7becffa7..32808a4861409e80f6415be94aa58aeedbc64c7b 100644 --- a/src/plugins/valgrind/xmlprotocol/errorlistmodel.h +++ b/src/plugins/valgrind/xmlprotocol/errorlistmodel.h @@ -32,39 +32,25 @@ #ifndef LIBVALGRIND_PROTOCOL_ERRORLISTMODEL_H #define LIBVALGRIND_PROTOCOL_ERRORLISTMODEL_H -#include +#include +#include + #include namespace Valgrind { namespace XmlProtocol { class Error; +class ErrorListModelPrivate; class Frame; -class ErrorListModel : public QAbstractItemModel +class ErrorListModel : public Utils::TreeModel { Q_OBJECT public: - enum Column { - WhatColumn = 0, - LocationColumn, - AbsoluteFilePathColumn, - LineColumn, - UniqueColumn, - TidColumn, - KindColumn, - LeakedBlocksColumn, - LeakedBytesColumn, - HelgrindThreadIdColumn, - ColumnCount - }; - enum Role { - ErrorRole = Qt::UserRole, - AbsoluteFilePathRole, - FileRole, - LineRole + ErrorRole = Analyzer::DetailedErrorView::FullTextRole + 1, }; class RelevantFrameFinder @@ -80,26 +66,11 @@ public: QSharedPointer relevantFrameFinder() const; void setRelevantFrameFinder(const QSharedPointer &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: void addError(const Valgrind::XmlProtocol::Error &error); private: - class Private; - Private *const d; + ErrorListModelPrivate *const d; }; } // namespace XmlProtocol diff --git a/tests/auto/valgrind/callgrind/callgrindparsertests.pro b/tests/auto/valgrind/callgrind/callgrindparsertests.pro index 03a705d97f0dc115b8fddaec8c3cb3db1d243a5d..ad143e6b98c61cf748277a6ddd9a52126ad03454 100644 --- a/tests/auto/valgrind/callgrind/callgrindparsertests.pro +++ b/tests/auto/valgrind/callgrind/callgrindparsertests.pro @@ -1,5 +1,5 @@ QTC_LIB_DEPENDS += utils ssh -QTC_PLUGIN_DEPENDS += projectexplorer +QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer include(../../qttest.pri) include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri) TARGET = tst_callgrindparsertests diff --git a/tests/auto/valgrind/memcheck/modeldemo.pro b/tests/auto/valgrind/memcheck/modeldemo.pro index 19a76b2b33146b6490df97d4ed5614aeea14e1ff..4144154944be360fa741e64ce4d42f865d5125bc 100644 --- a/tests/auto/valgrind/memcheck/modeldemo.pro +++ b/tests/auto/valgrind/memcheck/modeldemo.pro @@ -1,5 +1,5 @@ QTC_LIB_DEPENDS += utils ssh -QTC_PLUGIN_DEPENDS += projectexplorer +QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer include(../../../../qtcreator.pri) include(../../qttestrpath.pri) include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri) diff --git a/tests/auto/valgrind/memcheck/parsertests.pro b/tests/auto/valgrind/memcheck/parsertests.pro index 7824ae3c081df91e02d1fa0d07af8eb5b8c5ee90..74cf23c7976858705d4a26fe45d95ec29690be1e 100644 --- a/tests/auto/valgrind/memcheck/parsertests.pro +++ b/tests/auto/valgrind/memcheck/parsertests.pro @@ -1,5 +1,5 @@ QTC_LIB_DEPENDS += utils ssh -QTC_PLUGIN_DEPENDS += projectexplorer +QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer include(../../qttest.pri) include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri) diff --git a/tests/auto/valgrind/memcheck/testrunner.pro b/tests/auto/valgrind/memcheck/testrunner.pro index 44c2caa72e3a7ada87de9a84aa70e1e09fc04c08..b4b810d8e068dfb88cd474c4ac34c79adfa958b6 100644 --- a/tests/auto/valgrind/memcheck/testrunner.pro +++ b/tests/auto/valgrind/memcheck/testrunner.pro @@ -1,5 +1,5 @@ QTC_LIB_DEPENDS += utils ssh -QTC_PLUGIN_DEPENDS += projectexplorer +QTC_PLUGIN_DEPENDS += analyzerbase projectexplorer include(../../qttest.pri) include($$IDE_SOURCE_TREE/src/plugins/valgrind/valgrind_test.pri) diff --git a/tests/auto/valgrind/valgrindautotest.qbs b/tests/auto/valgrind/valgrindautotest.qbs index 22b79069263545c7790ddcd63a5635aea9b5a517..1de90b646fc6daa099f137757aec695c026359eb 100644 --- a/tests/auto/valgrind/valgrindautotest.qbs +++ b/tests/auto/valgrind/valgrindautotest.qbs @@ -1,6 +1,7 @@ import qbs QtcAutotest { + Depends { name: "AnalyzerBase" } Depends { name: "QtcSsh" } Depends { name: "Utils" } Depends { name: "ProjectExplorer" }