diff options
author | Sergey Morozov <dev@gres.biz> | 2018-07-30 21:42:47 +0300 |
---|---|---|
committer | Sergey Morozov <dev@gres.biz> | 2018-09-18 19:07:12 +0000 |
commit | 31b595314c0963cad42ed4cd18d7dc78502479e0 (patch) | |
tree | 670ab7ccac2a0d738cfb59ef47752ee22b26e371 /src/plugins/cppcheck/cppcheckrunner.cpp | |
parent | 20f3c8d65432bffaa1ba6ca9d2512ce3f3e3ce12 (diff) | |
download | qt-creator-31b595314c0963cad42ed4cd18d7dc78502479e0.tar.gz |
Cppcheck: Add cppcheck static analysis tool
Automatically checks currently opened documents and displays results via text marks/annotations.
CppcheckTrigger detects when to check files or clear results.
CppcheckTextMarkManager stores/clears text marks with checks' results.
CppcheckTool generates run arguments and parses output.
CppcheckRunner runs cppcheck binary.
CppcheckOptions configures CppcheckTool.
Task-number: QTCREATORBUG-20418
Change-Id: I8eafeac7af6137d2c9061ae75d4a56c85b3b5a2d
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
Diffstat (limited to 'src/plugins/cppcheck/cppcheckrunner.cpp')
-rw-r--r-- | src/plugins/cppcheck/cppcheckrunner.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/plugins/cppcheck/cppcheckrunner.cpp b/src/plugins/cppcheck/cppcheckrunner.cpp new file mode 100644 index 0000000000..21c0dbb853 --- /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 <utils/hostosinfo.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> + +#include <coreplugin/messagemanager.h> + +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<int>::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 |