ClangTools: Split generic part from static analyzer tool

To reuse it for other clang-based tools.

Change-Id: I6c0d8e9eee543fa08faf3bf93c9fac33e43c6820
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Ivan Donchevskii
2018-01-17 15:08:30 +01:00
parent 8936e51033
commit e9c462391e
50 changed files with 1271 additions and 814 deletions

View File

@@ -250,6 +250,10 @@ const Icon CODEMODEL_DISABLED_ERROR({
{":/utils/images/codemodelerror.png", Theme::IconsDisabledColor}}, Icon::Tint);
const Icon CODEMODEL_DISABLED_WARNING({
{":/utils/images/codemodelwarning.png", Theme::IconsDisabledColor}}, Icon::Tint);
const Icon CODEMODEL_FIXIT({{QLatin1String(":/texteditor/images/lightbulbcap.png"),
Utils::Theme::PanelTextColorMid},
{QLatin1String(":/texteditor/images/lightbulb.png"),
Utils::Theme::IconsWarningColor}},
Utils::Icon::Tint);
} // namespace Icons
} // namespace Utils

View File

@@ -142,6 +142,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_ERROR;
QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_WARNING;
QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_DISABLED_ERROR;
QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_DISABLED_WARNING;
QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_FIXIT;
} // namespace Icons
} // namespace Utils

View File

@@ -26,7 +26,7 @@
#include "clangstaticanalyzerconfigwidget.h"
#include "ui_clangstaticanalyzerconfigwidget.h"
#include "clangstaticanalyzerutils.h"
#include "clangtoolsutils.h"
#include <QDir>
#include <QThread>
@@ -35,7 +35,7 @@ namespace ClangTools {
namespace Internal {
ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget(
ClangStaticAnalyzerSettings *settings,
ClangToolsSettings *settings,
QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::ClangStaticAnalyzerConfigWidget)

View File

@@ -25,7 +25,7 @@
#pragma once
#include "clangstaticanalyzersettings.h"
#include "clangtoolssettings.h"
#include <QWidget>
@@ -41,7 +41,7 @@ class ClangStaticAnalyzerConfigWidget : public QWidget
Q_OBJECT
public:
explicit ClangStaticAnalyzerConfigWidget(ClangStaticAnalyzerSettings *settings,
explicit ClangStaticAnalyzerConfigWidget(ClangToolsSettings *settings,
QWidget *parent = 0);
~ClangStaticAnalyzerConfigWidget();
@@ -50,7 +50,7 @@ public:
private:
Ui::ClangStaticAnalyzerConfigWidget *m_ui;
ClangStaticAnalyzerSettings *m_settings;
ClangToolsSettings *m_settings;
};
} // namespace Internal

View File

@@ -25,10 +25,10 @@
#include "clangstaticanalyzerdiagnosticview.h"
#include "clangstaticanalyzerdiagnosticmodel.h"
#include "clangtoolsdiagnosticmodel.h"
#include "clangstaticanalyzerprojectsettings.h"
#include "clangstaticanalyzerprojectsettingsmanager.h"
#include "clangstaticanalyzerutils.h"
#include "clangtoolsutils.h"
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
@@ -54,7 +54,7 @@ void ClangStaticAnalyzerDiagnosticView::suppressCurrentDiagnostic()
const QModelIndexList indexes = selectionModel()->selectedRows();
QTC_ASSERT(indexes.count() == 1, return);
const Diagnostic diag = model()->data(indexes.first(),
ClangStaticAnalyzerDiagnosticModel::DiagnosticRole)
ClangToolsDiagnosticModel::DiagnosticRole)
.value<Diagnostic>();
QTC_ASSERT(diag.isValid(), return);

View File

@@ -25,9 +25,9 @@
#include "clangstaticanalyzerpreconfiguredsessiontests.h"
#include "clangstaticanalyzerdiagnostic.h"
#include "clangtoolsdiagnostic.h"
#include "clangstaticanalyzertool.h"
#include "clangstaticanalyzerutils.h"
#include "clangtoolsutils.h"
#include <cpptools/projectinfo.h>
#include <projectexplorer/kitinformation.h>

View File

@@ -25,7 +25,7 @@
#include "clangstaticanalyzerprojectsettings.h"
#include "clangstaticanalyzerdiagnostic.h"
#include "clangtoolsdiagnostic.h"
#include <utils/qtcassert.h>

View File

