Cppcheck: Add ability to manually run cppcheck

Run cppcheck on selected files from current project via "Analyze"->"Cppcheck...."
Show results in a separate view in the same manner as ClangTools plugin.

Fixes: QTCREATORBUG-21673
Change-Id: Ibcaf4057a387a990f1da59025f15ba58f996953f
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
Sergey Morozov
2019-11-03 23:00:16 +03:00
parent 07490e76de
commit 0aa95576c2
20 changed files with 936 additions and 126 deletions

View File

@@ -1,9 +1,13 @@
add_qtc_plugin(Cppcheck add_qtc_plugin(Cppcheck
DEPENDS Qt5::Widgets DEPENDS Qt5::Widgets
PLUGIN_DEPENDS Core CppTools ProjectExplorer TextEditor PLUGIN_DEPENDS Core Debugger CppTools ProjectExplorer TextEditor
SOURCES SOURCES
cppcheckconstants.h 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 cppcheckoptions.cpp cppcheckoptions.h
cppcheckplugin.cpp cppcheckplugin.h cppcheckplugin.cpp cppcheckplugin.h
cppcheckrunner.cpp cppcheckrunner.h cppcheckrunner.cpp cppcheckrunner.h

View File

@@ -1,6 +1,10 @@
include(../../qtcreatorplugin.pri) include(../../qtcreatorplugin.pri)
SOURCES += \ SOURCES += \
cppcheckdiagnostic.cpp \
cppcheckdiagnosticsmodel.cpp \
cppcheckdiagnosticview.cpp \
cppcheckmanualrundialog.cpp \
cppcheckoptions.cpp \ cppcheckoptions.cpp \
cppcheckplugin.cpp \ cppcheckplugin.cpp \
cppcheckrunner.cpp \ cppcheckrunner.cpp \
@@ -12,6 +16,10 @@ SOURCES += \
HEADERS += \ HEADERS += \
cppcheckconstants.h \ cppcheckconstants.h \
cppcheckdiagnostic.h \ cppcheckdiagnostic.h \
cppcheckdiagnosticmanager.h \
cppcheckdiagnosticsmodel.h \
cppcheckdiagnosticview.h \
cppcheckmanualrundialog.h \
cppcheckoptions.h \ cppcheckoptions.h \
cppcheckplugin.h \ cppcheckplugin.h \
cppcheckrunner.h \ cppcheckrunner.h \

View File

@@ -5,6 +5,7 @@ QtcPlugin {
Depends { name: "Core" } Depends { name: "Core" }
Depends { name: "CppTools" } Depends { name: "CppTools" }
Depends { name: "Debugger" }
Depends { name: "ProjectExplorer" } Depends { name: "ProjectExplorer" }
Depends { name: "TextEditor" } Depends { name: "TextEditor" }
Depends { name: "Utils" } Depends { name: "Utils" }
@@ -13,7 +14,15 @@ QtcPlugin {
files: [ files: [
"cppcheckconstants.h", "cppcheckconstants.h",
"cppcheckdiagnostic.cpp",
"cppcheckdiagnostic.h", "cppcheckdiagnostic.h",
"cppcheckdiagnosticmanager.h",
"cppcheckdiagnosticsmodel.cpp",
"cppcheckdiagnosticsmodel.h",
"cppcheckdiagnosticview.cpp",
"cppcheckdiagnosticview.h",
"cppcheckmanualrundialog.cpp",
"cppcheckmanualrundialog.h",
"cppcheckoptions.cpp", "cppcheckoptions.cpp",
"cppcheckoptions.h", "cppcheckoptions.h",
"cppcheckplugin.cpp", "cppcheckplugin.cpp",

View File

@@ -4,5 +4,6 @@ QTC_LIB_DEPENDS += \
utils utils
QTC_PLUGIN_DEPENDS += \ QTC_PLUGIN_DEPENDS += \
cpptools \ cpptools \
debugger \
projectexplorer \ projectexplorer \
texteditor texteditor

View File

@@ -49,7 +49,11 @@ const char SETTINGS_SHOW_OUTPUT[] = "showOutput";
const char SETTINGS_ADD_INCLUDE_PATHS[] = "addIncludePaths"; const char SETTINGS_ADD_INCLUDE_PATHS[] = "addIncludePaths";
const char SETTINGS_GUESS_ARGUMENTS[] = "guessArguments"; 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 Constants
} // namespace Cppcheck } // namespace Cppcheck

View File

@@ -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

View File

@@ -39,6 +39,7 @@ public:
bool isValid() const { bool isValid() const {
return !fileName.isEmpty() && lineNumber > 0; return !fileName.isEmpty() && lineNumber > 0;
} }
bool operator==(const Diagnostic& diagnostic) const;
Severity severity = Severity::Information; Severity severity = Severity::Information;
QString severityText; QString severityText;
@@ -48,5 +49,7 @@ public:
int lineNumber = 0; int lineNumber = 0;
}; };
quint32 qHash(const Diagnostic &diagnostic);
} // namespace Internal } // namespace Internal
} // namespace Cppcheck } // namespace Cppcheck

View File

@@ -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

View File

@@ -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 <coreplugin/fileiconprovider.h>
#include <debugger/analyzer/diagnosticlocation.h>
#include <utils/utilsicons.h>
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

View File

@@ -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 <cppcheck/cppcheckdiagnostic.h>
#include <cppcheck/cppcheckdiagnosticmanager.h>
#include <debugger/analyzer/detailederrorview.h>
#include <utils/treemodel.h>
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<Utils::TreeItem, FilePathItem, DiagnosticItem>;
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<QString, FilePathItem *> m_filePathToItem;
QSet<Diagnostic> m_diagnostics;
};
} // namespace Internal
} // namespace Cppcheck

