summaryrefslogtreecommitdiff
path: root/src/plugins/cppcheck/cppchecktool.cpp
diff options
context:
space:
mode:
authorSergey Morozov <dev@gres.biz>2018-07-30 21:42:47 +0300
committerSergey Morozov <dev@gres.biz>2018-09-18 19:07:12 +0000
commit31b595314c0963cad42ed4cd18d7dc78502479e0 (patch)
tree670ab7ccac2a0d738cfb59ef47752ee22b26e371 /src/plugins/cppcheck/cppchecktool.cpp
parent20f3c8d65432bffaa1ba6ca9d2512ce3f3e3ce12 (diff)
downloadqt-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/cppchecktool.cpp')
-rw-r--r--src/plugins/cppcheck/cppchecktool.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp
new file mode 100644
index 0000000000..2c80de3674
--- /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 <coreplugin/messagemanager.h>
+#include <coreplugin/progressmanager/futureprogress.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+
+#include <cpptools/cppmodelmanager.h>
+
+#include <utils/algorithm.h>
+#include <utils/macroexpander.h>
+#include <utils/qtcassert.h>
+
+#include <QThread>
+
+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<CppcheckRunner>(*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<CppTools::ProjectPart::Ptr, Utils::FileNameList> 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<QFutureInterface<void>>();
+ 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<QString, Diagnostic::Severity> 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