ClangTools: Add action for running on current file

Ideally, we would build the particular file before running the analyzer,
but not every project manager supports that.

For now, skip building the project for this action.

Change-Id: Ibc516c41dd0dbeb7b17b44c0ac35ae4b46ae801d
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
Nikolai Kosjar
2019-08-23 15:25:57 +02:00
parent fc37ea4276
commit 04f9c41432
21 changed files with 124 additions and 41 deletions

View File

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 102 B

View File

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 132 B

View File

Before

Width:  |  Height:  |  Size: 99 B

After

Width:  |  Height:  |  Size: 99 B

View File

Before

Width:  |  Height:  |  Size: 104 B

After

Width:  |  Height:  |  Size: 104 B

View File

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 151 B

View File

Before

Width:  |  Height:  |  Size: 162 B

After

Width:  |  Height:  |  Size: 162 B

View File

@@ -142,8 +142,14 @@
<file>images/Desktop.png</file> <file>images/Desktop.png</file>
<file>images/interrupt_small.png</file> <file>images/interrupt_small.png</file>
<file>images/interrupt_small@2x.png</file> <file>images/interrupt_small@2x.png</file>
<file>images/run_file.png</file>
<file>images/run_file@2x.png</file>
<file>images/run_small.png</file> <file>images/run_small.png</file>
<file>images/run_small@2x.png</file> <file>images/run_small@2x.png</file>
<file>images/runselected_boxes.png</file>
<file>images/runselected_boxes@2x.png</file>
<file>images/runselected_tickmarks.png</file>
<file>images/runselected_tickmarks@2x.png</file>
<file>images/stop_small.png</file> <file>images/stop_small.png</file>
<file>images/stop_small@2x.png</file> <file>images/stop_small@2x.png</file>
<file>images/boundingrect.png</file> <file>images/boundingrect.png</file>

View File

@@ -6,18 +6,12 @@
<file>images/leafsort@2x.png</file> <file>images/leafsort@2x.png</file>
<file>images/benchmark.png</file> <file>images/benchmark.png</file>
<file>images/benchmark@2x.png</file> <file>images/benchmark@2x.png</file>
<file>images/runselected_boxes.png</file>
<file>images/runselected_boxes@2x.png</file>
<file>images/runselected_tickmarks.png</file>
<file>images/runselected_tickmarks@2x.png</file>
<file>images/data.png</file> <file>images/data.png</file>
<file>images/data@2x.png</file> <file>images/data@2x.png</file>
<file>images/text.png</file> <file>images/text.png</file>
<file>images/text@2x.png</file> <file>images/text@2x.png</file>
<file>images/visual.png</file> <file>images/visual.png</file>
<file>images/visual@2x.png</file> <file>images/visual@2x.png</file>
<file>images/run_file.png</file>
<file>images/run_file@2x.png</file>
<file>images/suite.png</file> <file>images/suite.png</file>
<file>images/suite@2x.png</file> <file>images/suite@2x.png</file>
</qresource> </qresource>

View File