@@ -25,11 +25,11 @@
#include "clangstaticanalyzerruncontrol.h"
#include "clangstaticanalyzerlogfilereader.h"
#include "clangtoolslogfilereader.h"
#include "clangstaticanalyzerrunner.h"
#include "clangstaticanalyzersettings.h"
#include "clangtoolssettings.h"
#include "clangstaticanalyzertool.h"
#include "clangstaticanalyzerutils.h"
#include "clangtoolsutils.h"
#include <debugger/analyzer/analyzerconstants.h>
@@ -66,50 +66,12 @@
#include <QAction>
#include <QLoggingCategory>
using namespace CppTools;
using namespace ProjectExplorer;
using namespace Utils;
static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runcontrol")
static QStringList splitArgs(QString &argsString)
{
QStringList result;
Utils::QtcProcess::ArgIterator it(&argsString);
while (it.next())
result.append(it.value());
return result;
}
template<size_t Size>
static QStringList extraOptions(const char(&environment)[Size])
{
if (!qEnvironmentVariableIsSet(environment))
return QStringList();
QString arguments = QString::fromLocal8Bit(qgetenv(environment));
return splitArgs(arguments);
}
static QStringList extraClangStaticAnalyzerPrependOptions() {
constexpr char csaPrependOptions[] = "QTC_CLANG_CSA_CMD_PREPEND";
static const QStringList options = extraOptions(csaPrependOptions);
if (!options.isEmpty())
qWarning() << "ClangStaticAnalyzer options are prepended with " << options.toVector();
return options;
}
static QStringList extraClangStaticAnalyzerAppendOptions() {
constexpr char csaAppendOptions[] = "QTC_CLANG_CSA_CMD_APPEND";
static const QStringList options = extraOptions(csaAppendOptions);
if (!options.isEmpty())
qWarning() << "ClangStaticAnalyzer options are appended with " << options.toVector();
return options;
}
namespace ClangTools {
namespace Internal {
class ProjectBuilder : public RunWorker
class ProjectBuilder : public RunWorker, public BaseProjectBuilder
{
public:
ProjectBuilder(RunControl *runControl, Project *project)
@@ -118,7 +80,7 @@ public:
setDisplayName("ProjectBuilder");
}
bool success() const { return m_success; }
bool success() const override { return m_success; }
private:
void start() final
@@ -143,7 +105,7 @@ private:
"<p>Do you want to continue and run the tool in %2 mode?</p>"
"</body></html>")
.arg(toolName).arg(wrongMode);
if (CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(),
if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(),
title, message, Core::ICore::settings(),
"ClangStaticAnalyzerCorrectModeWarning") != QDialogButtonBox::Yes)
{
@@ -171,242 +133,19 @@ private:
bool m_success = false;
};
static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QVector<ProjectPart::Ptr> projectParts,
const QString &clangVersion,
const QString &clangResourceDirectory)
{
qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts.";
AnalyzeUnits unitsToAnalyze;
foreach (const ProjectPart::Ptr &projectPart, projectParts) {
if (!projectPart->selectedForBuilding || !projectPart.data())
continue;
foreach (const ProjectFile &file, projectPart->files) {
if (file.path == CppModelManager::configurationFileName())
continue;
QTC_CHECK(file.kind != ProjectFile::Unclassified);
QTC_CHECK(file.kind != ProjectFile::Unsupported);
if (ProjectFile::isSource(file.kind)) {
const CompilerOptionsBuilder::PchUsage pchUsage = CppTools::getPchUsage();
CompilerOptionsBuilder optionsBuilder(*projectPart, clangVersion,
clangResourceDirectory);
QStringList arguments = extraClangStaticAnalyzerPrependOptions();
arguments.append(optionsBuilder.build(file.kind, pchUsage));
arguments.append(extraClangStaticAnalyzerAppendOptions());
unitsToAnalyze << AnalyzeUnit(file.path, arguments);
}
}
}
return unitsToAnalyze;
}
static QString clangResourceDir(const QString &clangExecutable, const QString &clangVersion)
{
QDir llvmDir = QFileInfo(clangExecutable).dir();
llvmDir.cdUp();
return llvmDir.absolutePath() + clangIncludePath(clangVersion);
}
AnalyzeUnits ClangStaticAnalyzerToolRunner::sortedUnitsToAnalyze(const QString &clangVersion)
{
QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits());
const QString clangResourceDirectory = clangResourceDir(m_clangExecutable, clangVersion);
AnalyzeUnits units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts(), clangVersion,
clangResourceDirectory);
Utils::sort(units, &AnalyzeUnit::file);
return units;
}
static QDebug operator<<(QDebug debug, const Utils::Environment &environment)
{
foreach (const QString &entry, environment.toStringList())
debug << "\n " << entry;
return debug;
}
static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
{
foreach (const AnalyzeUnit &unit, analyzeUnits)
debug << "\n " << unit.file;
return debug;
}
ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runControl, Target *target)
: RunWorker(runControl), m_target(target)
ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(RunControl *runControl, Target *target)
: ClangToolRunControl(runControl, target)
{
setDisplayName("ClangStaticAnalyzerRunner");
setSupportsReRunning(false);
m_projectBuilder = new ProjectBuilder(runControl, target->project());
addStartDependency(m_projectBuilder);
auto *projectBuilder = new ProjectBuilder(runControl, target->project());
addStartDependency(projectBuilder);
m_projectBuilder = projectBuilder;
m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(target->project());
BuildConfiguration *buildConfiguration = target->activeBuildConfiguration();
QTC_ASSERT(buildConfiguration, return);
m_environment = buildConfiguration->environment();
ToolChain *toolChain = ToolChainKitInformation::toolChain(target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID);
QTC_ASSERT(toolChain, return);
m_targetTriple = toolChain->originalTargetTriple();
m_toolChainType = toolChain->typeId();
init();
}
void ClangStaticAnalyzerToolRunner::start()
{
m_success = m_projectBuilder->success();
if (!m_success) {
reportFailure();
return;
}
m_projectInfo = CppTools::CppModelManager::instance()->projectInfo(m_target->project());
// Some projects provides CompilerCallData once a build is finished,
if (m_projectInfo.configurationOrFilesChanged(m_projectInfoBeforeBuild)) {
// If it's more than a release/debug build configuration change, e.g.
// a version control checkout, files might be not valid C++ anymore
// or even gone, so better stop here.
reportFailure(tr("The project configuration changed since the start of "
"the Clang Static Analyzer. Please re-run with current configuration."));
return;
}
const Utils::FileName projectFile = m_projectInfo.project()->projectFilePath();
appendMessage(tr("Running Clang Static Analyzer on %1").arg(projectFile.toUserOutput()),
Utils::NormalMessageFormat);
// Check clang executable
bool isValidClangExecutable;
const QString executable = clangExecutableFromSettings(&isValidClangExecutable);
if (!isValidClangExecutable) {
const QString errorMessage = tr("Clang Static Analyzer: Invalid executable \"%1\", stop.")
.arg(executable);
appendMessage(errorMessage, Utils::ErrorMessageFormat);
TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
reportFailure();
return;
}
// Check clang version
const ClangExecutableVersion version = clangExecutableVersion(executable);
if (!version.isValid()) {
const QString warningMessage
= tr("Clang Static Analyzer: Running with possibly unsupported version, "
"could not determine version from executable \"%1\".")
.arg(executable);
appendMessage(warningMessage, Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
} else if (!version.isSupportedVersion()) {
const QString warningMessage
= tr("Clang Static Analyzer: Running with unsupported version %1, "
"supported version is %2.")
.arg(version.toString())
.arg(ClangExecutableVersion::supportedVersionAsString());
appendMessage(warningMessage, Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
}
m_clangExecutable = executable;
// Create log dir
Utils::TemporaryDirectory temporaryDir("qtc-clangstaticanalyzer-XXXXXX");
temporaryDir.setAutoRemove(false);
if (!temporaryDir.isValid()) {
const QString errorMessage
= tr("Clang Static Analyzer: Failed to create temporary dir, stop.");
appendMessage(errorMessage, Utils::ErrorMessageFormat);
TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
reportFailure(errorMessage);
return;
}
m_clangLogFileDir = temporaryDir.path();
// Collect files
const AnalyzeUnits unitsToProcess = sortedUnitsToAnalyze(version.toString());
qCDebug(LOG) << "Files to process:" << unitsToProcess;
m_unitsToProcess = unitsToProcess;
m_initialFilesToProcessSize = m_unitsToProcess.count();
m_filesAnalyzed = 0;
m_filesNotAnalyzed = 0;
// Set up progress information
using namespace Core;
m_progress = QFutureInterface<void>();
FutureProgress *futureProgress
= ProgressManager::addTask(m_progress.future(), tr("Analyzing"), "ClangStaticAnalyzer");
futureProgress->setKeepOnFinish(FutureProgress::HideOnFinish);
connect(futureProgress, &FutureProgress::canceled,
this, &ClangStaticAnalyzerToolRunner::onProgressCanceled);
m_progress.setProgressRange(0, m_initialFilesToProcessSize);
m_progress.reportStarted();
// Start process(es)
qCDebug(LOG) << "Environment:" << m_environment;
m_runners.clear();
const int parallelRuns = ClangStaticAnalyzerSettings::instance()->simultaneousProcesses();
QTC_ASSERT(parallelRuns >= 1, reportFailure(); return);
m_success = true;
if (m_unitsToProcess.isEmpty()) {
finalize();
return;
}
reportStarted();
while (m_runners.size() < parallelRuns && !m_unitsToProcess.isEmpty())
analyzeNextFile();
}
void ClangStaticAnalyzerToolRunner::stop()
{
QSetIterator<ClangStaticAnalyzerRunner *> i(m_runners);
while (i.hasNext()) {
ClangStaticAnalyzerRunner *runner = i.next();
QObject::disconnect(runner, 0, this, 0);
delete runner;
}
m_runners.clear();
m_unitsToProcess.clear();
m_progress.reportFinished();
//ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success);
reportStopped();
}
void ClangStaticAnalyzerToolRunner::analyzeNextFile()
{
if (m_progress.isFinished())
return; // The previous call already reported that we are finished.
if (m_unitsToProcess.isEmpty()) {
if (m_runners.isEmpty())
finalize();
return;
}
const AnalyzeUnit unit = m_unitsToProcess.takeFirst();
qCDebug(LOG) << "analyzeNextFile:" << unit.file;
ClangStaticAnalyzerRunner *runner = createRunner();
m_runners.insert(runner);
QTC_ASSERT(runner->run(unit.file, unit.arguments), return);
appendMessage(tr("Analyzing \"%1\".").arg(
Utils::FileName::fromString(unit.file).toUserOutput()),
Utils::StdOutFormat);
}
ClangStaticAnalyzerRunner *ClangStaticAnalyzerToolRunner::createRunner()
ClangToolRunner *ClangStaticAnalyzerRunControl::createRunner()
{
QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0);
QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0);
@@ -416,83 +155,15 @@ ClangStaticAnalyzerRunner *ClangStaticAnalyzerToolRunner::createRunner()
m_environment,
this);
connect(runner, &ClangStaticAnalyzerRunner::finishedWithSuccess,
this, &ClangStaticAnalyzerToolRunner::onRunnerFinishedWithSuccess);
this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess);
connect(runner, &ClangStaticAnalyzerRunner::finishedWithFailure,
this, &ClangStaticAnalyzerToolRunner::onRunnerFinishedWithFailure);
this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure);
return runner;
}
void ClangStaticAnalyzerToolRunner::onRunnerFinishedWithSuccess(const QString &logFilePath)
ClangTool *ClangStaticAnalyzerRunControl::tool()
{
qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath;
QString errorMessage;
const QList<Diagnostic> diagnostics = LogFileReader::read(logFilePath, &errorMessage);
if (!errorMessage.isEmpty()) {
qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath();
appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage),
Utils::StdErrFormat);
} else {
++m_filesAnalyzed;
if (!diagnostics.isEmpty())
ClangStaticAnalyzerTool::instance()->onNewDiagnosticsAvailable(diagnostics);
}
handleFinished();
}
void ClangStaticAnalyzerToolRunner::onRunnerFinishedWithFailure(const QString &errorMessage,
const QString &errorDetails)
{
qCDebug(LOG).noquote() << "onRunnerFinishedWithFailure:"
<< errorMessage << '\n' << errorDetails;
++m_filesNotAnalyzed;
m_success = false;
const QString filePath = qobject_cast<ClangStaticAnalyzerRunner *>(sender())->filePath();
appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage),
Utils::StdErrFormat);
appendMessage(errorDetails, Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::addTask(Task::Warning, errorDetails, Debugger::Constants::ANALYZERTASK_ID);
handleFinished();
}
void ClangStaticAnalyzerToolRunner::handleFinished()
{
m_runners.remove(qobject_cast<ClangStaticAnalyzerRunner *>(sender()));
updateProgressValue();
sender()->deleteLater();
analyzeNextFile();
}
void ClangStaticAnalyzerToolRunner::onProgressCanceled()
{
m_progress.reportCanceled();
runControl()->initiateStop();
}
void ClangStaticAnalyzerToolRunner::updateProgressValue()
{
m_progress.setProgressValue(m_initialFilesToProcessSize - m_unitsToProcess.size());
}
void ClangStaticAnalyzerToolRunner::finalize()
{
appendMessage(tr("Clang Static Analyzer finished: "
"Processed %1 files successfully, %2 failed.")
.arg(m_filesAnalyzed).arg(m_filesNotAnalyzed),
Utils::NormalMessageFormat);
if (m_filesNotAnalyzed != 0) {
QString msg = tr("Clang Static Analyzer: Not all files could be analyzed.");
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
}
m_progress.reportFinished();
runControl()->initiateStop();
return ClangStaticAnalyzerTool::instance();
}
} // namespace Internal

View File

