ClangTools: Split generic part from static analyzer tool

To reuse it for other clang-based tools.

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

View File

@@ -250,6 +250,10 @@ const Icon CODEMODEL_DISABLED_ERROR({
{":/utils/images/codemodelerror.png", Theme::IconsDisabledColor}}, Icon::Tint); {":/utils/images/codemodelerror.png", Theme::IconsDisabledColor}}, Icon::Tint);
const Icon CODEMODEL_DISABLED_WARNING({ const Icon CODEMODEL_DISABLED_WARNING({
{":/utils/images/codemodelwarning.png", Theme::IconsDisabledColor}}, Icon::Tint); {":/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 Icons
} // namespace Utils } // namespace Utils

View File

@@ -142,6 +142,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_ERROR;
QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_WARNING; QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_WARNING;
QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_DISABLED_ERROR; 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_DISABLED_WARNING;
QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_FIXIT;
} // namespace Icons } // namespace Icons
} // namespace Utils } // namespace Utils

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,11 +25,11 @@
#include "clangstaticanalyzerruncontrol.h" #include "clangstaticanalyzerruncontrol.h"
#include "clangstaticanalyzerlogfilereader.h" #include "clangtoolslogfilereader.h"
#include "clangstaticanalyzerrunner.h" #include "clangstaticanalyzerrunner.h"
#include "clangstaticanalyzersettings.h" #include "clangtoolssettings.h"
#include "clangstaticanalyzertool.h" #include "clangstaticanalyzertool.h"
#include "clangstaticanalyzerutils.h" #include "clangtoolsutils.h"
#include <debugger/analyzer/analyzerconstants.h> #include <debugger/analyzer/analyzerconstants.h>
@@ -66,50 +66,12 @@
#include <QAction> #include <QAction>
#include <QLoggingCategory> #include <QLoggingCategory>
using namespace CppTools;
using namespace ProjectExplorer; 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 ClangTools {
namespace Internal { namespace Internal {
class ProjectBuilder : public RunWorker class ProjectBuilder : public RunWorker, public BaseProjectBuilder
{ {
public: public:
ProjectBuilder(RunControl *runControl, Project *project) ProjectBuilder(RunControl *runControl, Project *project)
@@ -118,7 +80,7 @@ public:
setDisplayName("ProjectBuilder"); setDisplayName("ProjectBuilder");
} }
bool success() const { return m_success; } bool success() const override { return m_success; }
private: private:
void start() final void start() final
@@ -143,9 +105,9 @@ private:
"<p>Do you want to continue and run the tool in %2 mode?</p>" "<p>Do you want to continue and run the tool in %2 mode?</p>"
"</body></html>") "</body></html>")
.arg(toolName).arg(wrongMode); .arg(toolName).arg(wrongMode);
if (CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(), if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(),
title, message, Core::ICore::settings(), title, message, Core::ICore::settings(),
"ClangStaticAnalyzerCorrectModeWarning") != QDialogButtonBox::Yes) "ClangStaticAnalyzerCorrectModeWarning") != QDialogButtonBox::Yes)
{ {
reportFailure(); reportFailure();
return; return;
@@ -171,242 +133,19 @@ private:
bool m_success = false; bool m_success = false;
}; };
static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QVector<ProjectPart::Ptr> projectParts, ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(RunControl *runControl, Target *target)
const QString &clangVersion, : ClangToolRunControl(runControl, target)
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)
{ {
setDisplayName("ClangStaticAnalyzerRunner"); setDisplayName("ClangStaticAnalyzerRunner");
setSupportsReRunning(false);
m_projectBuilder = new ProjectBuilder(runControl, target->project()); auto *projectBuilder = new ProjectBuilder(runControl, target->project());
addStartDependency(m_projectBuilder); addStartDependency(projectBuilder);
m_projectBuilder = projectBuilder;
m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(target->project()); init();
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();
} }
void ClangStaticAnalyzerToolRunner::start() ClangToolRunner *ClangStaticAnalyzerRunControl::createRunner()
{
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()
{ {
QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0); QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0);
QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0); QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0);
@@ -416,83 +155,15 @@ ClangStaticAnalyzerRunner *ClangStaticAnalyzerToolRunner::createRunner()
m_environment, m_environment,
this); this);
connect(runner, &ClangStaticAnalyzerRunner::finishedWithSuccess, connect(runner, &ClangStaticAnalyzerRunner::finishedWithSuccess,
this, &ClangStaticAnalyzerToolRunner::onRunnerFinishedWithSuccess); this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess);
connect(runner, &ClangStaticAnalyzerRunner::finishedWithFailure, connect(runner, &ClangStaticAnalyzerRunner::finishedWithFailure,
this, &ClangStaticAnalyzerToolRunner::onRunnerFinishedWithFailure); this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure);
return runner; return runner;
} }
void ClangStaticAnalyzerToolRunner::onRunnerFinishedWithSuccess(const QString &logFilePath) ClangTool *ClangStaticAnalyzerRunControl::tool()
{ {
qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath; return ClangStaticAnalyzerTool::instance();
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();
} }
} // namespace Internal } // namespace Internal

