diff --git a/src/plugins/cppcheck/CMakeLists.txt b/src/plugins/cppcheck/CMakeLists.txt index 76c2148c797..3244b7ee9e9 100644 --- a/src/plugins/cppcheck/CMakeLists.txt +++ b/src/plugins/cppcheck/CMakeLists.txt @@ -1,9 +1,13 @@ add_qtc_plugin(Cppcheck DEPENDS Qt5::Widgets - PLUGIN_DEPENDS Core CppTools ProjectExplorer TextEditor + PLUGIN_DEPENDS Core Debugger CppTools ProjectExplorer TextEditor SOURCES cppcheckconstants.h - cppcheckdiagnostic.h + cppcheckdiagnostic.cpp cppcheckdiagnostic.h + cppcheckdiagnosticmanager.h + cppcheckdiagnosticsmodel.cppмcppcheckdiagnosticsmodel.h + cppcheckdiagnosticview.cpp cppcheckdiagnosticview.h + cppcheckmanualrundialog.cpp cppcheckmanualrundialog.h cppcheckoptions.cpp cppcheckoptions.h cppcheckplugin.cpp cppcheckplugin.h cppcheckrunner.cpp cppcheckrunner.h diff --git a/src/plugins/cppcheck/cppcheck.pro b/src/plugins/cppcheck/cppcheck.pro index f25918f5ab7..7fad12eb207 100644 --- a/src/plugins/cppcheck/cppcheck.pro +++ b/src/plugins/cppcheck/cppcheck.pro @@ -1,6 +1,10 @@ include(../../qtcreatorplugin.pri) SOURCES += \ + cppcheckdiagnostic.cpp \ + cppcheckdiagnosticsmodel.cpp \ + cppcheckdiagnosticview.cpp \ + cppcheckmanualrundialog.cpp \ cppcheckoptions.cpp \ cppcheckplugin.cpp \ cppcheckrunner.cpp \ @@ -12,6 +16,10 @@ SOURCES += \ HEADERS += \ cppcheckconstants.h \ cppcheckdiagnostic.h \ + cppcheckdiagnosticmanager.h \ + cppcheckdiagnosticsmodel.h \ + cppcheckdiagnosticview.h \ + cppcheckmanualrundialog.h \ cppcheckoptions.h \ cppcheckplugin.h \ cppcheckrunner.h \ diff --git a/src/plugins/cppcheck/cppcheck.qbs b/src/plugins/cppcheck/cppcheck.qbs index 9a113c2cf30..d9258267302 100644 --- a/src/plugins/cppcheck/cppcheck.qbs +++ b/src/plugins/cppcheck/cppcheck.qbs @@ -5,6 +5,7 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "CppTools" } + Depends { name: "Debugger" } Depends { name: "ProjectExplorer" } Depends { name: "TextEditor" } Depends { name: "Utils" } @@ -13,7 +14,15 @@ QtcPlugin { files: [ "cppcheckconstants.h", + "cppcheckdiagnostic.cpp", "cppcheckdiagnostic.h", + "cppcheckdiagnosticmanager.h", + "cppcheckdiagnosticsmodel.cpp", + "cppcheckdiagnosticsmodel.h", + "cppcheckdiagnosticview.cpp", + "cppcheckdiagnosticview.h", + "cppcheckmanualrundialog.cpp", + "cppcheckmanualrundialog.h", "cppcheckoptions.cpp", "cppcheckoptions.h", "cppcheckplugin.cpp", diff --git a/src/plugins/cppcheck/cppcheck_dependencies.pri b/src/plugins/cppcheck/cppcheck_dependencies.pri index e136adbab12..8d5c9d65d5d 100644 --- a/src/plugins/cppcheck/cppcheck_dependencies.pri +++ b/src/plugins/cppcheck/cppcheck_dependencies.pri @@ -4,5 +4,6 @@ QTC_LIB_DEPENDS += \ utils QTC_PLUGIN_DEPENDS += \ cpptools \ + debugger \ projectexplorer \ texteditor diff --git a/src/plugins/cppcheck/cppcheckconstants.h b/src/plugins/cppcheck/cppcheckconstants.h index 0611fe61272..8d56535fc42 100644 --- a/src/plugins/cppcheck/cppcheckconstants.h +++ b/src/plugins/cppcheck/cppcheckconstants.h @@ -49,7 +49,11 @@ const char SETTINGS_SHOW_OUTPUT[] = "showOutput"; const char SETTINGS_ADD_INCLUDE_PATHS[] = "addIncludePaths"; const char SETTINGS_GUESS_ARGUMENTS[] = "guessArguments"; -const char CHECK_PROGRESS_ID[] = "Cppcheck.Cppcheck.CheckingTask"; +const char CHECK_PROGRESS_ID[] = "Cppcheck.CheckingTask"; + +const char MANUAL_CHECK_PROGRESS_ID[] = "Cppcheck.ManualCheckingTask"; +const char MANUAL_RUN_ACTION[] = "Cppcheck.ManualRun"; +const char PERSPECTIVE_ID[] = "Cppcheck.Perspective"; } // namespace Constants } // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckdiagnostic.cpp b/src/plugins/cppcheck/cppcheckdiagnostic.cpp new file mode 100644 index 00000000000..6534018fc52 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckdiagnostic.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "cppcheckdiagnostic.h" + +namespace Cppcheck { +namespace Internal { + +bool Diagnostic::operator==(const Diagnostic &r) const +{ + return std::tie(severity, message, fileName, lineNumber) + == std::tie(r.severity, r.message, r.fileName, r.lineNumber); +} + +quint32 qHash(const Diagnostic &diagnostic) +{ + return qHash(diagnostic.message) ^ qHash(diagnostic.fileName) ^ diagnostic.lineNumber; +} +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckdiagnostic.h b/src/plugins/cppcheck/cppcheckdiagnostic.h index 566a60b974c..eea6ef430e6 100644 --- a/src/plugins/cppcheck/cppcheckdiagnostic.h +++ b/src/plugins/cppcheck/cppcheckdiagnostic.h @@ -39,6 +39,7 @@ public: bool isValid() const { return !fileName.isEmpty() && lineNumber > 0; } + bool operator==(const Diagnostic& diagnostic) const; Severity severity = Severity::Information; QString severityText; @@ -48,5 +49,7 @@ public: int lineNumber = 0; }; +quint32 qHash(const Diagnostic &diagnostic); + } // namespace Internal } // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckdiagnosticmanager.h b/src/plugins/cppcheck/cppcheckdiagnosticmanager.h new file mode 100644 index 00000000000..bc86756d866 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckdiagnosticmanager.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +namespace Cppcheck { +namespace Internal { + +class Diagnostic; + +class CppcheckDiagnosticManager +{ +public: + virtual ~CppcheckDiagnosticManager() = default; + virtual void add(const Diagnostic &diagnostic) = 0; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp new file mode 100644 index 00000000000..e9121793e30 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "cppcheckdiagnosticsmodel.h" + +#include + +#include + +#include + +namespace Cppcheck { +namespace Internal { + +using namespace Debugger; + +FilePathItem::FilePathItem(const QString &filePath) + : m_filePath(filePath) +{} + +QVariant FilePathItem::data(int column, int role) const +{ + if (column == DiagnosticsModel::DiagnosticColumn) { + switch (role) { + case Qt::DisplayRole: + return m_filePath; + case Qt::DecorationRole: + return Core::FileIconProvider::icon(m_filePath); + case Debugger::DetailedErrorView::FullTextRole: + return m_filePath; + default: + return QVariant(); + } + } + + return QVariant(); +} + +DiagnosticItem::DiagnosticItem(const Diagnostic &diagnostic) + : m_diagnostic(diagnostic) +{} + +static QIcon getIcon(const Diagnostic::Severity severity) +{ + switch (severity) { + case Diagnostic::Severity::Error: + return Utils::Icons::CRITICAL.icon(); + case Diagnostic::Severity::Warning: + return Utils::Icons::WARNING.icon(); + default: + return Utils::Icons::INFO.icon(); + } +} + +QVariant DiagnosticItem::data(int column, int role) const +{ + if (column == DiagnosticsModel::DiagnosticColumn) { + switch (role) { + case DetailedErrorView::LocationRole: { + const auto location = DiagnosticLocation(m_diagnostic.fileName.toString(), + m_diagnostic.lineNumber, + 0); + return QVariant::fromValue(location); + } + case Qt::DisplayRole: + return QString("%1: %2").arg(m_diagnostic.lineNumber).arg(m_diagnostic.message); + case Qt::ToolTipRole: + return QString("%1: %2").arg(m_diagnostic.severityText, m_diagnostic.checkId); + case Qt::DecorationRole: + return getIcon(m_diagnostic.severity); + case Debugger::DetailedErrorView::FullTextRole: + return QString("%1:%2: %3") + .arg(m_diagnostic.fileName.toString()) + .arg(m_diagnostic.lineNumber) + .arg(m_diagnostic.message); + default: + return QVariant(); + } + } + + return QVariant(); +} + +DiagnosticsModel::DiagnosticsModel(QObject *parent) + : BaseModel(parent) +{ + setHeader({tr("Diagnostic")}); +} + +void DiagnosticsModel::clear() +{ + const auto hasData = !m_diagnostics.isEmpty(); + m_filePathToItem.clear(); + m_diagnostics.clear(); + BaseModel::clear(); + if (hasData) + emit hasDataChanged(false); +} + +void DiagnosticsModel::add(const Diagnostic &diagnostic) +{ + if (m_diagnostics.contains(diagnostic)) + return; + + const auto hasData = !m_diagnostics.isEmpty(); + m_diagnostics.insert(diagnostic); + if (!hasData) + emit hasDataChanged(true); + + const QString filePath = diagnostic.fileName.toString(); + FilePathItem *&filePathItem = m_filePathToItem[filePath]; + if (!filePathItem) { + filePathItem = new FilePathItem(filePath); + rootItem()->appendChild(filePathItem); + } + + filePathItem->appendChild(new DiagnosticItem(diagnostic)); +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckdiagnosticsmodel.h b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.h new file mode 100644 index 00000000000..b630a93db94 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include + +#include + +#include + +namespace Cppcheck { +namespace Internal { + +class DiagnosticsModel; + +class FilePathItem : public Utils::TreeItem +{ +public: + explicit FilePathItem(const QString &filePath); + QVariant data(int column, int role) const override; + +private: + const QString m_filePath; +}; + +class DiagnosticItem : public Utils::TreeItem +{ +public: + explicit DiagnosticItem(const Diagnostic &diagnostic); + QVariant data(int column, int role) const override; + +private: + const Diagnostic m_diagnostic; +}; + +using BaseModel = Utils::TreeModel; + +class DiagnosticsModel : public BaseModel, public CppcheckDiagnosticManager +{ + Q_OBJECT +public: + enum Column {DiagnosticColumn}; + + explicit DiagnosticsModel(QObject *parent = nullptr); + void clear(); + void add(const Diagnostic &diagnostic) override; + +signals: + void hasDataChanged(bool hasData); + +private: + QHash m_filePathToItem; + QSet m_diagnostics; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckdiagnosticview.cpp b/src/plugins/cppcheck/cppcheckdiagnosticview.cpp new file mode 100644 index 00000000000..1f881e5330b --- /dev/null +++ b/src/plugins/cppcheck/cppcheckdiagnosticview.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "cppcheckdiagnosticview.h" + +#include + +#include + +namespace Cppcheck { +namespace Internal { + +using namespace Debugger; + +DiagnosticView::DiagnosticView(QWidget *parent) + : DetailedErrorView(parent) +{ + setFrameStyle(QFrame::NoFrame); + setAttribute(Qt::WA_MacShowFocusRect, false); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + setAutoScroll(false); + sortByColumn(DiagnosticColumn, Qt::AscendingOrder); + setObjectName("CppcheckIssuesView"); + setWindowTitle(tr("Cppcheck Diagnostics")); + setHeaderHidden(true); +} + +void DiagnosticView::goNext() +{ + const auto totalFiles = model()->rowCount(); + if (totalFiles == 0) + return; + + const QModelIndex currentIndex = selectionModel()->currentIndex(); + const QModelIndex parent = currentIndex.parent(); + const auto onDiagnostic = parent.isValid(); + if (onDiagnostic && currentIndex.row() < model()->rowCount(parent) - 1) { + selectIndex(currentIndex.sibling(currentIndex.row() + 1, 0)); + return; + } + auto newFileRow = 0; + if (!currentIndex.isValid()) // not selected + newFileRow = 0; + else if (!onDiagnostic) // selected file + newFileRow = currentIndex.row(); + else // selected last item in file + newFileRow = parent.row() == totalFiles - 1 ? 0 : parent.row() + 1; + const QModelIndex newParent = model()->index(newFileRow, 0); + selectIndex(model()->index(0, 0, newParent)); +} + +void DiagnosticView::goBack() +{ + const auto totalFiles = model()->rowCount(); + if (totalFiles == 0) + return; + + const QModelIndex currentIndex = selectionModel()->currentIndex(); + const QModelIndex parent = currentIndex.parent(); + const auto onDiagnostic = parent.isValid(); + if (onDiagnostic && currentIndex.row() > 0) { + selectIndex(currentIndex.sibling(currentIndex.row() - 1, 0)); + return; + } + auto newFileRow = 0; + if (!currentIndex.isValid()) // not selected + newFileRow = totalFiles - 1; + else if (!onDiagnostic) // selected file + newFileRow = currentIndex.row() == 0 ? totalFiles - 1 : currentIndex.row() - 1; + else // selected first item in file + newFileRow = parent.row() == 0 ? totalFiles - 1 : parent.row() - 1; + const QModelIndex newParent = model()->index(newFileRow, 0); + const auto newParentRows = model()->rowCount(newParent); + selectIndex(model()->index(newParentRows - 1, 0, newParent)); +} + +DiagnosticView::~DiagnosticView() = default; + +void DiagnosticView::openEditorForCurrentIndex() +{ + const QVariant v = model()->data(currentIndex(), Debugger::DetailedErrorView::LocationRole); + const auto loc = v.value(); + if (loc.isValid()) + Core::EditorManager::openEditorAt(loc.filePath, loc.line, loc.column - 1); +} + +void DiagnosticView::mouseDoubleClickEvent(QMouseEvent *event) +{ + openEditorForCurrentIndex(); + DetailedErrorView::mouseDoubleClickEvent(event); +} + +} // namespace Internal +} // namespace Cppcheck + +//#include "clangtoolsdiagnosticview.moc" diff --git a/src/plugins/cppcheck/cppcheckdiagnosticview.h b/src/plugins/cppcheck/cppcheckdiagnosticview.h new file mode 100644 index 00000000000..f55bab1edd2 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckdiagnosticview.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace Cppcheck { +namespace Internal { + +class DiagnosticView : public Debugger::DetailedErrorView +{ + Q_OBJECT +public: + explicit DiagnosticView(QWidget *parent = nullptr); + ~DiagnosticView() override; + + void goNext() override; + void goBack() override; + +private: + void openEditorForCurrentIndex(); + void mouseDoubleClickEvent(QMouseEvent *event) override; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.cpp b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp new file mode 100644 index 00000000000..68e2cc24ce5 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "cppcheckmanualrundialog.h" +#include "cppcheckoptions.h" + +#include + +#include + +#include + +#include +#include +#include + +namespace Cppcheck { +namespace Internal { + +ManualRunDialog::ManualRunDialog(const CppcheckOptions &options, + const ProjectExplorer::Project *project) + : QDialog(), + m_options(new OptionsWidget(this)), + m_model(new ProjectExplorer::SelectableFilesFromDirModel(this)) +{ + QTC_ASSERT(project, return ); + + setWindowTitle(tr("Cppcheck run configuration")); + + auto view = new QTreeView; + view->setHeaderHidden(true); + view->setModel(m_model); + + connect(m_model, &ProjectExplorer::SelectableFilesFromDirModel::parsingFinished, + view, [this, view] { + m_model->applyFilter("*.cpp;*.cxx;*.c;*.cc;*.C", {}); + view->expandToDepth(0); + }); + m_model->startParsing(project->rootProjectDirectory()); + + auto buttons = new QDialogButtonBox; + buttons->setStandardButtons(QDialogButtonBox::Cancel); + connect(buttons, &QDialogButtonBox::accepted, + this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, + this, &QDialog::reject); + + auto analyzeButton = new QPushButton(tr("Analyze")); + buttons->addButton(analyzeButton, QDialogButtonBox::AcceptRole); + analyzeButton->setEnabled(m_model->hasCheckedFiles()); + connect(m_model, &QAbstractItemModel::dataChanged, + analyzeButton, [this, analyzeButton]() { + analyzeButton->setEnabled(m_model->hasCheckedFiles()); + }); + + auto layout = new QVBoxLayout(this); + layout->addWidget(m_options); + layout->addWidget(view); + layout->addWidget(buttons); + + if (auto layout = m_options->layout()) + layout->setMargin(0); + + m_options->load(options); +} + +CppcheckOptions ManualRunDialog::options() const +{ + CppcheckOptions result; + m_options->save(result); + return result; +} + +Utils::FilePathList ManualRunDialog::filePaths() const +{ + return m_model->selectedFiles(); +} + +QSize ManualRunDialog::sizeHint() const +{ + const auto original = QDialog::sizeHint(); + return {original.width() * 2, original.height()}; +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.h b/src/plugins/cppcheck/cppcheckmanualrundialog.h new file mode 100644 index 00000000000..92b4dda806e --- /dev/null +++ b/src/plugins/cppcheck/cppcheckmanualrundialog.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Sergey Morozov +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace Utils { +class FilePath; +using FilePathList = QList; +} // namespace Utils + +namespace ProjectExplorer { +class Project; +class SelectableFilesFromDirModel; +} // namespace ProjectExplorer + +namespace Cppcheck { +namespace Internal { + +class OptionsWidget; +class CppcheckOptions; + +class ManualRunDialog : public QDialog +{ + Q_OBJECT +public: + ManualRunDialog(const CppcheckOptions &options, + const ProjectExplorer::Project *project); + + CppcheckOptions options() const; + Utils::FilePathList filePaths() const; + QSize sizeHint() const override; + +private: + OptionsWidget *m_options; + ProjectExplorer::SelectableFilesFromDirModel *m_model; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp index 81cbe0616d9..d0392c167b1 100644 --- a/src/plugins/cppcheck/cppcheckoptions.cpp +++ b/src/plugins/cppcheck/cppcheckoptions.cpp @@ -45,120 +45,98 @@ namespace Cppcheck { namespace Internal { -class OptionsWidget final : public QWidget +OptionsWidget::OptionsWidget(QWidget *parent) + : QWidget(parent), + m_binary(new Utils::PathChooser(this)), + m_customArguments(new QLineEdit(this)), + m_ignorePatterns(new QLineEdit(this)), + m_warning(new QCheckBox(tr("Warnings"), this)), + m_style(new QCheckBox(tr("Style"), this)), + m_performance(new QCheckBox(tr("Performance"), this)), + m_portability(new QCheckBox(tr("Portability"), this)), + m_information(new QCheckBox(tr("Information"), this)), + m_unusedFunction(new QCheckBox(tr("Unused functions"), this)), + m_missingInclude(new QCheckBox(tr("Missing includes"), this)), + m_inconclusive(new QCheckBox(tr("Inconclusive errors"), this)), + m_forceDefines(new QCheckBox(tr("Check all define combinations"), this)), + m_showOutput(new QCheckBox(tr("Show raw output"), this)), + m_addIncludePaths(new QCheckBox(tr("Add include paths"), this)), + m_guessArguments(new QCheckBox(tr("Calculate additional arguments"), this)) { - Q_DECLARE_TR_FUNCTIONS(CppcheckOptionsPage) -public: - explicit OptionsWidget(QWidget *parent = nullptr) - : QWidget(parent), - m_binary(new Utils::PathChooser(this)), - m_customArguments(new QLineEdit(this)), - m_ignorePatterns(new QLineEdit(this)), - m_warning(new QCheckBox(tr("Warnings"), this)), - m_style(new QCheckBox(tr("Style"), this)), - m_performance(new QCheckBox(tr("Performance"), this)), - m_portability(new QCheckBox(tr("Portability"), this)), - m_information(new QCheckBox(tr("Information"), this)), - m_unusedFunction(new QCheckBox(tr("Unused functions"), this)), - m_missingInclude(new QCheckBox(tr("Missing includes"), this)), - m_inconclusive(new QCheckBox(tr("Inconclusive errors"), this)), - m_forceDefines(new QCheckBox(tr("Check all define combinations"), this)), - m_showOutput(new QCheckBox(tr("Show raw output"), this)), - m_addIncludePaths(new QCheckBox(tr("Add include paths"), this)), - m_guessArguments(new QCheckBox(tr("Calculate additional arguments"), this)) - { - m_binary->setExpectedKind(Utils::PathChooser::ExistingCommand); - m_binary->setCommandVersionArguments({"--version"}); + m_binary->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_binary->setCommandVersionArguments({"--version"}); - auto variableChooser = new Core::VariableChooser(this); - variableChooser->addSupportedWidget (m_customArguments); + auto variableChooser = new Core::VariableChooser(this); + variableChooser->addSupportedWidget (m_customArguments); - m_unusedFunction->setToolTip(tr("Disables multithreaded check.")); - m_ignorePatterns->setToolTip(tr("Comma-separated wildcards of full file paths. " - "Files still can be checked if others include them.")); - m_addIncludePaths->setToolTip(tr("Can find missing includes but makes " - "checking slower. Use only when needed.")); - m_guessArguments->setToolTip(tr("Like C++ standard and language.")); + m_unusedFunction->setToolTip(tr("Disables multithreaded check.")); + m_ignorePatterns->setToolTip(tr("Comma-separated wildcards of full file paths. " + "Files still can be checked if others include them.")); + m_addIncludePaths->setToolTip(tr("Can find missing includes but makes " + "checking slower. Use only when needed.")); + m_guessArguments->setToolTip(tr("Like C++ standard and language.")); - auto layout = new QFormLayout(this); - layout->addRow(tr("Binary:"), m_binary); + auto layout = new QFormLayout(this); + layout->addRow(tr("Binary:"), m_binary); - auto checks = new Utils::FlowLayout; - layout->addRow(tr("Checks:"), checks); - checks->addWidget(m_warning); - checks->addWidget(m_style); - checks->addWidget(m_performance); - checks->addWidget(m_portability); - checks->addWidget(m_information); - checks->addWidget(m_unusedFunction); - checks->addWidget(m_missingInclude); + auto checks = new Utils::FlowLayout; + layout->addRow(tr("Checks:"), checks); + checks->addWidget(m_warning); + checks->addWidget(m_style); + checks->addWidget(m_performance); + checks->addWidget(m_portability); + checks->addWidget(m_information); + checks->addWidget(m_unusedFunction); + checks->addWidget(m_missingInclude); - layout->addRow(tr("Custom arguments:"), m_customArguments); - layout->addRow(tr("Ignored file patterns:"), m_ignorePatterns); - auto flags = new Utils::FlowLayout; - layout->addRow(flags); - flags->addWidget(m_inconclusive); - flags->addWidget(m_forceDefines); - flags->addWidget(m_showOutput); - flags->addWidget(m_addIncludePaths); - flags->addWidget(m_guessArguments); - } + layout->addRow(tr("Custom arguments:"), m_customArguments); + layout->addRow(tr("Ignored file patterns:"), m_ignorePatterns); + auto flags = new Utils::FlowLayout; + layout->addRow(flags); + flags->addWidget(m_inconclusive); + flags->addWidget(m_forceDefines); + flags->addWidget(m_showOutput); + flags->addWidget(m_addIncludePaths); + flags->addWidget(m_guessArguments); +} - void load(const CppcheckOptions &options) - { - m_binary->setPath(options.binary); - m_customArguments->setText(options.customArguments); - m_ignorePatterns->setText(options.ignoredPatterns); - m_warning->setChecked(options.warning); - m_style->setChecked(options.style); - m_performance->setChecked(options.performance); - m_portability->setChecked(options.portability); - m_information->setChecked(options.information); - m_unusedFunction->setChecked(options.unusedFunction); - m_missingInclude->setChecked(options.missingInclude); - m_inconclusive->setChecked(options.inconclusive); - m_forceDefines->setChecked(options.forceDefines); - m_showOutput->setChecked(options.showOutput); - m_addIncludePaths->setChecked(options.addIncludePaths); - m_guessArguments->setChecked(options.guessArguments); - } +void OptionsWidget::load(const CppcheckOptions &options) +{ + m_binary->setPath(options.binary); + m_customArguments->setText(options.customArguments); + m_ignorePatterns->setText(options.ignoredPatterns); + m_warning->setChecked(options.warning); + m_style->setChecked(options.style); + m_performance->setChecked(options.performance); + m_portability->setChecked(options.portability); + m_information->setChecked(options.information); + m_unusedFunction->setChecked(options.unusedFunction); + m_missingInclude->setChecked(options.missingInclude); + m_inconclusive->setChecked(options.inconclusive); + m_forceDefines->setChecked(options.forceDefines); + m_showOutput->setChecked(options.showOutput); + m_addIncludePaths->setChecked(options.addIncludePaths); + m_guessArguments->setChecked(options.guessArguments); +} - void save(CppcheckOptions &options) const - { - options.binary = m_binary->path(); - options.customArguments = m_customArguments->text(); - options.ignoredPatterns = m_ignorePatterns->text(); - options.warning = m_warning->isChecked(); - options.style = m_style->isChecked(); - options.performance = m_performance->isChecked(); - options.portability = m_portability->isChecked(); - options.information = m_information->isChecked(); - options.unusedFunction = m_unusedFunction->isChecked(); - options.missingInclude = m_missingInclude->isChecked(); - options.inconclusive = m_inconclusive->isChecked(); - options.forceDefines = m_forceDefines->isChecked(); - options.showOutput = m_showOutput->isChecked(); - options.addIncludePaths = m_addIncludePaths->isChecked(); - options.guessArguments = m_guessArguments->isChecked(); - } - -private: - Utils::PathChooser *m_binary = nullptr; - QLineEdit *m_customArguments = nullptr; - QLineEdit *m_ignorePatterns = nullptr; - QCheckBox *m_warning = nullptr; - QCheckBox *m_style = nullptr; - QCheckBox *m_performance = nullptr; - QCheckBox *m_portability = nullptr; - QCheckBox *m_information = nullptr; - QCheckBox *m_unusedFunction = nullptr; - QCheckBox *m_missingInclude = nullptr; - QCheckBox *m_inconclusive = nullptr; - QCheckBox *m_forceDefines = nullptr; - QCheckBox *m_showOutput = nullptr; - QCheckBox *m_addIncludePaths = nullptr; - QCheckBox *m_guessArguments = nullptr; -}; +void OptionsWidget::save(CppcheckOptions &options) const +{ + options.binary = m_binary->path(); + options.customArguments = m_customArguments->text(); + options.ignoredPatterns = m_ignorePatterns->text(); + options.warning = m_warning->isChecked(); + options.style = m_style->isChecked(); + options.performance = m_performance->isChecked(); + options.portability = m_portability->isChecked(); + options.information = m_information->isChecked(); + options.unusedFunction = m_unusedFunction->isChecked(); + options.missingInclude = m_missingInclude->isChecked(); + options.inconclusive = m_inconclusive->isChecked(); + options.forceDefines = m_forceDefines->isChecked(); + options.showOutput = m_showOutput->isChecked(); + options.addIncludePaths = m_addIncludePaths->isChecked(); + options.guessArguments = m_guessArguments->isChecked(); +} CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger): m_tool(tool), diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppcheckoptions.h index 0791b950e0c..50959609e04 100644 --- a/src/plugins/cppcheck/cppcheckoptions.h +++ b/src/plugins/cppcheck/cppcheckoptions.h @@ -27,7 +27,16 @@ #include +#include #include +#include + +class QLineEdit; +class QCheckBox; + +namespace Utils { +class PathChooser; +} namespace Cppcheck { namespace Internal { @@ -58,6 +67,32 @@ public: bool guessArguments = true; }; +class OptionsWidget final : public QWidget +{ + Q_DECLARE_TR_FUNCTIONS(CppcheckOptionsPage) +public: + explicit OptionsWidget(QWidget *parent = nullptr); + void load(const CppcheckOptions &options); + void save(CppcheckOptions &options) const; + +private: + Utils::PathChooser *m_binary = nullptr; + QLineEdit *m_customArguments = nullptr; + QLineEdit *m_ignorePatterns = nullptr; + QCheckBox *m_warning = nullptr; + QCheckBox *m_style = nullptr; + QCheckBox *m_performance = nullptr; + QCheckBox *m_portability = nullptr; + QCheckBox *m_information = nullptr; + QCheckBox *m_unusedFunction = nullptr; + QCheckBox *m_missingInclude = nullptr; + QCheckBox *m_inconclusive = nullptr; + QCheckBox *m_forceDefines = nullptr; + QCheckBox *m_showOutput = nullptr; + QCheckBox *m_addIncludePaths = nullptr; + QCheckBox *m_guessArguments = nullptr; +}; + class CppcheckOptionsPage final : public Core::IOptionsPage { Q_OBJECT diff --git a/src/plugins/cppcheck/cppcheckplugin.cpp b/src/plugins/cppcheck/cppcheckplugin.cpp index 5b96452f4e6..97bf20929e6 100644 --- a/src/plugins/cppcheck/cppcheckplugin.cpp +++ b/src/plugins/cppcheck/cppcheckplugin.cpp @@ -23,16 +23,34 @@ ** ****************************************************************************/ -#include "cppcheckoptions.h" #include "cppcheckplugin.h" +#include "cppcheckconstants.h" +#include "cppcheckdiagnosticview.h" #include "cppchecktextmarkmanager.h" #include "cppchecktool.h" #include "cppchecktrigger.h" +#include "cppcheckdiagnosticsmodel.h" +#include "cppcheckmanualrundialog.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include namespace Cppcheck { namespace Internal { -class CppcheckPluginPrivate final +class CppcheckPluginPrivate final : public QObject { public: explicit CppcheckPluginPrivate(); @@ -40,13 +58,98 @@ public: CppcheckTool tool; CppcheckTrigger trigger; CppcheckOptionsPage options; + DiagnosticsModel manualRunModel; + CppcheckTool manualRunTool; + Utils::Perspective perspective{Constants::PERSPECTIVE_ID, + tr("Cppcheck", "CppcheckPlugin")}; + QAction *manualRunAction; + + void startManualRun(); + void updateManualRunAction(); }; CppcheckPluginPrivate::CppcheckPluginPrivate() : - tool(marks), + tool(marks, Constants::CHECK_PROGRESS_ID), trigger(marks, tool), - options(tool, trigger) + options(tool, trigger), + manualRunTool(manualRunModel, Constants::MANUAL_CHECK_PROGRESS_ID) { + manualRunTool.updateOptions(tool.options()); + + auto manualRunView = new DiagnosticView; + manualRunView->setModel(&manualRunModel); + perspective.addWindow(manualRunView, Utils::Perspective::SplitVertical, nullptr); + + { + // Go to previous diagnostic + auto action = new QAction(this); + action->setEnabled(false); + action->setIcon(Utils::Icons::PREV_TOOLBAR.icon()); + action->setToolTip(tr("Go to previous diagnostic.")); + connect(action, &QAction::triggered, + manualRunView, &Debugger::DetailedErrorView::goBack); + connect (&manualRunModel, &DiagnosticsModel::hasDataChanged, + action, &QAction::setEnabled); + perspective.addToolBarAction(action); + } + + { + // Go to next diagnostic + auto action = new QAction(this); + action->setEnabled(false); + action->setIcon(Utils::Icons::NEXT_TOOLBAR.icon()); + action->setToolTip(tr("Go to next diagnostic.")); + connect(action, &QAction::triggered, + manualRunView, &Debugger::DetailedErrorView::goNext); + connect (&manualRunModel, &DiagnosticsModel::hasDataChanged, + action, &QAction::setEnabled); + perspective.addToolBarAction(action); + } + + { + // Clear data + auto action = new QAction(this); + action->setEnabled(false); + action->setIcon(Utils::Icons::CLEAN_TOOLBAR.icon()); + action->setToolTip(tr("Clear")); + connect(action, &QAction::triggered, + &manualRunModel, &DiagnosticsModel::clear); + connect (&manualRunModel, &DiagnosticsModel::hasDataChanged, + action, &QAction::setEnabled); + perspective.addToolBarAction(action); + } +} + +void CppcheckPluginPrivate::startManualRun() { + auto project = ProjectExplorer::SessionManager::startupProject(); + if (!project) + return; + + ManualRunDialog dialog(manualRunTool.options(), project); + if (dialog.exec() == ManualRunDialog::Rejected) + return; + + manualRunModel.clear(); + + const auto files = dialog.filePaths(); + if (files.isEmpty()) + return; + + manualRunTool.setProject(project); + manualRunTool.updateOptions(dialog.options()); + manualRunTool.check(files); + perspective.select(); +} + +void CppcheckPluginPrivate::updateManualRunAction() +{ + using namespace ProjectExplorer; + const Project *project = SessionManager::startupProject(); + const Target *target = SessionManager::startupTarget(); + const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID; + const bool canRun = target && project->projectLanguages().contains(cxx) + && ToolChainKitAspect::toolChain(target->kit(), cxx); + manualRunAction->setEnabled(canRun); } CppcheckPlugin::CppcheckPlugin() = default; @@ -60,6 +163,23 @@ bool CppcheckPlugin::initialize(const QStringList &arguments, QString *errorStri d.reset(new CppcheckPluginPrivate); + using namespace Core; + ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER); + + { + auto action = new QAction(tr("Cppcheck..."), this); + menu->addAction(ActionManager::registerAction(action, Constants::MANUAL_RUN_ACTION), + Debugger::Constants::G_ANALYZER_TOOLS); + connect(action, &QAction::triggered, + d.get(), &CppcheckPluginPrivate::startManualRun); + d->manualRunAction = action; + } + + using ProjectExplorer::ProjectExplorerPlugin; + connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions, + d.get(), &CppcheckPluginPrivate::updateManualRunAction); + d->updateManualRunAction(); + return true; } diff --git a/src/plugins/cppcheck/cppchecktextmarkmanager.h b/src/plugins/cppcheck/cppchecktextmarkmanager.h index 01be4708160..d9904915da8 100644 --- a/src/plugins/cppcheck/cppchecktextmarkmanager.h +++ b/src/plugins/cppcheck/cppchecktextmarkmanager.h @@ -25,6 +25,8 @@ #pragma once +#include + #include #include @@ -35,13 +37,13 @@ namespace Internal { class Diagnostic; class CppcheckTextMark; -class CppcheckTextMarkManager final +class CppcheckTextMarkManager final : public CppcheckDiagnosticManager { public: explicit CppcheckTextMarkManager(); - ~CppcheckTextMarkManager(); + ~CppcheckTextMarkManager() override; - void add(const Diagnostic &diagnostic); + void add(const Diagnostic &diagnostic) override; void clearFiles(const Utils::FilePathList &files); private: diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp index 02cb1d68deb..ded937c61e3 100644 --- a/src/plugins/cppcheck/cppchecktool.cpp +++ b/src/plugins/cppcheck/cppchecktool.cpp @@ -23,7 +23,6 @@ ** ****************************************************************************/ -#include "cppcheckconstants.h" #include "cppcheckdiagnostic.h" #include "cppcheckoptions.h" #include "cppcheckrunner.h" @@ -45,10 +44,12 @@ namespace Cppcheck { namespace Internal { -CppcheckTool::CppcheckTool(CppcheckTextMarkManager &marks) : - m_marks(marks), +CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager, + const Core::Id &progressId) : + m_manager(manager), m_progressRegexp("^.* checked (\\d+)% done$"), - m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$") + m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$"), + m_progressId(progressId) { m_runner = std::make_unique(*this); QTC_ASSERT(m_progressRegexp.isValid(), return); @@ -246,8 +247,7 @@ void CppcheckTool::startParsing() m_progress = std::make_unique>(); const Core::FutureProgress *progress = Core::ProgressManager::addTask( - m_progress->future(), QObject::tr("Cppcheck"), - Constants::CHECK_PROGRESS_ID); + m_progress->future(), QObject::tr("Cppcheck"), m_progressId); QObject::connect(progress, &Core::FutureProgress::canceled, this, [this]{stop({});}); m_progress->setProgressRange(0, 100); @@ -310,7 +310,7 @@ void CppcheckTool::parseErrorLine(const QString &line) diagnostic.checkId = match.captured(Id); diagnostic.message = match.captured(Message); if (diagnostic.isValid()) - m_marks.add(diagnostic); + m_manager.add(diagnostic); } void CppcheckTool::finishParsing() diff --git a/src/plugins/cppcheck/cppchecktool.h b/src/plugins/cppcheck/cppchecktool.h index 203dc2edd27..bc3d779e772 100644 --- a/src/plugins/cppcheck/cppchecktool.h +++ b/src/plugins/cppcheck/cppchecktool.h @@ -50,7 +50,7 @@ namespace Cppcheck { namespace Internal { class CppcheckRunner; -class CppcheckTextMarkManager; +class CppcheckDiagnosticManager; class CppcheckOptions; class CppcheckTool final : public QObject @@ -58,7 +58,7 @@ class CppcheckTool final : public QObject Q_OBJECT public: - explicit CppcheckTool(CppcheckTextMarkManager &marks); + CppcheckTool(CppcheckDiagnosticManager &manager, const Core::Id &progressId); ~CppcheckTool() override; void updateOptions(const CppcheckOptions &options); @@ -78,7 +78,7 @@ private: void addToQueue(const Utils::FilePathList &files, CppTools::ProjectPart &part); QStringList additionalArguments(const CppTools::ProjectPart &part) const; - CppcheckTextMarkManager &m_marks; + CppcheckDiagnosticManager &m_manager; CppcheckOptions m_options; QPointer m_project; std::unique_ptr m_runner; @@ -87,6 +87,7 @@ private: QVector m_filters; QRegularExpression m_progressRegexp; QRegularExpression m_messageRegexp; + Core::Id m_progressId; }; } // namespace Internal