aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
diff options
context:
space:
mode:
authorNikolai Kosjar <[email protected]>2014-09-25 11:11:58 +0200
committerEike Ziller <[email protected]>2014-10-16 13:36:09 +0300
commitb9f9eb7ae5d84c2b1200becc66680a4a8cda9e94 (patch)
tree283778fabb840733cbb0360e8347bab42674758d /plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
parentb3e07a53dbaa3ae9c89971da6ac2eaf044faf4c5 (diff)
Import Clang Static Analyzer plugin
This plugin adds "Clang Static Analyzer" to the Analyze mode, which processes all implementation/source project files of the current project. For this, it will call the clang executable for each file. The found diagnostics will be displayed in a view similar to the one used in "Valgrind Memory Analyzer". The user can specify the clang executable to use and the number of concurrent processes to launch in Menu: Tools > Options > Analyzer > Clang Static Analyzer. Main TODOs: * Fiddle around the appropriate command line options, currently only defines and include paths are passed on. * Tests on Windows / OS X. * Remove dependency to clangcodemodel by moving the functions that create command line arguments to CppTools. Mostly they are not even specific to clang (but would also work with gcc). * Maybe limit to a range of tested clang versions. * How to deal with directory containing all the log files after the user starts a new run or Creator is shut down? (delete it? leave it there? make it configurable?). * Find out how to properly integrate the tests. Imaginable future additions: * Adding a button to load result/log files from a directory, e.g. if the user used the 'scan-build' approach. * Adding a button with a filter menu in order to display only diagnostics from certain categories, similar to "Valgrind Memory Analyzer". Change-Id: I6aeb5dfdbdfa239a06c03dd8759a983df71b77ea Reviewed-by: Eike Ziller <[email protected]>
Diffstat (limited to 'plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp')
-rw-r--r--plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp165
1 files changed, 165 insertions, 0 deletions
diff --git a/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
new file mode 100644
index 00000000000..3db69c644b8
--- /dev/null
+++ b/plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+** This file is part of the Qt Enterprise LicenseChecker Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com <http://qt.digia.com/>
+**
+****************************************************************************/
+
+#include "clangstaticanalyzerrunner.h"
+
+#include "clangstaticanalyzerconstants.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QLoggingCategory>
+#include <QTemporaryFile>
+
+static Q_LOGGING_CATEGORY(LOG, "qt.clangstaticanalyzer.runner")
+
+static QString generalProcessError()
+{
+ return QObject::tr("An error occurred with the clang static analyzer process.");
+}
+
+static QString finishedDueToCrash()
+{
+ return QObject::tr("Clang static analyzer crashed.");
+}
+
+static QStringList constructCommandLineArguments(const QString &filePath,
+ const QString &logFile,
+ const QStringList &definesAndIncludes)
+{
+ QStringList arguments = QStringList()
+ << QLatin1String("--analyze")
+ << QLatin1String("-o")
+ << logFile
+ ;
+ arguments += definesAndIncludes;
+ arguments << filePath;
+ return arguments;
+}
+
+namespace ClangStaticAnalyzer {
+namespace Internal {
+
+QString finishedWithBadExitCode(int exitCode)
+{
+ return QObject::tr("Clang static analyzer finished with exit code: %1.").arg(exitCode);
+}
+
+ClangStaticAnalyzerRunner::ClangStaticAnalyzerRunner(const QString &clangExecutable,
+ const QString &clangLogFileDir,
+ QObject *parent)
+ : QObject(parent)
+ , m_clangExecutable(clangExecutable)
+ , m_clangLogFileDir(clangLogFileDir)
+{
+ QTC_CHECK(!m_clangExecutable.isEmpty());
+ QTC_CHECK(!m_clangLogFileDir.isEmpty());
+
+ m_process.setProcessChannelMode(QProcess::MergedChannels);
+ connect(&m_process, &QProcess::started,
+ this, &ClangStaticAnalyzerRunner::onProcessStarted);
+ connect(&m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
+ this, &ClangStaticAnalyzerRunner::onProcessFinished);
+ connect(&m_process, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
+ this, &ClangStaticAnalyzerRunner::onProcessError);
+ connect(&m_process, &QProcess::readyRead,
+ this, &ClangStaticAnalyzerRunner::onProcessOutput);
+}
+
+ClangStaticAnalyzerRunner::~ClangStaticAnalyzerRunner()
+{
+ const QProcess::ProcessState processState = m_process.state();
+ if (processState == QProcess::Starting || processState == QProcess::Running)
+ m_process.kill();
+}
+
+bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList &definesAndIncludes)
+{
+ QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
+
+ m_processOutput.clear();
+
+ m_logFile = createLogFile(filePath);
+ QTC_ASSERT(!m_logFile.isEmpty(), return false);
+ const QStringList arguments = constructCommandLineArguments(filePath, m_logFile,
+ definesAndIncludes);
+ m_commandLine = m_clangExecutable + QLatin1Char(' ') + arguments.join(QLatin1Char(' '));
+
+ qCDebug(LOG) << "Starting" << m_commandLine;
+ m_process.start(m_clangExecutable, arguments);
+ return true;
+}
+
+void ClangStaticAnalyzerRunner::onProcessStarted()
+{
+ emit started();
+}
+
+void ClangStaticAnalyzerRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ if (exitStatus == QProcess::NormalExit) {
+ if (exitCode == 0)
+ emit finishedWithSuccess(m_logFile);
+ else
+ emit finishedWithFailure(finishedWithBadExitCode(exitCode), processCommandlineAndOutput());
+ } else { // == QProcess::CrashExit
+ emit finishedWithFailure(finishedDueToCrash(), processCommandlineAndOutput());
+ }
+}
+
+void ClangStaticAnalyzerRunner::onProcessError(QProcess::ProcessError error)
+{
+ if (error == QProcess::Crashed)
+ return; // handled by slot of finished()
+
+ emit finishedWithFailure(generalProcessError(), processCommandlineAndOutput());
+}
+
+void ClangStaticAnalyzerRunner::onProcessOutput()
+{
+ m_processOutput.append(m_process.readAll());
+}
+
+QString ClangStaticAnalyzerRunner::createLogFile(const QString &filePath) const
+{
+ const QString fileName = QFileInfo(filePath).fileName();
+ const QString fileTemplate = m_clangLogFileDir
+ + QLatin1String("/report-") + fileName + QLatin1String("-XXXXXX.plist");
+
+ QTemporaryFile temporaryFile;
+ temporaryFile.setAutoRemove(false);
+ temporaryFile.setFileTemplate(fileTemplate);
+ if (temporaryFile.open()) {
+ temporaryFile.close();
+ return temporaryFile.fileName();
+ }
+ return QString();
+}
+
+QString ClangStaticAnalyzerRunner::processCommandlineAndOutput() const
+{
+ return QObject::tr("Command line: \"%1\"\n"
+ "Process Error: \"%2\"\n"
+ "Output:\n\"%3\"")
+ .arg(m_commandLine,
+ QString::number(m_process.error()),
+ QString::fromLocal8Bit(m_processOutput));
+}
+
+} // namespace Internal
+} // namespace ClangStaticAnalyzer