@@ -32,12 +32,12 @@ namespace Icons {
const Utils::Icon SORT_NATURALLY({ const Utils::Icon SORT_NATURALLY({
{":/autotest/images/leafsort.png", Utils::Theme::IconsBaseColor}}); {":/autotest/images/leafsort.png", Utils::Theme::IconsBaseColor}});
const Utils::Icon RUN_SELECTED_OVERLAY({
{":/autotest/images/runselected_boxes.png", Utils::Theme::BackgroundColorDark},
{":/autotest/images/runselected_tickmarks.png", Utils::Theme::IconsBaseColor}});
const Utils::Icon RUN_FILE_OVERLAY({
{":/autotest/images/run_file.png", Utils::Theme::IconsBaseColor}});
const Utils::Icon RUN_SELECTED_OVERLAY({
{":/utils/images/runselected_boxes.png", Utils::Theme::BackgroundColorDark},
{":/utils/images/runselected_tickmarks.png", Utils::Theme::IconsBaseColor}});
const Utils::Icon RUN_FILE_OVERLAY({
{":/utils/images/run_file.png", Utils::Theme::IconsBaseColor}});
const Utils::Icon RESULT_PASS({ const Utils::Icon RESULT_PASS({
{":/utils/images/filledcircle.png", Utils::Theme::OutputPanes_TestPassTextColor}}, {":/utils/images/filledcircle.png", Utils::Theme::OutputPanes_TestPassTextColor}},
Utils::Icon::Tint); Utils::Icon::Tint);

View File

@@ -33,10 +33,12 @@ using namespace ProjectExplorer;
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
ClangTidyClazyRunWorker::ClangTidyClazyRunWorker(RunControl *runControl, ClangTidyClazyRunWorker::ClangTidyClazyRunWorker(
const CppTools::ClangDiagnosticConfig &diagnosticConfig, RunControl *runControl,
const FileInfos &fileInfos) const CppTools::ClangDiagnosticConfig &diagnosticConfig,
: ClangToolRunWorker(runControl, fileInfos) const FileInfos &fileInfos,
bool preventBuild)
: ClangToolRunWorker(runControl, fileInfos, preventBuild)
, m_diagnosticConfig(diagnosticConfig) , m_diagnosticConfig(diagnosticConfig)
{ {
setId("ClangTidyClazyRunner"); setId("ClangTidyClazyRunner");

View File

@@ -39,7 +39,8 @@ class ClangTidyClazyRunWorker final : public ClangToolRunWorker
public: public:
ClangTidyClazyRunWorker(ProjectExplorer::RunControl *runControl, ClangTidyClazyRunWorker(ProjectExplorer::RunControl *runControl,
const CppTools::ClangDiagnosticConfig &diagnosticConfig, const CppTools::ClangDiagnosticConfig &diagnosticConfig,
const FileInfos &fileInfos); const FileInfos &fileInfos,
bool preventBuild);
protected: protected:
QList<RunnerCreator> runnerCreators() final; QList<RunnerCreator> runnerCreators() final;

View File

@@ -327,13 +327,20 @@ ClangTidyClazyTool::ClangTidyClazyTool()
action->setToolTip(toolTip); action->setToolTip(toolTip);
menu->addAction(ActionManager::registerAction(action, "ClangTidyClazy.Action"), menu->addAction(ActionManager::registerAction(action, "ClangTidyClazy.Action"),
Debugger::Constants::G_ANALYZER_TOOLS); Debugger::Constants::G_ANALYZER_TOOLS);
QObject::connect(action, &QAction::triggered, this, [this]() { startTool(true); }); QObject::connect(action, &QAction::triggered, this, [this]() {
startTool(ClangTidyClazyTool::FileSelection::AskUser);
});
QObject::connect(m_startAction, &QAction::triggered, action, &QAction::triggered); QObject::connect(m_startAction, &QAction::triggered, action, &QAction::triggered);
QObject::connect(m_startAction, &QAction::changed, action, [action, this] { QObject::connect(m_startAction, &QAction::changed, action, [action, this] {
action->setEnabled(m_startAction->isEnabled()); action->setEnabled(m_startAction->isEnabled());
}); });
QObject::connect(m_startOnCurrentFileAction, &QAction::triggered, this, [this] {
startTool(ClangTidyClazyTool::FileSelection::CurrentFile);
});
m_perspective.addToolBarAction(m_startAction); m_perspective.addToolBarAction(m_startAction);
m_perspective.addToolBarAction(m_startOnCurrentFileAction);
m_perspective.addToolBarAction(m_stopAction); m_perspective.addToolBarAction(m_stopAction);
m_perspective.addToolBarAction(m_loadExported); m_perspective.addToolBarAction(m_loadExported);
m_perspective.addToolBarAction(m_clear); m_perspective.addToolBarAction(m_clear);
@@ -373,7 +380,7 @@ static ClangDiagnosticConfig getDiagnosticConfig(Project *project)
return configsModel.configWithId(diagnosticConfigId); return configsModel.configWithId(diagnosticConfigId);
} }
void ClangTidyClazyTool::startTool(bool askUserForFileSelection) void ClangTidyClazyTool::startTool(FileSelection fileSelection)
{ {
Project *project = SessionManager::startupProject(); Project *project = SessionManager::startupProject();
QTC_ASSERT(project, return); QTC_ASSERT(project, return);
@@ -384,13 +391,15 @@ void ClangTidyClazyTool::startTool(bool askUserForFileSelection)
runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR); runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
runControl->setTarget(project->activeTarget()); runControl->setTarget(project->activeTarget());
const FileInfos fileInfos = collectFileInfos(project, askUserForFileSelection); const FileInfos fileInfos = collectFileInfos(project, fileSelection);
if (fileInfos.empty()) if (fileInfos.empty())
return; return;
const bool preventBuild = fileSelection == FileSelection::CurrentFile;
auto clangTool = new ClangTidyClazyRunWorker(runControl, auto clangTool = new ClangTidyClazyRunWorker(runControl,
getDiagnosticConfig(project), getDiagnosticConfig(project),
fileInfos); fileInfos,
preventBuild);
m_stopAction->disconnect(); m_stopAction->disconnect();
connect(m_stopAction, &QAction::triggered, runControl, [runControl] { connect(m_stopAction, &QAction::triggered, runControl, [runControl] {
@@ -424,24 +433,35 @@ void ClangTidyClazyTool::startTool(bool askUserForFileSelection)
void ClangTidyClazyTool::updateRunActions() void ClangTidyClazyTool::updateRunActions()
{ {
if (m_toolBusy) { if (m_toolBusy) {
m_startAction->setEnabled(false);
QString tooltipText = tr("Clang-Tidy and Clazy are still running."); QString tooltipText = tr("Clang-Tidy and Clazy are still running.");
m_startAction->setEnabled(false);
m_startAction->setToolTip(tooltipText); m_startAction->setToolTip(tooltipText);
m_startOnCurrentFileAction->setEnabled(false);
m_startOnCurrentFileAction->setToolTip(tooltipText);
m_stopAction->setEnabled(true); m_stopAction->setEnabled(true);
m_loadExported->setEnabled(false); m_loadExported->setEnabled(false);
m_clear->setEnabled(false); m_clear->setEnabled(false);
} else { } else {
QString toolTip = tr("Start Clang-Tidy and Clazy."); QString toolTipStart = m_startAction->text();
QString toolTipStartOnCurrentFile = m_startOnCurrentFileAction->text();
Project *project = SessionManager::startupProject(); Project *project = SessionManager::startupProject();
Target *target = project ? project->activeTarget() : nullptr; Target *target = project ? project->activeTarget() : nullptr;
const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID; const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
bool canRun = target && project->projectLanguages().contains(cxx) bool canRun = target && project->projectLanguages().contains(cxx)
&& ToolChainKitAspect::toolChain(target->kit(), cxx); && ToolChainKitAspect::toolChain(target->kit(), cxx);
if (!canRun) if (!canRun)
toolTip = tr("This is not a C++ project."); toolTipStart = toolTipStartOnCurrentFile = tr("This is not a C/C++ project.");
m_startAction->setToolTip(toolTip);
m_startAction->setEnabled(canRun); m_startAction->setEnabled(canRun);
m_startAction->setToolTip(toolTipStart);
m_startOnCurrentFileAction->setEnabled(canRun);
m_startOnCurrentFileAction->setToolTip(toolTipStartOnCurrentFile);
m_stopAction->setEnabled(false); m_stopAction->setEnabled(false);
m_loadExported->setEnabled(true); m_loadExported->setEnabled(true);
m_clear->setEnabled(m_diagnosticModel->diagnostics().count()); m_clear->setEnabled(m_diagnosticModel->diagnostics().count());

View File

@@ -51,7 +51,7 @@ public:
static ClangTidyClazyTool *instance(); static ClangTidyClazyTool *instance();
void startTool(bool askUserForFileSelection) final; void startTool(FileSelection fileSelection) final;
Diagnostics read(OutputFileFormat outputFileFormat, Diagnostics read(OutputFileFormat outputFileFormat,
const QString &logFilePath, const QString &logFilePath,

View File

@@ -34,6 +34,7 @@
#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h> #include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <cpptools/cppmodelmanager.h> #include <cpptools/cppmodelmanager.h>
@@ -97,7 +98,27 @@ ClangTool::ClangTool(const QString &name)
{ {
m_diagnosticModel = new ClangToolsDiagnosticModel(this); m_diagnosticModel = new ClangToolsDiagnosticModel(this);
m_startAction = Debugger::createStartAction(); const Utils::Icon RUN_FILE_OVERLAY(
{{":/utils/images/run_file.png", Utils::Theme::IconsBaseColor}});
const Utils::Icon RUN_SELECTED_OVERLAY(
{{":/utils/images/runselected_boxes.png", Utils::Theme::BackgroundColorDark},
{":/utils/images/runselected_tickmarks.png", Utils::Theme::IconsBaseColor}});
auto action = new QAction(tr("Analyze Project..."), this);
Utils::Icon runSelectedIcon = Utils::Icons::RUN_SMALL_TOOLBAR;
for (const Utils::IconMaskAndColor &maskAndColor : RUN_SELECTED_OVERLAY)
runSelectedIcon.append(maskAndColor);
action->setIcon(runSelectedIcon.icon());
m_startAction = action;
action = new QAction(tr("Analyze Current File"), this);
Utils::Icon runFileIcon = Utils::Icons::RUN_SMALL_TOOLBAR;
for (const Utils::IconMaskAndColor &maskAndColor : RUN_FILE_OVERLAY)
runFileIcon.append(maskAndColor);
action->setIcon(runFileIcon.icon());
m_startOnCurrentFileAction = action;
m_stopAction = Debugger::createStopAction(); m_stopAction = Debugger::createStopAction();
} }
@@ -106,21 +127,38 @@ ClangTool::~ClangTool()
delete m_diagnosticView; delete m_diagnosticView;
} }
FileInfos ClangTool::collectFileInfos(Project *project, bool askUserForFileSelection) const FileInfos ClangTool::collectFileInfos(Project *project, FileSelection fileSelection) const
{ {
auto projectInfo = CppTools::CppModelManager::instance()->projectInfo(project); auto projectInfo = CppTools::CppModelManager::instance()->projectInfo(project);
QTC_ASSERT(projectInfo.isValid(), return FileInfos()); QTC_ASSERT(projectInfo.isValid(), return FileInfos());
const FileInfos allFileInfos = sortedFileInfos(projectInfo.projectParts()); const FileInfos allFileInfos = sortedFileInfos(projectInfo.projectParts());
if (askUserForFileSelection) { if (fileSelection == FileSelection::AllFiles)
return allFileInfos;
if (fileSelection == FileSelection::AskUser) {
SelectableFilesDialog dialog(projectInfo, allFileInfos); SelectableFilesDialog dialog(projectInfo, allFileInfos);
if (dialog.exec() == QDialog::Rejected) if (dialog.exec() == QDialog::Rejected)
return FileInfos(); return FileInfos();
return dialog.filteredFileInfos(); return dialog.filteredFileInfos();
} else {
return allFileInfos;
} }
if (fileSelection == FileSelection::CurrentFile) {
if (const IDocument *document = EditorManager::currentDocument()) {
const Utils::FilePath filePath = document->filePath();
if (!filePath.isEmpty()) {
const FileInfo fileInfo = Utils::findOrDefault(allFileInfos,
[&](const FileInfo &fi) {
return fi.file == filePath;
});
if (!fileInfo.file.isEmpty())
return {fileInfo};
}
}
}
return {};
} }
const QString &ClangTool::name() const const QString &ClangTool::name() const

View File

@@ -49,7 +49,12 @@ public:
ClangTool(const QString &name); ClangTool(const QString &name);
~ClangTool() override; ~ClangTool() override;
virtual void startTool(bool askUserForFileSelection) = 0; enum class FileSelection {
AllFiles,
CurrentFile,
AskUser,
};
virtual void startTool(FileSelection fileSelection) = 0;
virtual Diagnostics read(OutputFileFormat outputFileFormat, virtual Diagnostics read(OutputFileFormat outputFileFormat,
const QString &logFilePath, const QString &logFilePath,
@@ -58,7 +63,7 @@ public:
QString *errorMessage) const = 0; QString *errorMessage) const = 0;
FileInfos collectFileInfos(ProjectExplorer::Project *project, FileInfos collectFileInfos(ProjectExplorer::Project *project,
bool askUserForFileSelection) const; FileSelection fileSelection) const;
// For testing. // For testing.
QSet<Diagnostic> diagnostics() const; QSet<Diagnostic> diagnostics() const;
@@ -67,6 +72,9 @@ public:
virtual void onNewDiagnosticsAvailable(const Diagnostics &diagnostics); virtual void onNewDiagnosticsAvailable(const Diagnostics &diagnostics);
QAction *startAction() const { return m_startAction; }
QAction *startOnCurrentFileAction() const { return m_startOnCurrentFileAction; }
signals: signals:
void finished(bool success); // For testing. void finished(bool success); // For testing.
@@ -80,6 +88,7 @@ protected:
QPointer<Debugger::DetailedErrorView> m_diagnosticView; QPointer<Debugger::DetailedErrorView> m_diagnosticView;
QAction *m_startAction = nullptr; QAction *m_startAction = nullptr;
QAction *m_startOnCurrentFileAction = nullptr;
QAction *m_stopAction = nullptr; QAction *m_stopAction = nullptr;
bool m_running = false; bool m_running = false;
bool m_toolBusy = false; bool m_toolBusy = false;

View File

@@ -229,12 +229,16 @@ static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits)
} }
ClangToolRunWorker::ClangToolRunWorker(RunControl *runControl, ClangToolRunWorker::ClangToolRunWorker(RunControl *runControl,
const FileInfos &fileInfos) const FileInfos &fileInfos,
bool preventBuild)
: RunWorker(runControl) : RunWorker(runControl)
, m_projectBuilder(new ProjectBuilder(runControl, this))
, m_temporaryDir("clangtools-XXXXXX") , m_temporaryDir("clangtools-XXXXXX")
, m_fileInfos(fileInfos) , m_fileInfos(fileInfos)
{ {
if (preventBuild)
return;
m_projectBuilder = new ProjectBuilder(runControl, this);
addStartDependency(m_projectBuilder); addStartDependency(m_projectBuilder);
ClangToolsProjectSettings *projectSettings = ClangToolsProjectSettingsManager::getSettings( ClangToolsProjectSettings *projectSettings = ClangToolsProjectSettingsManager::getSettings(
@@ -267,8 +271,7 @@ void ClangToolRunWorker::start()
TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID); TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID);
if (ClangToolsSettings::instance()->savedBuildBeforeAnalysis()) { if (ClangToolsSettings::instance()->savedBuildBeforeAnalysis()) {
QTC_ASSERT(m_projectBuilder, return;); if (m_projectBuilder && !m_projectBuilder->success()) {
if (!m_projectBuilder->success()) {
reportFailure(); reportFailure();
return; return;
} }

View File

@@ -66,7 +66,8 @@ class ClangToolRunWorker : public ProjectExplorer::RunWorker
public: public:
ClangToolRunWorker(ProjectExplorer::RunControl *runControl, ClangToolRunWorker(ProjectExplorer::RunControl *runControl,
const FileInfos &fileInfos); const FileInfos &fileInfos,
bool preventBuild);
bool success() const { return m_success; } // For testing. bool success() const { return m_success; } // For testing.
@@ -95,7 +96,7 @@ private:
void finalize(); void finalize();
protected: protected:
ProjectBuilder *m_projectBuilder; ProjectBuilder *m_projectBuilder = nullptr;
Utils::Environment m_environment; Utils::Environment m_environment;
Utils::TemporaryDirectory m_temporaryDir; Utils::TemporaryDirectory m_temporaryDir;

View File

@@ -28,6 +28,9 @@
namespace ClangTools { namespace ClangTools {
namespace Constants { namespace Constants {
const char RUN_ON_PROJECT[] = "ClangTools.RunOnProject";
const char RUN_ON_CURRENT_FILE[] = "ClangTools.RunOnCurrentFile";
const char SETTINGS_PAGE_ID[] = "Analyzer.ClangTools.Settings"; const char SETTINGS_PAGE_ID[] = "Analyzer.ClangTools.Settings";
const char SETTINGS_ID[] = "ClangTools"; const char SETTINGS_ID[] = "ClangTools";
const char CLANGTIDYCLAZY_RUN_MODE[] = "ClangTidyClazy.RunMode"; const char CLANGTIDYCLAZY_RUN_MODE[] = "ClangTidyClazy.RunMode";

View File

@@ -61,12 +61,13 @@
#include <QMessageBox> #include <QMessageBox>
#include <QMenu> #include <QMenu>
using namespace Core;
using namespace ProjectExplorer; using namespace ProjectExplorer;
namespace ClangTools { namespace ClangTools {
namespace Internal { namespace Internal {
class ClangToolsOptionsPage : public Core::IOptionsPage class ClangToolsOptionsPage : public IOptionsPage
{ {
public: public:
ClangToolsOptionsPage() ClangToolsOptionsPage()
@@ -121,6 +122,11 @@ bool ClangToolsPlugin::initialize(const QStringList &arguments, QString *errorSt
d = new ClangToolsPluginPrivate; d = new ClangToolsPluginPrivate;
ActionManager::registerAction(d->clangTidyClazyTool.startAction(),
Constants::RUN_ON_PROJECT);
ActionManager::registerAction(d->clangTidyClazyTool.startOnCurrentFileAction(),
Constants::RUN_ON_CURRENT_FILE);
auto panelFactory = new ProjectPanelFactory(); auto panelFactory = new ProjectPanelFactory();
panelFactory->setPriority(100); panelFactory->setPriority(100);
panelFactory->setDisplayName(tr("Clang Tools")); panelFactory->setDisplayName(tr("Clang Tools"));

View File

@@ -121,7 +121,7 @@ void PreconfiguredSessionTests::testPreconfiguredSession()
QVERIFY(switchToProjectAndTarget(project, target)); QVERIFY(switchToProjectAndTarget(project, target));
ClangTidyClazyTool::instance()->startTool(false); ClangTidyClazyTool::instance()->startTool(ClangTidyClazyTool::FileSelection::AllFiles);
QSignalSpy waitUntilAnalyzerFinished(ClangTidyClazyTool::instance(), SIGNAL(finished(bool))); QSignalSpy waitUntilAnalyzerFinished(ClangTidyClazyTool::instance(), SIGNAL(finished(bool)));
QVERIFY(waitUntilAnalyzerFinished.wait(30000)); QVERIFY(waitUntilAnalyzerFinished.wait(30000));
const QList<QVariant> arguments = waitUntilAnalyzerFinished.takeFirst(); const QList<QVariant> arguments = waitUntilAnalyzerFinished.takeFirst();

View File

@@ -131,7 +131,7 @@ void ClangToolsUnitTests::testProject()
clangToolsSettings->setDiagnosticConfigId(diagnosticConfig.id()); clangToolsSettings->setDiagnosticConfigId(diagnosticConfig.id());
clangToolsSettings->writeSettings(); clangToolsSettings->writeSettings();
tool->startTool(false); tool->startTool(ClangTidyClazyTool::FileSelection::AllFiles);
QSignalSpy waiter(tool, SIGNAL(finished(bool))); QSignalSpy waiter(tool, SIGNAL(finished(bool)));
QVERIFY(waiter.wait(30000)); QVERIFY(waiter.wait(30000));