diff --git a/src/plugins/clangtools/clangtidyclazytool.cpp b/src/plugins/clangtools/clangtidyclazytool.cpp index eb4964d5fe8..b1afe494e08 100644 --- a/src/plugins/clangtools/clangtidyclazytool.cpp +++ b/src/plugins/clangtools/clangtidyclazytool.cpp @@ -407,7 +407,7 @@ void ClangTidyClazyTool::updateRunActions() m_startAction->setToolTip(toolTip); m_startAction->setEnabled(canRun); m_stopAction->setEnabled(false); - m_clear->setEnabled(m_diagnosticModel->diagnosticsCount()); + m_clear->setEnabled(m_diagnosticModel->diagnostics().count()); } } @@ -418,7 +418,7 @@ void ClangTidyClazyTool::handleStateUpdate() QTC_ASSERT(m_diagnosticModel, return); QTC_ASSERT(m_diagnosticFilterModel, return); - const int issuesFound = m_diagnosticModel->diagnosticsCount(); + const int issuesFound = m_diagnosticModel->diagnostics().count(); const int issuesVisible = m_diagnosticFilterModel->rowCount(); m_goBack->setEnabled(issuesVisible > 1); m_goNext->setEnabled(issuesVisible > 1); @@ -439,10 +439,11 @@ void ClangTidyClazyTool::handleStateUpdate() } QList ClangTidyClazyTool::read(const QString &filePath, + const Utils::FileName &projectRootDir, const QString &logFilePath, QString *errorMessage) const { - return readSerializedDiagnostics(filePath, logFilePath, errorMessage); + return readSerializedDiagnostics(filePath, projectRootDir, logFilePath, errorMessage); } } // namespace Internal diff --git a/src/plugins/clangtools/clangtidyclazytool.h b/src/plugins/clangtools/clangtidyclazytool.h index 48ffe07906f..a82ff809993 100644 --- a/src/plugins/clangtools/clangtidyclazytool.h +++ b/src/plugins/clangtools/clangtidyclazytool.h @@ -54,6 +54,7 @@ public: void startTool(bool askUserForFileSelection) final; QList read(const QString &filePath, + const Utils::FileName &projectRootDir, const QString &logFilePath, QString *errorMessage) const final; diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index 8ca03b9634b..b02868cb5dc 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -135,7 +135,7 @@ void ClangTool::initDiagnosticView() m_diagnosticView->setAutoScroll(false); } -QList ClangTool::diagnostics() const +QSet ClangTool::diagnostics() const { return m_diagnosticModel->diagnostics(); } diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h index 93f7a610505..de4d08ceb9d 100644 --- a/src/plugins/clangtools/clangtool.h +++ b/src/plugins/clangtools/clangtool.h @@ -31,6 +31,7 @@ #include namespace Debugger { class DetailedErrorView; } +namespace Utils { class FileName; } namespace ClangTools { namespace Internal { @@ -49,6 +50,7 @@ public: virtual void startTool(bool askUserForFileSelection) = 0; virtual QList read(const QString &filePath, + const Utils::FileName &projectRootDir, const QString &logFilePath, QString *errorMessage) const = 0; @@ -56,7 +58,7 @@ public: bool askUserForFileSelection) const; // For testing. - QList diagnostics() const; + QSet diagnostics() const; const QString &name() const; diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 8a379095d93..a25d3fc9103 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -387,13 +387,24 @@ void ClangToolRunControl::analyzeNextFile() Utils::StdOutFormat); } +static Utils::FileName cleanPath(const Utils::FileName &filePath) +{ + return Utils::FileName::fromString(QDir::cleanPath(filePath.toString())); +} + void ClangToolRunControl::onRunnerFinishedWithSuccess(const QString &filePath) { const QString logFilePath = qobject_cast(sender())->logFilePath(); qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << logFilePath; + QTC_ASSERT(m_projectInfo.project(), return); + const Utils::FileName projectRootDir = cleanPath(m_projectInfo.project()->projectDirectory()); + QString errorMessage; - const QList diagnostics = tool()->read(filePath, logFilePath, &errorMessage); + const QList diagnostics = tool()->read(filePath, + projectRootDir, + logFilePath, + &errorMessage); QFile::remove(logFilePath); // Clean-up. if (!errorMessage.isEmpty()) { diff --git a/src/plugins/clangtools/clangtoolsdiagnostic.cpp b/src/plugins/clangtools/clangtoolsdiagnostic.cpp index 4f04c03338f..cb1f4ead62c 100644 --- a/src/plugins/clangtools/clangtoolsdiagnostic.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnostic.cpp @@ -38,5 +38,13 @@ bool Diagnostic::isValid() const return !description.isEmpty(); } +quint32 qHash(const Diagnostic &diagnostic) +{ + return qHash(diagnostic.description) + ^ qHash(diagnostic.location.filePath) + ^ diagnostic.location.line + ^ diagnostic.location.column; +} + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolsdiagnostic.h b/src/plugins/clangtools/clangtoolsdiagnostic.h index 9f6b5a99690..eb16803638f 100644 --- a/src/plugins/clangtools/clangtoolsdiagnostic.h +++ b/src/plugins/clangtools/clangtoolsdiagnostic.h @@ -66,6 +66,8 @@ public: bool hasFixits = false; }; +quint32 qHash(const Diagnostic &diagnostic); + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index eb13577b54b..4de6f8dcc6c 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp @@ -35,9 +35,12 @@ #include #include +#include #include +static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.model", QtWarningMsg) + namespace ClangTools { namespace Internal { @@ -60,6 +63,19 @@ ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent) connectFileWatcher(); } +QDebug operator<<(QDebug debug, const Diagnostic &d) +{ + return debug << "category:" << d.category + << "type:" << d.type + << "context:" << d.issueContext + << "contextKind:" << d.issueContextKind + << "hasFixits:" << d.hasFixits + << "explainingSteps:" << d.explainingSteps.size() + << "location:" << d.location + << "description:" << d.description + ; +} + void ClangToolsDiagnosticModel::addDiagnostics(const QList &diagnostics) { const auto onFixitStatusChanged = [this](FixitStatus newStatus) { @@ -70,28 +86,28 @@ void ClangToolsDiagnosticModel::addDiagnostics(const QList &diagnost emit fixItsToApplyCountChanged(m_fixItsToApplyCount); }; - if (!diagnostics.empty()) - addWatchedPath(diagnostics.front().location.filePath); + for (const Diagnostic &d : diagnostics) { + const int previousItemCount = m_diagnostics.count(); + m_diagnostics.insert(d); + if (m_diagnostics.count() == previousItemCount) { + qCDebug(LOG) << "Not adding duplicate diagnostic:" << d; + continue; + } - for (const Diagnostic &d : diagnostics) + qCDebug(LOG) << "Adding diagnostic:" << d; + addWatchedPath(d.location.filePath); rootItem()->appendChild(new DiagnosticItem(d, onFixitStatusChanged, this)); + } } -QList ClangToolsDiagnosticModel::diagnostics() const +QSet ClangToolsDiagnosticModel::diagnostics() const { - QList diags; - for (const Utils::TreeItem * const item : *rootItem()) - diags << static_cast(item)->diagnostic(); - return diags; -} - -int ClangToolsDiagnosticModel::diagnosticsCount() const -{ - return rootItem()->childCount(); + return m_diagnostics; } void ClangToolsDiagnosticModel::clear() { + m_diagnostics.clear(); clearAndSetupCache(); Utils::TreeModel<>::clear(); } diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h index 8ad10ca5ac0..b877b93b867 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h @@ -98,10 +98,8 @@ class ClangToolsDiagnosticModel : public Utils::TreeModel<> public: ClangToolsDiagnosticModel(QObject *parent = nullptr); - virtual void addDiagnostics(const QList &diagnostics); - virtual QList diagnostics() const; - - int diagnosticsCount() const; + void addDiagnostics(const QList &diagnostics); + QSet diagnostics() const; enum ItemRole { DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1 @@ -121,6 +119,7 @@ private: void clearAndSetupCache(); private: + QSet m_diagnostics; std::map, QVector> stepsToItemsCache; std::unique_ptr m_filesWatcher; int m_fixItsToApplyCount = 0; diff --git a/src/plugins/clangtools/clangtoolslogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp index d7602a26691..d1e98fac87b 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.cpp +++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp @@ -59,6 +59,7 @@ static Debugger::DiagnosticLocation diagLocationFromSourceLocation(CXSourceLocat Debugger::DiagnosticLocation location; location.filePath = fromCXString(clang_getFileName(file)); + location.filePath = QDir::cleanPath(location.filePath); // Normalize to find duplicates later location.line = line; location.column = column; return location; @@ -118,7 +119,9 @@ static ExplainingStep buildFixIt(const CXDiagnostic cxDiagnostic, unsigned index return fixItStep; } -static Diagnostic buildDiagnostic(const CXDiagnostic cxDiagnostic, const QString &nativeFilePath) +static Diagnostic buildDiagnostic(const CXDiagnostic cxDiagnostic, + const Utils::FileName &projectRootDir, + const QString &nativeFilePath) { Diagnostic diagnostic; diagnostic.type = cxDiagnosticType(cxDiagnostic); @@ -130,7 +133,13 @@ static Diagnostic buildDiagnostic(const CXDiagnostic cxDiagnostic, const QString return diagnostic; diagnostic.location = diagLocationFromSourceLocation(cxLocation); - if (diagnostic.location.filePath != nativeFilePath) + const auto diagnosticFilePath = Utils::FileName::fromString(diagnostic.location.filePath); + if (!diagnosticFilePath.isChildOf(projectRootDir)) + return diagnostic; + + // TODO: Introduce CppTools::ProjectFile::isGenerated to filter these out properly + const QString fileName = diagnosticFilePath.fileName(); + if ((fileName.startsWith("ui_") && fileName.endsWith(".h")) || fileName.endsWith(".moc")) return diagnostic; CXDiagnosticSet cxChildDiagnostics = clang_getChildDiagnostics(cxDiagnostic); @@ -165,6 +174,7 @@ static Diagnostic buildDiagnostic(const CXDiagnostic cxDiagnostic, const QString } static QList readSerializedDiagnostics_helper(const QString &filePath, + const Utils::FileName &projectRootDir, const QString &logFilePath) { QList list; @@ -187,7 +197,7 @@ static QList readSerializedDiagnostics_helper(const QString &filePat Utils::ExecuteOnDestruction cleanUpDiagnostic([&]() { clang_disposeDiagnostic(cxDiagnostic); }); - const Diagnostic diagnostic = buildDiagnostic(cxDiagnostic, nativeFilePath); + const Diagnostic diagnostic = buildDiagnostic(cxDiagnostic, projectRootDir, nativeFilePath); if (!diagnostic.isValid()) continue; @@ -213,13 +223,14 @@ static bool checkFilePath(const QString &filePath, QString *errorMessage) } QList readSerializedDiagnostics(const QString &filePath, + const Utils::FileName &projectRootDir, const QString &logFilePath, QString *errorMessage) { if (!checkFilePath(logFilePath, errorMessage)) return QList(); - return readSerializedDiagnostics_helper(filePath, logFilePath); + return readSerializedDiagnostics_helper(filePath, projectRootDir, logFilePath); } } // namespace Internal diff --git a/src/plugins/clangtools/clangtoolslogfilereader.h b/src/plugins/clangtools/clangtoolslogfilereader.h index 7118390935f..3d8a776932e 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.h +++ b/src/plugins/clangtools/clangtoolslogfilereader.h @@ -35,6 +35,7 @@ namespace ClangTools { namespace Internal { QList readSerializedDiagnostics(const QString &filePath, + const Utils::FileName &projectRootDir, const QString &logFilePath, QString *errorMessage);