@@ -25,75 +25,23 @@
#pragma once
#include <projectexplorer/runconfiguration.h>
#include <cpptools/projectinfo.h>
#include <utils/environment.h>
#include <QFutureInterface>
#include <QStringList>
#include "clangtoolruncontrol.h"
namespace ClangTools {
namespace Internal {
class ClangStaticAnalyzerRunner;
class ProjectBuilder;
class Diagnostic;
struct AnalyzeUnit {
AnalyzeUnit(const QString &file, const QStringList &options)
: file(file), arguments(options) {}
QString file;
QStringList arguments; // without file itself and "-o somePath"
};
typedef QList<AnalyzeUnit> AnalyzeUnits;
class ClangStaticAnalyzerToolRunner : public ProjectExplorer::RunWorker
class ClangStaticAnalyzerRunControl final : public ClangToolRunControl
{
Q_OBJECT
public:
ClangStaticAnalyzerToolRunner(ProjectExplorer::RunControl *runControl,
ClangStaticAnalyzerRunControl(ProjectExplorer::RunControl *runControl,
ProjectExplorer::Target *target);
bool success() const { return m_success; } // For testing.
protected:
ClangToolRunner *createRunner() final;
private:
void start() final;
void stop() final;
AnalyzeUnits sortedUnitsToAnalyze(const QString &clangVersion);
void analyzeNextFile();
ClangStaticAnalyzerRunner *createRunner();
void onRunnerFinishedWithSuccess(const QString &logFilePath);
void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails);
void handleFinished();
void onProgressCanceled();
void updateProgressValue();
void finalize();
private:
QPointer<ProjectExplorer::Target> m_target;
ProjectBuilder *m_projectBuilder;
CppTools::ProjectInfo m_projectInfoBeforeBuild;
CppTools::ProjectInfo m_projectInfo;
QString m_targetTriple;
Core::Id m_toolChainType;
Utils::Environment m_environment;
QString m_clangExecutable;
QString m_clangLogFileDir;
QFutureInterface<void> m_progress;
AnalyzeUnits m_unitsToProcess;
QSet<ClangStaticAnalyzerRunner *> m_runners;
int m_initialFilesToProcessSize = 0;
int m_filesAnalyzed = 0;
int m_filesNotAnalyzed = 0;
bool m_success = false;
ClangTool *tool() final;
};
} // namespace Internal

View File

@@ -25,15 +25,10 @@
#include "clangstaticanalyzerrunner.h"
#include "clangstaticanalyzerconstants.h"
#include <utils/qtcprocess.h>
#include <utils/synchronousprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLoggingCategory>
static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner")
@@ -41,159 +36,33 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner")
namespace ClangTools {
namespace Internal {
static QString generalProcessError()
{
return ClangStaticAnalyzerRunner::tr("An error occurred with the Clang Static Analyzer process.");
}
static QString finishedDueToCrash()
{
return ClangStaticAnalyzerRunner::tr("Clang Static Analyzer crashed.");
}
static QStringList constructCommandLineArguments(const QString &filePath,
const QString &logFile,
const QStringList &options)
{
QStringList arguments;
if (LOG().isDebugEnabled())
arguments << QLatin1String("-v");
arguments
<< QLatin1String("--analyze")
<< QLatin1String("-o")
<< QDir::toNativeSeparators(logFile)
;
arguments += options;
arguments << QDir::toNativeSeparators(filePath);
return arguments;
}
QString finishedWithBadExitCode(int exitCode)
{
return ClangStaticAnalyzerRunner::tr("Clang Static Analyzer finished with exit code: %1.").arg(exitCode);
}
ClangStaticAnalyzerRunner::ClangStaticAnalyzerRunner(const QString &clangExecutable,
const QString &clangLogFileDir,
const Utils::Environment &environment,
QObject *parent)
: QObject(parent)
, m_clangExecutable(QDir::toNativeSeparators(clangExecutable))
, m_clangLogFileDir(clangLogFileDir)
: ClangToolRunner(clangExecutable,
clangLogFileDir,
environment,
tr("Clang Static Analyzer"),
parent)
{
QTC_CHECK(!m_clangExecutable.isEmpty());
QTC_CHECK(!m_clangLogFileDir.isEmpty());
m_process.setProcessChannelMode(QProcess::MergedChannels);
m_process.setProcessEnvironment(environment.toProcessEnvironment());
m_process.setWorkingDirectory(m_clangLogFileDir); // Current clang-cl puts log file into working dir.
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, &QProcess::errorOccurred, this, &ClangStaticAnalyzerRunner::onProcessError);
connect(&m_process, &QProcess::readyRead, this, &ClangStaticAnalyzerRunner::onProcessOutput);
}
ClangStaticAnalyzerRunner::~ClangStaticAnalyzerRunner()
QStringList ClangStaticAnalyzerRunner::constructCommandLineArguments(const QStringList &options)
{
Utils::SynchronousProcess::stopProcess(m_process);
}
QStringList arguments;
bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList &compilerOptions)
{
QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
QTC_CHECK(!compilerOptions.contains(QLatin1String("-o")));
QTC_CHECK(!compilerOptions.contains(filePath));
if (LOG().isDebugEnabled())
arguments << QString("-v");
m_filePath = filePath;
m_processOutput.clear();
arguments << QLatin1String("--analyze")
<< QLatin1String("-o")
<< QDir::toNativeSeparators(m_logFile);
m_logFile = createLogFile(filePath);
QTC_ASSERT(!m_logFile.isEmpty(), return false);
const QStringList arguments = constructCommandLineArguments(filePath, m_logFile,
compilerOptions);
m_commandLine = Utils::QtcProcess::joinArgs(QStringList(m_clangExecutable) + arguments);
arguments += options;
qCDebug(LOG).noquote() << "Starting" << m_commandLine;
m_process.start(m_clangExecutable, arguments);
return true;
}
QString ClangStaticAnalyzerRunner::filePath() const
{
return m_filePath;
}
void ClangStaticAnalyzerRunner::onProcessStarted()
{
emit started();
}
void ClangStaticAnalyzerRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitStatus == QProcess::NormalExit) {
if (exitCode == 0) {
qCDebug(LOG).noquote() << "Output:\n" << Utils::SynchronousProcess::normalizeNewlines(
QString::fromLocal8Bit(m_processOutput));
emit finishedWithSuccess(actualLogFile());
}
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");
Utils::TemporaryFile temporaryFile("clangstaticanalyzer");
temporaryFile.setAutoRemove(false);
temporaryFile.setFileTemplate(fileTemplate);
if (temporaryFile.open()) {
temporaryFile.close();
return temporaryFile.fileName();
}
return QString();
}
QString ClangStaticAnalyzerRunner::processCommandlineAndOutput() const
{
return tr("Command line: %1\n"
"Process Error: %2\n"
"Output:\n%3")
.arg(m_commandLine,
QString::number(m_process.error()),
Utils::SynchronousProcess::normalizeNewlines(
QString::fromLocal8Bit(m_processOutput)));
}
QString ClangStaticAnalyzerRunner::actualLogFile() const
{
if (QFileInfo(m_logFile).size() == 0) {
// Current clang-cl ignores -o, always putting the log file into the working directory.
return m_clangLogFileDir + QLatin1Char('/') + QFileInfo(m_filePath).completeBaseName()
+ QLatin1String(".plist");
}
return m_logFile;
arguments << QDir::toNativeSeparators(filePath());
return arguments;
}
} // namespace Internal

View File

@@ -25,18 +25,12 @@
#pragma once
#include <QString>
#include <QProcess>
#include <utils/environment.h>
#include <utils/qtcassert.h>
#include "clangtoolrunner.h"
namespace ClangTools {
namespace Internal {
QString finishedWithBadExitCode(int exitCode); // exposed for tests
class ClangStaticAnalyzerRunner : public QObject
class ClangStaticAnalyzerRunner final : public ClangToolRunner
{
Q_OBJECT
@@ -44,39 +38,9 @@ public:
ClangStaticAnalyzerRunner(const QString &clangExecutable,
const QString &clangLogFileDir,
const Utils::Environment &environment,
QObject *parent = 0);
~ClangStaticAnalyzerRunner();
// compilerOptions is expected to contain everything except:
// (1) filePath, that is the file to analyze
// (2) -o output-file
bool run(const QString &filePath, const QStringList &compilerOptions = QStringList());
QString filePath() const;
signals:
void started();
void finishedWithSuccess(const QString &logFilePath);
void finishedWithFailure(const QString &errorMessage, const QString &errorDetails);
private:
void onProcessStarted();
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onProcessError(QProcess::ProcessError error);
void onProcessOutput();
QString createLogFile(const QString &filePath) const;
QString processCommandlineAndOutput() const;
QString actualLogFile() const;
private:
QString m_clangExecutable;
QString m_clangLogFileDir;
QString m_filePath;
QString m_logFile;
QString m_commandLine;
QProcess m_process;
QByteArray m_processOutput;
QObject *parent = nullptr);
protected:
QStringList constructCommandLineArguments(const QStringList &options) final;
};
} // namespace Internal

View File

