forked from qt-creator/qt-creator
ClangTools: Improve fix-its handling
Two areas are touched: 1. Select multiple diagnostics if they have the same set of fix-its. 2. Watch the files and invalidate diagnostics if corresponding file was edited. Change-Id: If4487ba91f45c25d1aed1a98990dd9b6df9d7fe2 Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
@@ -102,6 +102,9 @@ public:
|
|||||||
static void addFixitOperations(DiagnosticItem *diagnosticItem,
|
static void addFixitOperations(DiagnosticItem *diagnosticItem,
|
||||||
const FixitsRefactoringFile &file, bool apply)
|
const FixitsRefactoringFile &file, bool apply)
|
||||||
{
|
{
|
||||||
|
if (!diagnosticItem->hasNewFixIts())
|
||||||
|
return;
|
||||||
|
|
||||||
// Did we already created the fixit operations?
|
// Did we already created the fixit operations?
|
||||||
ReplacementOperations currentOps = diagnosticItem->fixitOperations();
|
ReplacementOperations currentOps = diagnosticItem->fixitOperations();
|
||||||
if (!currentOps.isEmpty()) {
|
if (!currentOps.isEmpty()) {
|
||||||
@@ -135,7 +138,7 @@ public:
|
|||||||
diagnosticItem->setFixitOperations(replacements);
|
diagnosticItem->setFixitOperations(replacements);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply()
|
void apply(ClangToolsDiagnosticModel *model)
|
||||||
{
|
{
|
||||||
for (auto it = m_refactoringFileInfos.begin(); it != m_refactoringFileInfos.end(); ++it) {
|
for (auto it = m_refactoringFileInfos.begin(); it != m_refactoringFileInfos.end(); ++it) {
|
||||||
RefactoringFileInfo &fileInfo = it.value();
|
RefactoringFileInfo &fileInfo = it.value();
|
||||||
@@ -167,18 +170,24 @@ public:
|
|||||||
for (DiagnosticItem *item : itemsScheduledOrSchedulable)
|
for (DiagnosticItem *item : itemsScheduledOrSchedulable)
|
||||||
ops += item->fixitOperations();
|
ops += item->fixitOperations();
|
||||||
|
|
||||||
|
if (ops.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
// Apply file
|
// Apply file
|
||||||
QVector<DiagnosticItem *> itemsApplied;
|
QVector<DiagnosticItem *> itemsApplied;
|
||||||
QVector<DiagnosticItem *> itemsFailedToApply;
|
QVector<DiagnosticItem *> itemsFailedToApply;
|
||||||
QVector<DiagnosticItem *> itemsInvalidated;
|
QVector<DiagnosticItem *> itemsInvalidated;
|
||||||
|
|
||||||
fileInfo.file.setReplacements(ops);
|
fileInfo.file.setReplacements(ops);
|
||||||
|
|
||||||
|
model->removeWatchedPath(ops.first()->fileName);
|
||||||
if (fileInfo.file.apply()) {
|
if (fileInfo.file.apply()) {
|
||||||
itemsApplied = itemsScheduled;
|
itemsApplied = itemsScheduled;
|
||||||
} else {
|
} else {
|
||||||
itemsFailedToApply = itemsScheduled;
|
itemsFailedToApply = itemsScheduled;
|
||||||
itemsInvalidated = itemsSchedulable;
|
itemsInvalidated = itemsSchedulable;
|
||||||
}
|
}
|
||||||
|
model->addWatchedPath(ops.first()->fileName);
|
||||||
|
|
||||||
// Update DiagnosticItem state
|
// Update DiagnosticItem state
|
||||||
for (DiagnosticItem *diagnosticItem : itemsScheduled)
|
for (DiagnosticItem *diagnosticItem : itemsScheduled)
|
||||||
@@ -262,7 +271,7 @@ ClangTidyClazyTool::ClangTidyClazyTool()
|
|||||||
diagnosticItems += static_cast<DiagnosticItem *>(item);
|
diagnosticItems += static_cast<DiagnosticItem *>(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
ApplyFixIts(diagnosticItems).apply();
|
ApplyFixIts(diagnosticItems).apply(m_diagnosticModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER);
|
ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER);
|
||||||
@@ -354,7 +363,9 @@ void ClangTidyClazyTool::startTool(bool askUserForFileSelection)
|
|||||||
|
|
||||||
m_perspective.select();
|
m_perspective.select();
|
||||||
|
|
||||||
|
m_diagnosticModel->clearAndSetupCache();
|
||||||
m_diagnosticModel->clear();
|
m_diagnosticModel->clear();
|
||||||
|
|
||||||
setToolBusy(true);
|
setToolBusy(true);
|
||||||
m_diagnosticFilterModel->setProject(project);
|
m_diagnosticFilterModel->setProject(project);
|
||||||
m_running = true;
|
m_running = true;
|
||||||
|
@@ -27,9 +27,9 @@
|
|||||||
|
|
||||||
#include <debugger/analyzer/diagnosticlocation.h>
|
#include <debugger/analyzer/diagnosticlocation.h>
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
namespace ClangTools {
|
namespace ClangTools {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -38,11 +38,15 @@ class ExplainingStep
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
bool operator<(const ExplainingStep &other) const {
|
||||||
|
return std::tie(location, ranges, message)
|
||||||
|
< std::tie(other.location, other.ranges, other.message);
|
||||||
|
}
|
||||||
|
|
||||||
QString message;
|
QString message;
|
||||||
QString extendedMessage;
|
QString extendedMessage;
|
||||||
Debugger::DiagnosticLocation location;
|
Debugger::DiagnosticLocation location;
|
||||||
QList<Debugger::DiagnosticLocation> ranges;
|
QVector<Debugger::DiagnosticLocation> ranges;
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
bool isFixIt = false;
|
bool isFixIt = false;
|
||||||
};
|
};
|
||||||
@@ -58,7 +62,7 @@ public:
|
|||||||
QString issueContextKind;
|
QString issueContextKind;
|
||||||
QString issueContext;
|
QString issueContext;
|
||||||
Debugger::DiagnosticLocation location;
|
Debugger::DiagnosticLocation location;
|
||||||
QList<ExplainingStep> explainingSteps;
|
QVector<ExplainingStep> explainingSteps;
|
||||||
bool hasFixits = false;
|
bool hasFixits = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -54,8 +54,10 @@ private:
|
|||||||
|
|
||||||
ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent)
|
ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent)
|
||||||
: Utils::TreeModel<>(parent)
|
: Utils::TreeModel<>(parent)
|
||||||
|
, m_filesWatcher(std::make_unique<QFileSystemWatcher>())
|
||||||
{
|
{
|
||||||
setHeader({tr("Issue"), tr("Location"), tr("Fixit Status")});
|
setHeader({tr("Issue"), tr("Location"), tr("Fixit Status")});
|
||||||
|
connectFileWatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangToolsDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics)
|
void ClangToolsDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnostics)
|
||||||
@@ -68,8 +70,11 @@ void ClangToolsDiagnosticModel::addDiagnostics(const QList<Diagnostic> &diagnost
|
|||||||
emit fixItsToApplyCountChanged(m_fixItsToApplyCount);
|
emit fixItsToApplyCountChanged(m_fixItsToApplyCount);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!diagnostics.empty())
|
||||||
|
addWatchedPath(diagnostics.front().location.filePath);
|
||||||
|
|
||||||
for (const Diagnostic &d : diagnostics)
|
for (const Diagnostic &d : diagnostics)
|
||||||
rootItem()->appendChild(new DiagnosticItem(d, onFixitStatusChanged));
|
rootItem()->appendChild(new DiagnosticItem(d, onFixitStatusChanged, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Diagnostic> ClangToolsDiagnosticModel::diagnostics() const
|
QList<Diagnostic> ClangToolsDiagnosticModel::diagnostics() const
|
||||||
@@ -85,6 +90,49 @@ int ClangToolsDiagnosticModel::diagnosticsCount() const
|
|||||||
return rootItem()->childCount();
|
return rootItem()->childCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClangToolsDiagnosticModel::updateItems(const DiagnosticItem *changedItem)
|
||||||
|
{
|
||||||
|
for (auto item : stepsToItemsCache[changedItem->diagnostic().explainingSteps]) {
|
||||||
|
if (item != changedItem)
|
||||||
|
item->setFixItStatus(changedItem->fixItStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsDiagnosticModel::connectFileWatcher()
|
||||||
|
{
|
||||||
|
connect(m_filesWatcher.get(),
|
||||||
|
&QFileSystemWatcher::fileChanged,
|
||||||
|
this,
|
||||||
|
&ClangToolsDiagnosticModel::onFileChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsDiagnosticModel::clearAndSetupCache()
|
||||||
|
{
|
||||||
|
m_filesWatcher = std::make_unique<QFileSystemWatcher>();
|
||||||
|
connectFileWatcher();
|
||||||
|
stepsToItemsCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsDiagnosticModel::onFileChanged(const QString &path)
|
||||||
|
{
|
||||||
|
for (Utils::TreeItem * const item : *rootItem()) {
|
||||||
|
auto diagnosticItem = static_cast<DiagnosticItem *>(item);
|
||||||
|
if (diagnosticItem->diagnostic().location.filePath == path)
|
||||||
|
diagnosticItem->setFixItStatus(FixitStatus::Invalidated);
|
||||||
|
}
|
||||||
|
removeWatchedPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsDiagnosticModel::removeWatchedPath(const QString &path)
|
||||||
|
{
|
||||||
|
m_filesWatcher->removePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangToolsDiagnosticModel::addWatchedPath(const QString &path)
|
||||||
|
{
|
||||||
|
m_filesWatcher->addPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
static QString createDiagnosticToolTipString(const Diagnostic &diagnostic)
|
static QString createDiagnosticToolTipString(const Diagnostic &diagnostic)
|
||||||
{
|
{
|
||||||
using StringPair = QPair<QString, QString>;
|
using StringPair = QPair<QString, QString>;
|
||||||
@@ -216,10 +264,12 @@ static QString fullText(const Diagnostic &diagnostic)
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiagnosticItem::DiagnosticItem(const Diagnostic &diag,
|
||||||
DiagnosticItem::DiagnosticItem(const Diagnostic &diag, const OnFixitStatusChanged &onFixitStatusChanged)
|
const OnFixitStatusChanged &onFixitStatusChanged,
|
||||||
|
ClangToolsDiagnosticModel *parent)
|
||||||
: m_diagnostic(diag)
|
: m_diagnostic(diag)
|
||||||
, m_onFixitStatusChanged(onFixitStatusChanged)
|
, m_onFixitStatusChanged(onFixitStatusChanged)
|
||||||
|
, m_parentModel(parent)
|
||||||
{
|
{
|
||||||
if (diag.hasFixits)
|
if (diag.hasFixits)
|
||||||
m_fixitStatus = FixitStatus::NotScheduled;
|
m_fixitStatus = FixitStatus::NotScheduled;
|
||||||
@@ -231,6 +281,9 @@ DiagnosticItem::DiagnosticItem(const Diagnostic &diag, const OnFixitStatusChange
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!diag.explainingSteps.isEmpty())
|
||||||
|
m_parentModel->stepsToItemsCache[diag.explainingSteps].push_back(this);
|
||||||
|
|
||||||
foreach (const ExplainingStep &s, diag.explainingSteps)
|
foreach (const ExplainingStep &s, diag.explainingSteps)
|
||||||
appendChild(new ExplainingStepItem(s));
|
appendChild(new ExplainingStepItem(s));
|
||||||
}
|
}
|
||||||
@@ -336,6 +389,7 @@ bool DiagnosticItem::setData(int column, const QVariant &data, int role)
|
|||||||
: FixitStatus::NotScheduled;
|
: FixitStatus::NotScheduled;
|
||||||
|
|
||||||
setFixItStatus(newStatus);
|
setFixItStatus(newStatus);
|
||||||
|
m_parentModel->updateItems(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,6 +411,14 @@ void DiagnosticItem::setFixitOperations(const ReplacementOperations &replacement
|
|||||||
m_fixitOperations = replacements;
|
m_fixitOperations = replacements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DiagnosticItem::hasNewFixIts() const
|
||||||
|
{
|
||||||
|
if (m_diagnostic.explainingSteps.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_parentModel->stepsToItemsCache[m_diagnostic.explainingSteps].front() == this;
|
||||||
|
}
|
||||||
|
|
||||||
ExplainingStepItem::ExplainingStepItem(const ExplainingStep &step) : m_step(step)
|
ExplainingStepItem::ExplainingStepItem(const ExplainingStep &step) : m_step(step)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -33,10 +33,14 @@
|
|||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/treemodel.h>
|
#include <utils/treemodel.h>
|
||||||
|
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace ProjectExplorer { class Project; }
|
namespace ProjectExplorer { class Project; }
|
||||||
|
|
||||||
@@ -52,11 +56,14 @@ enum class FixitStatus {
|
|||||||
Invalidated,
|
Invalidated,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ClangToolsDiagnosticModel;
|
||||||
|
|
||||||
class DiagnosticItem : public Utils::TreeItem
|
class DiagnosticItem : public Utils::TreeItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using OnFixitStatusChanged = std::function<void(FixitStatus newStatus)>;
|
using OnFixitStatusChanged = std::function<void(FixitStatus newStatus)>;
|
||||||
DiagnosticItem(const Diagnostic &diag, const OnFixitStatusChanged &onFixitStatusChanged);
|
DiagnosticItem(const Diagnostic &diag, const OnFixitStatusChanged &onFixitStatusChanged,
|
||||||
|
ClangToolsDiagnosticModel *parent);
|
||||||
~DiagnosticItem() override;
|
~DiagnosticItem() override;
|
||||||
|
|
||||||
const Diagnostic &diagnostic() const { return m_diagnostic; }
|
const Diagnostic &diagnostic() const { return m_diagnostic; }
|
||||||
@@ -64,6 +71,7 @@ public:
|
|||||||
FixitStatus fixItStatus() const { return m_fixitStatus; }
|
FixitStatus fixItStatus() const { return m_fixitStatus; }
|
||||||
void setFixItStatus(const FixitStatus &status);
|
void setFixItStatus(const FixitStatus &status);
|
||||||
|
|
||||||
|
bool hasNewFixIts() const;
|
||||||
ReplacementOperations &fixitOperations() { return m_fixitOperations; }
|
ReplacementOperations &fixitOperations() { return m_fixitOperations; }
|
||||||
void setFixitOperations(const ReplacementOperations &replacements);
|
void setFixitOperations(const ReplacementOperations &replacements);
|
||||||
|
|
||||||
@@ -78,12 +86,15 @@ private:
|
|||||||
|
|
||||||
ReplacementOperations m_fixitOperations;
|
ReplacementOperations m_fixitOperations;
|
||||||
FixitStatus m_fixitStatus = FixitStatus::NotAvailable;
|
FixitStatus m_fixitStatus = FixitStatus::NotAvailable;
|
||||||
|
ClangToolsDiagnosticModel *m_parentModel = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClangToolsDiagnosticModel : public Utils::TreeModel<>
|
class ClangToolsDiagnosticModel : public Utils::TreeModel<>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class DiagnosticItem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClangToolsDiagnosticModel(QObject *parent = nullptr);
|
ClangToolsDiagnosticModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
@@ -96,10 +107,22 @@ public:
|
|||||||
DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1
|
DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void clearAndSetupCache();
|
||||||
|
void removeWatchedPath(const QString &path);
|
||||||
|
void addWatchedPath(const QString &path);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fixItsToApplyCountChanged(int count);
|
void fixItsToApplyCountChanged(int count);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void connectFileWatcher();
|
||||||
|
void updateItems(const DiagnosticItem *changedItem);
|
||||||
|
void onFileChanged(const QString &path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<QVector<ExplainingStep>, QVector<DiagnosticItem *>> stepsToItemsCache;
|
||||||
|
std::unique_ptr<QFileSystemWatcher> m_filesWatcher;
|
||||||
|
QVector<QString> m_allowFileWriteOnce;
|
||||||
int m_fixItsToApplyCount = 0;
|
int m_fixItsToApplyCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -46,6 +46,12 @@ bool operator==(const DiagnosticLocation &first, const DiagnosticLocation &secon
|
|||||||
&& first.column == second.column;
|
&& first.column == second.column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator<(const DiagnosticLocation &first, const DiagnosticLocation &second)
|
||||||
|
{
|
||||||
|
return std::tie(first.filePath, first.line, first.column)
|
||||||
|
< std::tie(second.filePath, second.line, second.column);
|
||||||
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug dbg, const DiagnosticLocation &location)
|
QDebug operator<<(QDebug dbg, const DiagnosticLocation &location)
|
||||||
{
|
{
|
||||||
dbg.nospace() << "Location(" << location.filePath << ", "
|
dbg.nospace() << "Location(" << location.filePath << ", "
|
||||||
|
@@ -49,6 +49,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
DEBUGGER_EXPORT bool operator==(const DiagnosticLocation &first, const DiagnosticLocation &second);
|
DEBUGGER_EXPORT bool operator==(const DiagnosticLocation &first, const DiagnosticLocation &second);
|
||||||
|
DEBUGGER_EXPORT bool operator<(const DiagnosticLocation &first, const DiagnosticLocation &second);
|
||||||
DEBUGGER_EXPORT QDebug operator<<(QDebug dbg, const DiagnosticLocation &location);
|
DEBUGGER_EXPORT QDebug operator<<(QDebug dbg, const DiagnosticLocation &location);
|
||||||
|
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
Reference in New Issue
Block a user