diff --git a/src/libs/utils/utilsicons.cpp b/src/libs/utils/utilsicons.cpp index 43438c920be..96dd9e36fef 100644 --- a/src/libs/utils/utilsicons.cpp +++ b/src/libs/utils/utilsicons.cpp @@ -250,6 +250,10 @@ const Icon CODEMODEL_DISABLED_ERROR({ {":/utils/images/codemodelerror.png", Theme::IconsDisabledColor}}, Icon::Tint); const Icon CODEMODEL_DISABLED_WARNING({ {":/utils/images/codemodelwarning.png", Theme::IconsDisabledColor}}, Icon::Tint); - +const Icon CODEMODEL_FIXIT({{QLatin1String(":/texteditor/images/lightbulbcap.png"), + Utils::Theme::PanelTextColorMid}, + {QLatin1String(":/texteditor/images/lightbulb.png"), + Utils::Theme::IconsWarningColor}}, + Utils::Icon::Tint); } // namespace Icons } // namespace Utils diff --git a/src/libs/utils/utilsicons.h b/src/libs/utils/utilsicons.h index 306bd290ca5..acdc27ed9bc 100644 --- a/src/libs/utils/utilsicons.h +++ b/src/libs/utils/utilsicons.h @@ -142,6 +142,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_ERROR; QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_WARNING; QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_DISABLED_ERROR; QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_DISABLED_WARNING; +QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_FIXIT; } // namespace Icons } // namespace Utils diff --git a/src/plugins/clangtools/clangstaticanalyzerconfigwidget.cpp b/src/plugins/clangtools/clangstaticanalyzerconfigwidget.cpp index 57c1030aaa6..812984cafcf 100644 --- a/src/plugins/clangtools/clangstaticanalyzerconfigwidget.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerconfigwidget.cpp @@ -26,7 +26,7 @@ #include "clangstaticanalyzerconfigwidget.h" #include "ui_clangstaticanalyzerconfigwidget.h" -#include "clangstaticanalyzerutils.h" +#include "clangtoolsutils.h" #include #include @@ -35,7 +35,7 @@ namespace ClangTools { namespace Internal { ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget( - ClangStaticAnalyzerSettings *settings, + ClangToolsSettings *settings, QWidget *parent) : QWidget(parent) , m_ui(new Ui::ClangStaticAnalyzerConfigWidget) diff --git a/src/plugins/clangtools/clangstaticanalyzerconfigwidget.h b/src/plugins/clangtools/clangstaticanalyzerconfigwidget.h index d780f422831..629fe4d8192 100644 --- a/src/plugins/clangtools/clangstaticanalyzerconfigwidget.h +++ b/src/plugins/clangtools/clangstaticanalyzerconfigwidget.h @@ -25,7 +25,7 @@ #pragma once -#include "clangstaticanalyzersettings.h" +#include "clangtoolssettings.h" #include @@ -41,7 +41,7 @@ class ClangStaticAnalyzerConfigWidget : public QWidget Q_OBJECT public: - explicit ClangStaticAnalyzerConfigWidget(ClangStaticAnalyzerSettings *settings, + explicit ClangStaticAnalyzerConfigWidget(ClangToolsSettings *settings, QWidget *parent = 0); ~ClangStaticAnalyzerConfigWidget(); @@ -50,7 +50,7 @@ public: private: Ui::ClangStaticAnalyzerConfigWidget *m_ui; - ClangStaticAnalyzerSettings *m_settings; + ClangToolsSettings *m_settings; }; } // namespace Internal diff --git a/src/plugins/clangtools/clangstaticanalyzerdiagnosticview.cpp b/src/plugins/clangtools/clangstaticanalyzerdiagnosticview.cpp index 29f810c2ab2..a2a2c3d7b85 100644 --- a/src/plugins/clangtools/clangstaticanalyzerdiagnosticview.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerdiagnosticview.cpp @@ -25,10 +25,10 @@ #include "clangstaticanalyzerdiagnosticview.h" -#include "clangstaticanalyzerdiagnosticmodel.h" +#include "clangtoolsdiagnosticmodel.h" #include "clangstaticanalyzerprojectsettings.h" #include "clangstaticanalyzerprojectsettingsmanager.h" -#include "clangstaticanalyzerutils.h" +#include "clangtoolsutils.h" #include #include @@ -54,7 +54,7 @@ void ClangStaticAnalyzerDiagnosticView::suppressCurrentDiagnostic() const QModelIndexList indexes = selectionModel()->selectedRows(); QTC_ASSERT(indexes.count() == 1, return); const Diagnostic diag = model()->data(indexes.first(), - ClangStaticAnalyzerDiagnosticModel::DiagnosticRole) + ClangToolsDiagnosticModel::DiagnosticRole) .value(); QTC_ASSERT(diag.isValid(), return); diff --git a/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp b/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp index 405effc0807..392608e759c 100644 --- a/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerpreconfiguredsessiontests.cpp @@ -25,9 +25,9 @@ #include "clangstaticanalyzerpreconfiguredsessiontests.h" -#include "clangstaticanalyzerdiagnostic.h" +#include "clangtoolsdiagnostic.h" #include "clangstaticanalyzertool.h" -#include "clangstaticanalyzerutils.h" +#include "clangtoolsutils.h" #include #include diff --git a/src/plugins/clangtools/clangstaticanalyzerprojectsettings.cpp b/src/plugins/clangtools/clangstaticanalyzerprojectsettings.cpp index 9d2c645add6..62c2c41608c 100644 --- a/src/plugins/clangtools/clangstaticanalyzerprojectsettings.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerprojectsettings.cpp @@ -25,7 +25,7 @@ #include "clangstaticanalyzerprojectsettings.h" -#include "clangstaticanalyzerdiagnostic.h" +#include "clangtoolsdiagnostic.h" #include diff --git a/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp b/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp index 044b7a2afa3..5684ae875e6 100644 --- a/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerruncontrol.cpp @@ -25,11 +25,11 @@ #include "clangstaticanalyzerruncontrol.h" -#include "clangstaticanalyzerlogfilereader.h" +#include "clangtoolslogfilereader.h" #include "clangstaticanalyzerrunner.h" -#include "clangstaticanalyzersettings.h" +#include "clangtoolssettings.h" #include "clangstaticanalyzertool.h" -#include "clangstaticanalyzerutils.h" +#include "clangtoolsutils.h" #include @@ -66,50 +66,12 @@ #include #include -using namespace CppTools; using namespace ProjectExplorer; -using namespace Utils; - -static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runcontrol") - -static QStringList splitArgs(QString &argsString) -{ - QStringList result; - Utils::QtcProcess::ArgIterator it(&argsString); - while (it.next()) - result.append(it.value()); - return result; -} - -template -static QStringList extraOptions(const char(&environment)[Size]) -{ - if (!qEnvironmentVariableIsSet(environment)) - return QStringList(); - QString arguments = QString::fromLocal8Bit(qgetenv(environment)); - return splitArgs(arguments); -} - -static QStringList extraClangStaticAnalyzerPrependOptions() { - constexpr char csaPrependOptions[] = "QTC_CLANG_CSA_CMD_PREPEND"; - static const QStringList options = extraOptions(csaPrependOptions); - if (!options.isEmpty()) - qWarning() << "ClangStaticAnalyzer options are prepended with " << options.toVector(); - return options; -} - -static QStringList extraClangStaticAnalyzerAppendOptions() { - constexpr char csaAppendOptions[] = "QTC_CLANG_CSA_CMD_APPEND"; - static const QStringList options = extraOptions(csaAppendOptions); - if (!options.isEmpty()) - qWarning() << "ClangStaticAnalyzer options are appended with " << options.toVector(); - return options; -} namespace ClangTools { namespace Internal { -class ProjectBuilder : public RunWorker +class ProjectBuilder : public RunWorker, public BaseProjectBuilder { public: ProjectBuilder(RunControl *runControl, Project *project) @@ -118,7 +80,7 @@ public: setDisplayName("ProjectBuilder"); } - bool success() const { return m_success; } + bool success() const override { return m_success; } private: void start() final @@ -143,9 +105,9 @@ private: "

Do you want to continue and run the tool in %2 mode?

