diff --git a/src/plugins/cppcheck/Cppcheck.json.in b/src/plugins/cppcheck/Cppcheck.json.in new file mode 100644 index 00000000000..ef2b1f48ca9 --- /dev/null +++ b/src/plugins/cppcheck/Cppcheck.json.in @@ -0,0 +1,20 @@ +{ + \"Name\" : \"Cppcheck\", + \"Version\" : \"$$QTCREATOR_VERSION\", + \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", + \"Experimental\" : true, + \"Vendor\" : \"Sergey Morozov\", + \"Copyright\" : \"(C) 2018 Sergey Morozov, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", + \"License\" : [ \"Commercial 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.\" + ], + \"Category\" : \"Code Analyzer\", + \"Description\" : \"Cppcheck static analyzer tool integration. See http://cppcheck.sourceforge.net.\", + \"Url\" : \"http://www.qt.io\", + $$dependencyList +} diff --git a/src/plugins/cppcheck/cppcheck.pro b/src/plugins/cppcheck/cppcheck.pro new file mode 100644 index 00000000000..f25918f5ab7 --- /dev/null +++ b/src/plugins/cppcheck/cppcheck.pro @@ -0,0 +1,21 @@ +include(../../qtcreatorplugin.pri) + +SOURCES += \ + cppcheckoptions.cpp \ + cppcheckplugin.cpp \ + cppcheckrunner.cpp \ + cppchecktextmark.cpp \ + cppchecktextmarkmanager.cpp \ + cppchecktool.cpp \ + cppchecktrigger.cpp + +HEADERS += \ + cppcheckconstants.h \ + cppcheckdiagnostic.h \ + cppcheckoptions.h \ + cppcheckplugin.h \ + cppcheckrunner.h \ + cppchecktextmark.h \ + cppchecktextmarkmanager.h \ + cppchecktool.h \ + cppchecktrigger.h diff --git a/src/plugins/cppcheck/cppcheck.qbs b/src/plugins/cppcheck/cppcheck.qbs new file mode 100644 index 00000000000..56f6d737c10 --- /dev/null +++ b/src/plugins/cppcheck/cppcheck.qbs @@ -0,0 +1,33 @@ +import qbs + +QtcPlugin { + name: "Cppcheck" + + Depends { name: "Core" } + Depends { name: "CppTools" } + Depends { name: "ExtensionSystem" } + Depends { name: "ProjectExplorer" } + Depends { name: "TextEditor" } + Depends { name: "Utils" } + + Depends { name: "Qt.widgets" } + + files: [ + "cppcheckconstants.h", + "cppcheckdiagnostic.h", + "cppcheckoptions.cpp", + "cppcheckoptions.h", + "cppcheckplugin.cpp", + "cppcheckplugin.h", + "cppcheckrunner.cpp", + "cppcheckrunner.h", + "cppchecktextmark.cpp", + "cppchecktextmark.h", + "cppchecktextmarkmanager.cpp", + "cppchecktextmarkmanager.h", + "cppchecktool.cpp", + "cppchecktool.h", + "cppchecktrigger.cpp", + "cppchecktrigger.h" + ] +} diff --git a/src/plugins/cppcheck/cppcheck_dependencies.pri b/src/plugins/cppcheck/cppcheck_dependencies.pri new file mode 100644 index 00000000000..e136adbab12 --- /dev/null +++ b/src/plugins/cppcheck/cppcheck_dependencies.pri @@ -0,0 +1,8 @@ +QTC_PLUGIN_NAME = Cppcheck +QTC_LIB_DEPENDS += \ + extensionsystem \ + utils +QTC_PLUGIN_DEPENDS += \ + cpptools \ + projectexplorer \ + texteditor diff --git a/src/plugins/cppcheck/cppcheckconstants.h b/src/plugins/cppcheck/cppcheckconstants.h new file mode 100644 index 00000000000..0611fe61272 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckconstants.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** 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 Constants { + +const char TEXTMARK_CATEGORY_ID[] = "Cppcheck"; + +const char OPTIONS_PAGE_ID[] = "Analyzer.Cppcheck.Settings"; + +const char SETTINGS_ID[] = "Cppcheck"; +const char SETTINGS_BINARY[] = "binary"; +const char SETTINGS_WARNING[] = "warning"; +const char SETTINGS_STYLE[] = "style"; +const char SETTINGS_PERFORMANCE[] = "performance"; +const char SETTINGS_PORTABILITY[] = "portability"; +const char SETTINGS_INFORMATION[] = "information"; +const char SETTINGS_UNUSED_FUNCTION[] = "unusedFunction"; +const char SETTINGS_MISSING_INCLUDE[] = "missingInclude"; +const char SETTINGS_INCONCLUSIVE[] = "inconclusive"; +const char SETTINGS_FORCE_DEFINES[] = "forceDefines"; +const char SETTINGS_CUSTOM_ARGUMENTS[] = "customArguments"; +const char SETTINGS_IGNORE_PATTERNS[] = "ignorePatterns"; +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"; + +} // namespace Constants +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckdiagnostic.h b/src/plugins/cppcheck/cppcheckdiagnostic.h new file mode 100644 index 00000000000..66e0b1dc824 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckdiagnostic.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** 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 + +#include + +namespace Cppcheck { +namespace Internal { + +class Diagnostic final +{ +public: + enum class Severity { + Error, Warning, Performance, Portability, Style, Information + }; + bool isValid() const { + return !fileName.isEmpty() && lineNumber > 0; + } + + Severity severity = Severity::Information; + QString severityText; + QString checkId; + QString message; + Utils::FileName fileName; + int lineNumber = 0; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp new file mode 100644 index 00000000000..562741075a3 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckoptions.cpp @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "cppcheckconstants.h" +#include "cppcheckoptions.h" +#include "cppchecktool.h" +#include "cppchecktrigger.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace Cppcheck { +namespace Internal { + +class OptionsWidget final : public QWidget +{ +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 include"), 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"}); + + const 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.")); + + const auto layout = new QFormLayout(this); + layout->addRow(tr("Binary:"), m_binary); + + const 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); + const 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 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; +}; + +CppcheckOptionsPage::CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger): + m_tool(tool), + m_trigger(trigger) +{ + setId(Constants::OPTIONS_PAGE_ID); + setDisplayName(tr("Cppcheck")); + setCategory("T.Analyzer"); + + CppcheckOptions options; + if (Utils::HostOsInfo::isAnyUnixHost()) { + options.binary = "cppcheck"; + } else { + auto programFiles = QDir::fromNativeSeparators( + QString::fromLocal8Bit(qgetenv("PROGRAMFILES"))); + if (programFiles.isEmpty()) + programFiles = "C:/Program Files"; + options.binary = programFiles + "/Cppcheck/cppcheck.exe"; + } + + load(options); + + m_tool.updateOptions(options); +} + +QWidget *CppcheckOptionsPage::widget() +{ + if (!m_widget) + m_widget = new OptionsWidget; + m_widget->load(m_tool.options()); + return m_widget.data(); +} + +void CppcheckOptionsPage::apply() +{ + CppcheckOptions options; + m_widget->save(options); + save(options); + m_tool.updateOptions(options); + m_trigger.recheck(); +} + +void CppcheckOptionsPage::finish() +{ +} + +void CppcheckOptionsPage::save(const CppcheckOptions &options) const +{ + const auto s = Core::ICore::settings(); + QTC_ASSERT(s, return); + s->beginGroup(Constants::SETTINGS_ID); + s->setValue(Constants::SETTINGS_BINARY, options.binary); + s->setValue(Constants::SETTINGS_CUSTOM_ARGUMENTS, options.customArguments); + s->setValue(Constants::SETTINGS_IGNORE_PATTERNS, options.ignoredPatterns); + s->setValue(Constants::SETTINGS_WARNING, options.warning); + s->setValue(Constants::SETTINGS_STYLE, options.style); + s->setValue(Constants::SETTINGS_PERFORMANCE, options.performance); + s->setValue(Constants::SETTINGS_PORTABILITY, options.portability); + s->setValue(Constants::SETTINGS_INFORMATION, options.information); + s->setValue(Constants::SETTINGS_UNUSED_FUNCTION, options.unusedFunction); + s->setValue(Constants::SETTINGS_MISSING_INCLUDE, options.missingInclude); + s->setValue(Constants::SETTINGS_INCONCLUSIVE, options.inconclusive); + s->setValue(Constants::SETTINGS_FORCE_DEFINES, options.forceDefines); + s->setValue(Constants::SETTINGS_SHOW_OUTPUT, options.showOutput); + s->setValue(Constants::SETTINGS_ADD_INCLUDE_PATHS, options.addIncludePaths); + s->setValue(Constants::SETTINGS_GUESS_ARGUMENTS, options.guessArguments); + s->endGroup(); +} + +void CppcheckOptionsPage::load(CppcheckOptions &options) const +{ + const auto s = Core::ICore::settings(); + QTC_ASSERT(s, return); + s->beginGroup(Constants::SETTINGS_ID); + options.binary = s->value(Constants::SETTINGS_BINARY, + options.binary).toString(); + options.customArguments = s->value(Constants::SETTINGS_CUSTOM_ARGUMENTS, + options.customArguments).toString(); + options.ignoredPatterns = s->value(Constants::SETTINGS_IGNORE_PATTERNS, + options.ignoredPatterns).toString(); + options.warning = s->value(Constants::SETTINGS_WARNING, + options.warning).toBool(); + options.style = s->value(Constants::SETTINGS_STYLE, + options.style).toBool(); + options.performance = s->value(Constants::SETTINGS_PERFORMANCE, + options.performance).toBool(); + options.portability = s->value(Constants::SETTINGS_PORTABILITY, + options.portability).toBool(); + options.information = s->value(Constants::SETTINGS_INFORMATION, + options.information).toBool(); + options.unusedFunction = s->value(Constants::SETTINGS_UNUSED_FUNCTION, + options.unusedFunction).toBool(); + options.missingInclude = s->value(Constants::SETTINGS_MISSING_INCLUDE, + options.missingInclude).toBool(); + options.inconclusive = s->value(Constants::SETTINGS_INCONCLUSIVE, + options.inconclusive).toBool(); + options.forceDefines = s->value(Constants::SETTINGS_FORCE_DEFINES, + options.forceDefines).toBool(); + options.showOutput = s->value(Constants::SETTINGS_SHOW_OUTPUT, + options.showOutput).toBool(); + options.addIncludePaths = s->value(Constants::SETTINGS_ADD_INCLUDE_PATHS, + options.addIncludePaths).toBool(); + options.guessArguments = s->value(Constants::SETTINGS_GUESS_ARGUMENTS, + options.guessArguments).toBool(); + s->endGroup(); +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppcheckoptions.h new file mode 100644 index 00000000000..0791b950e0c --- /dev/null +++ b/src/plugins/cppcheck/cppcheckoptions.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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 + +#include + +#include + +namespace Cppcheck { +namespace Internal { + +class CppcheckTool; +class CppcheckTrigger; +class OptionsWidget; + +class CppcheckOptions final +{ +public: + QString binary; + + bool warning = true; + bool style = true; + bool performance = true; + bool portability = true; + bool information = true; + bool unusedFunction = false; + bool missingInclude = false; + bool inconclusive = false; + bool forceDefines = false; + + QString customArguments; + QString ignoredPatterns; + bool showOutput = false; + bool addIncludePaths = false; + bool guessArguments = true; +}; + +class CppcheckOptionsPage final : public Core::IOptionsPage +{ + Q_OBJECT +public: + explicit CppcheckOptionsPage(CppcheckTool &tool, CppcheckTrigger &trigger); + + QWidget *widget() final; + void apply() final; + void finish() final; + +private: + void save(const CppcheckOptions &options) const; + void load(CppcheckOptions &options) const; + + CppcheckTool &m_tool; + CppcheckTrigger &m_trigger; + QPointer m_widget; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckplugin.cpp b/src/plugins/cppcheck/cppcheckplugin.cpp new file mode 100644 index 00000000000..983955ef39f --- /dev/null +++ b/src/plugins/cppcheck/cppcheckplugin.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "cppcheckoptions.h" +#include "cppcheckplugin.h" +#include "cppchecktextmarkmanager.h" +#include "cppchecktool.h" +#include "cppchecktrigger.h" + +namespace Cppcheck { +namespace Internal { + +class CppcheckPluginPrivate final +{ +public: + explicit CppcheckPluginPrivate(); + CppcheckTextMarkManager marks; + CppcheckTool tool; + CppcheckTrigger trigger; + CppcheckOptionsPage options; +}; + +CppcheckPluginPrivate::CppcheckPluginPrivate() : + tool(marks), + trigger(marks, tool), + options(tool, trigger) +{ +} + +CppcheckPlugin::CppcheckPlugin() = default; + +CppcheckPlugin::~CppcheckPlugin() = default; + +bool CppcheckPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + Q_UNUSED(arguments); + Q_UNUSED(errorString); + + d.reset(new CppcheckPluginPrivate); + + return true; +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckplugin.h b/src/plugins/cppcheck/cppcheckplugin.h new file mode 100644 index 00000000000..e9e2770873c --- /dev/null +++ b/src/plugins/cppcheck/cppcheckplugin.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 + +#include + +#include + +namespace Cppcheck { +namespace Internal { + +class CppcheckPluginPrivate; + +class CppcheckPlugin final : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Cppcheck.json") + +public: + CppcheckPlugin(); + ~CppcheckPlugin() override; + +private: + bool initialize(const QStringList &arguments, QString *errorString) final; + void extensionsInitialized() final {} + + std::unique_ptr d; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckrunner.cpp b/src/plugins/cppcheck/cppcheckrunner.cpp new file mode 100644 index 00000000000..21c0dbb8535 --- /dev/null +++ b/src/plugins/cppcheck/cppcheckrunner.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "cppcheckrunner.h" +#include "cppchecktool.h" + +#include +#include +#include + +#include + +namespace Cppcheck { +namespace Internal { + +CppcheckRunner::CppcheckRunner(CppcheckTool &tool) : + m_tool(tool), + m_process(new Utils::QtcProcess(this)) +{ + if (Utils::HostOsInfo::hostOs() == Utils::OsTypeLinux) { + QProcess getConf; + getConf.start("getconf ARG_MAX"); + getConf.waitForFinished(2000); + const auto argMax = getConf.readAllStandardOutput().replace("\n", ""); + m_maxArgumentsLength = std::max(argMax.toInt(), m_maxArgumentsLength); + } + + connect(m_process, &QProcess::readyReadStandardOutput, + this, &CppcheckRunner::readOutput); + connect(m_process, &QProcess::readyReadStandardOutput, + this, &CppcheckRunner::readError); + connect(m_process, &QProcess::started, + this, &CppcheckRunner::handleStarted); + connect(m_process, QOverload::of(&QProcess::finished), + this, &CppcheckRunner::handleFinished); + + m_queueTimer.setSingleShot(true); + const auto checkDelayInMs = 200; + m_queueTimer.setInterval(checkDelayInMs); + connect(&m_queueTimer, &QTimer::timeout, + this, &CppcheckRunner::checkQueued); +} + +CppcheckRunner::~CppcheckRunner() +{ + stop(); + m_queueTimer.stop(); +} + +void CppcheckRunner::reconfigure(const QString &binary, const QString &arguments) +{ + m_binary = binary; + m_arguments = arguments; +} + +void CppcheckRunner::addToQueue(const Utils::FileNameList &files, + const QString &additionalArguments) +{ + auto &existing = m_queue[additionalArguments]; + if (existing.isEmpty()) { + existing = files; + } else { + std::copy_if(files.cbegin(), files.cend(), std::back_inserter(existing), + [&existing](const Utils::FileName &file) { return !existing.contains(file); }); + } + + if (m_isRunning) { + if (existing == m_currentFiles) + m_process->kill(); // Further processing in handleFinished + return; + } + + m_queueTimer.start(); +} + +void CppcheckRunner::stop() +{ + if (m_isRunning) + m_process->kill(); +} + +void CppcheckRunner::removeFromQueue(const Utils::FileNameList &files) +{ + if (m_queue.isEmpty()) + return; + + if (files.isEmpty()) { + m_queue.clear(); + } else { + for (auto it = m_queue.begin(), end = m_queue.end(); it != end;) { + for (const auto &file : files) + it.value().removeOne(file); + it = !it.value().isEmpty() ? ++it : m_queue.erase(it); + } + } +} + +const Utils::FileNameList &CppcheckRunner::currentFiles() const +{ + return m_currentFiles; +} + +QString CppcheckRunner::currentCommand() const +{ + return m_process->program() + ' ' + + m_process->arguments().join(' '); +} + +void CppcheckRunner::checkQueued() +{ + if (m_queue.isEmpty() || m_binary.isEmpty()) + return; + + auto files = m_queue.begin().value(); + QString arguments = m_arguments + ' ' + m_queue.begin().key(); + m_currentFiles.clear(); + int argumentsLength = arguments.length(); + while (!files.isEmpty()) { + argumentsLength += files.first().length() + 1; // +1 for separator + if (argumentsLength >= m_maxArgumentsLength) + break; + m_currentFiles.push_back(files.first()); + arguments += ' ' + files.first().toString(); + files.pop_front(); + } + + if (files.isEmpty()) + m_queue.erase(m_queue.begin()); + else + m_queue.begin().value() = files; + + m_process->setCommand(m_binary, arguments); + m_process->start(); +} + +void CppcheckRunner::readOutput() +{ + if (!m_isRunning) // workaround for QTBUG-30929 + handleStarted(); + + m_process->setReadChannel(QProcess::StandardOutput); + + while (!m_process->atEnd() && m_process->canReadLine()) { + auto line = QString::fromUtf8(m_process->readLine()); + if (line.endsWith('\n')) + line.chop(1); + m_tool.parseOutputLine(line); + } +} + +void CppcheckRunner::readError() +{ + if (!m_isRunning) // workaround for QTBUG-30929 + handleStarted(); + + m_process->setReadChannel(QProcess::StandardError); + + while (!m_process->atEnd() && m_process->canReadLine()) { + auto line = QString::fromUtf8(m_process->readLine()); + if (line.endsWith('\n')) + line.chop(1); + m_tool.parseErrorLine(line); + } +} + +void CppcheckRunner::handleStarted() +{ + if (m_isRunning) + return; + + m_isRunning = true; + m_tool.startParsing(); +} + +void CppcheckRunner::handleFinished(int) +{ + if (m_process->error() != QProcess::FailedToStart) { + readOutput(); + readError(); + m_tool.finishParsing(); + } else { + const QString message = tr("Cppcheck failed to start: \"%1\".").arg(currentCommand()); + Core::MessageManager::write(message, Core::MessageManager::Silent); + } + m_currentFiles.clear(); + m_process->close(); + m_isRunning = false; + + if (!m_queue.isEmpty()) + checkQueued(); +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppcheckrunner.h b/src/plugins/cppcheck/cppcheckrunner.h new file mode 100644 index 00000000000..760105c462c --- /dev/null +++ b/src/plugins/cppcheck/cppcheckrunner.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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 + +#include +#include + +namespace Utils { +class QtcProcess; +class FileName; +using FileNameList = QList; +} + +namespace Cppcheck { +namespace Internal { + +class CppcheckTool; + +class CppcheckRunner final : public QObject +{ + Q_OBJECT + +public: + explicit CppcheckRunner(CppcheckTool &tool); + ~CppcheckRunner() override; + + void reconfigure(const QString &binary, const QString &arguments); + void addToQueue(const Utils::FileNameList &files, + const QString &additionalArguments = {}); + void removeFromQueue(const Utils::FileNameList &files); + void stop(); + + const Utils::FileNameList ¤tFiles() const; + QString currentCommand() const; + +private: + void checkQueued(); + void readOutput(); + void readError(); + void handleStarted(); + void handleFinished(int); + + CppcheckTool &m_tool; + Utils::QtcProcess *m_process = nullptr; + QString m_binary; + QString m_arguments; + QHash m_queue; + Utils::FileNameList m_currentFiles; + QTimer m_queueTimer; + int m_maxArgumentsLength = 32767; + bool m_isRunning = false; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktextmark.cpp b/src/plugins/cppcheck/cppchecktextmark.cpp new file mode 100644 index 00000000000..b1a2185c57e --- /dev/null +++ b/src/plugins/cppcheck/cppchecktextmark.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "cppcheckconstants.h" +#include "cppcheckdiagnostic.h" +#include "cppchecktextmark.h" + +#include + +#include + +namespace Cppcheck { +namespace Internal { + +struct Visual +{ + Visual(Utils::Theme::Color color, TextEditor::TextMark::Priority priority, + const QIcon &icon) + : color(color), + priority(priority), + icon(icon) + {} + Utils::Theme::Color color; + TextEditor::TextMark::Priority priority; + QIcon icon; +}; + +static Visual getVisual(Diagnostic::Severity type) +{ + using Color = Utils::Theme::Color; + using Priority = TextEditor::TextMark::Priority; + + static const QMap visuals{ + {Diagnostic::Severity::Error, {Color::IconsErrorColor, Priority::HighPriority, + Utils::Icons::CRITICAL.icon()}}, + {Diagnostic::Severity::Warning, {Color::IconsWarningColor, Priority::NormalPriority, + Utils::Icons::WARNING.icon()}}, + }; + + return visuals.value(type, {Color::IconsInfoColor, Priority::LowPriority, + Utils::Icons::INFO.icon()}); +} + +CppcheckTextMark::CppcheckTextMark (const Diagnostic &diagnostic) + : TextEditor::TextMark (diagnostic.fileName, diagnostic.lineNumber, + Core::Id(Constants::TEXTMARK_CATEGORY_ID)), + m_severity(diagnostic.severity), + m_checkId(diagnostic.checkId), + m_message(diagnostic.message) +{ + const auto visual = getVisual(diagnostic.severity); + setPriority(visual.priority); + setColor(visual.color); + setIcon(visual.icon); + setToolTip(toolTipText(diagnostic.severityText)); + setLineAnnotation(diagnostic.message); +} + +QString CppcheckTextMark::toolTipText(const QString &severityText) const +{ + return QString( + "" + " " + " " + " " + " " + " " + " " + " " + "
Cppcheck %1: %2
%3
").arg(m_checkId, severityText, m_message); +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktextmark.h b/src/plugins/cppcheck/cppchecktextmark.h new file mode 100644 index 00000000000..20b2ba9c760 --- /dev/null +++ b/src/plugins/cppcheck/cppchecktextmark.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** 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 + +#include "cppcheckdiagnostic.h" + +#include + +namespace Cppcheck { +namespace Internal { + +class CppcheckTextMark final : public TextEditor::TextMark +{ +public: + explicit CppcheckTextMark(const Diagnostic &diagnostic); + + bool operator==(const Diagnostic &r) const { + return lineNumber() == r.lineNumber + && (std::tie(m_severity, m_checkId, m_message) == + std::tie(r.severity, r.checkId, r.message)); + } + +private: + QString toolTipText(const QString &severityText) const; + + Diagnostic::Severity m_severity = Diagnostic::Severity::Information; + QString m_checkId; + QString m_message; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktextmarkmanager.cpp b/src/plugins/cppcheck/cppchecktextmarkmanager.cpp new file mode 100644 index 00000000000..df2ffe3cee0 --- /dev/null +++ b/src/plugins/cppcheck/cppchecktextmarkmanager.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "cppcheckdiagnostic.h" +#include "cppchecktextmark.h" +#include "cppchecktextmarkmanager.h" + +#include + +namespace Cppcheck { +namespace Internal { + +CppcheckTextMarkManager::CppcheckTextMarkManager() = default; + +CppcheckTextMarkManager::~CppcheckTextMarkManager() = default; + +void CppcheckTextMarkManager::add(const Diagnostic &diagnostic) +{ + auto &fileMarks = m_marks[diagnostic.fileName]; + const auto finder = [diagnostic] (const MarkPtr &mark) {return *mark == diagnostic;}; + if (Utils::contains(fileMarks, finder)) + return; + + fileMarks.push_back(std::make_unique(diagnostic)); +} + +void CppcheckTextMarkManager::clearFiles(const Utils::FileNameList &files) +{ + if (m_marks.empty()) + return; + + if (!files.empty()) { + for (const auto &file: files) + m_marks.erase(file); + } else { + m_marks.clear(); + } +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktextmarkmanager.h b/src/plugins/cppcheck/cppchecktextmarkmanager.h new file mode 100644 index 00000000000..e509fe9985f --- /dev/null +++ b/src/plugins/cppcheck/cppchecktextmarkmanager.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** 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 + +#include + +#include + +namespace Cppcheck { +namespace Internal { + +class Diagnostic; +class CppcheckTextMark; + +class CppcheckTextMarkManager final +{ +public: + explicit CppcheckTextMarkManager(); + ~CppcheckTextMarkManager(); + + void add(const Diagnostic &diagnostic); + void clearFiles(const Utils::FileNameList &files); + +private: + using MarkPtr = std::unique_ptr; + std::unordered_map> m_marks; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp new file mode 100644 index 00000000000..2c80de36741 --- /dev/null +++ b/src/plugins/cppcheck/cppchecktool.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "cppcheckconstants.h" +#include "cppcheckdiagnostic.h" +#include "cppcheckoptions.h" +#include "cppcheckrunner.h" +#include "cppchecktextmarkmanager.h" +#include "cppchecktool.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +namespace Cppcheck { +namespace Internal { + +CppcheckTool::CppcheckTool(CppcheckTextMarkManager &marks) : + m_marks(marks), + m_progressRegexp("^.* checked (\\d)% done$"), + m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$") +{ + m_runner = std::make_unique(*this); + QTC_ASSERT(m_progressRegexp.isValid(), return); + QTC_ASSERT(m_messageRegexp.isValid(), return); +} + +CppcheckTool::~CppcheckTool() = default; + +void CppcheckTool::updateOptions(const CppcheckOptions &options) +{ + m_options = options; + m_filters.clear(); + const auto patterns = m_options.ignoredPatterns.split(','); + for (const auto &pattern : patterns) { + const auto trimmedPattern = pattern.trimmed(); + if (trimmedPattern.isEmpty()) + continue; + + const QRegExp re(trimmedPattern, Qt::CaseSensitive, QRegExp::Wildcard); + if (re.isValid()) + m_filters.push_back(re); + } + + updateArguments(); +} + +void CppcheckTool::setProject(ProjectExplorer::Project *project) +{ + m_project = project; + updateArguments(); +} + +void CppcheckTool::updateArguments() +{ + if (!m_project) + return; + + m_cachedAdditionalArguments.clear(); + + QStringList arguments; + if (!m_options.customArguments.isEmpty()) { + auto expander = Utils::globalMacroExpander(); + const auto expanded = expander->expand(m_options.customArguments); + arguments.push_back(expanded); + } + + if (m_options.warning) + arguments.push_back("--enable=warning"); + if (m_options.style) + arguments.push_back("--enable=style"); + if (m_options.performance) + arguments.push_back("--enable=performance"); + if (m_options.portability) + arguments.push_back("--enable=portability"); + if (m_options.information) + arguments.push_back("--enable=information"); + if (m_options.unusedFunction) + arguments.push_back("--enable=unusedFunction"); + if (m_options.missingInclude) + arguments.push_back("--enable=missingInclude"); + if (m_options.inconclusive) + arguments.push_back("--inconclusive"); + if (m_options.forceDefines) + arguments.push_back("--force"); + + if (!m_options.unusedFunction && !m_options.customArguments.contains("-j ")) + arguments.push_back("-j " + QString::number(QThread::idealThreadCount())); + + arguments.push_back("--template={file},{line},{severity},{id},{message}"); + + m_runner->reconfigure(m_options.binary, arguments.join(' ')); +} + +QStringList CppcheckTool::additionalArguments(const CppTools::ProjectPart &part) const +{ + QStringList result; + + if (m_options.addIncludePaths) { + for (const auto &path : qAsConst(part.headerPaths)) { + const auto projectDir = m_project->projectDirectory().toString(); + if (path.type == ProjectExplorer::HeaderPathType::User + && path.path.startsWith(projectDir)) + result.push_back("-I " + path.path); + } + } + + if (!m_options.guessArguments) + return result; + + using Version = CppTools::ProjectPart::LanguageVersion; + switch (part.languageVersion) { + case Version::C89: + result.push_back("--std=c89 --language=c"); + break; + case Version::C99: + result.push_back("--std=c99 --language=c"); + break; + case Version::C11: + result.push_back("--std=c11 --language=c"); + break; + case Version::CXX03: + result.push_back("--std=c++03 --language=c++"); + break; + case Version::CXX11: + result.push_back("--std=c++11 --language=c++"); + break; + case Version::CXX14: + result.push_back("--std=c++14 --language=c++"); + break; + case Version::CXX98: + case Version::CXX17: + result.push_back("--language=c++"); + break; + } + + using QtVersion = CppTools::ProjectPart::QtVersion; + if (part.qtVersion != QtVersion::NoQt) + result.push_back("--library=qt"); + + return result; +} + +const CppcheckOptions &CppcheckTool::options() const +{ + return m_options; +} + +void CppcheckTool::check(const Utils::FileNameList &files) +{ + QTC_ASSERT(m_project, return); + + Utils::FileNameList filtered; + if (m_filters.isEmpty()) { + filtered = files; + } else { + std::copy_if(files.cbegin(), files.cend(), std::back_inserter(filtered), + [this](const Utils::FileName &file) { + const auto stringed = file.toString(); + const auto filter = [stringed](const QRegExp &re) {return re.exactMatch(stringed);}; + return !Utils::contains(m_filters, filter); + }); + } + + if (filtered.isEmpty()) + return; + + const auto info = CppTools::CppModelManager::instance()->projectInfo(m_project); + const auto parts = info.projectParts(); + if (parts.size() == 1) { + QTC_ASSERT(parts.first(), return); + addToQueue(filtered, *parts.first()); + return; + } + + std::map groups; + for (const auto &file : qAsConst(filtered)) { + const auto stringed = file.toString(); + for (const auto &part : parts) { + QTC_ASSERT(part, continue); + const auto &partFiles = part->files; + using File = CppTools::ProjectFile; + const auto match = [stringed](const File &file){return file.path == stringed;}; + if (Utils::contains(partFiles, match)) + groups[part].push_back(file); + } + } + + for (const auto &group : groups) + addToQueue(group.second, *group.first); +} + +void CppcheckTool::addToQueue(const Utils::FileNameList &files, CppTools::ProjectPart &part) +{ + const auto key = part.id(); + if (!m_cachedAdditionalArguments.contains(key)) + m_cachedAdditionalArguments.insert(key, additionalArguments(part).join(' ')); + m_runner->addToQueue(files, m_cachedAdditionalArguments[key]); +} + +void CppcheckTool::stop(const Utils::FileNameList &files) +{ + m_runner->removeFromQueue(files); + m_runner->stop(); +} + +void CppcheckTool::startParsing() +{ + if (m_options.showOutput) { + const auto message = tr("Cppcheck started: \"%1\".").arg(m_runner->currentCommand()); + Core::MessageManager::write(message, Core::MessageManager::Silent); + } + + m_progress = std::make_unique>(); + const auto progress = Core::ProgressManager::addTask( + m_progress->future(), QObject::tr("Cppcheck"), + Constants::CHECK_PROGRESS_ID); + QObject::connect(progress, &Core::FutureProgress::canceled, + this, [this]{stop({});}); + m_progress->setProgressRange(0, 100); + m_progress->reportStarted(); +} + +void CppcheckTool::parseOutputLine(const QString &line) +{ + if (line.isEmpty()) + return; + + if (m_options.showOutput) + Core::MessageManager::write(line, Core::MessageManager::Silent); + + enum Matches { Percentage = 1 }; + const auto match = m_progressRegexp.match(line); + if (!match.hasMatch()) + return; + + const auto done = match.captured(Percentage).toInt(); + QTC_ASSERT(m_progress, return); + m_progress->setProgressValue(done); +} + +static Diagnostic::Severity toSeverity(const QString &text) +{ + static const QMap values{ + {"error", Diagnostic::Severity::Error}, + {"warning", Diagnostic::Severity::Warning}, + {"performance", Diagnostic::Severity::Performance}, + {"portability", Diagnostic::Severity::Portability}, + {"style", Diagnostic::Severity::Style}, + {"information", Diagnostic::Severity::Information} + }; + return values.value(text, Diagnostic::Severity::Information); +} + +void CppcheckTool::parseErrorLine(const QString &line) +{ + if (line.isEmpty()) + return; + + if (m_options.showOutput) + Core::MessageManager::write(line, Core::MessageManager::Silent); + + enum Matches { File = 1, Line, Severity, Id, Message }; + const auto match = m_messageRegexp.match(line); + if (!match.hasMatch()) + return; + + const auto fileName = Utils::FileName::fromString( + QDir::fromNativeSeparators(match.captured(File))); + if (!m_runner->currentFiles().contains(fileName)) + return; + + Diagnostic diagnostic; + diagnostic.fileName = fileName; + diagnostic.lineNumber = std::max(match.captured(Line).toInt(), 1); + diagnostic.severityText = match.captured(Severity); + diagnostic.severity = toSeverity(diagnostic.severityText); + diagnostic.checkId = match.captured(Id); + diagnostic.message = match.captured(Message); + if (diagnostic.isValid()) + m_marks.add(diagnostic); +} + +void CppcheckTool::finishParsing() +{ + if (m_options.showOutput) + Core::MessageManager::write(tr("Cppcheck finished."), Core::MessageManager::Silent); + + QTC_ASSERT(m_progress, return); + m_progress->reportFinished(); +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktool.h b/src/plugins/cppcheck/cppchecktool.h new file mode 100644 index 00000000000..b80e3436a7c --- /dev/null +++ b/src/plugins/cppcheck/cppchecktool.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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 + +#include + +#include +#include +#include + +#include + +namespace Utils { +class FileName; +using FileNameList = QList; +} + +namespace CppTools { +class ProjectPart; +} + +namespace ProjectExplorer { +class Project; +} + +namespace Cppcheck { +namespace Internal { + +class Diagnostic; +class CppcheckRunner; +class CppcheckTextMarkManager; +class CppcheckOptions; + +class CppcheckTool final : public QObject +{ + Q_OBJECT + +public: + explicit CppcheckTool(CppcheckTextMarkManager &marks); + ~CppcheckTool() override; + + void updateOptions(const CppcheckOptions &options); + void setProject(ProjectExplorer::Project *project); + void check(const Utils::FileNameList &files); + void stop(const Utils::FileNameList &files); + + void startParsing(); + void parseOutputLine(const QString &line); + void parseErrorLine(const QString &line); + void finishParsing(); + + const CppcheckOptions &options() const; + +private: + void updateArguments(); + void addToQueue(const Utils::FileNameList &files, CppTools::ProjectPart &part); + QStringList additionalArguments(const CppTools::ProjectPart &part) const; + + CppcheckTextMarkManager &m_marks; + CppcheckOptions m_options; + QPointer m_project; + std::unique_ptr m_runner; + std::unique_ptr> m_progress; + QHash m_cachedAdditionalArguments; + QVector m_filters; + QRegularExpression m_progressRegexp; + QRegularExpression m_messageRegexp; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktrigger.cpp b/src/plugins/cppcheck/cppchecktrigger.cpp new file mode 100644 index 00000000000..b9ce34448c8 --- /dev/null +++ b/src/plugins/cppcheck/cppchecktrigger.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "cppchecktextmarkmanager.h" +#include "cppchecktool.h" +#include "cppchecktrigger.h" + +#include + +#include + +#include +#include + +#include +#include + +namespace Cppcheck { +namespace Internal { + +CppcheckTrigger::CppcheckTrigger(CppcheckTextMarkManager &marks, CppcheckTool &tool) : + m_marks(marks), + m_tool(tool) +{ + using EditorManager = Core::EditorManager; + using SessionManager = ProjectExplorer::SessionManager; + using CppModelManager = CppTools::CppModelManager; + + connect(EditorManager::instance(), &EditorManager::editorOpened, + this, [this](Core::IEditor *editor) {checkEditors({editor});}); + connect(EditorManager::instance(), &EditorManager::editorsClosed, + this, &CppcheckTrigger::removeEditors); + connect(EditorManager::instance(), &EditorManager::aboutToSave, + this, &CppcheckTrigger::checkChangedDocument); + + connect(SessionManager::instance(), &SessionManager::startupProjectChanged, + this, &CppcheckTrigger::changeCurrentProject); + + connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated, + this, &CppcheckTrigger::updateProjectFiles); +} + +CppcheckTrigger::~CppcheckTrigger() = default; + +void CppcheckTrigger::recheck() +{ + removeEditors(); + checkEditors(); +} + +void CppcheckTrigger::checkEditors(const QList &editors) +{ + if (!m_currentProject) + return; + + using CppModelManager = CppTools::CppModelManager; + const auto info = CppModelManager::instance()->projectInfo(m_currentProject); + if (!info.isValid()) + return; + + const auto editorList = !editors.isEmpty() + ? editors : Core::DocumentModel::editorsForOpenedDocuments(); + + Utils::FileNameList toCheck; + for (const auto editor: editorList) { + QTC_ASSERT(editor, continue); + const auto document = editor->document(); + QTC_ASSERT(document, continue); + const auto &path = document->filePath(); + QTC_ASSERT(!path.isEmpty(), continue); + + if (m_checkedFiles.contains(path)) + continue; + + if (!m_currentProject->isKnownFile(path)) + continue; + + const auto &pathString = path.toString(); + if (!info.sourceFiles().contains(pathString)) + continue; + + connect(document, &Core::IDocument::aboutToReload, + this, [this, document]{checkChangedDocument(document);}); + connect(document, &Core::IDocument::contentsChanged, + this, [this, document] { + if (!document->isModified()) + checkChangedDocument(document); + }); + + m_checkedFiles.insert(path, QDateTime::currentDateTime()); + toCheck.push_back(path); + } + + if (!toCheck.isEmpty()) { + remove(toCheck); + check(toCheck); + } +} + +void CppcheckTrigger::removeEditors(const QList &editors) +{ + if (!m_currentProject) + return; + + const auto editorList = !editors.isEmpty() + ? editors : Core::DocumentModel::editorsForOpenedDocuments(); + + Utils::FileNameList toRemove; + for (const auto editor: editorList) { + QTC_ASSERT(editor, return); + const auto document = editor->document(); + QTC_ASSERT(document, return); + const auto &path = document->filePath(); + QTC_ASSERT(!path.isEmpty(), return); + + if (!m_checkedFiles.contains(path)) + continue; + + disconnect(document, nullptr, this, nullptr); + m_checkedFiles.remove(path); + toRemove.push_back(path); + } + + if (!toRemove.isEmpty()) + remove(toRemove); +} + +void CppcheckTrigger::checkChangedDocument(Core::IDocument *document) +{ + QTC_ASSERT(document, return); + + if (!m_currentProject) + return; + + const auto &path = document->filePath(); + QTC_ASSERT(!path.isEmpty(), return); + if (!m_checkedFiles.contains(path)) + return; + + remove({path}); + check({path}); +} + +void CppcheckTrigger::changeCurrentProject(ProjectExplorer::Project *project) +{ + m_currentProject = project; + m_checkedFiles.clear(); + remove({}); + m_tool.setProject(project); + checkEditors(Core::DocumentModel::editorsForOpenedDocuments()); +} + +void CppcheckTrigger::updateProjectFiles(ProjectExplorer::Project *project) +{ + if (project != m_currentProject) + return; + + m_checkedFiles.clear(); + remove({}); + m_tool.setProject(project); + checkEditors(Core::DocumentModel::editorsForOpenedDocuments()); +} + +void CppcheckTrigger::check(const Utils::FileNameList &files) +{ + m_tool.check(files); +} + +void CppcheckTrigger::remove(const Utils::FileNameList &files) +{ + m_marks.clearFiles(files); + m_tool.stop(files); +} + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/cppcheck/cppchecktrigger.h b/src/plugins/cppcheck/cppchecktrigger.h new file mode 100644 index 00000000000..aa44f46fb70 --- /dev/null +++ b/src/plugins/cppcheck/cppchecktrigger.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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 + +#include +#include + +namespace Utils { +class FileName; +using FileNameList = QList; +} + +namespace ProjectExplorer { +class Project; +} + +namespace Core { +class IDocument; +class IEditor; +} + +namespace CppTools { +class ProjectInfo; +} + +namespace Cppcheck { +namespace Internal { + +class CppcheckTextMarkManager; +class CppcheckTool; + +class CppcheckTrigger final : public QObject +{ + Q_OBJECT + +public: + explicit CppcheckTrigger(CppcheckTextMarkManager &marks, CppcheckTool &tool); + ~CppcheckTrigger() override; + + void recheck(); + +private: + void checkEditors(const QList &editors = {}); + void removeEditors(const QList &editors = {}); + void checkChangedDocument(Core::IDocument *document); + void changeCurrentProject(ProjectExplorer::Project *project); + void updateProjectFiles(ProjectExplorer::Project *project); + void check(const Utils::FileNameList &files); + void remove(const Utils::FileNameList &files); + + CppcheckTextMarkManager &m_marks; + CppcheckTool &m_tool; + QPointer m_currentProject; + QHash m_checkedFiles; +}; + +} // namespace Internal +} // namespace Cppcheck diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index f96a4978339..2829f1f2330 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -57,7 +57,8 @@ SUBDIRS = \ scxmleditor \ welcome \ silversearcher \ - languageclient + languageclient \ + cppcheck qtHaveModule(serialport) { SUBDIRS += serialterminal diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs index 69fc53a661b..4962d159056 100644 --- a/src/plugins/plugins.qbs +++ b/src/plugins/plugins.qbs @@ -24,6 +24,7 @@ Project { "coreplugin/images/logo/logo.qbs", "cpaster/cpaster.qbs", "cpaster/frontend/frontend.qbs", + "cppcheck/cppcheck.qbs", "cppeditor/cppeditor.qbs", "cpptools/cpptools.qbs", "cvs/cvs.qbs",