View File

@@ -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 <coreplugin/editormanager/editormanager.h>
#include <debugger/analyzer/diagnosticlocation.h>
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<Debugger::DiagnosticLocation>();
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"

View File

@@ -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 <debugger/analyzer/detailederrorview.h>
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

View File

@@ -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 <projectexplorer/selectablefilesmodel.h>
#include <cpptools/projectinfo.h>
#include <utils/qtcassert.h>
#include <QBoxLayout>
#include <QDialogButtonBox>
#include <QPushButton>
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

View File

@@ -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 <QDialog>
namespace Utils {
class FilePath;
using FilePathList = QList<FilePath>;
} // 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

View File

@@ -45,11 +45,7 @@
namespace Cppcheck { namespace Cppcheck {
namespace Internal { namespace Internal {
class OptionsWidget final : public QWidget OptionsWidget::OptionsWidget(QWidget *parent)
{
Q_DECLARE_TR_FUNCTIONS(CppcheckOptionsPage)
public:
explicit OptionsWidget(QWidget *parent = nullptr)
: QWidget(parent), : QWidget(parent),
m_binary(new Utils::PathChooser(this)), m_binary(new Utils::PathChooser(this)),
m_customArguments(new QLineEdit(this)), m_customArguments(new QLineEdit(this)),
@@ -104,7 +100,7 @@ public:
flags->addWidget(m_guessArguments); flags->addWidget(m_guessArguments);
} }
void load(const CppcheckOptions &options) void OptionsWidget::load(const CppcheckOptions &options)
{ {
m_binary->setPath(options.binary); m_binary->setPath(options.binary);
m_customArguments->setText(options.customArguments); m_customArguments->setText(options.customArguments);
@@ -123,7 +119,7 @@ public:
m_guessArguments->setChecked(options.guessArguments); m_guessArguments->setChecked(options.guessArguments);
} }
void save(CppcheckOptions &options) const void OptionsWidget::save(CppcheckOptions &options) const
{ {
options.binary = m_binary->path(); options.binary = m_binary->path();
options.customArguments = m_customArguments->text(); options.customArguments = m_customArguments->text();
@@ -142,24 +138,6 @@ public:
options.guessArguments = m_guessArguments->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;
};
CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger): CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger):
m_tool(tool), m_tool(tool),
m_trigger(trigger) m_trigger(trigger)

View File

@@ -27,7 +27,16 @@
#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/dialogs/ioptionspage.h>
#include <QCoreApplication>
#include <QPointer> #include <QPointer>
#include <QWidget>
class QLineEdit;
class QCheckBox;
namespace Utils {
class PathChooser;
}
namespace Cppcheck { namespace Cppcheck {
namespace Internal { namespace Internal {
@@ -58,6 +67,32 @@ public:
bool guessArguments = true; 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 class CppcheckOptionsPage final : public Core::IOptionsPage
{ {
Q_OBJECT Q_OBJECT

View File

@@ -23,16 +23,34 @@
** **
****************************************************************************/ ****************************************************************************/
#include "cppcheckoptions.h"
#include "cppcheckplugin.h" #include "cppcheckplugin.h"
#include "cppcheckconstants.h"
#include "cppcheckdiagnosticview.h"
#include "cppchecktextmarkmanager.h" #include "cppchecktextmarkmanager.h"
#include "cppchecktool.h" #include "cppchecktool.h"
#include "cppchecktrigger.h" #include "cppchecktrigger.h"
#include "cppcheckdiagnosticsmodel.h"
#include "cppcheckmanualrundialog.h"
#include <projectexplorer/session.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/target.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <debugger/analyzer/analyzerconstants.h>
#include <debugger/debuggermainwindow.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
namespace Cppcheck { namespace Cppcheck {
namespace Internal { namespace Internal {
class CppcheckPluginPrivate final class CppcheckPluginPrivate final : public QObject
{ {
public: public:
explicit CppcheckPluginPrivate(); explicit CppcheckPluginPrivate();
@@ -40,13 +58,98 @@ public:
CppcheckTool tool; CppcheckTool tool;
CppcheckTrigger trigger; CppcheckTrigger trigger;
CppcheckOptionsPage options; CppcheckOptionsPage options;
DiagnosticsModel manualRunModel;
CppcheckTool manualRunTool;
Utils::Perspective perspective{Constants::PERSPECTIVE_ID,
tr("Cppcheck", "CppcheckPlugin")};
QAction *manualRunAction;
void startManualRun();
void updateManualRunAction();
}; };
CppcheckPluginPrivate::CppcheckPluginPrivate() : CppcheckPluginPrivate::CppcheckPluginPrivate() :
tool(marks), tool(marks, Constants::CHECK_PROGRESS_ID),
trigger(marks, tool), 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; CppcheckPlugin::CppcheckPlugin() = default;
@@ -60,6 +163,23 @@ bool CppcheckPlugin::initialize(const QStringList &arguments, QString *errorStri
d.reset(new CppcheckPluginPrivate); 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; return true;
} }

View File

@@ -25,6 +25,8 @@
#pragma once #pragma once
#include <cppcheck/cppcheckdiagnosticmanager.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <unordered_map> #include <unordered_map>
@@ -35,13 +37,13 @@ namespace Internal {
class Diagnostic; class Diagnostic;
class CppcheckTextMark; class CppcheckTextMark;
class CppcheckTextMarkManager final class CppcheckTextMarkManager final : public CppcheckDiagnosticManager
{ {
public: public:
explicit CppcheckTextMarkManager(); explicit CppcheckTextMarkManager();
~CppcheckTextMarkManager(); ~CppcheckTextMarkManager() override;
void add(const Diagnostic &diagnostic); void add(const Diagnostic &diagnostic) override;
void clearFiles(const Utils::FilePathList &files); void clearFiles(const Utils::FilePathList &files);
private: private:

View File

@@ -23,7 +23,6 @@
** **
****************************************************************************/ ****************************************************************************/
#include "cppcheckconstants.h"
#include "cppcheckdiagnostic.h" #include "cppcheckdiagnostic.h"
#include "cppcheckoptions.h" #include "cppcheckoptions.h"
#include "cppcheckrunner.h" #include "cppcheckrunner.h"
@@ -45,10 +44,12 @@
namespace Cppcheck { namespace Cppcheck {
namespace Internal { namespace Internal {
CppcheckTool::CppcheckTool(CppcheckTextMarkManager &marks) : CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager,
m_marks(marks), const Core::Id &progressId) :
m_manager(manager),
m_progressRegexp("^.* checked (\\d+)% done$"), m_progressRegexp("^.* checked (\\d+)% done$"),
m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$") m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$"),
m_progressId(progressId)
{ {
m_runner = std::make_unique<CppcheckRunner>(*this); m_runner = std::make_unique<CppcheckRunner>(*this);
QTC_ASSERT(m_progressRegexp.isValid(), return); QTC_ASSERT(m_progressRegexp.isValid(), return);
@@ -246,8 +247,7 @@ void CppcheckTool::startParsing()
m_progress = std::make_unique<QFutureInterface<void>>(); m_progress = std::make_unique<QFutureInterface<void>>();
const Core::FutureProgress *progress = Core::ProgressManager::addTask( const Core::FutureProgress *progress = Core::ProgressManager::addTask(
m_progress->future(), QObject::tr("Cppcheck"), m_progress->future(), QObject::tr("Cppcheck"), m_progressId);
Constants::CHECK_PROGRESS_ID);
QObject::connect(progress, &Core::FutureProgress::canceled, QObject::connect(progress, &Core::FutureProgress::canceled,
this, [this]{stop({});}); this, [this]{stop({});});
m_progress->setProgressRange(0, 100); m_progress->setProgressRange(0, 100);
@@ -310,7 +310,7 @@ void CppcheckTool::parseErrorLine(const QString &line)
diagnostic.checkId = match.captured(Id); diagnostic.checkId = match.captured(Id);
diagnostic.message = match.captured(Message); diagnostic.message = match.captured(Message);
if (diagnostic.isValid()) if (diagnostic.isValid())
m_marks.add(diagnostic); m_manager.add(diagnostic);
} }
void CppcheckTool::finishParsing() void CppcheckTool::finishParsing()

View File

@@ -50,7 +50,7 @@ namespace Cppcheck {
namespace Internal { namespace Internal {
class CppcheckRunner; class CppcheckRunner;
class CppcheckTextMarkManager; class CppcheckDiagnosticManager;
class CppcheckOptions; class CppcheckOptions;
class CppcheckTool final : public QObject class CppcheckTool final : public QObject
@@ -58,7 +58,7 @@ class CppcheckTool final : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit CppcheckTool(CppcheckTextMarkManager &marks); CppcheckTool(CppcheckDiagnosticManager &manager, const Core::Id &progressId);
~CppcheckTool() override; ~CppcheckTool() override;
void updateOptions(const CppcheckOptions &options); void updateOptions(const CppcheckOptions &options);
@@ -78,7 +78,7 @@ private:
void addToQueue(const Utils::FilePathList &files, CppTools::ProjectPart &part); void addToQueue(const Utils::FilePathList &files, CppTools::ProjectPart &part);
QStringList additionalArguments(const CppTools::ProjectPart &part) const; QStringList additionalArguments(const CppTools::ProjectPart &part) const;
CppcheckTextMarkManager &m_marks; CppcheckDiagnosticManager &m_manager;
CppcheckOptions m_options; CppcheckOptions m_options;
QPointer<ProjectExplorer::Project> m_project; QPointer<ProjectExplorer::Project> m_project;
std::unique_ptr<CppcheckRunner> m_runner; std::unique_ptr<CppcheckRunner> m_runner;
@@ -87,6 +87,7 @@ private:
QVector<QRegExp> m_filters; QVector<QRegExp> m_filters;
QRegularExpression m_progressRegexp; QRegularExpression m_progressRegexp;
QRegularExpression m_messageRegexp; QRegularExpression m_messageRegexp;
Core::Id m_progressId;
}; };
} // namespace Internal } // namespace Internal