" "") .arg(toolName).arg(wrongMode); - if (CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(), - title, message, Core::ICore::settings(), - "ClangStaticAnalyzerCorrectModeWarning") != QDialogButtonBox::Yes) + if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(), + title, message, Core::ICore::settings(), + "ClangStaticAnalyzerCorrectModeWarning") != QDialogButtonBox::Yes) { reportFailure(); return; @@ -171,242 +133,19 @@ private: bool m_success = false; }; -static AnalyzeUnits unitsToAnalyzeFromProjectParts(const QVector projectParts, - const QString &clangVersion, - const QString &clangResourceDirectory) -{ - qCDebug(LOG) << "Taking arguments for analyzing from ProjectParts."; - - AnalyzeUnits unitsToAnalyze; - - foreach (const ProjectPart::Ptr &projectPart, projectParts) { - if (!projectPart->selectedForBuilding || !projectPart.data()) - continue; - - foreach (const ProjectFile &file, projectPart->files) { - if (file.path == CppModelManager::configurationFileName()) - continue; - QTC_CHECK(file.kind != ProjectFile::Unclassified); - QTC_CHECK(file.kind != ProjectFile::Unsupported); - if (ProjectFile::isSource(file.kind)) { - const CompilerOptionsBuilder::PchUsage pchUsage = CppTools::getPchUsage(); - CompilerOptionsBuilder optionsBuilder(*projectPart, clangVersion, - clangResourceDirectory); - QStringList arguments = extraClangStaticAnalyzerPrependOptions(); - arguments.append(optionsBuilder.build(file.kind, pchUsage)); - arguments.append(extraClangStaticAnalyzerAppendOptions()); - unitsToAnalyze << AnalyzeUnit(file.path, arguments); - } - } - } - - return unitsToAnalyze; -} - -static QString clangResourceDir(const QString &clangExecutable, const QString &clangVersion) -{ - QDir llvmDir = QFileInfo(clangExecutable).dir(); - llvmDir.cdUp(); - return llvmDir.absolutePath() + clangIncludePath(clangVersion); -} - -AnalyzeUnits ClangStaticAnalyzerToolRunner::sortedUnitsToAnalyze(const QString &clangVersion) -{ - QTC_ASSERT(m_projectInfo.isValid(), return AnalyzeUnits()); - - const QString clangResourceDirectory = clangResourceDir(m_clangExecutable, clangVersion); - AnalyzeUnits units = unitsToAnalyzeFromProjectParts(m_projectInfo.projectParts(), clangVersion, - clangResourceDirectory); - - Utils::sort(units, &AnalyzeUnit::file); - return units; -} - -static QDebug operator<<(QDebug debug, const Utils::Environment &environment) -{ - foreach (const QString &entry, environment.toStringList()) - debug << "\n " << entry; - return debug; -} - -static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits) -{ - foreach (const AnalyzeUnit &unit, analyzeUnits) - debug << "\n " << unit.file; - return debug; -} - -ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runControl, Target *target) - : RunWorker(runControl), m_target(target) +ClangStaticAnalyzerRunControl::ClangStaticAnalyzerRunControl(RunControl *runControl, Target *target) + : ClangToolRunControl(runControl, target) { setDisplayName("ClangStaticAnalyzerRunner"); - setSupportsReRunning(false); - m_projectBuilder = new ProjectBuilder(runControl, target->project()); - addStartDependency(m_projectBuilder); + auto *projectBuilder = new ProjectBuilder(runControl, target->project()); + addStartDependency(projectBuilder); + m_projectBuilder = projectBuilder; - m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(target->project()); - - BuildConfiguration *buildConfiguration = target->activeBuildConfiguration(); - QTC_ASSERT(buildConfiguration, return); - m_environment = buildConfiguration->environment(); - - ToolChain *toolChain = ToolChainKitInformation::toolChain(target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID); - QTC_ASSERT(toolChain, return); - m_targetTriple = toolChain->originalTargetTriple(); - m_toolChainType = toolChain->typeId(); + init(); } -void ClangStaticAnalyzerToolRunner::start() -{ - m_success = m_projectBuilder->success(); - if (!m_success) { - reportFailure(); - return; - } - - m_projectInfo = CppTools::CppModelManager::instance()->projectInfo(m_target->project()); - - // Some projects provides CompilerCallData once a build is finished, - if (m_projectInfo.configurationOrFilesChanged(m_projectInfoBeforeBuild)) { - // If it's more than a release/debug build configuration change, e.g. - // a version control checkout, files might be not valid C++ anymore - // or even gone, so better stop here. - reportFailure(tr("The project configuration changed since the start of " - "the Clang Static Analyzer. Please re-run with current configuration.")); - return; - } - - const Utils::FileName projectFile = m_projectInfo.project()->projectFilePath(); - appendMessage(tr("Running Clang Static Analyzer on %1").arg(projectFile.toUserOutput()), - Utils::NormalMessageFormat); - - // Check clang executable - bool isValidClangExecutable; - const QString executable = clangExecutableFromSettings(&isValidClangExecutable); - if (!isValidClangExecutable) { - const QString errorMessage = tr("Clang Static Analyzer: Invalid executable \"%1\", stop.") - .arg(executable); - appendMessage(errorMessage, Utils::ErrorMessageFormat); - TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID); - TaskHub::requestPopup(); - reportFailure(); - return; - } - - // Check clang version - const ClangExecutableVersion version = clangExecutableVersion(executable); - if (!version.isValid()) { - const QString warningMessage - = tr("Clang Static Analyzer: Running with possibly unsupported version, " - "could not determine version from executable \"%1\".") - .arg(executable); - appendMessage(warningMessage, Utils::StdErrFormat); - TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID); - TaskHub::requestPopup(); - } else if (!version.isSupportedVersion()) { - const QString warningMessage - = tr("Clang Static Analyzer: Running with unsupported version %1, " - "supported version is %2.") - .arg(version.toString()) - .arg(ClangExecutableVersion::supportedVersionAsString()); - appendMessage(warningMessage, Utils::StdErrFormat); - TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID); - TaskHub::requestPopup(); - } - - m_clangExecutable = executable; - - // Create log dir - Utils::TemporaryDirectory temporaryDir("qtc-clangstaticanalyzer-XXXXXX"); - temporaryDir.setAutoRemove(false); - if (!temporaryDir.isValid()) { - const QString errorMessage - = tr("Clang Static Analyzer: Failed to create temporary dir, stop."); - appendMessage(errorMessage, Utils::ErrorMessageFormat); - TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID); - TaskHub::requestPopup(); - reportFailure(errorMessage); - return; - } - m_clangLogFileDir = temporaryDir.path(); - - // Collect files - const AnalyzeUnits unitsToProcess = sortedUnitsToAnalyze(version.toString()); - qCDebug(LOG) << "Files to process:" << unitsToProcess; - m_unitsToProcess = unitsToProcess; - m_initialFilesToProcessSize = m_unitsToProcess.count(); - m_filesAnalyzed = 0; - m_filesNotAnalyzed = 0; - - // Set up progress information - using namespace Core; - m_progress = QFutureInterface(); - 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 i(m_runners); - while (i.hasNext()) { - ClangStaticAnalyzerRunner *runner = i.next(); - QObject::disconnect(runner, 0, this, 0); - delete runner; - } - m_runners.clear(); - m_unitsToProcess.clear(); - m_progress.reportFinished(); - //ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success); - reportStopped(); -} - -void ClangStaticAnalyzerToolRunner::analyzeNextFile() -{ - if (m_progress.isFinished()) - return; // The previous call already reported that we are finished. - - if (m_unitsToProcess.isEmpty()) { - if (m_runners.isEmpty()) - finalize(); - return; - } - - const AnalyzeUnit unit = m_unitsToProcess.takeFirst(); - qCDebug(LOG) << "analyzeNextFile:" << unit.file; - - ClangStaticAnalyzerRunner *runner = createRunner(); - m_runners.insert(runner); - QTC_ASSERT(runner->run(unit.file, unit.arguments), return); - - appendMessage(tr("Analyzing \"%1\".").arg( - Utils::FileName::fromString(unit.file).toUserOutput()), - Utils::StdOutFormat); -} - -ClangStaticAnalyzerRunner *ClangStaticAnalyzerToolRunner::createRunner() +ClangToolRunner *ClangStaticAnalyzerRunControl::createRunner() { QTC_ASSERT(!m_clangExecutable.isEmpty(), return 0); QTC_ASSERT(!m_clangLogFileDir.isEmpty(), return 0); @@ -416,83 +155,15 @@ ClangStaticAnalyzerRunner *ClangStaticAnalyzerToolRunner::createRunner() m_environment, this); connect(runner, &ClangStaticAnalyzerRunner::finishedWithSuccess, - this, &ClangStaticAnalyzerToolRunner::onRunnerFinishedWithSuccess); + this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithSuccess); connect(runner, &ClangStaticAnalyzerRunner::finishedWithFailure, - this, &ClangStaticAnalyzerToolRunner::onRunnerFinishedWithFailure); + this, &ClangStaticAnalyzerRunControl::onRunnerFinishedWithFailure); return runner; } -void ClangStaticAnalyzerToolRunner::onRunnerFinishedWithSuccess(const QString &logFilePath) +ClangTool *ClangStaticAnalyzerRunControl::tool() { - qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath; - - QString errorMessage; - const QList diagnostics = LogFileReader::read(logFilePath, &errorMessage); - if (!errorMessage.isEmpty()) { - qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage; - const QString filePath = qobject_cast(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(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(sender())); - updateProgressValue(); - sender()->deleteLater(); - analyzeNextFile(); -} - -void ClangStaticAnalyzerToolRunner::onProgressCanceled() -{ - m_progress.reportCanceled(); - runControl()->initiateStop(); -} - -void ClangStaticAnalyzerToolRunner::updateProgressValue() -{ - m_progress.setProgressValue(m_initialFilesToProcessSize - m_unitsToProcess.size()); -} - -void ClangStaticAnalyzerToolRunner::finalize() -{ - appendMessage(tr("Clang Static Analyzer finished: " - "Processed %1 files successfully, %2 failed.") - .arg(m_filesAnalyzed).arg(m_filesNotAnalyzed), - Utils::NormalMessageFormat); - - if (m_filesNotAnalyzed != 0) { - QString msg = tr("Clang Static Analyzer: Not all files could be analyzed."); - TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); - TaskHub::requestPopup(); - } - - m_progress.reportFinished(); - runControl()->initiateStop(); + return ClangStaticAnalyzerTool::instance(); } } // namespace Internal diff --git a/src/plugins/clangtools/clangstaticanalyzerruncontrol.h b/src/plugins/clangtools/clangstaticanalyzerruncontrol.h index d69948935c8..98bd4f18f1b 100644 --- a/src/plugins/clangtools/clangstaticanalyzerruncontrol.h +++ b/src/plugins/clangtools/clangstaticanalyzerruncontrol.h @@ -25,75 +25,23 @@ #pragma once -#include -#include -#include - -#include -#include +#include "clangtoolruncontrol.h" namespace ClangTools { namespace Internal { -class ClangStaticAnalyzerRunner; -class ProjectBuilder; -class Diagnostic; - -struct AnalyzeUnit { - AnalyzeUnit(const QString &file, const QStringList &options) - : file(file), arguments(options) {} - - QString file; - QStringList arguments; // without file itself and "-o somePath" -}; -typedef QList AnalyzeUnits; - -class ClangStaticAnalyzerToolRunner : public ProjectExplorer::RunWorker +class ClangStaticAnalyzerRunControl final : public ClangToolRunControl { Q_OBJECT public: - ClangStaticAnalyzerToolRunner(ProjectExplorer::RunControl *runControl, + ClangStaticAnalyzerRunControl(ProjectExplorer::RunControl *runControl, ProjectExplorer::Target *target); - bool success() const { return m_success; } // For testing. +protected: + ClangToolRunner *createRunner() final; -private: - void start() final; - void stop() final; - - AnalyzeUnits sortedUnitsToAnalyze(const QString &clangVersion); - void analyzeNextFile(); - ClangStaticAnalyzerRunner *createRunner(); - - void onRunnerFinishedWithSuccess(const QString &logFilePath); - void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails); - void handleFinished(); - - void onProgressCanceled(); - void updateProgressValue(); - - void finalize(); - -private: - QPointer 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 m_progress; - AnalyzeUnits m_unitsToProcess; - QSet m_runners; - int m_initialFilesToProcessSize = 0; - int m_filesAnalyzed = 0; - int m_filesNotAnalyzed = 0; - bool m_success = false; + ClangTool *tool() final; }; } // namespace Internal diff --git a/src/plugins/clangtools/clangstaticanalyzerrunner.cpp b/src/plugins/clangtools/clangstaticanalyzerrunner.cpp index 59d2ed3b4ad..c72141430a2 100644 --- a/src/plugins/clangtools/clangstaticanalyzerrunner.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerrunner.cpp @@ -25,15 +25,10 @@ #include "clangstaticanalyzerrunner.h" -#include "clangstaticanalyzerconstants.h" - -#include #include -#include #include #include -#include #include static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner") @@ -41,159 +36,33 @@ static Q_LOGGING_CATEGORY(LOG, "qtc.clangstaticanalyzer.runner") namespace ClangTools { namespace Internal { -static QString generalProcessError() -{ - return ClangStaticAnalyzerRunner::tr("An error occurred with the Clang Static Analyzer process."); -} - -static QString finishedDueToCrash() -{ - return ClangStaticAnalyzerRunner::tr("Clang Static Analyzer crashed."); -} - -static QStringList constructCommandLineArguments(const QString &filePath, - const QString &logFile, - const QStringList &options) -{ - QStringList arguments; - - if (LOG().isDebugEnabled()) - arguments << QLatin1String("-v"); - - arguments - << QLatin1String("--analyze") - << QLatin1String("-o") - << QDir::toNativeSeparators(logFile) - ; - arguments += options; - arguments << QDir::toNativeSeparators(filePath); - return arguments; -} - -QString finishedWithBadExitCode(int exitCode) -{ - return ClangStaticAnalyzerRunner::tr("Clang Static Analyzer finished with exit code: %1.").arg(exitCode); -} - ClangStaticAnalyzerRunner::ClangStaticAnalyzerRunner(const QString &clangExecutable, const QString &clangLogFileDir, const Utils::Environment &environment, QObject *parent) - : QObject(parent) - , m_clangExecutable(QDir::toNativeSeparators(clangExecutable)) - , m_clangLogFileDir(clangLogFileDir) + : ClangToolRunner(clangExecutable, + clangLogFileDir, + environment, + tr("Clang Static Analyzer"), + parent) { - QTC_CHECK(!m_clangExecutable.isEmpty()); - QTC_CHECK(!m_clangLogFileDir.isEmpty()); - - m_process.setProcessChannelMode(QProcess::MergedChannels); - m_process.setProcessEnvironment(environment.toProcessEnvironment()); - m_process.setWorkingDirectory(m_clangLogFileDir); // Current clang-cl puts log file into working dir. - connect(&m_process, &QProcess::started, this, &ClangStaticAnalyzerRunner::onProcessStarted); - connect(&m_process, static_cast(&QProcess::finished), - this, &ClangStaticAnalyzerRunner::onProcessFinished); - connect(&m_process, &QProcess::errorOccurred, this, &ClangStaticAnalyzerRunner::onProcessError); - connect(&m_process, &QProcess::readyRead, this, &ClangStaticAnalyzerRunner::onProcessOutput); } -ClangStaticAnalyzerRunner::~ClangStaticAnalyzerRunner() +QStringList ClangStaticAnalyzerRunner::constructCommandLineArguments(const QStringList &options) { - Utils::SynchronousProcess::stopProcess(m_process); -} + QStringList arguments; -bool ClangStaticAnalyzerRunner::run(const QString &filePath, const QStringList &compilerOptions) -{ - QTC_ASSERT(!m_clangExecutable.isEmpty(), return false); - QTC_CHECK(!compilerOptions.contains(QLatin1String("-o"))); - QTC_CHECK(!compilerOptions.contains(filePath)); + if (LOG().isDebugEnabled()) + arguments << QString("-v"); - m_filePath = filePath; - m_processOutput.clear(); + arguments << QLatin1String("--analyze") + << QLatin1String("-o") + << QDir::toNativeSeparators(m_logFile); - m_logFile = createLogFile(filePath); - QTC_ASSERT(!m_logFile.isEmpty(), return false); - const QStringList arguments = constructCommandLineArguments(filePath, m_logFile, - compilerOptions); - m_commandLine = Utils::QtcProcess::joinArgs(QStringList(m_clangExecutable) + arguments); + arguments += options; - qCDebug(LOG).noquote() << "Starting" << m_commandLine; - m_process.start(m_clangExecutable, arguments); - return true; -} - -QString ClangStaticAnalyzerRunner::filePath() const -{ - return m_filePath; -} - -void ClangStaticAnalyzerRunner::onProcessStarted() -{ - emit started(); -} - -void ClangStaticAnalyzerRunner::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - if (exitStatus == QProcess::NormalExit) { - if (exitCode == 0) { - qCDebug(LOG).noquote() << "Output:\n" << Utils::SynchronousProcess::normalizeNewlines( - QString::fromLocal8Bit(m_processOutput)); - emit finishedWithSuccess(actualLogFile()); - } - else - emit finishedWithFailure(finishedWithBadExitCode(exitCode), processCommandlineAndOutput()); - } else { // == QProcess::CrashExit - emit finishedWithFailure(finishedDueToCrash(), processCommandlineAndOutput()); - } -} - -void ClangStaticAnalyzerRunner::onProcessError(QProcess::ProcessError error) -{ - if (error == QProcess::Crashed) - return; // handled by slot of finished() - - emit finishedWithFailure(generalProcessError(), processCommandlineAndOutput()); -} - -void ClangStaticAnalyzerRunner::onProcessOutput() -{ - m_processOutput.append(m_process.readAll()); -} - -QString ClangStaticAnalyzerRunner::createLogFile(const QString &filePath) const -{ - const QString fileName = QFileInfo(filePath).fileName(); - const QString fileTemplate = m_clangLogFileDir - + QLatin1String("/report-") + fileName + QLatin1String("-XXXXXX.plist"); - - Utils::TemporaryFile temporaryFile("clangstaticanalyzer"); - temporaryFile.setAutoRemove(false); - temporaryFile.setFileTemplate(fileTemplate); - if (temporaryFile.open()) { - temporaryFile.close(); - return temporaryFile.fileName(); - } - return QString(); -} - -QString ClangStaticAnalyzerRunner::processCommandlineAndOutput() const -{ - return tr("Command line: %1\n" - "Process Error: %2\n" - "Output:\n%3") - .arg(m_commandLine, - QString::number(m_process.error()), - Utils::SynchronousProcess::normalizeNewlines( - QString::fromLocal8Bit(m_processOutput))); -} - -QString ClangStaticAnalyzerRunner::actualLogFile() const -{ - if (QFileInfo(m_logFile).size() == 0) { - // Current clang-cl ignores -o, always putting the log file into the working directory. - return m_clangLogFileDir + QLatin1Char('/') + QFileInfo(m_filePath).completeBaseName() - + QLatin1String(".plist"); - } - return m_logFile; + arguments << QDir::toNativeSeparators(filePath()); + return arguments; } } // namespace Internal diff --git a/src/plugins/clangtools/clangstaticanalyzerrunner.h b/src/plugins/clangtools/clangstaticanalyzerrunner.h index add441e5d95..e5c5b81239e 100644 --- a/src/plugins/clangtools/clangstaticanalyzerrunner.h +++ b/src/plugins/clangtools/clangstaticanalyzerrunner.h @@ -25,18 +25,12 @@ #pragma once -#include -#include - -#include -#include +#include "clangtoolrunner.h" namespace ClangTools { namespace Internal { -QString finishedWithBadExitCode(int exitCode); // exposed for tests - -class ClangStaticAnalyzerRunner : public QObject +class ClangStaticAnalyzerRunner final : public ClangToolRunner { Q_OBJECT @@ -44,39 +38,9 @@ public: ClangStaticAnalyzerRunner(const QString &clangExecutable, const QString &clangLogFileDir, const Utils::Environment &environment, - QObject *parent = 0); - ~ClangStaticAnalyzerRunner(); - - // compilerOptions is expected to contain everything except: - // (1) filePath, that is the file to analyze - // (2) -o output-file - bool run(const QString &filePath, const QStringList &compilerOptions = QStringList()); - - QString filePath() const; - -signals: - void started(); - void finishedWithSuccess(const QString &logFilePath); - void finishedWithFailure(const QString &errorMessage, const QString &errorDetails); - -private: - void onProcessStarted(); - void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); - void onProcessError(QProcess::ProcessError error); - void onProcessOutput(); - - QString createLogFile(const QString &filePath) const; - QString processCommandlineAndOutput() const; - QString actualLogFile() const; - -private: - QString m_clangExecutable; - QString m_clangLogFileDir; - QString m_filePath; - QString m_logFile; - QString m_commandLine; - QProcess m_process; - QByteArray m_processOutput; + QObject *parent = nullptr); +protected: + QStringList constructCommandLineArguments(const QStringList &options) final; }; } // namespace Internal diff --git a/src/plugins/clangtools/clangstaticanalyzertool.cpp b/src/plugins/clangtools/clangstaticanalyzertool.cpp index 0132d4c3c93..a3873e16ca9 100644 --- a/src/plugins/clangtools/clangstaticanalyzertool.cpp +++ b/src/plugins/clangtools/clangstaticanalyzertool.cpp @@ -25,16 +25,14 @@ #include "clangstaticanalyzertool.h" -#include "clangstaticanalyzerconstants.h" -#include "clangstaticanalyzerdiagnostic.h" -#include "clangstaticanalyzerdiagnosticmodel.h" +#include "clangtoolsconstants.h" +#include "clangtoolsdiagnosticmodel.h" +#include "clangtoolslogfilereader.h" #include "clangstaticanalyzerdiagnosticview.h" #include "clangstaticanalyzerruncontrol.h" #include #include -#include -#include #include @@ -44,13 +42,9 @@ #include #include -#include #include #include -#include -#include -#include using namespace Core; using namespace Debugger; @@ -63,24 +57,20 @@ namespace Internal { static ClangStaticAnalyzerTool *s_instance; ClangStaticAnalyzerTool::ClangStaticAnalyzerTool() + : ClangTool(tr("Clang Static Analyzer")) { setObjectName("ClangStaticAnalyzerTool"); s_instance = this; - // - // Diagnostic View - // - m_diagnosticView = new ClangStaticAnalyzerDiagnosticView; - m_diagnosticView->setFrameStyle(QFrame::NoFrame); - m_diagnosticView->setAttribute(Qt::WA_MacShowFocusRect, false); - m_diagnosticModel = new ClangStaticAnalyzerDiagnosticModel(this); m_diagnosticFilterModel = new ClangStaticAnalyzerDiagnosticFilterModel(this); m_diagnosticFilterModel->setSourceModel(m_diagnosticModel); + + m_diagnosticView = new ClangStaticAnalyzerDiagnosticView; + initDiagnosticView(); m_diagnosticView->setModel(m_diagnosticFilterModel); - m_diagnosticView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - m_diagnosticView->setAutoScroll(false); m_diagnosticView->setObjectName(QLatin1String("ClangStaticAnalyzerIssuesView")); m_diagnosticView->setWindowTitle(tr("Clang Static Analyzer Issues")); + foreach (auto * const model, QList() << m_diagnosticModel << m_diagnosticFilterModel) { connect(model, &QAbstractItemModel::rowsInserted, @@ -93,13 +83,6 @@ ClangStaticAnalyzerTool::ClangStaticAnalyzerTool() this, &ClangStaticAnalyzerTool::handleStateUpdate); } - // - // Toolbar widget - // - - m_startAction = Debugger::createStartAction(); - m_stopAction = Debugger::createStopAction(); - // Go to previous diagnostic auto action = new QAction(this); action->setDisabled(true); @@ -148,11 +131,6 @@ ClangStaticAnalyzerTool::ClangStaticAnalyzerTool() this, &ClangStaticAnalyzerTool::updateRunActions); } -ClangStaticAnalyzerTool::~ClangStaticAnalyzerTool() -{ - -} - ClangStaticAnalyzerTool *ClangStaticAnalyzerTool::instance() { return s_instance; @@ -166,8 +144,9 @@ void ClangStaticAnalyzerTool::startTool() Project *project = SessionManager::startupProject(); QTC_ASSERT(project, return); + QTC_ASSERT(project->activeTarget(), return); - auto clangTool = new ClangStaticAnalyzerToolRunner(runControl, project->activeTarget()); + auto clangTool = new ClangStaticAnalyzerRunControl(runControl, project->activeTarget()); m_stopAction->disconnect(); connect(m_stopAction, &QAction::triggered, runControl, [runControl] { @@ -197,17 +176,6 @@ void ClangStaticAnalyzerTool::startTool() ProjectExplorerPlugin::startRunControl(runControl); } -QList ClangStaticAnalyzerTool::diagnostics() const -{ - return m_diagnosticModel->diagnostics(); -} - -void ClangStaticAnalyzerTool::onNewDiagnosticsAvailable(const QList &diagnostics) -{ - QTC_ASSERT(m_diagnosticModel, return); - m_diagnosticModel->addDiagnostics(diagnostics); -} - void ClangStaticAnalyzerTool::updateRunActions() { if (m_toolBusy) { @@ -230,14 +198,6 @@ void ClangStaticAnalyzerTool::updateRunActions() } } -void ClangStaticAnalyzerTool::setToolBusy(bool busy) -{ - QTC_ASSERT(m_diagnosticView, return); - QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor); - m_diagnosticView->setCursor(cursor); - m_toolBusy = busy; -} - void ClangStaticAnalyzerTool::handleStateUpdate() { QTC_ASSERT(m_goBack, return); @@ -266,5 +226,11 @@ void ClangStaticAnalyzerTool::handleStateUpdate() Debugger::showPermanentStatusMessage(message); } +QList ClangStaticAnalyzerTool::read(const QString &filePath, + QString *errorMessage) const +{ + return LogFileReader::readPlist(filePath, errorMessage); +} + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangstaticanalyzertool.h b/src/plugins/clangtools/clangstaticanalyzertool.h index 999caab5b32..8816e36a3c8 100644 --- a/src/plugins/clangtools/clangstaticanalyzertool.h +++ b/src/plugins/clangtools/clangstaticanalyzertool.h @@ -25,54 +25,42 @@ #pragma once -#include -#include +#include "clangtool.h" namespace ClangTools { namespace Internal { class ClangStaticAnalyzerDiagnosticFilterModel; -class ClangStaticAnalyzerDiagnosticModel; +class ClangToolsDiagnosticModel; class ClangStaticAnalyzerDiagnosticView; class Diagnostic; const char ClangStaticAnalyzerPerspectiveId[] = "ClangStaticAnalyzer.Perspective"; const char ClangStaticAnalyzerDockId[] = "ClangStaticAnalyzer.Dock"; -class ClangStaticAnalyzerTool : public QObject +class ClangStaticAnalyzerTool final : public ClangTool { Q_OBJECT public: ClangStaticAnalyzerTool(); - ~ClangStaticAnalyzerTool(); static ClangStaticAnalyzerTool *instance(); - // For testing. - QList diagnostics() const; - void startTool(); + void startTool() final; - void onNewDiagnosticsAvailable(const QList &diagnostics); - -signals: - void finished(bool success); // For testing. + QList read(const QString &filePath, + QString *errorMessage) const final; private: - void setToolBusy(bool busy); - void handleStateUpdate(); + void handleStateUpdate() final; + void updateRunActions(); - ClangStaticAnalyzerDiagnosticModel *m_diagnosticModel = nullptr; ClangStaticAnalyzerDiagnosticFilterModel *m_diagnosticFilterModel = nullptr; - ClangStaticAnalyzerDiagnosticView *m_diagnosticView = nullptr; - QAction *m_startAction = nullptr; - QAction *m_stopAction = nullptr; QAction *m_goBack = nullptr; QAction *m_goNext = nullptr; - bool m_running = false; - bool m_toolBusy = false; }; } // namespace Internal diff --git a/src/plugins/clangtools/clangstaticanalyzerunittests.cpp b/src/plugins/clangtools/clangstaticanalyzerunittests.cpp index 33e8c0f7f3e..259c2efa2cd 100644 --- a/src/plugins/clangtools/clangstaticanalyzerunittests.cpp +++ b/src/plugins/clangtools/clangstaticanalyzerunittests.cpp @@ -25,9 +25,9 @@ #include "clangstaticanalyzerunittests.h" -#include "clangstaticanalyzerdiagnostic.h" +#include "clangtoolsdiagnostic.h" #include "clangstaticanalyzertool.h" -#include "clangstaticanalyzerutils.h" +#include "clangtoolsutils.h" #include #include diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp new file mode 100644 index 00000000000..7031cc98c56 --- /dev/null +++ b/src/plugins/clangtools/clangtool.cpp @@ -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 +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +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 ClangTool::diagnostics() const +{ + return m_diagnosticModel->diagnostics(); +} + +void ClangTool::onNewDiagnosticsAvailable(const QList &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 diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h new file mode 100644 index 00000000000..b0fb84b68c0 --- /dev/null +++ b/src/plugins/clangtools/clangtool.h @@ -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 +#include + +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 read(const QString &filePath, + QString *errorMessage) const = 0; + + // For testing. + QList diagnostics() const; + + const QString &name() const; + + void onNewDiagnosticsAvailable(const QList &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 diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp new file mode 100644 index 00000000000..7c71c31da99 --- /dev/null +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -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 + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +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 +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 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(); + 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 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 diagnostics = tool()->read(logFilePath, &errorMessage); + if (!errorMessage.isEmpty()) { + qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage; + const QString filePath = qobject_cast(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(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(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 diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h new file mode 100644 index 00000000000..195a2150908 --- /dev/null +++ b/src/plugins/clangtools/clangtoolruncontrol.h @@ -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 +#include +#include + +#include +#include + +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 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 m_target; + + CppTools::ProjectInfo m_projectInfoBeforeBuild; + CppTools::ProjectInfo m_projectInfo; + QString m_targetTriple; + Core::Id m_toolChainType; + + QFutureInterface m_progress; + AnalyzeUnits m_unitsToProcess; + QSet m_runners; + int m_initialFilesToProcessSize = 0; + int m_filesAnalyzed = 0; + int m_filesNotAnalyzed = 0; + bool m_success = false; +}; + +} // namespace Internal +} // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp new file mode 100644 index 00000000000..6de55f85b5b --- /dev/null +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -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 +#include +#include +#include +#include + +#include +#include +#include +#include + +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(&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 diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h new file mode 100644 index 00000000000..9cc3ee31853 --- /dev/null +++ b/src/plugins/clangtools/clangtoolrunner.h @@ -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 +#include + +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 diff --git a/src/plugins/clangtools/clangtools.pro b/src/plugins/clangtools/clangtools.pro index 77362b4721e..7b8a194a1bf 100644 --- a/src/plugins/clangtools/clangtools.pro +++ b/src/plugins/clangtools/clangtools.pro @@ -2,37 +2,43 @@ include(../../qtcreatorplugin.pri) SOURCES += \ clangstaticanalyzerconfigwidget.cpp \ - clangstaticanalyzerdiagnostic.cpp \ - clangstaticanalyzerdiagnosticmodel.cpp \ clangstaticanalyzerdiagnosticview.cpp \ - clangstaticanalyzerlogfilereader.cpp \ - clangtoolsplugin.cpp \ clangstaticanalyzerprojectsettings.cpp \ clangstaticanalyzerprojectsettingsmanager.cpp \ clangstaticanalyzerprojectsettingswidget.cpp \ clangstaticanalyzerruncontrol.cpp \ clangstaticanalyzerrunner.cpp \ - clangstaticanalyzersettings.cpp \ clangstaticanalyzertool.cpp \ - clangstaticanalyzerutils.cpp + clangtool.cpp \ + clangtoolruncontrol.cpp \ + clangtoolrunner.cpp \ + clangtoolsdiagnostic.cpp \ + clangtoolsdiagnosticmodel.cpp \ + clangtoolslogfilereader.cpp \ + clangtoolsplugin.cpp \ + clangtoolssettings.cpp \ + clangtoolsutils.cpp HEADERS += \ clangstaticanalyzerconfigwidget.h \ - clangstaticanalyzerconstants.h \ - clangstaticanalyzerdiagnostic.h \ - clangstaticanalyzerdiagnosticmodel.h \ clangstaticanalyzerdiagnosticview.h \ - clangstaticanalyzer_global.h \ - clangstaticanalyzerlogfilereader.h \ - clangtoolsplugin.h \ clangstaticanalyzerprojectsettings.h \ clangstaticanalyzerprojectsettingsmanager.h \ clangstaticanalyzerprojectsettingswidget.h \ clangstaticanalyzerruncontrol.h \ clangstaticanalyzerrunner.h \ - clangstaticanalyzersettings.h \ clangstaticanalyzertool.h \ - clangstaticanalyzerutils.h + clangtool.h \ + clangtoolruncontrol.h \ + clangtoolrunner.h \ + clangtools_global.h \ + clangtoolsconstants.h \ + clangtoolsdiagnostic.h \ + clangtoolsdiagnosticmodel.h \ + clangtoolslogfilereader.h \ + clangtoolsplugin.h \ + clangtoolssettings.h \ + clangtoolsutils.h FORMS += \ clangstaticanalyzerconfigwidget.ui \ diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs index 5466393d046..f744e4c604e 100644 --- a/src/plugins/clangtools/clangtools.qbs +++ b/src/plugins/clangtools/clangtools.qbs @@ -19,19 +19,11 @@ QtcPlugin { ] files: [ - "clangstaticanalyzer_global.h", "clangstaticanalyzerconfigwidget.cpp", "clangstaticanalyzerconfigwidget.h", "clangstaticanalyzerconfigwidget.ui", - "clangstaticanalyzerconstants.h", - "clangstaticanalyzerdiagnostic.cpp", - "clangstaticanalyzerdiagnostic.h", - "clangstaticanalyzerdiagnosticmodel.cpp", - "clangstaticanalyzerdiagnosticmodel.h", "clangstaticanalyzerdiagnosticview.cpp", "clangstaticanalyzerdiagnosticview.h", - "clangstaticanalyzerlogfilereader.cpp", - "clangstaticanalyzerlogfilereader.h", "clangstaticanalyzerprojectsettings.cpp", "clangstaticanalyzerprojectsettings.h", "clangstaticanalyzerprojectsettingsmanager.cpp", @@ -43,12 +35,26 @@ QtcPlugin { "clangstaticanalyzerruncontrol.h", "clangstaticanalyzerrunner.cpp", "clangstaticanalyzerrunner.h", - "clangstaticanalyzersettings.cpp", - "clangstaticanalyzersettings.h", "clangstaticanalyzertool.cpp", "clangstaticanalyzertool.h", - "clangstaticanalyzerutils.cpp", - "clangstaticanalyzerutils.h", + "clangtool.cpp", + "clangtool.h", + "clangtoolruncontrol.cpp", + "clangtoolruncontrol.h", + "clangtoolrunner.cpp", + "clangtoolrunner.h", + "clangtools_global.h", + "clangtoolsconstants.h", + "clangtoolsdiagnostic.cpp", + "clangtoolsdiagnostic.h", + "clangtoolsdiagnosticmodel.cpp", + "clangtoolsdiagnosticmodel.h", + "clangtoolslogfilereader.cpp", + "clangtoolslogfilereader.h", + "clangtoolssettings.cpp", + "clangtoolssettings.h", + "clangtoolsutils.cpp", + "clangtoolsutils.h", "clangtoolsplugin.cpp", "clangtoolsplugin.h", ] diff --git a/src/plugins/clangtools/clangstaticanalyzer_global.h b/src/plugins/clangtools/clangtools_global.h similarity index 100% rename from src/plugins/clangtools/clangstaticanalyzer_global.h rename to src/plugins/clangtools/clangtools_global.h diff --git a/src/plugins/clangtools/clangstaticanalyzerconstants.h b/src/plugins/clangtools/clangtoolsconstants.h similarity index 91% rename from src/plugins/clangtools/clangstaticanalyzerconstants.h rename to src/plugins/clangtools/clangtoolsconstants.h index bbd28fd73b3..4650c9f355c 100644 --- a/src/plugins/clangtools/clangstaticanalyzerconstants.h +++ b/src/plugins/clangtools/clangtoolsconstants.h @@ -28,8 +28,9 @@ namespace ClangTools { namespace Constants { -const char SETTINGS_ID[] = "ClangStaticAnalyzer"; +const char SETTINGS_ID[] = "ClangTools"; const char CLANGSTATICANALYZER_RUN_MODE[] = "ClangStaticAnalyzer.RunMode"; +const char CLANGTIDYCLAZY_RUN_MODE[] = "ClangTidyClazy.RunMode"; } // Constants -} // ClangStaticAnalyzer +} // ClangTools diff --git a/src/plugins/clangtools/clangstaticanalyzerdiagnostic.cpp b/src/plugins/clangtools/clangtoolsdiagnostic.cpp similarity index 97% rename from src/plugins/clangtools/clangstaticanalyzerdiagnostic.cpp rename to src/plugins/clangtools/clangtoolsdiagnostic.cpp index 13d151b4194..078113fe9e6 100644 --- a/src/plugins/clangtools/clangstaticanalyzerdiagnostic.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnostic.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "clangstaticanalyzerdiagnostic.h" +#include "clangtoolsdiagnostic.h" namespace ClangTools { namespace Internal { diff --git a/src/plugins/clangtools/clangstaticanalyzerdiagnostic.h b/src/plugins/clangtools/clangtoolsdiagnostic.h similarity index 100% rename from src/plugins/clangtools/clangstaticanalyzerdiagnostic.h rename to src/plugins/clangtools/clangtoolsdiagnostic.h diff --git a/src/plugins/clangtools/clangstaticanalyzerdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp similarity index 91% rename from src/plugins/clangtools/clangstaticanalyzerdiagnosticmodel.cpp rename to src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index c8a8ba137c2..ac10caf268b 100644 --- a/src/plugins/clangtools/clangstaticanalyzerdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp @@ -23,15 +23,16 @@ ** ****************************************************************************/ -#include "clangstaticanalyzerdiagnosticmodel.h" +#include "clangtoolsdiagnosticmodel.h" #include "clangstaticanalyzerdiagnosticview.h" #include "clangstaticanalyzerprojectsettingsmanager.h" -#include "clangstaticanalyzerutils.h" +#include "clangtoolsutils.h" #include #include #include +#include #include #include @@ -65,19 +66,19 @@ private: const ExplainingStep m_step; }; -ClangStaticAnalyzerDiagnosticModel::ClangStaticAnalyzerDiagnosticModel(QObject *parent) +ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent) : Utils::TreeModel<>(parent) { setHeader({tr("Issue"), tr("Location")}); } -void ClangStaticAnalyzerDiagnosticModel::addDiagnostics(const QList &diagnostics) +void ClangToolsDiagnosticModel::addDiagnostics(const QList &diagnostics) { foreach (const Diagnostic &d, diagnostics) rootItem()->appendChild(new DiagnosticItem(d)); } -QList ClangStaticAnalyzerDiagnosticModel::diagnostics() const +QList ClangToolsDiagnosticModel::diagnostics() const { QList diags; for (const Utils::TreeItem * const item : *rootItem()) @@ -224,7 +225,7 @@ DiagnosticItem::DiagnosticItem(const Diagnostic &diag) : m_diagnostic(diag) appendChild(new ExplainingStepItem(s)); } -QVariant locationData(int role, const Debugger::DiagnosticLocation &location) +static QVariant locationData(int role, const Debugger::DiagnosticLocation &location) { switch (role) { case Debugger::DetailedErrorView::LocationRole: @@ -236,6 +237,19 @@ QVariant locationData(int role, const Debugger::DiagnosticLocation &location) } } +static QVariant iconData(const QString &type) +{ + if (type == "warning") + return Utils::Icons::CODEMODEL_WARNING.icon(); + if (type == "error") + return Utils::Icons::CODEMODEL_ERROR.icon(); + if (type == "note") + return Utils::Icons::BOOKMARK.icon(); + if (type == "fix-it") + return Utils::Icons::CODEMODEL_FIXIT.icon(); + return QVariant(); +} + QVariant DiagnosticItem::data(int column, int role) const { if (column == Debugger::DetailedErrorView::LocationColumn) @@ -245,12 +259,14 @@ QVariant DiagnosticItem::data(int column, int role) const switch (role) { case Debugger::DetailedErrorView::FullTextRole: return fullText(m_diagnostic); - case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole: + case ClangToolsDiagnosticModel::DiagnosticRole: return QVariant::fromValue(m_diagnostic); case Qt::DisplayRole: return m_diagnostic.description; case Qt::ToolTipRole: return createDiagnosticToolTipString(m_diagnostic); + case Qt::DecorationRole: + return iconData(m_diagnostic.type); default: return QVariant(); } @@ -269,7 +285,7 @@ QVariant ExplainingStepItem::data(int column, int role) const switch (role) { case Debugger::DetailedErrorView::FullTextRole: return fullText(static_cast(parent())->diagnostic()); - case ClangStaticAnalyzerDiagnosticModel::DiagnosticRole: + case ClangToolsDiagnosticModel::DiagnosticRole: return QVariant::fromValue(static_cast(parent())->diagnostic()); case Qt::DisplayRole: { const int row = indexInParent() + 1; @@ -330,7 +346,7 @@ bool ClangStaticAnalyzerDiagnosticFilterModel::filterAcceptsRow(int sourceRow, { if (sourceParent.isValid()) return true; - const Diagnostic diag = static_cast(sourceModel()) + const Diagnostic diag = static_cast(sourceModel()) ->diagnostics().at(sourceRow); foreach (const SuppressedDiagnostic &d, m_suppressedDiagnostics) { if (d.description != diag.description) diff --git a/src/plugins/clangtools/clangstaticanalyzerdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h similarity index 86% rename from src/plugins/clangtools/clangstaticanalyzerdiagnosticmodel.h rename to src/plugins/clangtools/clangtoolsdiagnosticmodel.h index c2ae8c8626a..0ca4dc9bac9 100644 --- a/src/plugins/clangtools/clangstaticanalyzerdiagnosticmodel.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h @@ -25,7 +25,7 @@ #pragma once -#include "clangstaticanalyzerdiagnostic.h" +#include "clangtoolsdiagnostic.h" #include "clangstaticanalyzerprojectsettings.h" #include @@ -40,15 +40,15 @@ namespace ProjectExplorer { class Project; } namespace ClangTools { namespace Internal { -class ClangStaticAnalyzerDiagnosticModel : public Utils::TreeModel<> +class ClangToolsDiagnosticModel : public Utils::TreeModel<> { Q_OBJECT public: - ClangStaticAnalyzerDiagnosticModel(QObject *parent = 0); + ClangToolsDiagnosticModel(QObject *parent = nullptr); - void addDiagnostics(const QList &diagnostics); - QList diagnostics() const; + virtual void addDiagnostics(const QList &diagnostics); + virtual QList diagnostics() const; enum ItemRole { DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1 @@ -60,7 +60,7 @@ class ClangStaticAnalyzerDiagnosticFilterModel : public QSortFilterProxyModel Q_OBJECT public: - ClangStaticAnalyzerDiagnosticFilterModel(QObject *parent = 0); + ClangStaticAnalyzerDiagnosticFilterModel(QObject *parent = nullptr); void setProject(ProjectExplorer::Project *project); void addSuppressedDiagnostic(const SuppressedDiagnostic &diag); diff --git a/src/plugins/clangtools/clangstaticanalyzerlogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp similarity index 95% rename from src/plugins/clangtools/clangstaticanalyzerlogfilereader.cpp rename to src/plugins/clangtools/clangtoolslogfilereader.cpp index ab166593cb3..22e4b6f1610 100644 --- a/src/plugins/clangtools/clangstaticanalyzerlogfilereader.cpp +++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp @@ -23,12 +23,14 @@ ** ****************************************************************************/ -#include "clangstaticanalyzerlogfilereader.h" +#include "clangtoolslogfilereader.h" #include +#include #include #include #include +#include #include #include @@ -72,19 +74,25 @@ private: QList m_diagnostics; }; -QList LogFileReader::read(const QString &filePath, QString *errorMessage) +static bool checkFilePath(const QString &filePath, QString *errorMessage) { - const QList emptyList; - - // Check file path QFileInfo fi(filePath); if (!fi.exists() || !fi.isReadable()) { if (errorMessage) { - *errorMessage = tr("File \"%1\" does not exist or is not readable.") + *errorMessage + = QString(QT_TRANSLATE_NOOP("LogFileReader", + "File \"%1\" does not exist or is not readable.")) .arg(filePath); } - return emptyList; + return false; } + return true; +} + +QList LogFileReader::readPlist(const QString &filePath, QString *errorMessage) +{ + if (!checkFilePath(filePath, errorMessage)) + return QList(); // Read ClangStaticAnalyzerLogFileReader reader(filePath); @@ -121,7 +129,7 @@ QList LogFileReader::read(const QString &filePath, QString *errorMes } Q_FALLTHROUGH(); default: - return emptyList; + return QList(); } } diff --git a/src/plugins/clangtools/clangstaticanalyzerlogfilereader.h b/src/plugins/clangtools/clangtoolslogfilereader.h similarity index 91% rename from src/plugins/clangtools/clangstaticanalyzerlogfilereader.h rename to src/plugins/clangtools/clangtoolslogfilereader.h index 9236fff2e98..9b66dd854c2 100644 --- a/src/plugins/clangtools/clangstaticanalyzerlogfilereader.h +++ b/src/plugins/clangtools/clangtoolslogfilereader.h @@ -25,7 +25,7 @@ #pragma once -#include "clangstaticanalyzerdiagnostic.h" +#include "clangtoolsdiagnostic.h" #include #include @@ -37,7 +37,7 @@ class LogFileReader { Q_DECLARE_TR_FUNCTIONS(ClangTools::Internal::LogFileReader) public: - static QList read(const QString &filePath, QString *errorMessage); + static QList readPlist(const QString &filePath, QString *errorMessage); }; } // namespace Internal diff --git a/src/plugins/clangtools/clangtoolsplugin.cpp b/src/plugins/clangtools/clangtoolsplugin.cpp index cd83c22e038..604981a839b 100644 --- a/src/plugins/clangtools/clangtoolsplugin.cpp +++ b/src/plugins/clangtools/clangtoolsplugin.cpp @@ -26,7 +26,7 @@ #include "clangtoolsplugin.h" #include "clangstaticanalyzerconfigwidget.h" -#include "clangstaticanalyzerconstants.h" +#include "clangtoolsconstants.h" #include "clangstaticanalyzerprojectsettingswidget.h" #include "clangstaticanalyzerruncontrol.h" #include "clangstaticanalyzertool.h" @@ -80,13 +80,13 @@ public: QWidget *widget() { if (!m_widget) - m_widget = new ClangStaticAnalyzerConfigWidget(ClangStaticAnalyzerSettings::instance()); + m_widget = new ClangStaticAnalyzerConfigWidget(ClangToolsSettings::instance()); return m_widget; } void apply() { - ClangStaticAnalyzerSettings::instance()->writeSettings(); + ClangToolsSettings::instance()->writeSettings(); } void finish() @@ -101,7 +101,7 @@ private: class ClangToolsPluginPrivate { public: - ClangStaticAnalyzerTool tool; + ClangStaticAnalyzerTool staticAnalyzerTool; ClangStaticAnalyzerOptionsPage optionsPage; }; @@ -119,7 +119,7 @@ bool ClangToolsPlugin::initialize(const QStringList &arguments, QString *errorSt auto panelFactory = new ProjectPanelFactory(); panelFactory->setPriority(100); - panelFactory->setDisplayName(tr("Clang Static Analyzer")); + panelFactory->setDisplayName(tr("Clang Tools")); panelFactory->setCreateWidgetFunction([](Project *project) { return new ProjectSettingsWidget(project); }); ProjectPanelFactory::registerFactory(panelFactory); diff --git a/src/plugins/clangtools/clangstaticanalyzersettings.cpp b/src/plugins/clangtools/clangtoolssettings.cpp similarity index 81% rename from src/plugins/clangtools/clangstaticanalyzersettings.cpp rename to src/plugins/clangtools/clangtoolssettings.cpp index d7a0a61ebac..5c51ed84713 100644 --- a/src/plugins/clangtools/clangstaticanalyzersettings.cpp +++ b/src/plugins/clangtools/clangtoolssettings.cpp @@ -23,9 +23,9 @@ ** ****************************************************************************/ -#include "clangstaticanalyzersettings.h" +#include "clangtoolssettings.h" -#include "clangstaticanalyzerconstants.h" +#include "clangtoolsconstants.h" #include @@ -41,15 +41,15 @@ static const char simultaneousProcessesKey[] = "simultaneousProcesses"; namespace ClangTools { namespace Internal { -ClangStaticAnalyzerSettings::ClangStaticAnalyzerSettings() +ClangToolsSettings::ClangToolsSettings() : m_simultaneousProcesses(-1) { readSettings(); } -ClangStaticAnalyzerSettings *ClangStaticAnalyzerSettings::instance() +ClangToolsSettings *ClangToolsSettings::instance() { - static ClangStaticAnalyzerSettings instance; + static ClangToolsSettings instance; return &instance; } @@ -58,7 +58,7 @@ static QString clangExecutableFileName() return QLatin1String("clang" QTC_HOST_EXE_SUFFIX); } -QString ClangStaticAnalyzerSettings::defaultClangExecutable() const +QString ClangToolsSettings::defaultClangExecutable() const { const QString shippedBinary = Core::ICore::libexecPath() + QLatin1String("/clang/bin/") @@ -68,7 +68,7 @@ QString ClangStaticAnalyzerSettings::defaultClangExecutable() const return clangExecutableFileName(); } -QString ClangStaticAnalyzerSettings::clangExecutable(bool *isSet) const +QString ClangToolsSettings::clangExecutable(bool *isSet) const { if (m_clangExecutable.isEmpty()) { if (isSet) @@ -80,23 +80,23 @@ QString ClangStaticAnalyzerSettings::clangExecutable(bool *isSet) const return m_clangExecutable; } -void ClangStaticAnalyzerSettings::setClangExecutable(const QString &exectuable) +void ClangToolsSettings::setClangExecutable(const QString &exectuable) { m_clangExecutable = exectuable; } -int ClangStaticAnalyzerSettings::simultaneousProcesses() const +int ClangToolsSettings::simultaneousProcesses() const { return m_simultaneousProcesses; } -void ClangStaticAnalyzerSettings::setSimultaneousProcesses(int processes) +void ClangToolsSettings::setSimultaneousProcesses(int processes) { QTC_ASSERT(processes >=1, return); m_simultaneousProcesses = processes; } -void ClangStaticAnalyzerSettings::readSettings() +void ClangToolsSettings::readSettings() { QSettings *settings = Core::ICore::settings(); settings->beginGroup(QLatin1String(Constants::SETTINGS_ID)); @@ -110,7 +110,7 @@ void ClangStaticAnalyzerSettings::readSettings() settings->endGroup(); } -void ClangStaticAnalyzerSettings::writeSettings() const +void ClangToolsSettings::writeSettings() const { QSettings *settings = Core::ICore::settings(); settings->beginGroup(QLatin1String(Constants::SETTINGS_ID)); diff --git a/src/plugins/clangtools/clangstaticanalyzersettings.h b/src/plugins/clangtools/clangtoolssettings.h similarity index 93% rename from src/plugins/clangtools/clangstaticanalyzersettings.h rename to src/plugins/clangtools/clangtoolssettings.h index e6a1f7fb134..0739d630ec0 100644 --- a/src/plugins/clangtools/clangstaticanalyzersettings.h +++ b/src/plugins/clangtools/clangtoolssettings.h @@ -30,10 +30,10 @@ namespace ClangTools { namespace Internal { -class ClangStaticAnalyzerSettings +class ClangToolsSettings { public: - static ClangStaticAnalyzerSettings *instance(); + static ClangToolsSettings *instance(); void writeSettings() const; @@ -45,7 +45,7 @@ public: void setSimultaneousProcesses(int processes); private: - ClangStaticAnalyzerSettings(); + ClangToolsSettings(); void readSettings(); QString m_clangExecutable; diff --git a/src/plugins/clangtools/clangstaticanalyzerutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp similarity index 92% rename from src/plugins/clangtools/clangstaticanalyzerutils.cpp rename to src/plugins/clangtools/clangtoolsutils.cpp index 857ab731184..cd43b397371 100644 --- a/src/plugins/clangtools/clangstaticanalyzerutils.cpp +++ b/src/plugins/clangtools/clangtoolsutils.cpp @@ -23,10 +23,10 @@ ** ****************************************************************************/ -#include "clangstaticanalyzerutils.h" +#include "clangtoolsutils.h" -#include "clangstaticanalyzerdiagnostic.h" -#include "clangstaticanalyzersettings.h" +#include "clangtoolsdiagnostic.h" +#include "clangtoolssettings.h" #include @@ -52,7 +52,7 @@ namespace Internal { QString clangExecutableFromSettings(bool *isValid) { - QString executable = ClangStaticAnalyzerSettings::instance()->clangExecutable(); + QString executable = ClangToolsSettings::instance()->clangExecutable(); if (executable.isEmpty()) { *isValid = false; return executable; @@ -82,9 +82,8 @@ QString clangExecutableFromSettings(bool *isValid) QString createFullLocationString(const Debugger::DiagnosticLocation &location) { - const QString filePath = location.filePath; - const QString lineNumber = QString::number(location.line); - return filePath + QLatin1Char(':') + lineNumber; + return location.filePath + QLatin1Char(':') + QString::number(location.line) + + QLatin1Char(':') + QString::number(location.column); } bool isClangExecutableUsable(const QString &filePath, QString *errorMessage) @@ -92,7 +91,7 @@ bool isClangExecutableUsable(const QString &filePath, QString *errorMessage) const QFileInfo fi(filePath); if (fi.isSymLink() && fi.symLinkTarget().contains(QLatin1String("icecc"))) { if (errorMessage) { - *errorMessage = QCoreApplication::translate("ClangStaticAnalyzer", + *errorMessage = QCoreApplication::translate("ClangTools", "The chosen file \"%1\" seems to point to an icecc binary not suitable " "for analyzing.\nPlease set a real Clang executable.") .arg(filePath); diff --git a/src/plugins/clangtools/clangstaticanalyzerutils.h b/src/plugins/clangtools/clangtoolsutils.h similarity index 100% rename from src/plugins/clangtools/clangstaticanalyzerutils.h rename to src/plugins/clangtools/clangtoolsutils.h diff --git a/src/plugins/debugger/analyzer/detailederrorview.cpp b/src/plugins/debugger/analyzer/detailederrorview.cpp index 874e304aaa3..a5ee070a1aa 100644 --- a/src/plugins/debugger/analyzer/detailederrorview.cpp +++ b/src/plugins/debugger/analyzer/detailederrorview.cpp @@ -60,9 +60,10 @@ private: const auto location = index.model()->data(index, DetailedErrorView::LocationRole) .value(); return location.isValid() - ? QString::fromLatin1("%2:%3") + ? QString::fromLatin1("%2:%3:%4") .arg(location.filePath, QFileInfo(location.filePath).fileName()) .arg(location.line) + .arg(location.column) : QString(); } diff --git a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro b/tests/auto/clangtools/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro deleted file mode 100644 index 4a1f96e77f7..00000000000 --- a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.pro +++ /dev/null @@ -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 diff --git a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.qbs b/tests/auto/clangtools/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.qbs deleted file mode 100644 index 5fc992aa064..00000000000 --- a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/clangstaticanalyzerlogfilereader.qbs +++ /dev/null @@ -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" - ] -} diff --git a/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro b/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro index 7893e8761a4..c0231c0aa01 100644 --- a/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro +++ b/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.pro @@ -1,9 +1,11 @@ -include(../clangstaticanalyzertest.pri) +include(../clangtoolstest.pri) TARGET = tst_clangstaticanalyzerrunnertest SOURCES += \ tst_clangstaticanalyzerrunner.cpp \ + $$PLUGINDIR/clangtoolrunner.cpp \ $$PLUGINDIR/clangstaticanalyzerrunner.cpp HEADERS += \ + $$PLUGINDIR/clangtoolrunner.h \ $$PLUGINDIR/clangstaticanalyzerrunner.h diff --git a/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs b/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs index 6b0f9da3f37..94580dda1bd 100644 --- a/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs +++ b/tests/auto/clangtools/clangstaticanalyzerrunner/clangstaticanalyzerrunner.qbs @@ -1,7 +1,7 @@ import qbs -import "../clangstaticanalyzerautotest.qbs" as ClangStaticAnalyzerAutotest +import "../clangtoolsautotest.qbs" as ClangToolsAutotest -ClangStaticAnalyzerAutotest { +ClangToolsAutotest { name: "ClangStaticAnalyzerRunner Autotest" Group { @@ -10,6 +10,8 @@ ClangStaticAnalyzerAutotest { files: [ "clangstaticanalyzerrunner.cpp", "clangstaticanalyzerrunner.h", + "clangtoolrunner.cpp", + "clangtoolrunner.h", ] } diff --git a/tests/auto/clangtools/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp b/tests/auto/clangtools/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp index 97e618c01d4..e0b1c4e364c 100644 --- a/tests/auto/clangtools/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp +++ b/tests/auto/clangtools/clangstaticanalyzerrunner/tst_clangstaticanalyzerrunner.cpp @@ -23,9 +23,10 @@ ** ****************************************************************************/ -#include +#include #include +#include #include #include @@ -193,7 +194,7 @@ void ClangStaticAnalyzerRunnerTest::runWithNonExistentFileToAnalyze() QVERIFY(runner.run(QLatin1String("not.existing.file.111"))); QVERIFY(st.expectStartedSignal()); - QVERIFY(st.expectFinishWithFailureSignal(finishedWithBadExitCode(1))); + QVERIFY(st.expectFinishWithFailureSignal(finishedWithBadExitCode("Clang Static Analyzer", 1))); } int main(int argc, char *argv[]) diff --git a/tests/auto/clangtools/clangtools.pro b/tests/auto/clangtools/clangtools.pro index 72e7c586e9d..5d550ea70c5 100644 --- a/tests/auto/clangtools/clangtools.pro +++ b/tests/auto/clangtools/clangtools.pro @@ -3,4 +3,4 @@ CONFIG += ordered SUBDIRS = \ clangstaticanalyzerrunner \ - clangstaticanalyzerlogfilereader + clangtoolslogfilereader diff --git a/tests/auto/clangtools/clangtools.qbs b/tests/auto/clangtools/clangtools.qbs index e9ee45d733a..7340c07363e 100644 --- a/tests/auto/clangtools/clangtools.qbs +++ b/tests/auto/clangtools/clangtools.qbs @@ -1,9 +1,9 @@ import qbs Project { - name: "ClangStaticAnalyzer autotests" + name: "ClangTools autotests" references: [ - "clangstaticanalyzerlogfilereader", + "clangtoolslogfilereader", "clangstaticanalyzerrunner", ] } diff --git a/tests/auto/clangtools/clangstaticanalyzerautotest.qbs b/tests/auto/clangtools/clangtoolsautotest.qbs similarity index 100% rename from tests/auto/clangtools/clangstaticanalyzerautotest.qbs rename to tests/auto/clangtools/clangtoolsautotest.qbs diff --git a/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.pro b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.pro new file mode 100644 index 00000000000..5271b5f18e4 --- /dev/null +++ b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.pro @@ -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 diff --git a/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.qbs b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.qbs new file mode 100644 index 00000000000..993733954ca --- /dev/null +++ b/tests/auto/clangtools/clangtoolslogfilereader/clangtoolslogfilereader.qbs @@ -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" + ] +} diff --git a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist b/tests/auto/clangtools/clangtoolslogfilereader/data/noDiagnostics.plist similarity index 100% rename from tests/auto/clangtools/clangstaticanalyzerlogfilereader/data/noDiagnostics.plist rename to tests/auto/clangtools/clangtoolslogfilereader/data/noDiagnostics.plist diff --git a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist b/tests/auto/clangtools/clangtoolslogfilereader/data/someDiagnostics.plist similarity index 100% rename from tests/auto/clangtools/clangstaticanalyzerlogfilereader/data/someDiagnostics.plist rename to tests/auto/clangtools/clangtoolslogfilereader/data/someDiagnostics.plist diff --git a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp b/tests/auto/clangtools/clangtoolslogfilereader/tst_clangtoolslogfilereader.cpp similarity index 88% rename from tests/auto/clangtools/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp rename to tests/auto/clangtools/clangtoolslogfilereader/tst_clangtoolslogfilereader.cpp index 8bc4a045c21..33cdf5fd66c 100644 --- a/tests/auto/clangtools/clangstaticanalyzerlogfilereader/tst_clangstaticanalyzerlogfilereader.cpp +++ b/tests/auto/clangtools/clangtoolslogfilereader/tst_clangtoolslogfilereader.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include +#include #include @@ -83,7 +83,7 @@ QString testFilePath(const QString &relativePath) } // anonymous namespace -class ClangStaticAnalyzerLogFileReaderTest : public QObject +class ClangToolsLogFileReaderTest : public QObject { Q_OBJECT @@ -93,35 +93,35 @@ private slots: void readFileWithDiagnostics(); }; -void ClangStaticAnalyzerLogFileReaderTest::readEmptyFile() +void ClangToolsLogFileReaderTest::readEmptyFile() { const QString filePath = QDir::tempPath() + QLatin1String("/empty.file"); QVERIFY(createEmptyFile(filePath)); QString errorMessage; - const QList diagnostics = LogFileReader::read(filePath, &errorMessage); + const QList diagnostics = LogFileReader::readPlist(filePath, &errorMessage); QVERIFY(!errorMessage.isEmpty()); if (debug) qDebug() << errorMessage; QVERIFY(diagnostics.isEmpty()); } -void ClangStaticAnalyzerLogFileReaderTest::readFileWithNoDiagnostics() +void ClangToolsLogFileReaderTest::readFileWithNoDiagnostics() { const QString filePath = testFilePath(QLatin1String("/data/noDiagnostics.plist")); QString errorMessage; - const QList diagnostics = LogFileReader::read(filePath, &errorMessage); + const QList diagnostics = LogFileReader::readPlist(filePath, &errorMessage); QVERIFY(errorMessage.isEmpty()); QVERIFY(diagnostics.isEmpty()); } -void ClangStaticAnalyzerLogFileReaderTest::readFileWithDiagnostics() +void ClangToolsLogFileReaderTest::readFileWithDiagnostics() { const QString filePath = testFilePath(QLatin1String("/data/someDiagnostics.plist")); QString errorMessage; - const QList diagnostics = LogFileReader::read(filePath, &errorMessage); + const QList diagnostics = LogFileReader::readPlist(filePath, &errorMessage); QVERIFY(errorMessage.isEmpty()); QVERIFY(!diagnostics.isEmpty()); @@ -157,6 +157,6 @@ void ClangStaticAnalyzerLogFileReaderTest::readFileWithDiagnostics() QCOMPARE(step2.extendedMessage, step2.message); } -QTEST_MAIN(ClangStaticAnalyzerLogFileReaderTest) +QTEST_MAIN(ClangToolsLogFileReaderTest) -#include "tst_clangstaticanalyzerlogfilereader.moc" +#include "tst_clangtoolslogfilereader.moc" diff --git a/tests/auto/clangtools/clangstaticanalyzertest.pri b/tests/auto/clangtools/clangtoolstest.pri similarity index 100% rename from tests/auto/clangtools/clangstaticanalyzertest.pri rename to tests/auto/clangtools/clangtoolstest.pri