forked from qt-creator/qt-creator
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 <eike.ziller@theqtcompany.com>
This commit is contained in:
committed by
Eike Ziller
parent
b3e07a53db
commit
b9f9eb7ae5
165
plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
Normal file
165
plugins/clangstaticanalyzer/clangstaticanalyzerrunner.cpp
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user