View File

@@ -25,75 +25,23 @@
#pragma once #pragma once
#include <projectexplorer/runconfiguration.h> #include "clangtoolruncontrol.h"
#include <cpptools/projectinfo.h>
#include <utils/environment.h>
#include <QFutureInterface>
#include <QStringList>
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
class ClangStaticAnalyzerRunner; class ClangStaticAnalyzerRunControl final : public ClangToolRunControl
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
{ {
Q_OBJECT Q_OBJECT
public: public:
ClangStaticAnalyzerToolRunner(ProjectExplorer::RunControl *runControl, ClangStaticAnalyzerRunControl(ProjectExplorer::RunControl *runControl,
ProjectExplorer::Target *target); ProjectExplorer::Target *target);
bool success() const { return m_success; } // For testing. protected:
ClangToolRunner *createRunner() final;
private: ClangTool *tool() final;
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;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -25,15 +25,10 @@
#include "clangstaticanalyzerrunner.h" #include "clangstaticanalyzerrunner.h"
#include "clangstaticanalyzerconstants.h"
#include <utils/qtcprocess.h>
#include <utils/synchronousprocess.h> #include <utils/synchronousprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QFileInfo>
#include <QLoggingCategory> #include <QLoggingCategory>
static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner") static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner")
@@ -41,159 +36,33 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner")
namespace ClangTools { namespace ClangTools {
namespace Internal { 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, ClangStaticAnalyzerRunner::ClangStaticAnalyzerRunner(const QString &clangExecutable,
const QString &clangLogFileDir, const QString &clangLogFileDir,
const Utils::Environment &environment, const Utils::Environment &environment,
QObject *parent) QObject *parent)
: QObject(parent) : ClangToolRunner(clangExecutable,
, m_clangExecutable(QDir::toNativeSeparators(clangExecutable)) clangLogFileDir,
, m_clangLogFileDir(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) if (LOG().isDebugEnabled())
{ arguments << QString("-v");
QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
QTC_CHECK(!compilerOptions.contains(QLatin1String("-o")));
QTC_CHECK(!compilerOptions.contains(filePath));
m_filePath = filePath; arguments << QLatin1String("--analyze")
m_processOutput.clear(); << QLatin1String("-o")
<< QDir::toNativeSeparators(m_logFile);
m_logFile = createLogFile(filePath); arguments += options;
QTC_ASSERT(!m_logFile.isEmpty(), return false);
const QStringList arguments = constructCommandLineArguments(filePath, m_logFile,
compilerOptions);
m_commandLine = Utils::QtcProcess::joinArgs(QStringList(m_clangExecutable) + arguments);
qCDebug(LOG).noquote() << "Starting" << m_commandLine; arguments << QDir::toNativeSeparators(filePath());
m_process.start(m_clangExecutable, arguments); return 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;
} }
} // namespace Internal } // namespace Internal

View File

@@ -25,18 +25,12 @@
#pragma once #pragma once
#include <QString> #include "clangtoolrunner.h"
#include <QProcess>
#include <utils/environment.h>
#include <utils/qtcassert.h>
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
QString finishedWithBadExitCode(int exitCode); // exposed for tests class ClangStaticAnalyzerRunner final : public ClangToolRunner
class ClangStaticAnalyzerRunner : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -44,39 +38,9 @@ public:
ClangStaticAnalyzerRunner(const QString &clangExecutable, ClangStaticAnalyzerRunner(const QString &clangExecutable,
const QString &clangLogFileDir, const QString &clangLogFileDir,
const Utils::Environment &environment, const Utils::Environment &environment,
QObject *parent = 0); QObject *parent = nullptr);
~ClangStaticAnalyzerRunner(); protected:
QStringList constructCommandLineArguments(const QStringList &options) final;
// 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;
}; };
} // namespace Internal } // namespace Internal

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,103 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangtool.h"
#include "clangtoolsconstants.h"
#include "clangtoolsdiagnostic.h"
#include "clangtoolsdiagnosticmodel.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <debugger/analyzer/analyzermanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/target.h>
#include <projectexplorer/session.h>
#include <utils/fancymainwindow.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QLabel>
#include <QSortFilterProxyModel>
#include <QToolButton>
using namespace Core;
using namespace Debugger;
using namespace ProjectExplorer;
using namespace Utils;
namespace ClangTools {
namespace Internal {
ClangTool::ClangTool(const QString &name)
: m_name(name)
{
m_diagnosticModel = new ClangToolsDiagnosticModel(this);
m_startAction = Debugger::createStartAction();
m_stopAction = Debugger::createStopAction();
}
const QString &ClangTool::name() const
{
return m_name;
}
void ClangTool::initDiagnosticView()
{
m_diagnosticView->setFrameStyle(QFrame::NoFrame);
m_diagnosticView->setAttribute(Qt::WA_MacShowFocusRect, false);
m_diagnosticView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
m_diagnosticView->setAutoScroll(false);
}
QList<Diagnostic> ClangTool::diagnostics() const
{
return m_diagnosticModel->diagnostics();
}
void ClangTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics)
{
QTC_ASSERT(m_diagnosticModel, return);
m_diagnosticModel->addDiagnostics(diagnostics);
}
void ClangTool::setToolBusy(bool busy)
{
QTC_ASSERT(m_diagnosticView, return);
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
m_diagnosticView->setCursor(cursor);
m_toolBusy = busy;
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,81 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <projectexplorer/runconfiguration.h>
#include <cpptools/projectinfo.h>
namespace Debugger { class DetailedErrorView; }
namespace ClangTools {
namespace Internal {
class ClangToolsDiagnosticModel;
class Diagnostic;
class ClangTool : public QObject
{
Q_OBJECT
public:
ClangTool(const QString &name);
virtual ~ClangTool() = default;
virtual void startTool() = 0;
virtual QList<Diagnostic> read(const QString &filePath,
QString *errorMessage) const = 0;
// For testing.
QList<Diagnostic> diagnostics() const;
const QString &name() const;
void onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics);
signals:
void finished(bool success); // For testing.
protected:
virtual void handleStateUpdate() = 0;
void setToolBusy(bool busy);
void initDiagnosticView();
ClangToolsDiagnosticModel *m_diagnosticModel = nullptr;
Debugger::DetailedErrorView *m_diagnosticView = nullptr;
QAction *m_startAction = nullptr;
QAction *m_stopAction = nullptr;
bool m_running = false;
bool m_toolBusy = false;
private:
const QString m_name;
};
} // namespace Internal
} // namespace ClangTools

View File

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

View File

@@ -0,0 +1,117 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <projectexplorer/runconfiguration.h>
#include <cpptools/projectinfo.h>
#include <utils/environment.h>
#include <QFutureInterface>
#include <QStringList>
namespace ClangTools {
namespace Internal {
class ClangTool;
class ClangToolRunner;
class Diagnostic;
class ProjectBuilder;
struct AnalyzeUnit {
AnalyzeUnit(const QString &file, const QStringList &options)
: file(file), arguments(options) {}
QString file;
QStringList arguments; // without file itself and "-o somePath"
};
typedef QList<AnalyzeUnit> AnalyzeUnits;
class BaseProjectBuilder
{
public:
virtual ~BaseProjectBuilder() {}
virtual bool success() const { return true; }
};
class ClangToolRunControl : public ProjectExplorer::RunWorker
{
Q_OBJECT
public:
ClangToolRunControl(ProjectExplorer::RunControl *runControl,
ProjectExplorer::Target *target);
bool success() const { return m_success; } // For testing.
protected:
void init();
virtual ClangTool *tool() = 0;
virtual ClangToolRunner *createRunner() = 0;
void onRunnerFinishedWithSuccess(const QString &logFilePath);
void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails);
private:
void start() final;
void stop() final;
AnalyzeUnits sortedUnitsToAnalyze(const QString &clangVersion);
void analyzeNextFile();
void handleFinished();
void onProgressCanceled();
void updateProgressValue();
void finalize();
protected:
BaseProjectBuilder *m_projectBuilder = nullptr;
Utils::Environment m_environment;
QString m_clangExecutable;
QString m_clangLogFileDir;
private:
QPointer<ProjectExplorer::Target> m_target;
CppTools::ProjectInfo m_projectInfoBeforeBuild;
CppTools::ProjectInfo m_projectInfo;
QString m_targetTriple;
Core::Id m_toolChainType;
QFutureInterface<void> m_progress;
AnalyzeUnits m_unitsToProcess;
QSet<ClangToolRunner *> m_runners;
int m_initialFilesToProcessSize = 0;
int m_filesAnalyzed = 0;
int m_filesNotAnalyzed = 0;
bool m_success = false;
};
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,184 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangtoolrunner.h"
#include "clangtoolsconstants.h"
#include <utils/environment.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/synchronousprocess.h>
#include <utils/temporaryfile.h>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLoggingCategory>
static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner")
namespace ClangTools {
namespace Internal {
static QString generalProcessError(const QString &name)
{
return ClangToolRunner::tr("An error occurred with the %1 process.").arg(name);
}
static QString finishedDueToCrash(const QString &name)
{
return ClangToolRunner::tr("%1 crashed.").arg(name);
}
QString finishedWithBadExitCode(const QString &name, int exitCode)
{
return ClangToolRunner::tr("%1 finished with exit code: %2.").arg(name).arg(exitCode);
}
ClangToolRunner::ClangToolRunner(const QString &clangExecutable,
const QString &clangLogFileDir,
const Utils::Environment &environment,
const QString &name,
QObject *parent)
: QObject(parent)
, m_clangExecutable(QDir::toNativeSeparators(clangExecutable))
, m_clangLogFileDir(clangLogFileDir)
, m_name(name)
{
QTC_CHECK(!m_clangExecutable.isEmpty());
QTC_CHECK(!m_clangLogFileDir.isEmpty());
m_process.setProcessChannelMode(QProcess::MergedChannels);
m_process.setProcessEnvironment(environment.toProcessEnvironment());
m_process.setWorkingDirectory(m_clangLogFileDir); // Current clang-cl puts log file into working dir.
connect(&m_process, &QProcess::started, this, &ClangToolRunner::onProcessStarted);
connect(&m_process, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, &ClangToolRunner::onProcessFinished);
connect(&m_process, &QProcess::errorOccurred, this, &ClangToolRunner::onProcessError);
connect(&m_process, &QProcess::readyRead, this, &ClangToolRunner::onProcessOutput);
}
ClangToolRunner::~ClangToolRunner()
{
Utils::SynchronousProcess::stopProcess(m_process);
}
bool ClangToolRunner::run(const QString &filePath, const QStringList &compilerOptions)
{
QTC_ASSERT(!m_clangExecutable.isEmpty(), return false);
QTC_CHECK(!compilerOptions.contains(QLatin1String("-o")));
QTC_CHECK(!compilerOptions.contains(filePath));
m_filePath = filePath;
m_processOutput.clear();
m_logFile = createLogFile(filePath);
QTC_ASSERT(!m_logFile.isEmpty(), return false);
const QStringList arguments = constructCommandLineArguments(compilerOptions);
m_commandLine = Utils::QtcProcess::joinArgs(QStringList(m_clangExecutable) + arguments);
qCDebug(LOG).noquote() << "Starting" << m_commandLine;
m_process.start(m_clangExecutable, arguments);
return true;
}
QString ClangToolRunner::filePath() const
{
return m_filePath;
}
void ClangToolRunner::onProcessStarted()
{
emit started();
}
void ClangToolRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (exitStatus == QProcess::NormalExit) {
if (exitCode == 0) {
qCDebug(LOG).noquote() << "Output:\n" << Utils::SynchronousProcess::normalizeNewlines(
QString::fromLocal8Bit(m_processOutput));
emit finishedWithSuccess(actualLogFile());
}
else
emit finishedWithFailure(finishedWithBadExitCode(m_name, exitCode), processCommandlineAndOutput());
} else { // == QProcess::CrashExit
emit finishedWithFailure(finishedDueToCrash(m_name), processCommandlineAndOutput());
}
}
void ClangToolRunner::onProcessError(QProcess::ProcessError error)
{
if (error == QProcess::Crashed)
return; // handled by slot of finished()
emit finishedWithFailure(generalProcessError(m_name), processCommandlineAndOutput());
}
void ClangToolRunner::onProcessOutput()
{
m_processOutput.append(m_process.readAll());
}
QString ClangToolRunner::createLogFile(const QString &filePath) const
{
const QString fileName = QFileInfo(filePath).fileName();
const QString fileTemplate = m_clangLogFileDir
+ QLatin1String("/report-") + fileName + QLatin1String("-XXXXXX.plist");
Utils::TemporaryFile temporaryFile("clangtools");
temporaryFile.setAutoRemove(false);
temporaryFile.setFileTemplate(fileTemplate);
if (temporaryFile.open()) {
temporaryFile.close();
return temporaryFile.fileName();
}
return QString();
}
QString ClangToolRunner::processCommandlineAndOutput() const
{
return tr("Command line: %1\n"
"Process Error: %2\n"
"Output:\n%3")
.arg(m_commandLine,
QString::number(m_process.error()),
Utils::SynchronousProcess::normalizeNewlines(
QString::fromLocal8Bit(m_processOutput)));
}
QString ClangToolRunner::actualLogFile() const
{
if (QFileInfo(m_logFile).size() == 0) {
// Current clang-cl ignores -o, always putting the log file into the working directory.
return m_clangLogFileDir + QLatin1Char('/') + QFileInfo(m_filePath).completeBaseName()
+ QLatin1String(".plist");
}
return m_logFile;
}
} // namespace Internal
} // namespace ClangTools

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QString>
#include <QProcess>
namespace Utils { class Environment; }
namespace ClangTools {
namespace Internal {
QString finishedWithBadExitCode(const QString &name, int exitCode); // exposed for tests
class ClangToolRunner : public QObject
{
Q_OBJECT
public:
ClangToolRunner(const QString &clangExecutable,
const QString &clangLogFileDir,
const Utils::Environment &environment,
const QString &name,
QObject *parent = nullptr);
virtual ~ClangToolRunner();
// compilerOptions is expected to contain everything except:
// (1) filePath, that is the file to analyze
// (2) -o output-file
bool run(const QString &filePath, const QStringList &compilerOptions = QStringList());
QString filePath() const;
signals:
void started();
void finishedWithSuccess(const QString &logFilePath);
void finishedWithFailure(const QString &errorMessage, const QString &errorDetails);
protected:
virtual QStringList constructCommandLineArguments(const QStringList &options) = 0;
virtual void onProcessOutput();
private:
void onProcessStarted();
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onProcessError(QProcess::ProcessError error);
QString createLogFile(const QString &filePath) const;
QString processCommandlineAndOutput() const;
QString actualLogFile() const;
protected:
QString m_logFile;
QProcess m_process;
QByteArray m_processOutput;
private:
QString m_clangExecutable;
QString m_clangLogFileDir;
QString m_filePath;
QString m_commandLine;
const QString m_name;
};
} // namespace Internal
} // namespace ClangTools

View File

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

View File

@@ -19,19 +19,11 @@ QtcPlugin {
] ]
files: [ files: [
"clangstaticanalyzer_global.h",
"clangstaticanalyzerconfigwidget.cpp", "clangstaticanalyzerconfigwidget.cpp",
"clangstaticanalyzerconfigwidget.h", "clangstaticanalyzerconfigwidget.h",
"clangstaticanalyzerconfigwidget.ui", "clangstaticanalyzerconfigwidget.ui",
"clangstaticanalyzerconstants.h",
"clangstaticanalyzerdiagnostic.cpp",
"clangstaticanalyzerdiagnostic.h",
"clangstaticanalyzerdiagnosticmodel.cpp",
"clangstaticanalyzerdiagnosticmodel.h",
"clangstaticanalyzerdiagnosticview.cpp", "clangstaticanalyzerdiagnosticview.cpp",
"clangstaticanalyzerdiagnosticview.h", "clangstaticanalyzerdiagnosticview.h",
"clangstaticanalyzerlogfilereader.cpp",
"clangstaticanalyzerlogfilereader.h",
"clangstaticanalyzerprojectsettings.cpp", "clangstaticanalyzerprojectsettings.cpp",
"clangstaticanalyzerprojectsettings.h", "clangstaticanalyzerprojectsettings.h",
"clangstaticanalyzerprojectsettingsmanager.cpp", "clangstaticanalyzerprojectsettingsmanager.cpp",
@@ -43,12 +35,26 @@ QtcPlugin {
"clangstaticanalyzerruncontrol.h", "clangstaticanalyzerruncontrol.h",
"clangstaticanalyzerrunner.cpp", "clangstaticanalyzerrunner.cpp",
"clangstaticanalyzerrunner.h", "clangstaticanalyzerrunner.h",
"clangstaticanalyzersettings.cpp",
"clangstaticanalyzersettings.h",
"clangstaticanalyzertool.cpp", "clangstaticanalyzertool.cpp",
"clangstaticanalyzertool.h", "clangstaticanalyzertool.h",
"clangstaticanalyzerutils.cpp", "clangtool.cpp",
"clangstaticanalyzerutils.h", "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.cpp",
"clangtoolsplugin.h", "clangtoolsplugin.h",
] ]

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,12 +23,14 @@
** **
****************************************************************************/ ****************************************************************************/
#include "clangstaticanalyzerlogfilereader.h" #include "clangtoolslogfilereader.h"
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QObject> #include <QObject>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QRegularExpression>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -72,19 +74,25 @@ private:
QList<Diagnostic> m_diagnostics; 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); QFileInfo fi(filePath);
if (!fi.exists() || !fi.isReadable()) { if (!fi.exists() || !fi.isReadable()) {
if (errorMessage) { 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); .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 // Read
ClangStaticAnalyzerLogFileReader reader(filePath); ClangStaticAnalyzerLogFileReader reader(filePath);
@@ -121,7 +129,7 @@ QList<Diagnostic> LogFileReader::read(const QString &filePath, QString *errorMes
} }
Q_FALLTHROUGH(); Q_FALLTHROUGH();
default: default:
return emptyList; return QList<Diagnostic>();
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -60,9 +60,10 @@ private:
const auto location = index.model()->data(index, DetailedErrorView::LocationRole) const auto location = index.model()->data(index, DetailedErrorView::LocationRole)
.value<DiagnosticLocation>(); .value<DiagnosticLocation>();
return location.isValid() 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.filePath, QFileInfo(location.filePath).fileName())
.arg(location.line) .arg(location.line)
.arg(location.column)
: QString(); : QString();
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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