forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "clangstaticanalyzerprojectsettings.h"
|
||||
|
||||
#include "clangstaticanalyzerdiagnostic.h"
|
||||
#include "clangtoolsdiagnostic.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
103
src/plugins/clangtools/clangtool.cpp
Normal file
103
src/plugins/clangtools/clangtool.cpp
Normal 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
|
81
src/plugins/clangtools/clangtool.h
Normal file
81
src/plugins/clangtools/clangtool.h
Normal 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
|
429
src/plugins/clangtools/clangtoolruncontrol.cpp
Normal file
429
src/plugins/clangtools/clangtoolruncontrol.cpp
Normal 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
|
117
src/plugins/clangtools/clangtoolruncontrol.h
Normal file
117
src/plugins/clangtools/clangtoolruncontrol.h
Normal 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
|
184
src/plugins/clangtools/clangtoolrunner.cpp
Normal file
184
src/plugins/clangtools/clangtoolrunner.cpp
Normal 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
|
90
src/plugins/clangtools/clangtoolrunner.h
Normal file
90
src/plugins/clangtools/clangtoolrunner.h
Normal 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
|
@@ -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 \
|
||||
|
@@ -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",
|
||||
]
|
||||
|
@@ -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
|
@@ -23,7 +23,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangstaticanalyzerdiagnostic.h"
|
||||
#include "clangtoolsdiagnostic.h"
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
@@ -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)
|
@@ -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);
|
@@ -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>();
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
@@ -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);
|
||||
|
||||
|
@@ -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));
|
@@ -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;
|
@@ -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);
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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
|
@@ -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"
|
||||
]
|
||||
}
|
@@ -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
|
||||
|
@@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
|
@@ -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[])
|
||||
|
@@ -3,4 +3,4 @@ CONFIG += ordered
|
||||
|
||||
SUBDIRS = \
|
||||
clangstaticanalyzerrunner \
|
||||
clangstaticanalyzerlogfilereader
|
||||
clangtoolslogfilereader
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import qbs
|
||||
|
||||
Project {
|
||||
name: "ClangStaticAnalyzer autotests"
|
||||
name: "ClangTools autotests"
|
||||
references: [
|
||||
"clangstaticanalyzerlogfilereader",
|
||||
"clangtoolslogfilereader",
|
||||
"clangstaticanalyzerrunner",
|
||||
]
|
||||
}
|
||||
|
@@ -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
|
@@ -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"
|
||||
]
|
||||
}
|
@@ -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"
|
Reference in New Issue
Block a user