@@ -25,16 +25,14 @@
#include "clangstaticanalyzertool.h"
#include "clangstaticanalyzerconstants.h"
#include "clangstaticanalyzerdiagnostic.h"
#include "clangstaticanalyzerdiagnosticmodel.h"
#include "clangtoolsconstants.h"
#include "clangtoolsdiagnosticmodel.h"
#include "clangtoolslogfilereader.h"
#include "clangstaticanalyzerdiagnosticview.h"
#include "clangstaticanalyzerruncontrol.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <debugger/analyzer/analyzermanager.h>
@@ -44,13 +42,9 @@
#include <projectexplorer/target.h>
#include <projectexplorer/session.h>
#include <utils/fancymainwindow.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QLabel>
#include <QSortFilterProxyModel>
#include <QToolButton>
using namespace Core;
using namespace Debugger;
@@ -63,24 +57,20 @@ namespace Internal {
static ClangStaticAnalyzerTool *s_instance;
ClangStaticAnalyzerTool::ClangStaticAnalyzerTool()
: ClangTool(tr("Clang Static Analyzer"))
{
setObjectName("ClangStaticAnalyzerTool");
s_instance = this;
//
// Diagnostic View
//
m_diagnosticView = new ClangStaticAnalyzerDiagnosticView;
m_diagnosticView->setFrameStyle(QFrame::NoFrame);
m_diagnosticView->setAttribute(Qt::WA_MacShowFocusRect, false);
m_diagnosticModel = new ClangStaticAnalyzerDiagnosticModel(this);
m_diagnosticFilterModel = new ClangStaticAnalyzerDiagnosticFilterModel(this);
m_diagnosticFilterModel->setSourceModel(m_diagnosticModel);
m_diagnosticView = new ClangStaticAnalyzerDiagnosticView;
initDiagnosticView();
m_diagnosticView->setModel(m_diagnosticFilterModel);
m_diagnosticView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
m_diagnosticView->setAutoScroll(false);
m_diagnosticView->setObjectName(QLatin1String("ClangStaticAnalyzerIssuesView"));
m_diagnosticView->setWindowTitle(tr("Clang Static Analyzer Issues"));
foreach (auto * const model,
QList<QAbstractItemModel *>() << m_diagnosticModel << m_diagnosticFilterModel) {
connect(model, &QAbstractItemModel::rowsInserted,
@@ -93,13 +83,6 @@ ClangStaticAnalyzerTool::ClangStaticAnalyzerTool()
this, &ClangStaticAnalyzerTool::handleStateUpdate);
}
//
// Toolbar widget
//
m_startAction = Debugger::createStartAction();
m_stopAction = Debugger::createStopAction();
// Go to previous diagnostic
auto action = new QAction(this);
action->setDisabled(true);
@@ -148,11 +131,6 @@ ClangStaticAnalyzerTool::ClangStaticAnalyzerTool()
this, &ClangStaticAnalyzerTool::updateRunActions);
}
ClangStaticAnalyzerTool::~ClangStaticAnalyzerTool()
{
}
ClangStaticAnalyzerTool *ClangStaticAnalyzerTool::instance()
{
return s_instance;
@@ -166,8 +144,9 @@ void ClangStaticAnalyzerTool::startTool()
Project *project = SessionManager::startupProject();
QTC_ASSERT(project, return);
QTC_ASSERT(project->activeTarget(), return);
auto clangTool = new ClangStaticAnalyzerToolRunner(runControl, project->activeTarget());
auto clangTool = new ClangStaticAnalyzerRunControl(runControl, project->activeTarget());
m_stopAction->disconnect();
connect(m_stopAction, &QAction::triggered, runControl, [runControl] {
@@ -197,17 +176,6 @@ void ClangStaticAnalyzerTool::startTool()
ProjectExplorerPlugin::startRunControl(runControl);
}
QList<Diagnostic> ClangStaticAnalyzerTool::diagnostics() const
{
return m_diagnosticModel->diagnostics();
}
void ClangStaticAnalyzerTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics)
{
QTC_ASSERT(m_diagnosticModel, return);
m_diagnosticModel->addDiagnostics(diagnostics);
}
void ClangStaticAnalyzerTool::updateRunActions()
{
if (m_toolBusy) {
@@ -230,14 +198,6 @@ void ClangStaticAnalyzerTool::updateRunActions()
}
}
void ClangStaticAnalyzerTool::setToolBusy(bool busy)
{
QTC_ASSERT(m_diagnosticView, return);
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
m_diagnosticView->setCursor(cursor);
m_toolBusy = busy;
}
void ClangStaticAnalyzerTool::handleStateUpdate()
{
QTC_ASSERT(m_goBack, return);
@@ -266,5 +226,11 @@ void ClangStaticAnalyzerTool::handleStateUpdate()
Debugger::showPermanentStatusMessage(message);
}
QList<Diagnostic> ClangStaticAnalyzerTool::read(const QString &filePath,
QString *errorMessage) const
{
return LogFileReader::readPlist(filePath, errorMessage);
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -25,54 +25,42 @@
#pragma once
#include <projectexplorer/runconfiguration.h>
#include <cpptools/projectinfo.h>
#include "clangtool.h"
namespace ClangTools {
namespace Internal {
class ClangStaticAnalyzerDiagnosticFilterModel;
class ClangStaticAnalyzerDiagnosticModel;
class ClangToolsDiagnosticModel;
class ClangStaticAnalyzerDiagnosticView;
class Diagnostic;
const char ClangStaticAnalyzerPerspectiveId[] = "ClangStaticAnalyzer.Perspective";
const char ClangStaticAnalyzerDockId[] = "ClangStaticAnalyzer.Dock";
class ClangStaticAnalyzerTool : public QObject
class ClangStaticAnalyzerTool final : public ClangTool
{
Q_OBJECT
public:
ClangStaticAnalyzerTool();
~ClangStaticAnalyzerTool();
static ClangStaticAnalyzerTool *instance();
// For testing.
QList<Diagnostic> diagnostics() const;
void startTool();
void startTool() final;
void onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
signals:
void finished(bool success); // For testing.
QList<Diagnostic> read(const QString &filePath,
QString *errorMessage) const final;
private:
void setToolBusy(bool busy);
void handleStateUpdate();
void handleStateUpdate() final;
void updateRunActions();
ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel = nullptr;
ClangStaticAnalyzerDiagnosticFilterModel *m_diagnosticFilterModel = nullptr;
ClangStaticAnalyzerDiagnosticView *m_diagnosticView = nullptr;
QAction *m_startAction = nullptr;
QAction *m_stopAction = nullptr;
QAction *m_goBack = nullptr;
QAction *m_goNext = nullptr;
bool m_running = false;
bool m_toolBusy = false;
};
} // namespace Internal

View File

@@ -25,9 +25,9 @@
#include "clangstaticanalyzerunittests.h"
#include "clangstaticanalyzerdiagnostic.h"
#include "clangtoolsdiagnostic.h"
#include "clangstaticanalyzertool.h"
#include "clangstaticanalyzerutils.h"
#include "clangtoolsutils.h"
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cpptoolstestcase.h>

View File

@@ -0,0 +1,103 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** 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 "clangtool.h"
#include "clangtoolsconstants.h"
#include "clangtoolsdiagnostic.h"
#include "clangtoolsdiagnosticmodel.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <debugger/analyzer/analyzermanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/target.h>
#include <projectexplorer/session.h>
#include <utils/fancymainwindow.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QLabel>
#include <QSortFilterProxyModel>
#include <QToolButton>
using namespace Core;
using namespace Debugger;
using namespace ProjectExplorer;
using namespace Utils;
namespace ClangTools {
namespace Internal {
ClangTool::ClangTool(const QString &name)
: m_name(name)
{
m_diagnosticModel = new ClangToolsDiagnosticModel(this);
m_startAction = Debugger::createStartAction();
m_stopAction = Debugger::createStopAction();
}
const QString &ClangTool::name() const
{
return m_name;
}
void ClangTool::initDiagnosticView()
{
m_diagnosticView->setFrameStyle(QFrame::NoFrame);
m_diagnosticView->setAttribute(Qt::WA_MacShowFocusRect, false);
m_diagnosticView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
m_diagnosticView->setAutoScroll(false);
}
QList<Diagnostic> ClangTool::diagnostics() const
{
return m_diagnosticModel->diagnostics();
}
void ClangTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics)
{
QTC_ASSERT(m_diagnosticModel, return);
m_diagnosticModel->addDiagnostics(diagnostics);
}
void ClangTool::setToolBusy(bool busy)
{
QTC_ASSERT(m_diagnosticView, return);
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
m_diagnosticView->setCursor(cursor);
m_toolBusy = busy;
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,81 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** 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 <projectexplorer/runconfiguration.h>
#include <cpptools/projectinfo.h>
namespace Debugger { class DetailedErrorView; }
namespace ClangTools {
namespace Internal {
class ClangToolsDiagnosticModel;
class Diagnostic;
class ClangTool : public QObject
{
Q_OBJECT
public:
ClangTool(const QString &name);
virtual ~ClangTool() = default;
virtual void startTool() = 0;
virtual QList<Diagnostic> read(const QString &filePath,
QString *errorMessage) const = 0;
// For testing.
QList<Diagnostic> diagnostics() const;
const QString &name() const;
void onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
signals:
void finished(bool success); // For testing.
protected:
virtual void handleStateUpdate() = 0;
void setToolBusy(bool busy);
void initDiagnosticView();
ClangToolsDiagnosticModel *m_diagnosticModel = nullptr;
Debugger::DetailedErrorView *m_diagnosticView = nullptr;
QAction *m_startAction = nullptr;
QAction *m_stopAction = nullptr;
bool m_running = false;
bool m_toolBusy = false;
private:
const QString m_name;
};
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,429 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** 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 "clangtoolruncontrol.h"
#include "clangtool.h"
#include "clangtoolslogfilereader.h"
#include "clangtoolssettings.h"
#include "clangtoolsutils.h"
#include "clangtoolrunner.h"
#include <debugger/analyzer/analyzerconstants.h>
#include <clangcodemodel/clangutils.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cpptools/compileroptionsbuilder.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cppprojectfile.h>
#include <cpptools/cpptoolsreuse.h>
#include <cpptools/projectinfo.h>
#include <projectexplorer/abi.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/hostosinfo.h>
#include <utils/temporarydirectory.h>
#include <utils/qtcprocess.h>
#include <QAction>
#include <QLoggingCategory>
using namespace CppTools;
using namespace ProjectExplorer;
using namespace Utils;
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol")
static QStringList splitArgs(QString &argsString)
{
QStringList result;
Utils::QtcProcess::ArgIterator it(&argsString);
while (it.next())
result.append(it.value());
return result;
}
template<size_t Size>
static QStringList extraOptions(const char(&environment)[Size])
{
if (!qEnvironmentVariableIsSet(environment))
return QStringList();
QString arguments = QString::fromLocal8Bit(qgetenv(environment));
return splitArgs(arguments);
}
static QStringList extraClangToolsPrependOptions() {
constexpr char csaPrependOptions[] = "QTC_CLANG_CSA_CMD_PREPEND";
constexpr char toolsPrependOptions[] = "QTC_CLANG_TOOLS_CMD_PREPEND";
static const QStringList options = extraOptions(csaPrependOptions)
+ extraOptions(toolsPrependOptions);
if (!options.isEmpty())
qWarning() << "ClangTools options are prepended with " << options.toVector();
return options;
}
static QStringList extraClangToolsAppendOptions() {
constexpr char csaAppendOptions[] = "QTC_CLANG_CSA_CMD_APPEND";
constexpr char toolsAppendOptions[] = "QTC_CLANG_TOOLS_CMD_APPEND";
static const QStringList options = extraOptions(csaAppendOptions)
+ extraOptions(toolsAppendOptions);
if (!options.isEmpty())
qWarning() << "ClangTools options are appended with " << options.toVector();
return options;
}
namespace ClangTools {
namespace Internal {
static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QVector<ProjectPart::Ptr> projectParts,
const QString &clangVersion,
const QString &clangResourceDirectory)
{
qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts.";
AnalyzeUnits unitsToAnalyze;
foreach (const ProjectPart::Ptr &projectPart, projectParts) {
if (!projectPart->selectedForBuilding || !projectPart.data())
continue;
foreach (const ProjectFile &file, projectPart->files) {
if (file.path == CppModelManager::configurationFileName())
continue;
QTC_CHECK(file.kind != ProjectFile::Unclassified);
QTC_CHECK(file.kind != ProjectFile::Unsupported);
if (ProjectFile::isSource(file.kind)) {
const CompilerOptionsBuilder::PchUsage pchUsage = CppTools::getPchUsage();
CompilerOptionsBuilder optionsBuilder(*projectPart, clangVersion,
clangResourceDirectory);
QStringList arguments = extraClangToolsPrependOptions();
arguments.append(optionsBuilder.build(file.kind, pchUsage));
arguments.append(extraClangToolsAppendOptions());
unitsToAnalyze << AnalyzeUnit(file.path, arguments);
}
}
}
return unitsToAnalyze;
}
static QString clangResourceDir(const QString &clangExecutable, const QString &clangVersion)
{
QDir llvmDir = QFileInfo(clangExecutable).dir();
llvmDir.cdUp();
return llvmDir.absolutePath() + clangIncludePath(clangVersion);
}
AnalyzeUnits ClangToolRunControl::sortedUnitsToAnalyze(const QString &clangVersion)
{
QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits());
const QString clangResourceDirectory = clangResourceDir(m_clangExecutable, clangVersion);
AnalyzeUnits units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts(), clangVersion,
clangResourceDirectory);
Utils::sort(units, &AnalyzeUnit::file);
return units;
}
static QDebug operator<<(QDebug debug, const Utils::Environment &environment)
{
foreach (const QString &entry, environment.toStringList())
debug << "\n " << entry;
return debug;
}
static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
{
foreach (const AnalyzeUnit &unit, analyzeUnits)
debug << "\n " << unit.file;
return debug;
}
ClangToolRunControl::ClangToolRunControl(RunControl *runControl, Target *target)
: RunWorker(runControl), m_target(target)
{
}
void ClangToolRunControl::init()
{
setSupportsReRunning(false);
m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(
m_target->project());
BuildConfiguration *buildConfiguration = m_target->activeBuildConfiguration();
QTC_ASSERT(buildConfiguration, return);
m_environment = buildConfiguration->environment();
ToolChain *toolChain = ToolChainKitInformation::toolChain(m_target->kit(),
ProjectExplorer::Constants::CXX_LANGUAGE_ID);
QTC_ASSERT(toolChain, return);
m_targetTriple = toolChain->originalTargetTriple();
m_toolChainType = toolChain->typeId();
}
void ClangToolRunControl::start()
{
m_success = m_projectBuilder ? m_projectBuilder->success() : true;
if (!m_success) {
reportFailure();
return;
}
m_projectInfo = CppTools::CppModelManager::instance()->projectInfo(m_target->project());
const QString toolName = tool()->name();
// Some projects provides CompilerCallData once a build is finished,
if (m_projectInfo.configurationOrFilesChanged(m_projectInfoBeforeBuild)) {
// If it's more than a release/debug build configuration change, e.g.
// a version control checkout, files might be not valid C++ anymore
// or even gone, so better stop here.
reportFailure(tr("The project configuration changed since the start of "
"the %1. Please re-run with current configuration.").arg(toolName));
return;
}
const Utils::FileName projectFile = m_projectInfo.project()->projectFilePath();
appendMessage(tr("Running %1 on %2").arg(toolName).arg(projectFile.toUserOutput()),
Utils::NormalMessageFormat);
// Check clang executable
bool isValidClangExecutable;
const QString executable = clangExecutableFromSettings(&isValidClangExecutable);
if (!isValidClangExecutable) {
const QString errorMessage = toolName
+ tr(": Invalid executable \"%1\", stop.").arg(executable);
appendMessage(errorMessage, Utils::ErrorMessageFormat);
TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
reportFailure();
return;
}
// Check clang version
const ClangExecutableVersion version = clangExecutableVersion(executable);
if (!version.isValid()) {
const QString warningMessage
= toolName + tr(": Running with possibly unsupported version, "
"could not determine version from executable \"%1\".")
.arg(executable);
appendMessage(warningMessage, Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
} else if (!version.isSupportedVersion()) {
const QString warningMessage
= toolName + tr(": Running with unsupported version %1, "
"supported version is %2.")
.arg(version.toString())
.arg(ClangExecutableVersion::supportedVersionAsString());
appendMessage(warningMessage, Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
}
m_clangExecutable = executable;
// Create log dir
Utils::TemporaryDirectory temporaryDir("qtc-clangtools-XXXXXX");
temporaryDir.setAutoRemove(false);
if (!temporaryDir.isValid()) {
const QString errorMessage
= toolName + tr(": Failed to create temporary dir, stop.");
appendMessage(errorMessage, Utils::ErrorMessageFormat);
TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
reportFailure(errorMessage);
return;
}
m_clangLogFileDir = temporaryDir.path();
// Collect files
const AnalyzeUnits unitsToProcess = sortedUnitsToAnalyze(version.toString());
qCDebug(LOG) << "Files to process:" << unitsToProcess;
m_unitsToProcess = unitsToProcess;
m_initialFilesToProcessSize = m_unitsToProcess.count();
m_filesAnalyzed = 0;
m_filesNotAnalyzed = 0;
// Set up progress information
using namespace Core;
m_progress = QFutureInterface<void>();
FutureProgress *futureProgress
= ProgressManager::addTask(m_progress.future(), tr("Analyzing"),
toolName.toStdString().c_str());
futureProgress->setKeepOnFinish(FutureProgress::HideOnFinish);
connect(futureProgress, &FutureProgress::canceled,
this, &ClangToolRunControl::onProgressCanceled);
m_progress.setProgressRange(0, m_initialFilesToProcessSize);
m_progress.reportStarted();
// Start process(es)
qCDebug(LOG) << "Environment:" << m_environment;
m_runners.clear();
const int parallelRuns = ClangToolsSettings::instance()->simultaneousProcesses();
QTC_ASSERT(parallelRuns >= 1, reportFailure(); return);
m_success = true;
if (m_unitsToProcess.isEmpty()) {
finalize();
return;
}
reportStarted();
while (m_runners.size() < parallelRuns && !m_unitsToProcess.isEmpty())
analyzeNextFile();
}
void ClangToolRunControl::stop()
{
QSetIterator<ClangToolRunner *> i(m_runners);
while (i.hasNext()) {
ClangToolRunner *runner = i.next();
QObject::disconnect(runner, 0, this, 0);
delete runner;
}
m_runners.clear();
m_unitsToProcess.clear();
m_progress.reportFinished();
reportStopped();
}
void ClangToolRunControl::analyzeNextFile()
{
if (m_progress.isFinished())
return; // The previous call already reported that we are finished.
if (m_unitsToProcess.isEmpty()) {
if (m_runners.isEmpty())
finalize();
return;
}
const AnalyzeUnit unit = m_unitsToProcess.takeFirst();
qCDebug(LOG) << "analyzeNextFile:" << unit.file;
ClangToolRunner *runner = createRunner();
m_runners.insert(runner);
QTC_ASSERT(runner->run(unit.file, unit.arguments), return);
appendMessage(tr("Analyzing \"%1\".").arg(
Utils::FileName::fromString(unit.file).toUserOutput()),
Utils::StdOutFormat);
}
void ClangToolRunControl::onRunnerFinishedWithSuccess(const QString &logFilePath)
{
qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath;
QString errorMessage;
const QList<Diagnostic> diagnostics = tool()->read(logFilePath, &errorMessage);
if (!errorMessage.isEmpty()) {
qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage;
const QString filePath = qobject_cast<ClangToolRunner *>(sender())->filePath();
appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage),
Utils::StdErrFormat);
} else {
++m_filesAnalyzed;
if (!diagnostics.isEmpty())
tool()->onNewDiagnosticsAvailable(diagnostics);
}
handleFinished();
}
void ClangToolRunControl::onRunnerFinishedWithFailure(const QString &errorMessage,
const QString &errorDetails)
{
qCDebug(LOG).noquote() << "onRunnerFinishedWithFailure:"
<< errorMessage << '\n' << errorDetails;
++m_filesNotAnalyzed;
m_success = false;
const QString filePath = qobject_cast<ClangToolRunner *>(sender())->filePath();
appendMessage(tr("Failed to analyze \"%1\": %2").arg(filePath, errorMessage),
Utils::StdErrFormat);
appendMessage(errorDetails, Utils::StdErrFormat);
TaskHub::addTask(Task::Warning, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::addTask(Task::Warning, errorDetails, Debugger::Constants::ANALYZERTASK_ID);
handleFinished();
}
void ClangToolRunControl::handleFinished()
{
m_runners.remove(qobject_cast<ClangToolRunner *>(sender()));
updateProgressValue();
sender()->deleteLater();
analyzeNextFile();
}
void ClangToolRunControl::onProgressCanceled()
{
m_progress.reportCanceled();
runControl()->initiateStop();
}
void ClangToolRunControl::updateProgressValue()
{
m_progress.setProgressValue(m_initialFilesToProcessSize - m_unitsToProcess.size());
}
void ClangToolRunControl::finalize()
{
const QString toolName = tool()->name();
appendMessage(toolName + tr(" finished: "
"Processed %1 files successfully, %2 failed.")
.arg(m_filesAnalyzed).arg(m_filesNotAnalyzed),
Utils::NormalMessageFormat);
if (m_filesNotAnalyzed != 0) {
QString msg = toolName + tr(": Not all files could be analyzed.");
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
TaskHub::requestPopup();
}
m_progress.reportFinished();
runControl()->initiateStop();
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,117 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** 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 <projectexplorer/runconfiguration.h>
#include <cpptools/projectinfo.h>
#include <utils/environment.h>
#include <QFutureInterface>
#include <QStringList>
namespace ClangTools {
namespace Internal {
class ClangTool;
class ClangToolRunner;
class Diagnostic;
class ProjectBuilder;
struct AnalyzeUnit {
AnalyzeUnit(const QString &file, const QStringList &options)
: file(file), arguments(options) {}
QString file;
QStringList arguments; // without file itself and "-o somePath"
};
typedef QList<AnalyzeUnit> AnalyzeUnits;
class BaseProjectBuilder
{
public:
virtual ~BaseProjectBuilder() {}
virtual bool success() const { return true; }
};
class ClangToolRunControl : public ProjectExplorer::RunWorker
{
Q_OBJECT
public:
ClangToolRunControl(ProjectExplorer::RunControl *runControl,
ProjectExplorer::Target *target);
bool success() const { return m_success; } // For testing.
protected:
void init();
virtual ClangTool *tool() = 0;
virtual ClangToolRunner *createRunner() = 0;
void onRunnerFinishedWithSuccess(const QString &logFilePath);
void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails);
private:
void start() final;
void stop() final;
AnalyzeUnits sortedUnitsToAnalyze(const QString &clangVersion);
void analyzeNextFile();
void handleFinished();
void onProgressCanceled();
void updateProgressValue();
void finalize();
protected:
BaseProjectBuilder *m_projectBuilder = nullptr;
Utils::Environment m_environment;
QString m_clangExecutable;
QString m_clangLogFileDir;
private:
QPointer<ProjectExplorer::Target> m_target;
CppTools::ProjectInfo m_projectInfoBeforeBuild;
CppTools::ProjectInfo m_projectInfo;
QString m_targetTriple;
Core::Id m_toolChainType;
QFutureInterface<void> m_progress;
AnalyzeUnits m_unitsToProcess;
QSet<ClangToolRunner *> m_runners;
int m_initialFilesToProcessSize = 0;
int m_filesAnalyzed = 0;
int m_filesNotAnalyzed = 0;
bool m_success = false;
};
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,184 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** 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 "clangtoolrunner.h"
#include "clangtoolsconstants.h"
#include <utils/environment.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/synchronousprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLoggingCategory>
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner")
namespace ClangTools {
namespace Internal {
static QString generalProcessError(const QString &name)
{
return ClangToolRunner::tr("An error occurred with the %1 process.").arg(name);
}
static QString finishedDueToCrash(const QString &name)
{
return ClangToolRunner::tr("%1 crashed.").arg(name);
}
QString finishedWithBadExitCode(const QString &name, int exitCode)
{
return ClangToolRunner::tr("%1 finished with exit code: %2.").arg(name).arg(exitCode);
}
ClangToolRunner::ClangToolRunner(const QString &clangExecutable,
const QString &clangLogFileDir,
const Utils::Environment &environment,
const QString &name,
QObject *parent)
: QObject(parent)
, m_clangExecutable(QDir::toNativeSeparators(clangExecutable))
, m_clangLogFileDir(clangLogFileDir)
, m_name(name)
{
QTC_CHECK(!m_clangExecutable.isEmpty());
QTC_CHECK(!m_clangLogFileDir.isEmpty());
m_process.setProcessChannelMode(QProcess::MergedChannels);
m_process.setProcessEnvironment(environment.toProcessEnvironment());
m_process.setWorkingDirectory(m_clangLogFileDir); // Current clang-cl puts log file into working dir.
connect(&m_process, &QProcess::started, this, &ClangToolRunner::onProcessStarted);
connect(&m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, &ClangToolRunner::onProcessFinished);
connect(&m_process, &QProcess::errorOccurred, this, &ClangToolRunner::onProcessError);
connect(&m_process, &QProcess::readyRead, this, &ClangToolRunner::onProcessOutput);
}
ClangToolRunner::~ClangToolRunner()
{
Utils::SynchronousProcess::stopProcess(m_process);
}
bool ClangToolRunner::run(const QString &filePath, const QStringList &compilerOptions)
{
QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
QTC_CHECK(!compilerOptions.contains(QLatin1String("-o")));
QTC_CHECK(!compilerOptions.contains(filePath));
m_filePath = filePath;
m_processOutput.clear();
m_logFile = createLogFile(filePath);
QTC_ASSERT(!m_logFile.isEmpty(), return false);
const QStringList arguments = constructCommandLineArguments(compilerOptions);
m_commandLine = Utils::QtcProcess::joinArgs(QStringList(m_clangExecutable) + arguments);
qCDebug(LOG).noquote() << "Starting" << m_commandLine;
m_process.start(m_clangExecutable, arguments);
return true;
}
QString ClangToolRunner::filePath() const
{
return m_filePath;
}
void ClangToolRunner::onProcessStarted()
{
emit started();
}
void ClangToolRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitStatus == QProcess::NormalExit) {
if (exitCode == 0) {
qCDebug(LOG).noquote() << "Output:\n" << Utils::SynchronousProcess::normalizeNewlines(
QString::fromLocal8Bit(m_processOutput));
emit finishedWithSuccess(actualLogFile());
}
else
emit finishedWithFailure(finishedWithBadExitCode(m_name, exitCode), processCommandlineAndOutput());
} else { // == QProcess::CrashExit
emit finishedWithFailure(finishedDueToCrash(m_name), processCommandlineAndOutput());
}
}
void ClangToolRunner::onProcessError(QProcess::ProcessError error)
{
if (error == QProcess::Crashed)
return; // handled by slot of finished()
emit finishedWithFailure(generalProcessError(m_name), processCommandlineAndOutput());
}
void ClangToolRunner::onProcessOutput()
{
m_processOutput.append(m_process.readAll());
}
QString ClangToolRunner::createLogFile(const QString &filePath) const
{
const QString fileName = QFileInfo(filePath).fileName();
const QString fileTemplate = m_clangLogFileDir
+ QLatin1String("/report-") + fileName + QLatin1String("-XXXXXX.plist");
Utils::TemporaryFile temporaryFile("clangtools");
temporaryFile.setAutoRemove(false);
temporaryFile.setFileTemplate(fileTemplate);
if (temporaryFile.open()) {
temporaryFile.close();
return temporaryFile.fileName();
}
return QString();
}
QString ClangToolRunner::processCommandlineAndOutput() const
{
return tr("Command line: %1\n"
"Process Error: %2\n"
"Output:\n%3")
.arg(m_commandLine,
QString::number(m_process.error()),
Utils::SynchronousProcess::normalizeNewlines(
QString::fromLocal8Bit(m_processOutput)));
}
QString ClangToolRunner::actualLogFile() const
{
if (QFileInfo(m_logFile).size() == 0) {
// Current clang-cl ignores -o, always putting the log file into the working directory.
return m_clangLogFileDir + QLatin1Char('/') + QFileInfo(m_filePath).completeBaseName()
+ QLatin1String(".plist");
}
return m_logFile;
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** 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 <QString>
#include <QProcess>
namespace Utils { class Environment; }
namespace ClangTools {
namespace Internal {
QString finishedWithBadExitCode(const QString &name, int exitCode); // exposed for tests
class ClangToolRunner : public QObject
{
Q_OBJECT
public:
ClangToolRunner(const QString &clangExecutable,
const QString &clangLogFileDir,
const Utils::Environment &environment,
const QString &name,
QObject *parent = nullptr);
virtual ~ClangToolRunner();
// compilerOptions is expected to contain everything except:
// (1) filePath, that is the file to analyze
// (2) -o output-file
bool run(const QString &filePath, const QStringList &compilerOptions = QStringList());
QString filePath() const;
signals:
void started();
void finishedWithSuccess(const QString &logFilePath);
void finishedWithFailure(const QString &errorMessage, const QString &errorDetails);
protected:
virtual QStringList constructCommandLineArguments(const QStringList &options) = 0;
virtual void onProcessOutput();
private:
void onProcessStarted();
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onProcessError(QProcess::ProcessError error);
QString createLogFile(const QString &filePath) const;
QString processCommandlineAndOutput() const;
QString actualLogFile() const;
protected:
QString m_logFile;
QProcess m_process;
QByteArray m_processOutput;
private:
QString m_clangExecutable;
QString m_clangLogFileDir;
QString m_filePath;
QString m_commandLine;
const QString m_name;
};
} // namespace Internal
} // namespace ClangTools

View File

@@ -2,37 +2,43 @@ include(../../qtcreatorplugin.pri)
SOURCES += \
clangstaticanalyzerconfigwidget.cpp \
clangstaticanalyzerdiagnostic.cpp \
clangstaticanalyzerdiagnosticmodel.cpp \
clangstaticanalyzerdiagnosticview.cpp \
clangstaticanalyzerlogfilereader.cpp \
clangtoolsplugin.cpp \
clangstaticanalyzerprojectsettings.cpp \
clangstaticanalyzerprojectsettingsmanager.cpp \
clangstaticanalyzerprojectsettingswidget.cpp \
clangstaticanalyzerruncontrol.cpp \
clangstaticanalyzerrunner.cpp \
clangstaticanalyzersettings.cpp \
clangstaticanalyzertool.cpp \
clangstaticanalyzerutils.cpp
clangtool.cpp \
clangtoolruncontrol.cpp \
clangtoolrunner.cpp \
clangtoolsdiagnostic.cpp \
clangtoolsdiagnosticmodel.cpp \
clangtoolslogfilereader.cpp \
clangtoolsplugin.cpp \
clangtoolssettings.cpp \
clangtoolsutils.cpp
HEADERS += \
clangstaticanalyzerconfigwidget.h \
clangstaticanalyzerconstants.h \
clangstaticanalyzerdiagnostic.h \
clangstaticanalyzerdiagnosticmodel.h \
clangstaticanalyzerdiagnosticview.h \
clangstaticanalyzer_global.h \
clangstaticanalyzerlogfilereader.h \
clangtoolsplugin.h \
clangstaticanalyzerprojectsettings.h \
clangstaticanalyzerprojectsettingsmanager.h \
clangstaticanalyzerprojectsettingswidget.h \
clangstaticanalyzerruncontrol.h \
clangstaticanalyzerrunner.h \
clangstaticanalyzersettings.h \
clangstaticanalyzertool.h \
clangstaticanalyzerutils.h
clangtool.h \
clangtoolruncontrol.h \
clangtoolrunner.h \
clangtools_global.h \
clangtoolsconstants.h \
clangtoolsdiagnostic.h \
clangtoolsdiagnosticmodel.h \
clangtoolslogfilereader.h \
clangtoolsplugin.h \
clangtoolssettings.h \
clangtoolsutils.h
FORMS += \
clangstaticanalyzerconfigwidget.ui \

View File

@@ -19,19 +19,11 @@ QtcPlugin {
]
files: [
"clangstaticanalyzer_global.h",
"clangstaticanalyzerconfigwidget.cpp",
"clangstaticanalyzerconfigwidget.h",
"clangstaticanalyzerconfigwidget.ui",
"clangstaticanalyzerconstants.h",
"clangstaticanalyzerdiagnostic.cpp",
"clangstaticanalyzerdiagnostic.h",
"clangstaticanalyzerdiagnosticmodel.cpp",
"clangstaticanalyzerdiagnosticmodel.h",
"clangstaticanalyzerdiagnosticview.cpp",
"clangstaticanalyzerdiagnosticview.h",
"clangstaticanalyzerlogfilereader.cpp",
"clangstaticanalyzerlogfilereader.h",
"clangstaticanalyzerprojectsettings.cpp",
"clangstaticanalyzerprojectsettings.h",
"clangstaticanalyzerprojectsettingsmanager.cpp",
@@ -43,12 +35,26 @@ QtcPlugin {
"clangstaticanalyzerruncontrol.h",
"clangstaticanalyzerrunner.cpp",
"clangstaticanalyzerrunner.h",
"clangstaticanalyzersettings.cpp",
"clangstaticanalyzersettings.h",
"clangstaticanalyzertool.cpp",
"clangstaticanalyzertool.h",
"clangstaticanalyzerutils.cpp",
"clangstaticanalyzerutils.h",
"clangtool.cpp",
"clangtool.h",
"clangtoolruncontrol.cpp",
"clangtoolruncontrol.h",
"clangtoolrunner.cpp",
"clangtoolrunner.h",
"clangtools_global.h",
"clangtoolsconstants.h",
"clangtoolsdiagnostic.cpp",
"clangtoolsdiagnostic.h",
"clangtoolsdiagnosticmodel.cpp",
"clangtoolsdiagnosticmodel.h",
"clangtoolslogfilereader.cpp",
"clangtoolslogfilereader.h",
"clangtoolssettings.cpp",
"clangtoolssettings.h",
"clangtoolsutils.cpp",
"clangtoolsutils.h",
"clangtoolsplugin.cpp",
"clangtoolsplugin.h",
]

View File

@@ -28,8 +28,9 @@
namespace ClangTools {
namespace Constants {
const char SETTINGS_ID[] = "ClangStaticAnalyzer";
const char SETTINGS_ID[] = "ClangTools";
const char CLANGSTATICANALYZER_RUN_MODE[] = "ClangStaticAnalyzer.RunMode";
const char CLANGTIDYCLAZY_RUN_MODE[] = "ClangTidyClazy.RunMode";
} // Constants
} // ClangStaticAnalyzer
} // ClangTools

View File

@@ -23,7 +23,7 @@
**
****************************************************************************/
#include "clangstaticanalyzerdiagnostic.h"
#include "clangtoolsdiagnostic.h"
namespace ClangTools {
namespace Internal {

View File

@@ -23,15 +23,16 @@
**
****************************************************************************/
#include "clangstaticanalyzerdiagnosticmodel.h"
#include "clangtoolsdiagnosticmodel.h"
#include "clangstaticanalyzerdiagnosticview.h"
#include "clangstaticanalyzerprojectsettingsmanager.h"
#include "clangstaticanalyzerutils.h"
#include "clangtoolsutils.h"
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <QCoreApplication>
#include <QFileInfo>
@@ -65,19 +66,19 @@ private:
const ExplainingStep m_step;
};
ClangStaticAnalyzerDiagnosticModel::ClangStaticAnalyzerDiagnosticModel(QObject *parent)
ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent)
: Utils::TreeModel<>(parent)
{
setHeader({tr("Issue"), tr("Location")});
}
void ClangStaticAnalyzerDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics)
void ClangToolsDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics)
{
foreach (const Diagnostic &d, diagnostics)
rootItem()->appendChild(new DiagnosticItem(d));
}
QList<Diagnostic> ClangStaticAnalyzerDiagnosticModel::diagnostics() const
QList<Diagnostic> ClangToolsDiagnosticModel::diagnostics() const
{
QList<Diagnostic> diags;
for (const Utils::TreeItem * const item : *rootItem())
@@ -224,7 +225,7 @@ DiagnosticItem::DiagnosticItem(const Diagnostic &diag) : m_diagnostic(diag)
appendChild(new ExplainingStepItem(s));
}
QVariant locationData(int role, const Debugger::DiagnosticLocation &location)
static QVariant locationData(int role, const Debugger::DiagnosticLocation &location)
{
switch (role) {
case Debugger::DetailedErrorView::LocationRole:
@@ -236,6 +237,19 @@ QVariant locationData(int role, const Debugger::DiagnosticLocation &location)
}
}
static QVariant iconData(const QString &type)
{
if (type == "warning")
return Utils::Icons::CODEMODEL_WARNING.icon();
if (type == "error")
return Utils::Icons::CODEMODEL_ERROR.icon();
if (type == "note")
return Utils::Icons::BOOKMARK.icon();
if (type == "fix-it")
return Utils::Icons::CODEMODEL_FIXIT.icon();
return QVariant();
}
QVariant DiagnosticItem::data(int column, int role) const
{
if (column == Debugger::DetailedErrorView::LocationColumn)
@@ -245,12 +259,14 @@ QVariant DiagnosticItem::data(int column, int role) const
switch (role) {
case Debugger::DetailedErrorView::FullTextRole:
return fullText(m_diagnostic);
case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole:
case ClangToolsDiagnosticModel::DiagnosticRole:
return QVariant::fromValue(m_diagnostic);
case Qt::DisplayRole:
return m_diagnostic.description;
case Qt::ToolTipRole:
return createDiagnosticToolTipString(m_diagnostic);
case Qt::DecorationRole:
return iconData(m_diagnostic.type);
default:
return QVariant();
}
@@ -269,7 +285,7 @@ QVariant ExplainingStepItem::data(int column, int role) const
switch (role) {
case Debugger::DetailedErrorView::FullTextRole:
return fullText(static_cast<DiagnosticItem *>(parent())->diagnostic());
case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole:
case ClangToolsDiagnosticModel::DiagnosticRole:
return QVariant::fromValue(static_cast<DiagnosticItem *>(parent())->diagnostic());
case Qt::DisplayRole: {
const int row = indexInParent() + 1;
@@ -330,7 +346,7 @@ bool ClangStaticAnalyzerDiagnosticFilterModel::filterAcceptsRow(int sourceRow,
{
if (sourceParent.isValid())
return true;
const Diagnostic diag = static_cast<ClangStaticAnalyzerDiagnosticModel *>(sourceModel())
const Diagnostic diag = static_cast<ClangToolsDiagnosticModel *>(sourceModel())
->diagnostics().at(sourceRow);
foreach (const SuppressedDiagnostic &d, m_suppressedDiagnostics) {
if (d.description != diag.description)

View File

@@ -25,7 +25,7 @@
#pragma once
#include "clangstaticanalyzerdiagnostic.h"
#include "clangtoolsdiagnostic.h"
#include "clangstaticanalyzerprojectsettings.h"
#include <debugger/analyzer/detailederrorview.h>
@@ -40,15 +40,15 @@ namespace ProjectExplorer { class Project; }
namespace ClangTools {
namespace Internal {
class ClangStaticAnalyzerDiagnosticModel : public Utils::TreeModel<>
class ClangToolsDiagnosticModel : public Utils::TreeModel<>
{
Q_OBJECT
public:
ClangStaticAnalyzerDiagnosticModel(QObject *parent = 0);
ClangToolsDiagnosticModel(QObject *parent = nullptr);
void addDiagnostics(const QList<Diagnostic> &diagnostics);
QList<Diagnostic> diagnostics() const;
virtual void addDiagnostics(const QList<Diagnostic> &diagnostics);
virtual QList<Diagnostic> diagnostics() const;
enum ItemRole {
DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1
@@ -60,7 +60,7 @@ class ClangStaticAnalyzerDiagnosticFilterModel : public QSortFilterProxyModel
Q_OBJECT
public:
ClangStaticAnalyzerDiagnosticFilterModel(QObject *parent = 0);
ClangStaticAnalyzerDiagnosticFilterModel(QObject *parent = nullptr);
void setProject(ProjectExplorer::Project *project);
void addSuppressedDiagnostic(const SuppressedDiagnostic &diag);

View File

@@ -23,12 +23,14 @@
**
****************************************************************************/
#include "clangstaticanalyzerlogfilereader.h"
#include "clangtoolslogfilereader.h"
#include <QDebug>
#include <QDir>
#include <QObject>
#include <QFile>
#include <QFileInfo>
#include <QRegularExpression>
#include <QXmlStreamReader>
#include <utils/qtcassert.h>
@@ -72,19 +74,25 @@ private:
QList<Diagnostic> m_diagnostics;
};
QList<Diagnostic> LogFileReader::read(const QString &filePath, QString *errorMessage)
static bool checkFilePath(const QString &filePath, QString *errorMessage)
{
const QList<Diagnostic> emptyList;
// Check file path
QFileInfo fi(filePath);
if (!fi.exists() || !fi.isReadable()) {
if (errorMessage) {
*errorMessage = tr("File \"%1\" does not exist or is not readable.")
*errorMessage
= QString(QT_TRANSLATE_NOOP("LogFileReader",
"File \"%1\" does not exist or is not readable."))
.arg(filePath);
}
return emptyList;
return false;
}
return true;
}
QList<Diagnostic> LogFileReader::readPlist(const QString &filePath, QString *errorMessage)
{
if (!checkFilePath(filePath, errorMessage))
return QList<Diagnostic>();
// Read
ClangStaticAnalyzerLogFileReader reader(filePath);
@@ -121,7 +129,7 @@ QList<Diagnostic> LogFileReader::read(const QString &filePath, QString *errorMes
}
Q_FALLTHROUGH();
default:
return emptyList;
return QList<Diagnostic>();
}
}

View File

@@ -25,7 +25,7 @@
#pragma once
#include "clangstaticanalyzerdiagnostic.h"
#include "clangtoolsdiagnostic.h"
#include <QList>
#include <QCoreApplication>
@@ -37,7 +37,7 @@ class LogFileReader
{
Q_DECLARE_TR_FUNCTIONS(ClangTools::Internal::LogFileReader)
public:
static QList<Diagnostic> read(const QString &filePath, QString *errorMessage);
static QList<Diagnostic> readPlist(const QString &filePath, QString *errorMessage);
};
} // namespace Internal

View File

@@ -26,7 +26,7 @@
#include "clangtoolsplugin.h"
#include "clangstaticanalyzerconfigwidget.h"
#include "clangstaticanalyzerconstants.h"
#include "clangtoolsconstants.h"
#include "clangstaticanalyzerprojectsettingswidget.h"
#include "clangstaticanalyzerruncontrol.h"
#include "clangstaticanalyzertool.h"
@@ -80,13 +80,13 @@ public:
QWidget *widget()
{
if (!m_widget)
m_widget = new ClangStaticAnalyzerConfigWidget(ClangStaticAnalyzerSettings::instance());
m_widget = new ClangStaticAnalyzerConfigWidget(ClangToolsSettings::instance());
return m_widget;
}
void apply()
{
ClangStaticAnalyzerSettings::instance()->writeSettings();
ClangToolsSettings::instance()->writeSettings();
}
void finish()
@@ -101,7 +101,7 @@ private:
class ClangToolsPluginPrivate
{
public:
ClangStaticAnalyzerTool tool;
ClangStaticAnalyzerTool staticAnalyzerTool;
ClangStaticAnalyzerOptionsPage optionsPage;
};
@@ -119,7 +119,7 @@ bool ClangToolsPlugin::initialize(const QStringList &arguments, QString *errorSt
auto panelFactory = new ProjectPanelFactory();
panelFactory->setPriority(100);
panelFactory->setDisplayName(tr("Clang Static Analyzer"));
panelFactory->setDisplayName(tr("Clang Tools"));
panelFactory->setCreateWidgetFunction([](Project *project) { return new ProjectSettingsWidget(project); });
ProjectPanelFactory::registerFactory(panelFactory);

View File

@@ -23,9 +23,9 @@
**
****************************************************************************/
#include "clangstaticanalyzersettings.h"
#include "clangtoolssettings.h"
#include "clangstaticanalyzerconstants.h"
#include "clangtoolsconstants.h"
#include <coreplugin/icore.h>
@@ -41,15 +41,15 @@ static const char simultaneousProcessesKey[] = "simultaneousProcesses";
namespace ClangTools {
namespace Internal {
ClangStaticAnalyzerSettings::ClangStaticAnalyzerSettings()
ClangToolsSettings::ClangToolsSettings()
: m_simultaneousProcesses(-1)
{
readSettings();
}
ClangStaticAnalyzerSettings *ClangStaticAnalyzerSettings::instance()
ClangToolsSettings *ClangToolsSettings::instance()
{
static ClangStaticAnalyzerSettings instance;
static ClangToolsSettings instance;
return &instance;
}
@@ -58,7 +58,7 @@ static QString clangExecutableFileName()
return QLatin1String("clang" QTC_HOST_EXE_SUFFIX);
}
QString ClangStaticAnalyzerSettings::defaultClangExecutable() const
QString ClangToolsSettings::defaultClangExecutable() const
{
const QString shippedBinary = Core::ICore::libexecPath()
+ QLatin1String("/clang/bin/")
@@ -68,7 +68,7 @@ QString ClangStaticAnalyzerSettings::defaultClangExecutable() const
return clangExecutableFileName();
}
QString ClangStaticAnalyzerSettings::clangExecutable(bool *isSet) const
QString ClangToolsSettings::clangExecutable(bool *isSet) const
{
if (m_clangExecutable.isEmpty()) {
if (isSet)
@@ -80,23 +80,23 @@ QString ClangStaticAnalyzerSettings::clangExecutable(bool *isSet) const
return m_clangExecutable;
}
void ClangStaticAnalyzerSettings::setClangExecutable(const QString &exectuable)
void ClangToolsSettings::setClangExecutable(const QString &exectuable)
{
m_clangExecutable = exectuable;
}
int ClangStaticAnalyzerSettings::simultaneousProcesses() const
int ClangToolsSettings::simultaneousProcesses() const
{
return m_simultaneousProcesses;
}
void ClangStaticAnalyzerSettings::setSimultaneousProcesses(int processes)
void ClangToolsSettings::setSimultaneousProcesses(int processes)
{
QTC_ASSERT(processes >=1, return);
m_simultaneousProcesses = processes;
}
void ClangStaticAnalyzerSettings::readSettings()
void ClangToolsSettings::readSettings()
{
QSettings *settings = Core::ICore::settings();
settings->beginGroup(QLatin1String(Constants::SETTINGS_ID));
@@ -110,7 +110,7 @@ void ClangStaticAnalyzerSettings::readSettings()
settings->endGroup();
}
void ClangStaticAnalyzerSettings::writeSettings() const
void ClangToolsSettings::writeSettings() const
{
QSettings *settings = Core::ICore::settings();
settings->beginGroup(QLatin1String(Constants::SETTINGS_ID));

View File

@@ -30,10 +30,10 @@
namespace ClangTools {
namespace Internal {
class ClangStaticAnalyzerSettings
class ClangToolsSettings
{
public:
static ClangStaticAnalyzerSettings *instance();
static ClangToolsSettings *instance();
void writeSettings() const;
@@ -45,7 +45,7 @@ public:
void setSimultaneousProcesses(int processes);
private:
ClangStaticAnalyzerSettings();
ClangToolsSettings();
void readSettings();
QString m_clangExecutable;

View File

@@ -23,10 +23,10 @@
**
****************************************************************************/
#include "clangstaticanalyzerutils.h"
#include "clangtoolsutils.h"
#include "clangstaticanalyzerdiagnostic.h"
#include "clangstaticanalyzersettings.h"
#include "clangtoolsdiagnostic.h"
#include "clangtoolssettings.h"
#include <projectexplorer/projectexplorerconstants.h>
@@ -52,7 +52,7 @@ namespace Internal {
QString clangExecutableFromSettings(bool *isValid)
{
QString executable = ClangStaticAnalyzerSettings::instance()->clangExecutable();
QString executable = ClangToolsSettings::instance()->clangExecutable();
if (executable.isEmpty()) {
*isValid = false;
return executable;
@@ -82,9 +82,8 @@ QString clangExecutableFromSettings(bool *isValid)
QString createFullLocationString(const Debugger::DiagnosticLocation &location)
{
const QString filePath = location.filePath;
const QString lineNumber = QString::number(location.line);
return filePath + QLatin1Char(':') + lineNumber;
return location.filePath + QLatin1Char(':') + QString::number(location.line)
+ QLatin1Char(':') + QString::number(location.column);
}
bool isClangExecutableUsable(const QString &filePath, QString *errorMessage)
@@ -92,7 +91,7 @@ bool isClangExecutableUsable(const QString &filePath, QString *errorMessage)
const QFileInfo fi(filePath);
if (fi.isSymLink() && fi.symLinkTarget().contains(QLatin1String("icecc"))) {
if (errorMessage) {
*errorMessage = QCoreApplication::translate("ClangStaticAnalyzer",
*errorMessage = QCoreApplication::translate("ClangTools",
"The chosen file \"%1\" seems to point to an icecc binary not suitable "
"for analyzing.\nPlease set a real Clang executable.")
.arg(filePath);

View File

@@ -60,9 +60,10 @@ private:
const auto location = index.model()->data(index, DetailedErrorView::LocationRole)
.value<DiagnosticLocation>();
return location.isValid()
? QString::fromLatin1("<a href=\"file://%1\">%2:%3")
? QString::fromLatin1("<a href=\"file://%1\">%2:%3:%4")
.arg(location.filePath, QFileInfo(location.filePath).fileName())
.arg(location.line)
.arg(location.column)
: QString();
}

View File

@@ -1,14 +0,0 @@
include(../clangstaticanalyzertest.pri)
TARGET = tst_clangstaticanalyzerlogfilereader
DEFINES += SRCDIR=\\\"$$PWD/\\\"
SOURCES += \
tst_clangstaticanalyzerlogfilereader.cpp \
$$PLUGINDIR/clangstaticanalyzerdiagnostic.cpp \
$$PLUGINDIR/clangstaticanalyzerlogfilereader.cpp
HEADERS += \
$$PLUGINDIR/clangstaticanalyzerdiagnostic.h \
$$PLUGINDIR/clangstaticanalyzerlogfilereader.h

View File

@@ -1,22 +0,0 @@
import qbs
import "../clangstaticanalyzerautotest.qbs" as ClangStaticAnalyzerAutotest
ClangStaticAnalyzerAutotest {
name: "ClangStaticAnalyzerLogFileReader Autotest"
cpp.defines: base.concat('SRCDIR="' + sourceDirectory + '"')
Group {
name: "sources from plugin"
prefix: pluginDir + '/'
files: [
"clangstaticanalyzerdiagnostic.cpp",
"clangstaticanalyzerdiagnostic.h",
"clangstaticanalyzerlogfilereader.cpp",
"clangstaticanalyzerlogfilereader.h",
]
}
files: [
"tst_clangstaticanalyzerlogfilereader.cpp"
]
}

View File

@@ -1,9 +1,11 @@
include(../clangstaticanalyzertest.pri)
include(../clangtoolstest.pri)
TARGET = tst_clangstaticanalyzerrunnertest
SOURCES += \
tst_clangstaticanalyzerrunner.cpp \
$$PLUGINDIR/clangtoolrunner.cpp \
$$PLUGINDIR/clangstaticanalyzerrunner.cpp
HEADERS += \
$$PLUGINDIR/clangtoolrunner.h \
$$PLUGINDIR/clangstaticanalyzerrunner.h

View File

@@ -1,7 +1,7 @@
import qbs
import "../clangstaticanalyzerautotest.qbs" as ClangStaticAnalyzerAutotest
import "../clangtoolsautotest.qbs" as ClangToolsAutotest
ClangStaticAnalyzerAutotest {
ClangToolsAutotest {
name: "ClangStaticAnalyzerRunner Autotest"
Group {
@@ -10,6 +10,8 @@ ClangStaticAnalyzerAutotest {
files: [
"clangstaticanalyzerrunner.cpp",
"clangstaticanalyzerrunner.h",
"clangtoolrunner.cpp",
"clangtoolrunner.h",
]
}

View File

@@ -23,9 +23,10 @@
**
****************************************************************************/
#include <clangtools/clangstaticanalyzerconstants.h>
#include <clangtools/clangtoolsconstants.h>
#include <clangtools/clangstaticanalyzerrunner.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/temporarydirectory.h>
@@ -193,7 +194,7 @@ void ClangStaticAnalyzerRunnerTest::runWithNonExistentFileToAnalyze()
QVERIFY(runner.run(QLatin1String("not.existing.file.111")));
QVERIFY(st.expectStartedSignal());
QVERIFY(st.expectFinishWithFailureSignal(finishedWithBadExitCode(1)));
QVERIFY(st.expectFinishWithFailureSignal(finishedWithBadExitCode("Clang Static Analyzer", 1)));
}
int main(int argc, char *argv[])

View File

@@ -3,4 +3,4 @@ CONFIG += ordered
SUBDIRS = \
clangstaticanalyzerrunner \
clangstaticanalyzerlogfilereader
clangtoolslogfilereader

View File

@@ -1,9 +1,9 @@
import qbs
Project {
name: "ClangStaticAnalyzer autotests"
name: "ClangTools autotests"
references: [
"clangstaticanalyzerlogfilereader",
"clangtoolslogfilereader",
"clangstaticanalyzerrunner",
]
}

View File

@@ -0,0 +1,14 @@
include(../clangtoolstest.pri)
TARGET = tst_clangtoolslogfilereader
DEFINES += SRCDIR=\\\"$$PWD/\\\"
SOURCES += \
tst_clangtoolslogfilereader.cpp \
$$PLUGINDIR/clangtoolsdiagnostic.cpp \
$$PLUGINDIR/clangtoolslogfilereader.cpp
HEADERS += \
$$PLUGINDIR/clangtoolsdiagnostic.h \
$$PLUGINDIR/clangtoolslogfilereader.h

View File

@@ -0,0 +1,22 @@
import qbs
import "../clangtoolsautotest.qbs" as ClangToolsAutotest
ClangToolsAutotest {
name: "ClangToolsLogFileReader Autotest"
cpp.defines: base.concat('SRCDIR="' + sourceDirectory + '"')
Group {
name: "sources from plugin"
prefix: pluginDir + '/'
files: [
"clangtoolsdiagnostic.cpp",
"clangtoolsdiagnostic.h",
"clangtoolslogfilereader.cpp",
"clangtoolslogfilereader.h",
]
}
files: [
"tst_clangtoolslogfilereader.cpp"
]
}

View File

@@ -23,7 +23,7 @@
**
****************************************************************************/
#include <clangtools/clangstaticanalyzerlogfilereader.h>
#include <clangtools/clangtoolslogfilereader.h>
#include <utils/fileutils.h>
@@ -83,7 +83,7 @@ QString testFilePath(const QString &relativePath)
} // anonymous namespace
class ClangStaticAnalyzerLogFileReaderTest : public QObject
class ClangToolsLogFileReaderTest : public QObject
{
Q_OBJECT
@@ -93,35 +93,35 @@ private slots:
void readFileWithDiagnostics();
};
void ClangStaticAnalyzerLogFileReaderTest::readEmptyFile()
void ClangToolsLogFileReaderTest::readEmptyFile()
{
const QString filePath = QDir::tempPath() + QLatin1String("/empty.file");
QVERIFY(createEmptyFile(filePath));
QString errorMessage;
const QList<Diagnostic> diagnostics = LogFileReader::read(filePath, &errorMessage);
const QList<Diagnostic> diagnostics = LogFileReader::readPlist(filePath, &errorMessage);
QVERIFY(!errorMessage.isEmpty());
if (debug)
qDebug() << errorMessage;
QVERIFY(diagnostics.isEmpty());
}
void ClangStaticAnalyzerLogFileReaderTest::readFileWithNoDiagnostics()
void ClangToolsLogFileReaderTest::readFileWithNoDiagnostics()
{
const QString filePath = testFilePath(QLatin1String("/data/noDiagnostics.plist"));
QString errorMessage;
const QList<Diagnostic> diagnostics = LogFileReader::read(filePath, &errorMessage);
const QList<Diagnostic> diagnostics = LogFileReader::readPlist(filePath, &errorMessage);
QVERIFY(errorMessage.isEmpty());
QVERIFY(diagnostics.isEmpty());
}
void ClangStaticAnalyzerLogFileReaderTest::readFileWithDiagnostics()
void ClangToolsLogFileReaderTest::readFileWithDiagnostics()
{
const QString filePath = testFilePath(QLatin1String("/data/someDiagnostics.plist"));
QString errorMessage;
const QList<Diagnostic> diagnostics = LogFileReader::read(filePath, &errorMessage);
const QList<Diagnostic> diagnostics = LogFileReader::readPlist(filePath, &errorMessage);
QVERIFY(errorMessage.isEmpty());
QVERIFY(!diagnostics.isEmpty());
@@ -157,6 +157,6 @@ void ClangStaticAnalyzerLogFileReaderTest::readFileWithDiagnostics()
QCOMPARE(step2.extendedMessage, step2.message);
}
QTEST_MAIN(ClangStaticAnalyzerLogFileReaderTest)
QTEST_MAIN(ClangToolsLogFileReaderTest)
#include "tst_clangstaticanalyzerlogfilereader.moc"
#include "tst_clangtoolslogfilereader.moc"