aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorNikolai Kosjar <[email protected]>2019-10-28 16:25:07 +0100
committerNikolai Kosjar <[email protected]>2019-12-03 13:24:24 +0000
commitfbd350f31f35c56e7eccb833c2943870f93e0c89 (patch)
treeb61afeaf00dec4f13eb96f7ce44fd0e0e8f2a3e9 /src/plugins
parent7bb333f15a3a5633d3373416aad4dd900de62aee (diff)
ClangTools: Introduce an info bar
...displaying status information and errors. Change-Id: I4f86b440b28e82786299700dee572e77de7334f3 Reviewed-by: Cristian Adam <[email protected]>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/clangtools/clangtool.cpp594
-rw-r--r--src/plugins/clangtools/clangtool.h38
-rw-r--r--src/plugins/clangtools/clangtoolruncontrol.cpp112
-rw-r--r--src/plugins/clangtools/clangtoolruncontrol.h11
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp63
-rw-r--r--src/plugins/clangtools/clangtoolsdiagnosticmodel.h19
-rw-r--r--src/plugins/clangtools/clangtoolsutils.cpp2
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp12
-rw-r--r--src/plugins/projectexplorer/projectexplorer.h1
9 files changed, 620 insertions, 232 deletions
diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp
index bad06028518..decad6ca1de 100644
--- a/src/plugins/clangtools/clangtool.cpp
+++ b/src/plugins/clangtools/clangtool.cpp
@@ -59,13 +59,17 @@
#include <texteditor/textdocument.h>
#include <utils/algorithm.h>
+#include <utils/checkablemessagebox.h>
#include <utils/fancylineedit.h>
#include <utils/fancymainwindow.h>
+#include <utils/progressindicator.h>
+#include <utils/theme/theme.h>
#include <utils/utilsicons.h>
#include <QAction>
#include <QCheckBox>
#include <QFileDialog>
+#include <QHBoxLayout>
#include <QLabel>
#include <QSortFilterProxyModel>
#include <QToolButton>
@@ -81,6 +85,145 @@ namespace Internal {
static ClangTool *s_instance;
+static QString makeLink(const QString &text)
+{
+ return QString("<a href=t>%1</a>").arg(text);
+}
+
+class IconAndLabel : public QWidget
+{
+ Q_OBJECT
+
+public:
+ IconAndLabel(const QPixmap &pixmap, const QString &text = {})
+ : m_icon(new QLabel)
+ , m_label(new QLabel)
+ {
+ QSizePolicy minSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ minSizePolicy.setHorizontalStretch(0);
+
+ m_icon->setPixmap(pixmap);
+ m_icon->setSizePolicy(minSizePolicy);
+
+ m_label->setSizePolicy(minSizePolicy);
+ m_label->setText(text);
+ m_label->setTextInteractionFlags(Qt::TextBrowserInteraction);
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(m_icon);
+ layout->addWidget(m_label);
+ setLayout(layout);
+
+ connect(m_label, &QLabel::linkActivated, this, &IconAndLabel::linkActivated);
+ }
+
+ void setIcon(const QPixmap &pixmap) {
+ m_icon->setPixmap(pixmap);
+ m_icon->setVisible(!pixmap.isNull());
+ }
+
+ QString text() const { return m_label->text(); }
+ void setText(const QString &text)
+ {
+ m_label->setText(text);
+ setVisible(!text.isEmpty());
+ }
+
+signals:
+ void linkActivated(const QString &link);
+
+private:
+ QLabel *m_icon;
+ QLabel *m_label;
+};
+
+class InfoBarWidget : public QFrame
+{
+ Q_OBJECT
+
+public:
+ InfoBarWidget()
+ : m_progressIndicator(new Utils::ProgressIndicator(ProgressIndicatorSize::Small))
+ , m_info(new IconAndLabel(Utils::Icons::INFO.pixmap()))
+ , m_error(new IconAndLabel(Utils::Icons::WARNING.pixmap()))
+ , m_diagStats(new QLabel)
+ {
+ m_diagStats->setTextInteractionFlags(Qt::TextBrowserInteraction);
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->setContentsMargins(5, 5, 5, 5);
+ layout->addWidget(m_progressIndicator);
+ layout->addWidget(m_info);
+ layout->addWidget(m_error);
+ layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
+ layout->addWidget(m_diagStats);
+ setLayout(layout);
+
+ QPalette pal;
+ pal.setColor(QPalette::Window, Utils::creatorTheme()->color(Utils::Theme::InfoBarBackground));
+ pal.setColor(QPalette::WindowText, Utils::creatorTheme()->color(Utils::Theme::InfoBarText));
+ setPalette(pal);
+
+ setAutoFillBackground(true);
+ }
+
+ // Info
+ enum InfoIconType { ProgressIcon, InfoIcon };
+ void setInfoIcon(InfoIconType type)
+ {
+ const bool showProgress = type == ProgressIcon;
+ m_progressIndicator->setVisible(showProgress);
+ m_info->setIcon(showProgress ? QPixmap() : Utils::Icons::INFO.pixmap());
+ }
+ QString infoText() const { return m_info->text(); }
+ void setInfoText(const QString &text)
+ {
+ m_info->setText(text);
+ evaluateVisibility();
+ }
+
+ // Error
+ using OnLinkActivated = std::function<void()>;
+ enum IssueType { Warning, Error };
+
+ QString errorText() const { return m_error->text(); }
+ void setError(IssueType type,
+ const QString &text,
+ const OnLinkActivated &linkAction = OnLinkActivated())
+ {
+ m_error->setText(text);
+ m_error->setIcon(type == Warning ? Utils::Icons::WARNING.pixmap()
+ : Utils::Icons::CRITICAL.pixmap());
+ m_error->disconnect();
+ if (linkAction)
+ connect(m_error, &IconAndLabel::linkActivated, this, linkAction);
+ evaluateVisibility();
+ }
+
+ // Diag stats
+ void setDiagText(const QString &text) { m_diagStats->setText(text); }
+
+ void reset()
+ {
+ setInfoIcon(InfoIcon);
+ setInfoText({});
+ setError(Warning, {}, {});
+ setDiagText({});
+ }
+
+ void evaluateVisibility()
+ {
+ setVisible(!infoText().isEmpty() || !errorText().isEmpty());
+ }
+
+private:
+ Utils::ProgressIndicator *m_progressIndicator;
+ IconAndLabel *m_info;
+ IconAndLabel *m_error;
+ QLabel *m_diagStats;
+};
+
class SelectFixitsCheckBox : public QCheckBox
{
Q_OBJECT
@@ -257,11 +400,12 @@ static FileInfos sortedFileInfos(const QVector<CppTools::ProjectPart::Ptr> &proj
static RunSettings runSettings()
{
- Project *project = SessionManager::startupProject();
- auto *projectSettings = ClangToolsProjectSettingsManager::getSettings(project);
- if (projectSettings->useGlobalSettings())
- return ClangToolsSettings::instance()->runSettings();
- return projectSettings->runSettings();
+ if (Project *project = SessionManager::startupProject()) {
+ auto *projectSettings = ClangToolsProjectSettingsManager::getSettings(project);
+ if (!projectSettings->useGlobalSettings())
+ return projectSettings->runSettings();
+ }
+ return ClangToolsSettings::instance()->runSettings();
}
static ClangDiagnosticConfig diagnosticConfig(const Core::Id &diagConfigId)
@@ -310,25 +454,25 @@ ClangTool::ClangTool()
m_diagnosticFilterModel->setSourceModel(m_diagnosticModel);
m_diagnosticFilterModel->setDynamicSortFilter(true);
+ m_infoBarWidget = new InfoBarWidget;
+
m_diagnosticView = new DiagnosticView;
initDiagnosticView();
m_diagnosticView->setModel(m_diagnosticFilterModel);
m_diagnosticView->setSortingEnabled(true);
m_diagnosticView->sortByColumn(Debugger::DetailedErrorView::DiagnosticColumn,
Qt::AscendingOrder);
- m_diagnosticView->setObjectName(QLatin1String("ClangTidyClazyIssuesView"));
- m_diagnosticView->setWindowTitle(tr("Clang-Tidy and Clazy Diagnostics"));
foreach (auto * const model,
QList<QAbstractItemModel *>({m_diagnosticModel, m_diagnosticFilterModel})) {
connect(model, &QAbstractItemModel::rowsInserted,
- this, &ClangTool::handleStateUpdate);
+ this, &ClangTool::updateForCurrentState);
connect(model, &QAbstractItemModel::rowsRemoved,
- this, &ClangTool::handleStateUpdate);
+ this, &ClangTool::updateForCurrentState);
connect(model, &QAbstractItemModel::modelReset,
- this, &ClangTool::handleStateUpdate);
+ this, &ClangTool::updateForCurrentState);
connect(model, &QAbstractItemModel::layoutChanged, // For QSortFilterProxyModel::invalidate()
- this, &ClangTool::handleStateUpdate);
+ this, &ClangTool::updateForCurrentState);
}
// Go to previous diagnostic
@@ -359,10 +503,9 @@ ClangTool::ClangTool()
action->setDisabled(true);
action->setIcon(Utils::Icons::CLEAN_TOOLBAR.icon());
action->setToolTip(tr("Clear"));
- connect(action, &QAction::triggered, [this](){
- m_clear->setEnabled(false);
- m_diagnosticModel->clear();
- Debugger::showPermanentStatusMessage(QString());
+ connect(action, &QAction::triggered, this, [this]() {
+ reset();
+ update();
});
m_clear = action;
@@ -410,18 +553,20 @@ ClangTool::ClangTool()
connect(m_diagnosticModel, &ClangToolsDiagnosticModel::fixitStatusChanged,
m_diagnosticFilterModel, &DiagnosticFilterModel::onFixitStatusChanged);
- connect(m_diagnosticFilterModel, &DiagnosticFilterModel::fixitStatisticsChanged,
+ connect(m_diagnosticFilterModel, &DiagnosticFilterModel::fixitCountersChanged,
this,
- [this](int scheduled, int scheduableTotal){
- m_selectFixitsCheckBox->setEnabled(scheduableTotal > 0);
+ [this](int scheduled, int scheduable){
+ m_selectFixitsCheckBox->setEnabled(scheduable > 0);
m_applyFixitsButton->setEnabled(scheduled > 0);
if (scheduled == 0)
m_selectFixitsCheckBox->setCheckState(Qt::Unchecked);
- else if (scheduled == scheduableTotal)
+ else if (scheduled == scheduable)
m_selectFixitsCheckBox->setCheckState(Qt::Checked);
else
m_selectFixitsCheckBox->setCheckState(Qt::PartiallyChecked);
+
+ updateForCurrentState();
});
connect(m_applyFixitsButton, &QToolButton::clicked, [this]() {
QVector<DiagnosticItem *> diagnosticItems;
@@ -445,7 +590,17 @@ ClangTool::ClangTool()
const QString toolTip = tr("Clang-Tidy and Clazy use a customized Clang executable from the "
"Clang project to search for diagnostics.");
- m_perspective.addWindow(m_diagnosticView, Perspective::SplitVertical, nullptr);
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->setContentsMargins(0, 0, 0, 0);
+ mainLayout->setSpacing(1);
+ mainLayout->addWidget(m_infoBarWidget);
+ mainLayout->addWidget(m_diagnosticView);
+ auto mainWidget = new QWidget;
+ mainWidget->setObjectName("ClangTidyClazyIssuesView");
+ mainWidget->setWindowTitle(tr("Clang-Tidy and Clazy"));
+ mainWidget->setLayout(mainLayout);
+
+ m_perspective.addWindow(mainWidget, Perspective::SplitVertical, nullptr);
action = new QAction(tr("Clang-Tidy and Clazy..."), this);
action->setToolTip(toolTip);
@@ -479,10 +634,14 @@ ClangTool::ClangTool()
m_perspective.addToolBarWidget(m_selectFixitsCheckBox);
m_perspective.addToolBarWidget(m_applyFixitsButton);
- updateRunActions();
+ update();
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions,
- this, &ClangTool::updateRunActions);
+ this, &ClangTool::update);
+ connect(CppModelManager::instance(), &CppModelManager::projectPartsUpdated,
+ this, &ClangTool::update);
+ connect(ClangToolsSettings::instance(), &ClangToolsSettings::changed,
+ this, &ClangTool::update);
}
ClangTool::~ClangTool()
@@ -501,6 +660,33 @@ void ClangTool::startTool(ClangTool::FileSelection fileSelection)
startTool(fileSelection, theRunSettings, diagnosticConfig(theRunSettings.diagnosticConfigId()));
}
+static bool continueDespiteReleaseBuild(const QString &toolName)
+{
+ const QString wrongMode = ClangTool::tr("Release");
+ const QString title = ClangTool::tr("Run %1 in %2 Mode?").arg(toolName, wrongMode);
+ const QString problem
+ = ClangTool::tr(
+ "You are trying to run the tool \"%1\" on an application in %2 mode. The tool is "
+ "designed to be used in Debug mode since enabled assertions can reduce the number of "
+ "false positives.")
+ .arg(toolName, wrongMode);
+ const QString question = ClangTool::tr(
+ "Do you want to continue and run the tool in %1 mode?")
+ .arg(wrongMode);
+ const QString message = QString("<html><head/><body>"
+ "<p>%1</p>"
+ "<p>%2</p>"
+ "</body></html>")
+ .arg(problem, question);
+ return CheckableMessageBox::doNotAskAgainQuestion(ICore::mainWindow(),
+ title,
+ message,
+ ICore::settings(),
+ "ClangToolsCorrectModeWarning")
+ == QDialogButtonBox::Yes;
+}
+
+
void ClangTool::startTool(ClangTool::FileSelection fileSelection,
const RunSettings &runSettings,
const CppTools::ClangDiagnosticConfig &diagnosticConfig)
@@ -509,51 +695,58 @@ void ClangTool::startTool(ClangTool::FileSelection fileSelection,
QTC_ASSERT(project, return);
QTC_ASSERT(project->activeTarget(), return);
- auto runControl = new RunControl(Constants::CLANGTIDYCLAZY_RUN_MODE);
- runControl->setDisplayName(tr("Clang-Tidy and Clazy"));
- runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
- runControl->setTarget(project->activeTarget());
+ // Continue despite release mode?
+ if (BuildConfiguration *bc = project->activeTarget()->activeBuildConfiguration()) {
+ if (bc->buildType() == BuildConfiguration::Release)
+ if (!continueDespiteReleaseBuild(m_name))
+ return;
+ }
+ // Collect files to analyze
const FileInfos fileInfos = collectFileInfos(project, fileSelection);
if (fileInfos.empty())
return;
- const bool preventBuild = fileSelection == FileSelection::CurrentFile;
- auto clangTool = new ClangToolRunWorker(runControl,
- runSettings,
- diagnosticConfig,
- fileInfos,
- preventBuild);
+ // Reset
+ reset();
+ // Run control
+ m_runControl = new RunControl(Constants::CLANGTIDYCLAZY_RUN_MODE);
+ m_runControl->setDisplayName(tr("Clang-Tidy and Clazy"));
+ m_runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR);
+ m_runControl->setTarget(project->activeTarget());
m_stopAction->disconnect();
- connect(m_stopAction, &QAction::triggered, runControl, [runControl] {
- runControl->appendMessage(tr("Clang-Tidy and Clazy tool stopped by user."),
- NormalMessageFormat);
- runControl->initiateStop();
- });
-
- connect(runControl, &RunControl::stopped, this, [this, clangTool] {
- bool success = clangTool->success();
- setToolBusy(false);
- m_running = false;
- handleStateUpdate();
- updateRunActions();
- emit finished(success);
+ connect(m_stopAction, &QAction::triggered, m_runControl, [this] {
+ m_runControl->appendMessage(tr("Clang-Tidy and Clazy tool stopped by user."),
+ NormalMessageFormat);
+ m_runControl->initiateStop();
+ setState(State::StoppedByUser);
});
+ connect(m_runControl, &RunControl::stopped, this, &ClangTool::onRunControlStopped);
- m_perspective.select();
- m_diagnosticModel->clear();
-
+ // Run worker
+ const bool preventBuild = fileSelection == FileSelection::CurrentFile;
+ const bool buildBeforeAnalysis = !preventBuild && runSettings.buildBeforeAnalysis();
+ m_runWorker = new ClangToolRunWorker(m_runControl,
+ runSettings,
+ diagnosticConfig,
+ fileInfos,
+ buildBeforeAnalysis);
+ connect(m_runWorker, &ClangToolRunWorker::buildFailed,this, &ClangTool::onBuildFailed);
+ connect(m_runWorker, &ClangToolRunWorker::startFailed, this, &ClangTool::onStartFailed);
+ connect(m_runWorker, &ClangToolRunWorker::started, this, &ClangTool::onStarted);
+ connect(m_runWorker, &ClangToolRunWorker::runnerFinished,
+ this, &ClangTool::updateForCurrentState);
+
+ // More init and UI update
m_diagnosticFilterModel->setProject(project);
- m_selectFixitsCheckBox->setEnabled(false);
- m_applyFixitsButton->setEnabled(false);
- m_running = true;
-
- setToolBusy(true);
- handleStateUpdate();
- updateRunActions();
+ m_perspective.select();
+ if (buildBeforeAnalysis)
+ m_infoBarWidget->setInfoText("Waiting for build to finish...");
+ setState(State::PreparationStarted);
- ProjectExplorerPlugin::startRunControl(runControl);
+ // Start
+ ProjectExplorerPlugin::startRunControl(m_runControl);
}
Diagnostics ClangTool::read(OutputFileFormat outputFileFormat,
@@ -656,12 +849,145 @@ void ClangTool::loadDiagnosticsFromFiles()
}
// Show errors
- if (!errors.isEmpty())
+ if (!errors.isEmpty()) {
AsynchronousMessageBox::critical(tr("Error Loading Diagnostics"), errors);
+ return;
+ }
// Show imported
- m_diagnosticModel->clear();
+ reset();
onNewDiagnosticsAvailable(diagnostics);
+ setState(State::ImportFinished);
+}
+
+void ClangTool::showOutputPane()
+{
+ ProjectExplorerPlugin::showOutputPaneForRunControl(m_runControl);
+}
+
+void ClangTool::reset()
+{
+ m_clear->setEnabled(false);
+ m_selectFixitsCheckBox->setEnabled(false);
+ m_applyFixitsButton->setEnabled(false);
+
+ m_diagnosticModel->clear();
+ m_diagnosticFilterModel->resetCounters();
+
+ m_infoBarWidget->reset();
+
+ m_state = State::Initial;
+ m_runControl = nullptr;
+ m_runWorker = nullptr;
+}
+
+static bool canAnalyzeProject(Project *project)
+{
+ if (const Target *target = project->activeTarget()) {
+ const Core::Id c = ProjectExplorer::Constants::C_LANGUAGE_ID;
+ const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
+ const bool projectSupportsLanguage = project->projectLanguages().contains(c)
+ || project->projectLanguages().contains(cxx);
+ return projectSupportsLanguage
+ && CppModelManager::instance()->projectInfo(project).isValid()
+ && ToolChainKitAspect::toolChain(target->kit(), cxx);
+ }
+ return false;
+}
+
+struct CheckResult {
+ enum {
+ InvalidTidyExecutable,
+ InvalidClazyExecutable,
+ ProjectNotOpen,
+ ProjectNotSuitable,
+ ReadyToAnalyze,
+ } kind;
+ QString errorText;
+};
+
+static CheckResult canAnalyze()
+{
+ const ClangDiagnosticConfig config = diagnosticConfig(runSettings().diagnosticConfigId());
+
+ if (config.isClangTidyEnabled() && !isFileExecutable(clangTidyExecutable())) {
+ return {CheckResult::InvalidTidyExecutable,
+ ClangTool::tr("Set a valid Clang-Tidy executable.")};
+ }
+
+ if (config.isClazyEnabled() && !isFileExecutable(clazyStandaloneExecutable())) {
+ return {CheckResult::InvalidClazyExecutable,
+ ClangTool::tr("Set a valid Clazy-Standalone executable.")};
+ }
+
+ if (Project *project = SessionManager::startupProject()) {
+ if (!canAnalyzeProject(project)) {
+ return {CheckResult::ProjectNotSuitable,
+ ClangTool::tr("Project \"%1\" is not a C/C++ project.")
+ .arg(project->displayName())};
+ }
+ } else {
+ return {CheckResult::ProjectNotOpen,
+ ClangTool::tr("Open a C/C++ project to start analyzing.")};
+ }
+
+ return {CheckResult::ReadyToAnalyze, {}};
+}
+
+void ClangTool::updateForInitialState()
+{
+ if (m_state != State::Initial)
+ return;
+
+ m_infoBarWidget->reset();
+
+ const CheckResult result = canAnalyze();
+ switch (result.kind)
+ case CheckResult::InvalidTidyExecutable: {
+ case CheckResult::InvalidClazyExecutable:
+ m_infoBarWidget->setError(InfoBarWidget::Warning,
+ makeLink(result.errorText),
+ [](){ ICore::showOptionsDialog(Constants::SETTINGS_PAGE_ID); });
+ break;
+ case CheckResult::ProjectNotSuitable:
+ case CheckResult::ProjectNotOpen:
+ case CheckResult::ReadyToAnalyze:
+ break;
+ }
+}
+
+void ClangTool::onBuildFailed()
+{
+ m_infoBarWidget->setError(InfoBarWidget::Error,
+ tr("Failed to build the project."),
+ [this]() { showOutputPane(); });
+ setState(State::PreparationFailed);
+}
+
+void ClangTool::onStartFailed()
+{
+ m_infoBarWidget->setError(InfoBarWidget::Error,
+ makeLink(tr("Failed to start the analyzer.")),
+ [this]() { showOutputPane(); });
+ setState(State::PreparationFailed);
+}
+
+void ClangTool::onStarted()
+{
+ setState(State::AnalyzerRunning);
+}
+
+void ClangTool::onRunControlStopped()
+{
+ if (m_state != State::StoppedByUser && m_state != State::PreparationFailed)
+ setState(State::AnalyzerFinished);
+ emit finished(m_runWorker->success());
+}
+
+void ClangTool::update()
+{
+ updateForInitialState();
+ updateForCurrentState();
}
using DocumentPredicate = std::function<bool(Core::IDocument *)>;
@@ -726,6 +1052,12 @@ FileInfoProviders ClangTool::fileInfoProviders(ProjectExplorer::Project *project
};
}
+void ClangTool::setState(ClangTool::State state)
+{
+ m_state = state;
+ updateForCurrentState();
+}
+
QSet<Diagnostic> ClangTool::diagnostics() const
{
return Utils::filtered(m_diagnosticModel->diagnostics(), [](const Diagnostic &diagnostic) {
@@ -742,82 +1074,98 @@ void ClangTool::onNewDiagnosticsAvailable(const Diagnostics &diagnostics)
m_diagnosticFilterModel->invalidateFilter();
}
-void ClangTool::updateRunActions()
+void ClangTool::updateForCurrentState()
{
- if (m_toolBusy) {
- QString tooltipText = tr("Clang-Tidy and Clazy are still running.");
-
- m_startAction->setEnabled(false);
- m_startAction->setToolTip(tooltipText);
-
- m_startOnCurrentFileAction->setEnabled(false);
- m_startOnCurrentFileAction->setToolTip(tooltipText);
-
- m_stopAction->setEnabled(true);
- m_loadExported->setEnabled(false);
- m_clear->setEnabled(false);
- } else {
- QString toolTipStart = m_startAction->text();
- QString toolTipStartOnCurrentFile = m_startOnCurrentFileAction->text();
-
- Project *project = SessionManager::startupProject();
- Target *target = project ? project->activeTarget() : nullptr;
- const Core::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID;
- bool canRun = target && project->projectLanguages().contains(cxx)
- && ToolChainKitAspect::toolChain(target->kit(), cxx);
- if (!canRun)
- toolTipStart = toolTipStartOnCurrentFile = tr("This is not a C/C++ project.");
-
- m_startAction->setEnabled(canRun);
- m_startAction->setToolTip(toolTipStart);
-
- m_startOnCurrentFileAction->setEnabled(canRun);
- m_startOnCurrentFileAction->setToolTip(toolTipStartOnCurrentFile);
-
- m_stopAction->setEnabled(false);
- m_loadExported->setEnabled(true);
- m_clear->setEnabled(m_diagnosticModel->diagnostics().count());
+ // Actions
+ bool canStart = false;
+ const bool isPreparing = m_state == State::PreparationStarted;
+ const bool isRunning = m_state == State::AnalyzerRunning;
+ QString startActionToolTip = m_startAction->text();
+ QString startOnCurrentToolTip = m_startOnCurrentFileAction->text();
+ if (!isRunning) {
+ const CheckResult result = canAnalyze();
+ canStart = result.kind == CheckResult::ReadyToAnalyze;
+ if (!canStart) {
+ startActionToolTip = result.errorText;
+ startOnCurrentToolTip = result.errorText;
+ }
}
-}
-
-void ClangTool::handleStateUpdate()
-{
- QTC_ASSERT(m_goBack, return);
- QTC_ASSERT(m_goNext, return);
- QTC_ASSERT(m_diagnosticModel, return);
- QTC_ASSERT(m_diagnosticFilterModel, return);
+ m_startAction->setEnabled(canStart);
+ m_startAction->setToolTip(startActionToolTip);
+ m_startOnCurrentFileAction->setEnabled(canStart);
+ m_startOnCurrentFileAction->setToolTip(startOnCurrentToolTip);
+ m_stopAction->setEnabled(isPreparing || isRunning);
const int issuesFound = m_diagnosticModel->diagnostics().count();
- const int issuesVisible = m_diagnosticFilterModel->rowCount();
+ const int issuesVisible = m_diagnosticFilterModel->diagnostics();
m_goBack->setEnabled(issuesVisible > 1);
m_goNext->setEnabled(issuesVisible > 1);
- m_clear->setEnabled(issuesFound > 0);
+ m_clear->setEnabled(!isRunning);
m_expandCollapse->setEnabled(issuesVisible);
+ m_loadExported->setEnabled(!isRunning);
- m_loadExported->setEnabled(!m_running);
+ // Diagnostic view
+ m_diagnosticView->setCursor(isRunning ? Qt::BusyCursor : Qt::ArrowCursor);
- QString message;
- if (m_running) {
- if (issuesFound)
- message = tr("Running - %n diagnostics", nullptr, issuesFound);
- else
- message = tr("Running - No diagnostics");
- } else {
- if (issuesFound)
- message = tr("Finished - %n diagnostics", nullptr, issuesFound);
- else
- message = tr("Finished - No diagnostics");
+ // Info bar: errors
+ const bool hasErrorText = !m_infoBarWidget->errorText().isEmpty();
+ const bool hasErrors = m_runWorker && m_runWorker->filesNotAnalyzed() > 0;
+ if (hasErrors && !hasErrorText) {
+ const QString text = makeLink( tr("Failed to analyze %1 files.").arg(m_runWorker->filesNotAnalyzed()));
+ m_infoBarWidget->setError(InfoBarWidget::Warning, text, [this]() { showOutputPane(); });
}
- Debugger::showPermanentStatusMessage(message);
-}
-
-void ClangTool::setToolBusy(bool busy)
-{
- QTC_ASSERT(m_diagnosticView, return);
- QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
- m_diagnosticView->setCursor(cursor);
- m_toolBusy = busy;
+ // Info bar: info
+ bool showProgressIcon = false;
+ QString infoText;
+ switch (m_state) {
+ case State::Initial:
+ infoText = m_infoBarWidget->infoText();
+ break;
+ case State::AnalyzerRunning:
+ showProgressIcon = true;
+ if (m_runWorker->totalFilesToAnalyze() == 0) {
+ infoText = tr("Analyzing..."); // Not yet fully started/initialized
+ } else {
+ infoText = tr("Analyzing... %1 of %2 files processed.")
+ .arg(m_runWorker->filesAnalyzed() + m_runWorker->filesNotAnalyzed())
+ .arg(m_runWorker->totalFilesToAnalyze());
+ }
+ break;
+ case State::PreparationStarted:
+ showProgressIcon = true;
+ infoText = m_infoBarWidget->infoText();
+ break;
+ case State::PreparationFailed:
+ break; // OK, we just show an error.
+ case State::StoppedByUser:
+ infoText = tr("Analysis stopped by user.");
+ break;
+ case State::AnalyzerFinished:
+ infoText = tr("Finished processing %1 files.").arg(m_runWorker->totalFilesToAnalyze());
+ break;
+ case State::ImportFinished:
+ infoText = tr("Diagnostics imported.");
+ break;
+ }
+ m_infoBarWidget->setInfoText(infoText);
+ m_infoBarWidget->setInfoIcon(showProgressIcon ? InfoBarWidget::ProgressIcon
+ : InfoBarWidget::InfoIcon);
+
+ // Info bar: diagnostic stats
+ QString diagText;
+ if (issuesFound) {
+ diagText = tr("%1 diagnostics. %2 fixits, %4 selected.")
+ .arg(issuesVisible)
+ .arg(m_diagnosticFilterModel->fixitsScheduable())
+ .arg(m_diagnosticFilterModel->fixitsScheduled());
+ } else if (m_state != State::AnalyzerRunning
+ && m_state != State::Initial
+ && m_state != State::PreparationStarted
+ && m_state != State::PreparationFailed) {
+ diagText = tr("No diagnostics.");
+ }
+ m_infoBarWidget->setDiagText(diagText);
}
} // namespace Internal
diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h
index 7835bdcd678..2630c0d5101 100644
--- a/src/plugins/clangtools/clangtool.h
+++ b/src/plugins/clangtools/clangtool.h
@@ -35,6 +35,7 @@
#include <cpptools/projectinfo.h>
QT_BEGIN_NAMESPACE
+class QFrame;
class QToolButton;
QT_END_NAMESPACE
@@ -44,6 +45,9 @@ class ClangDiagnosticConfig;
namespace Debugger {
class DetailedErrorView;
}
+namespace ProjectExplorer {
+class RunControl;
+}
namespace Utils {
class FilePath;
class FancyLineEdit;
@@ -52,7 +56,9 @@ class FancyLineEdit;
namespace ClangTools {
namespace Internal {
+class InfoBarWidget;
class ClangToolsDiagnosticModel;
+class ClangToolRunWorker;
class Diagnostic;
class DiagnosticFilterModel;
class RunSettings;
@@ -105,25 +111,47 @@ signals:
void finished(bool success); // For testing.
private:
- void updateRunActions();
- void handleStateUpdate();
+ enum class State {
+ Initial,
+ PreparationStarted,
+ PreparationFailed,
+ AnalyzerRunning,
+ StoppedByUser,
+ AnalyzerFinished,
+ ImportFinished,
+ };
+ void setState(State state);
+ void update();
+ void updateForCurrentState();
+ void updateForInitialState();
- void setToolBusy(bool busy);
+ void onBuildFailed();
+ void onStartFailed();
+ void onStarted();
+ void onRunControlStopped();
void initDiagnosticView();
void loadDiagnosticsFromFiles();
+ void showOutputPane();
+
+ void reset();
+
FileInfoProviders fileInfoProviders(ProjectExplorer::Project *project,
const FileInfos &allFileInfos);
ClangToolsDiagnosticModel *m_diagnosticModel = nullptr;
+ ProjectExplorer::RunControl *m_runControl = nullptr;
+ ClangToolRunWorker *m_runWorker = nullptr;
+
+ InfoBarWidget *m_infoBarWidget = nullptr;
QPointer<Debugger::DetailedErrorView> m_diagnosticView;
QAction *m_startAction = nullptr;
QAction *m_startOnCurrentFileAction = nullptr;
QAction *m_stopAction = nullptr;
- bool m_running = false;
- bool m_toolBusy = false;
+
+ State m_state = State::Initial;
DiagnosticFilterModel *m_diagnosticFilterModel = nullptr;
diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp
index 82c2ba0b38c..1503961aab8 100644
--- a/src/plugins/clangtools/clangtoolruncontrol.cpp
+++ b/src/plugins/clangtools/clangtoolruncontrol.cpp
@@ -56,11 +56,9 @@
#include <projectexplorer/projectexplorericons.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
-#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h>
-#include <utils/checkablemessagebox.h>
#include <utils/hostosinfo.h>
#include <utils/qtcprocess.h>
@@ -137,29 +135,6 @@ private:
Target *target = runControl()->target();
QTC_ASSERT(target, reportFailure(); return);
- if (runControl()->buildType() == BuildConfiguration::Release) {
- const QString wrongMode = ClangToolRunWorker::tr("Release");
- const QString toolName = tool()->name();
- const QString title = ClangToolRunWorker::tr("Run %1 in %2 Mode?").arg(toolName, wrongMode);
- const QString problem = ClangToolRunWorker::tr(
- "You are trying to run the tool \"%1\" on an application in %2 mode. The tool is "
- "designed to be used in Debug mode since enabled assertions can reduce the number of "
- "false positives.").arg(toolName, wrongMode);
- const QString question = ClangToolRunWorker::tr(
- "Do you want to continue and run the tool in %1 mode?").arg(wrongMode);
- const QString message = QString("<html><head/><body>"
- "<p>%1</p>"
- "<p>%2</p>"
- "</body></html>").arg(problem, question);
- if (Utils::CheckableMessageBox::doNotAskAgainQuestion(Core::ICore::mainWindow(),
- title, message, Core::ICore::settings(),
- "ClangToolsCorrectModeWarning") != QDialogButtonBox::Yes)
- {
- reportFailure();
- return;
- }
- }
-
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
this, &ProjectBuilder::onBuildFinished, Qt::QueuedConnection);
@@ -225,7 +200,7 @@ ClangToolRunWorker::ClangToolRunWorker(RunControl *runControl,
const RunSettings &runSettings,
const CppTools::ClangDiagnosticConfig &diagnosticConfig,
const FileInfos &fileInfos,
- bool preventBuild)
+ bool buildBeforeAnalysis)
: RunWorker(runControl)
, m_runSettings(runSettings)
, m_diagnosticConfig(diagnosticConfig)
@@ -235,7 +210,7 @@ ClangToolRunWorker::ClangToolRunWorker(RunControl *runControl,
setId("ClangTidyClazyRunner");
setSupportsReRunning(false);
- if (!preventBuild && runSettings.buildBeforeAnalysis()) {
+ if (buildBeforeAnalysis) {
m_projectBuilder = new ProjectBuilder(runControl);
addStartDependency(m_projectBuilder);
}
@@ -273,11 +248,11 @@ QList<RunnerCreator> ClangToolRunWorker::runnerCreators()
void ClangToolRunWorker::start()
{
- TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID);
ProjectExplorerPlugin::saveModifiedFiles();
if (m_projectBuilder && !m_projectBuilder->success()) {
- reportFailure();
+ emit buildFailed();
+ reportFailure(tr("Failed to build the project."));
return;
}
@@ -286,13 +261,23 @@ void ClangToolRunWorker::start()
m_projectInfo = CppTools::CppModelManager::instance()->projectInfo(project);
m_projectFiles = Utils::toSet(project->files(Project::AllFiles));
- // Some projects provides CompilerCallData once a build is finished,
+ // Project changed in the mean time?
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));
+ "the %1. Please re-run with current configuration.")
+ .arg(toolName));
+ emit startFailed();
+ return;
+ }
+
+ // Create log dir
+ if (!m_temporaryDir.isValid()) {
+ reportFailure(
+ tr("Failed to create temporary directory: %1.").arg(m_temporaryDir.errorString()));
+ emit startFailed();
return;
}
@@ -303,17 +288,6 @@ void ClangToolRunWorker::start()
.arg(m_diagnosticConfig.displayName()),
Utils::NormalMessageFormat);
- // Create log dir
- if (!m_temporaryDir.isValid()) {
- const QString errorMessage
- = tr("%1: Failed to create temporary directory. Stopped.").arg(toolName);
- appendMessage(errorMessage, Utils::ErrorMessageFormat);
- TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
- TaskHub::requestPopup();
- reportFailure(errorMessage);
- return;
- }
-
// Collect files
const AnalyzeUnits unitsToProcess = unitsToAnalyze();
qCDebug(LOG) << "Files to process:" << unitsToProcess;
@@ -389,22 +363,14 @@ void ClangToolRunWorker::analyzeNextFile()
ClangToolRunner *runner = queueItem.runnerCreator();
m_runners.insert(runner);
- const QString executable = runner->executable();
- if (!isFileExecutable(executable)) {
- const QString errorMessage = tr("%1: Invalid executable \"%2\". Stopped.")
- .arg(runner->name(), executable);
- TaskHub::addTask(Task::Error, errorMessage, Debugger::Constants::ANALYZERTASK_ID);
- TaskHub::requestPopup();
- reportFailure(errorMessage);
+ if (runner->run(unit.file, unit.arguments)) {
+ const QString filePath = FilePath::fromString(unit.file).toUserOutput();
+ appendMessage(tr("Analyzing \"%1\" [%2].").arg(filePath, runner->name()),
+ Utils::StdOutFormat);
+ } else {
+ reportFailure(tr("Failed to start runner \"%1\".").arg(runner->name()));
stop();
- return;
}
-
- QTC_ASSERT(runner->run(unit.file, unit.arguments), return);
-
- appendMessage(tr("Analyzing \"%1\" [%2].")
- .arg(FilePath::fromString(unit.file).toUserOutput(), runner->name()),
- Utils::StdOutFormat);
}
void ClangToolRunWorker::onRunnerFinishedWithSuccess(const QString &filePath)
@@ -413,6 +379,8 @@ void ClangToolRunWorker::onRunnerFinishedWithSuccess(const QString &filePath)
const QString outputFilePath = runner->outputFilePath();
qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << outputFilePath;
+ emit runnerFinished();
+
QString errorMessage;
const Diagnostics diagnostics = tool()->read(runner->outputFileFormat(),
outputFilePath,
@@ -444,6 +412,8 @@ void ClangToolRunWorker::onRunnerFinishedWithFailure(const QString &errorMessage
qCDebug(LOG).noquote() << "onRunnerFinishedWithFailure:"
<< errorMessage << '\n' << errorDetails;
+ emit runnerFinished();
+
auto *toolRunner = qobject_cast<ClangToolRunner *>(sender());
const QString fileToAnalyze = toolRunner->fileToAnalyze();
const QString outputFilePath = toolRunner->outputFilePath();
@@ -458,7 +428,6 @@ void ClangToolRunWorker::onRunnerFinishedWithFailure(const QString &errorMessage
const QString message = tr("Failed to analyze \"%1\": %2").arg(fileToAnalyze, errorMessage);
appendMessage(message, Utils::StdErrFormat);
appendMessage(errorDetails, Utils::StdErrFormat);
- TaskHub::addTask(Task::Error, message, Debugger::Constants::ANALYZERTASK_ID);
handleFinished();
}
@@ -484,29 +453,26 @@ void ClangToolRunWorker::updateProgressValue()
void ClangToolRunWorker::finalize()
{
const QString toolName = tool()->name();
- appendMessage(tr("%1 finished: "
- "Processed %2 files successfully, %3 failed.")
- .arg(toolName)
- .arg(m_filesAnalyzed.size())
- .arg(m_filesNotAnalyzed.size()),
- Utils::NormalMessageFormat);
-
if (m_filesNotAnalyzed.size() != 0) {
- QString msg = tr("%1: Not all files could be analyzed.").arg(toolName);
- TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
+ appendMessage(tr("Error: Failed to analyze %1 files.").arg(m_filesAnalyzed.size()),
+ ErrorMessageFormat);
Target *target = runControl()->target();
if (target && target->activeBuildConfiguration() && !target->activeBuildConfiguration()->buildDirectory().exists()
&& !m_runSettings.buildBeforeAnalysis()) {
- msg = tr("%1: You might need to build the project to generate or update source "
- "files. To build automatically, enable \"Build the project before starting "
- "analysis\".")
- .arg(toolName);
- TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
+ appendMessage(
+ tr("Note: You might need to build the project to generate or update source "
+ "files. To build automatically, enable \"Build the project before analysis\"."),
+ NormalMessageFormat);
}
-
- TaskHub::requestPopup();
}
+ appendMessage(tr("%1 finished: "
+ "Processed %2 files successfully, %3 failed.")
+ .arg(toolName)
+ .arg(m_filesAnalyzed.size())
+ .arg(m_filesNotAnalyzed.size()),
+ Utils::NormalMessageFormat);
+
m_progress.reportFinished();
runControl()->initiateStop();
}
diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h
index d1c97c206c8..5a9fbbbdd54 100644
--- a/src/plugins/clangtools/clangtoolruncontrol.h
+++ b/src/plugins/clangtools/clangtoolruncontrol.h
@@ -70,10 +70,19 @@ public:
const RunSettings &runSettings,
const CppTools::ClangDiagnosticConfig &diagnosticConfig,
const FileInfos &fileInfos,
- bool preventBuild);
+ bool buildBeforeAnalysis);
bool success() const { return m_success; } // For testing.
+ int filesAnalyzed() const { return m_filesAnalyzed.size(); }
+ int filesNotAnalyzed() const { return m_filesNotAnalyzed.size(); }
+ int totalFilesToAnalyze() const { return m_fileInfos.size(); }
+
+signals:
+ void buildFailed();
+ void runnerFinished();
+ void startFailed();
+
protected:
void onRunnerFinishedWithSuccess(const QString &filePath);
void onRunnerFinishedWithFailure(const QString &errorMessage, const QString &errorDetails);
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
index f45a7abef4d..1e8780a1cc3 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
+++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp
@@ -553,19 +553,22 @@ DiagnosticFilterModel::DiagnosticFilterModel(QObject *parent)
setProject(project);
});
connect(this, &QAbstractItemModel::modelReset, this, [this]() {
- m_fixItsScheduled = 0;
- m_fixItsScheduableInTotal = 0;
- emit fixitStatisticsChanged(m_fixItsScheduled, m_fixItsScheduableInTotal);
+ resetCounters();
+ emit fixitCountersChanged(m_fixitsScheduled, m_fixitsScheduable);
});
connect(this, &QAbstractItemModel::rowsInserted,
this, [this](const QModelIndex &parent, int first, int last) {
- m_fixItsScheduableInTotal += diagnosticsWithFixits(parent, first, last);
- emit fixitStatisticsChanged(m_fixItsScheduled, m_fixItsScheduableInTotal);
+ const Counters counters = countDiagnostics(parent, first, last);
+ m_diagnostics += counters.diagnostics;
+ m_fixitsScheduable += counters.fixits;
+ emit fixitCountersChanged(m_fixitsScheduled, m_fixitsScheduable);
});
connect(this, &QAbstractItemModel::rowsAboutToBeRemoved,
this, [this](const QModelIndex &parent, int first, int last) {
- m_fixItsScheduableInTotal -= diagnosticsWithFixits(parent, first, last);
- emit fixitStatisticsChanged(m_fixItsScheduled, m_fixItsScheduableInTotal);
+ const Counters counters = countDiagnostics(parent, first, last);
+ m_diagnostics -= counters.diagnostics;
+ m_fixitsScheduable -= counters.fixits;
+ emit fixitCountersChanged(m_fixitsScheduled, m_fixitsScheduable);
});
}
@@ -605,34 +608,46 @@ void DiagnosticFilterModel::onFixitStatusChanged(const QModelIndex &sourceIndex,
return;
if (newStatus == FixitStatus::Scheduled)
- ++m_fixItsScheduled;
+ ++m_fixitsScheduled;
else if (oldStatus == FixitStatus::Scheduled) {
- --m_fixItsScheduled;
+ --m_fixitsScheduled;
if (newStatus != FixitStatus::NotScheduled)
- --m_fixItsScheduableInTotal;
+ --m_fixitsScheduable;
}
- emit fixitStatisticsChanged(m_fixItsScheduled, m_fixItsScheduableInTotal);
+ emit fixitCountersChanged(m_fixitsScheduled, m_fixitsScheduable);
}
-int DiagnosticFilterModel::diagnosticsWithFixits(const QModelIndex &parent,
- int first,
- int last) const
+void DiagnosticFilterModel::resetCounters()
{
- if (!parent.isValid())
- return 0;
+ m_fixitsScheduled = 0;
+ m_fixitsScheduable = 0;
+ m_diagnostics = 0;
+}
+
+DiagnosticFilterModel::Counters DiagnosticFilterModel::countDiagnostics(const QModelIndex &parent,
+ int first,
+ int last) const
+{
+ Counters counters;
+ const auto countItem = [&](Utils::TreeItem *item){
+ if (!mapFromSource(item->index()).isValid())
+ return; // Do not count filtered out items.
+ ++counters.diagnostics;
+ if (static_cast<DiagnosticItem *>(item)->diagnostic().hasFixits)
+ ++counters.fixits;
+ };
- int count = 0;
auto model = static_cast<ClangToolsDiagnosticModel *>(sourceModel());
- for (int idx = first; idx <= last; ++idx) {
- Utils::TreeItem *treeItem = model->itemForIndex(mapToSource(index(idx, 0, parent)));
- if (treeItem->level() == 2) {
- if (static_cast<DiagnosticItem *>(treeItem)->diagnostic().hasFixits)
- ++count;
- }
+ for (int row = first; row <= last; ++row) {
+ Utils::TreeItem *treeItem = model->itemForIndex(mapToSource(index(row, 0, parent)));
+ if (treeItem->level() == 1)
+ static_cast<FilePathItem *>(treeItem)->forChildrenAtLevel(1, countItem);
+ else if (treeItem->level() == 2)
+ countItem(treeItem);
}
- return count;
+ return counters;
}
bool DiagnosticFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h
index 1ecfc0e7913..c6ca1a6e319 100644
--- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h
+++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h
@@ -161,22 +161,31 @@ public:
FixitStatus oldStatus,
FixitStatus newStatus);
+ void resetCounters();
+ int diagnostics() const { return m_diagnostics; }
+ int fixitsScheduable() const { return m_fixitsScheduable; }
+ int fixitsScheduled() const { return m_fixitsScheduled; }
+
signals:
- void fixitStatisticsChanged(int scheduled, int scheduableTotal);
+ void fixitCountersChanged(int scheduled, int scheduableTotal);
private:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &l, const QModelIndex &r) const override;
-
- int diagnosticsWithFixits(const QModelIndex &parent, int first, int last) const;
+ struct Counters {
+ int diagnostics = 0;
+ int fixits = 0;
+ };
+ Counters countDiagnostics(const QModelIndex &parent, int first, int last) const;
void handleSuppressedDiagnosticsChanged();
QPointer<ProjectExplorer::Project> m_project;
Utils::FilePath m_lastProjectDirectory;
SuppressedDiagnosticsList m_suppressedDiagnostics;
- int m_fixItsScheduableInTotal = 0;
- int m_fixItsScheduled = 0;
+ int m_diagnostics = 0;
+ int m_fixitsScheduable = 0;
+ int m_fixitsScheduled = 0;
};
} // namespace Internal
diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp
index fed839d1166..333a573161b 100644
--- a/src/plugins/clangtools/clangtoolsutils.cpp
+++ b/src/plugins/clangtools/clangtoolsutils.cpp
@@ -80,7 +80,7 @@ bool isFileExecutable(const QString &filePath)
return false;
const QFileInfo fileInfo(filePath);
- return fileInfo.isFile() && fileInfo.isExecutable();
+ return fileInfo.exists() && fileInfo.isFile() && fileInfo.isExecutable();
}
QString shippedClangTidyExecutable()
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index 0b64c69ce7d..dead62ae052 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -368,6 +368,7 @@ public:
void addToRecentProjects(const QString &fileName, const QString &displayName);
void startRunControl(RunControl *runControl);
+ void showOutputPaneForRunControl(RunControl *runControl);
void updateActions();
void updateContext();
@@ -2342,6 +2343,11 @@ void ProjectExplorerPlugin::startRunControl(RunControl *runControl)
dd->startRunControl(runControl);
}
+void ProjectExplorerPlugin::showOutputPaneForRunControl(RunControl *runControl)
+{
+ dd->showOutputPaneForRunControl(runControl);
+}
+
void ProjectExplorerPluginPrivate::startRunControl(RunControl *runControl)
{
m_outputPane.createNewOutputWindow(runControl);
@@ -2361,6 +2367,12 @@ void ProjectExplorerPluginPrivate::startRunControl(RunControl *runControl)
emit m_instance->updateRunActions();
}
+void ProjectExplorerPluginPrivate::showOutputPaneForRunControl(RunControl *runControl)
+{
+ m_outputPane.showTabFor(runControl);
+ m_outputPane.popup(IOutputPane::NoModeSwitch | IOutputPane::WithFocus);
+}
+
void ProjectExplorerPluginPrivate::checkForShutdown()
{
--m_activeRunControlCount;
diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h
index 488e905ce1f..93d2c141b6c 100644
--- a/src/plugins/projectexplorer/projectexplorer.h
+++ b/src/plugins/projectexplorer/projectexplorer.h
@@ -141,6 +141,7 @@ public:
static void showQtSettings();
static void startRunControl(RunControl *runControl);
+ static void showOutputPaneForRunControl(RunControl *runControl);
static void showRunErrorMessage(const QString &errorMessage);
// internal public for FlatModel