From f6c46ce35d9018335a19d8d75fda4dfd7adf69b3 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Mon, 28 Jan 2019 12:40:03 +0100 Subject: [PATCH] Clang: Add tooltip action to remove specific warnings/checks ...from the diagnostic configuration. If no custom diagnostic configuration is set in Projects Mode > Clang, one is created and set for the current project. Otherwise the current custom diagnostic set in the project settings is modified. Change-Id: I5c48280c90f0e807e7333122d504dda302a8b0a9 Reviewed-by: Ivan Donchevskii --- .../clangdiagnostictooltipwidget.cpp | 50 +------ src/plugins/clangcodemodel/clangtextmark.cpp | 123 +++++++++++++++++- src/plugins/clangcodemodel/clangutils.cpp | 46 +++++++ src/plugins/clangcodemodel/clangutils.h | 17 +++ .../cpptools/clangdiagnosticconfigsmodel.cpp | 6 +- .../cpptools/clangdiagnosticconfigswidget.cpp | 27 ---- src/plugins/cpptools/cppcodemodelsettings.cpp | 23 +++- src/plugins/cpptools/cpptoolsreuse.cpp | 11 ++ src/plugins/cpptools/cpptoolsreuse.h | 2 + 9 files changed, 226 insertions(+), 79 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp index 497af719930..5f85a2e2263 100644 --- a/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp +++ b/src/plugins/clangcodemodel/clangdiagnostictooltipwidget.cpp @@ -25,6 +25,7 @@ #include "clangdiagnostictooltipwidget.h" #include "clangfixitoperation.h" +#include "clangutils.h" #include @@ -149,7 +150,7 @@ public: QTC_CHECK(!"Link target cannot be handled."); if (hideToolTipAfterLinkActivation) - Utils::ToolTip::hideImmediately(); + ::Utils::ToolTip::hideImmediately(); }); return label; @@ -171,50 +172,6 @@ public: private: enum class IndentMode { Indent, DoNotIndent }; - static bool isClazyOption(const QString &option) { return option.startsWith("-Wclazy"); } - - class DiagnosticTextInfo - { - public: - DiagnosticTextInfo(const QString &text) - : m_text(text) - , m_squareBracketStartIndex(text.lastIndexOf('[')) - {} - - QString textWithoutOption() const - { - if (m_squareBracketStartIndex == -1) - return m_text; - - return m_text.mid(0, m_squareBracketStartIndex - 1); - } - - QString option() const - { - if (m_squareBracketStartIndex == -1) - return QString(); - - const int index = m_squareBracketStartIndex + 1; - return m_text.mid(index, m_text.count() - index - 1); - } - - QString category() const - { - if (m_squareBracketStartIndex == -1) - return QString(); - - const int index = m_squareBracketStartIndex + 1; - if (isClazyOption(m_text.mid(index))) - return QCoreApplication::translate("ClangDiagnosticWidget", "Clazy Issue"); - else - return QCoreApplication::translate("ClangDiagnosticWidget", "Clang-Tidy Issue"); - } - - private: - const QString m_text; - const int m_squareBracketStartIndex; - }; - // Diagnostics from clazy/tidy do not have any category or option set but // we will conclude them from the diagnostic message. // @@ -233,6 +190,7 @@ private: ClangBackEnd::DiagnosticContainer supplementedDiagnostic = diagnostic; + using namespace ClangCodeModel::Utils; DiagnosticTextInfo info(diagnostic.text); supplementedDiagnostic.enableOption = info.option(); supplementedDiagnostic.category = info.category(); @@ -269,7 +227,7 @@ private: QString option = optionAsUtf8String.toString(); // Clazy - if (isClazyOption(option)) { + if (ClangCodeModel::Utils::DiagnosticTextInfo::isClazyOption(option)) { option = optionAsUtf8String.mid(8); // Remove "-Wclazy-" prefix. return QString::fromUtf8(CLAZY_DOCUMENTATION_URL_TEMPLATE).arg(option); } diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp index b8dc9a92a4d..41ee4c1533e 100644 --- a/src/plugins/clangcodemodel/clangtextmark.cpp +++ b/src/plugins/clangcodemodel/clangtextmark.cpp @@ -27,11 +27,20 @@ #include "clangconstants.h" #include "clangdiagnostictooltipwidget.h" +#include "clangeditordocumentprocessor.h" +#include "clangmodelmanagersupport.h" +#include "clangprojectsettings.h" #include "clangutils.h" -#include +#include +#include +#include +#include + +#include #include #include +#include #include #include @@ -64,6 +73,102 @@ static Core::Id categoryForSeverity(ClangBackEnd::DiagnosticSeverity severity) return isWarningOrNote(severity) ? Constants::CLANG_WARNING : Constants::CLANG_ERROR; } +ProjectExplorer::Project *projectForCurrentEditor() +{ + using namespace CppTools; + using namespace ClangCodeModel::Internal; + + const QString filePath = Utils::currentCppEditorDocumentFilePath(); + if (filePath.isEmpty()) + return nullptr; + + if (auto processor = ClangEditorDocumentProcessor::get(filePath)) { + if (ProjectPart::Ptr projectPart = processor->projectPart()) + return projectPart->project; + } + + return nullptr; +} + +void disableDiagnosticInConfig(CppTools::ClangDiagnosticConfig &config, + const ClangBackEnd::DiagnosticContainer &diagnostic) +{ + // Clang check + if (!diagnostic.disableOption.isEmpty()) { + config.setClangOptions(config.clangOptions() + QStringList(diagnostic.disableOption)); + return; + } + + // Clazy check + using namespace ClangCodeModel::Utils; + DiagnosticTextInfo textInfo(diagnostic.text); + if (DiagnosticTextInfo::isClazyOption(textInfo.option())) { + const QString checkName = DiagnosticTextInfo::clazyCheckName(textInfo.option()); + QStringList newChecks = config.clazyChecks().split(','); + newChecks.removeOne(checkName); + config.setClazyChecks(newChecks.join(',')); + return; + } + + // Tidy check + config.setClangTidyChecks(config.clangTidyChecks() + QString(",-") + textInfo.option()); +} + +void disableDiagnosticInCurrentProjectConfig(const ClangBackEnd::DiagnosticContainer &diagnostic) +{ + using namespace CppTools; + using namespace ClangCodeModel::Internal; + + ProjectExplorer::Project *project = projectForCurrentEditor(); + QTC_ASSERT(project, return ); + + // Get settings + ClangProjectSettings &projectSettings = ClangModelManagerSupport::instance()->projectSettings( + project); + const QSharedPointer globalSettings = codeModelSettings(); + + // Get config id + Core::Id currentConfigId = projectSettings.warningConfigId(); + if (projectSettings.useGlobalConfig()) + currentConfigId = globalSettings->clangDiagnosticConfigId(); + + // Get config + const ClangDiagnosticConfigs originalConfigs = globalSettings->clangCustomDiagnosticConfigs(); + ClangDiagnosticConfigsModel configsModel(globalSettings->clangCustomDiagnosticConfigs()); + QTC_ASSERT(configsModel.hasConfigWithId(currentConfigId), return ); + ClangDiagnosticConfig config = configsModel.configWithId(currentConfigId); + + // Create copy if needed + if (config.isReadOnly()) { + const QString name = QCoreApplication::translate("ClangDiagnosticConfig", + "Project: %1 (based on %2)") + .arg(project->displayName(), config.displayName()); + config = ClangDiagnosticConfigsModel::createCustomConfig(config, name); + } + + // Modify diagnostic config + disableDiagnosticInConfig(config, diagnostic); + configsModel.appendOrUpdate(config); + + // Set global settings + globalSettings->setClangCustomDiagnosticConfigs(configsModel.customConfigs()); + globalSettings->toSettings(Core::ICore::settings()); + + // Set project settings + if (projectSettings.useGlobalConfig()) + projectSettings.setUseGlobalConfig(false); + projectSettings.setWarningConfigId(config.id()); + projectSettings.store(); + + // Notify the user about changed project specific settings + const QString text + = QCoreApplication::translate("ClangDiagnosticConfig", + "Changes applied in Projects Mode > Clang Code Model"); + ::Utils::FadingIndicator::showText(Core::ICore::mainWindow(), + text, + ::Utils::FadingIndicator::SmallText); +} + } // anonymous namespace ClangTextMark::ClangTextMark(const FileName &fileName, @@ -88,6 +193,8 @@ ClangTextMark::ClangTextMark(const FileName &fileName, : ::Utils::Theme::CodeModel_Error_TextMarkColor); } + // Copy to clipboard action + QVector actions; QAction *action = new QAction(); action->setIcon(QIcon::fromTheme("edit-copy", ::Utils::Icons::COPY.icon())); QObject::connect(action, &QAction::triggered, [diagnostic]() { @@ -96,7 +203,19 @@ ClangTextMark::ClangTextMark(const FileName &fileName, ClangDiagnosticWidget::InfoBar); QApplication::clipboard()->setText(text, QClipboard::Clipboard); }); - setActions({action}); + actions << action; + + // Remove diagnostic warning action + if (projectForCurrentEditor()) { + action = new QAction(); + action->setIcon(::Utils::Icons::BROKEN.icon()); + QObject::connect(action, &QAction::triggered, [diagnostic]() { + disableDiagnosticInCurrentProjectConfig(diagnostic); + }); + actions << action; + } + + setActions(actions); } void ClangTextMark::updateIcon(bool valid) diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index 8a27a115a2d..481b0774f8f 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -373,5 +373,51 @@ QString currentCppEditorDocumentFilePath() return filePath; } +DiagnosticTextInfo::DiagnosticTextInfo(const QString &text) + : m_text(text) + , m_squareBracketStartIndex(text.lastIndexOf('[')) +{} + +QString DiagnosticTextInfo::textWithoutOption() const +{ + if (m_squareBracketStartIndex == -1) + return m_text; + + return m_text.mid(0, m_squareBracketStartIndex - 1); +} + +QString DiagnosticTextInfo::option() const +{ + if (m_squareBracketStartIndex == -1) + return QString(); + + const int index = m_squareBracketStartIndex + 1; + return m_text.mid(index, m_text.count() - index - 1); +} + +QString DiagnosticTextInfo::category() const +{ + if (m_squareBracketStartIndex == -1) + return QString(); + + const int index = m_squareBracketStartIndex + 1; + if (isClazyOption(m_text.mid(index))) + return QCoreApplication::translate("ClangDiagnosticWidget", "Clazy Issue"); + else + return QCoreApplication::translate("ClangDiagnosticWidget", "Clang-Tidy Issue"); +} + +bool DiagnosticTextInfo::isClazyOption(const QString &option) +{ + return option.startsWith("-Wclazy"); +} + +QString DiagnosticTextInfo::clazyCheckName(const QString &option) +{ + if (option.startsWith("-Wclazy")) + return option.mid(8); // Chop "-Wclazy-" + return option; +} + } // namespace Utils } // namespace Clang diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h index b7f705b57b8..4db869896b0 100644 --- a/src/plugins/clangcodemodel/clangutils.h +++ b/src/plugins/clangcodemodel/clangutils.h @@ -72,6 +72,23 @@ QString diagnosticCategoryPrefixRemoved(const QString &text); void generateCompilationDB(::Utils::FileName projectDir, CppTools::ProjectInfo projectInfo); +class DiagnosticTextInfo +{ +public: + DiagnosticTextInfo(const QString &text); + + QString textWithoutOption() const; + QString option() const; + QString category() const; + + static bool isClazyOption(const QString &option); + static QString clazyCheckName(const QString &option); + +private: + const QString m_text; + const int m_squareBracketStartIndex; +}; + namespace Text { template diff --git a/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp b/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp index 881e849b905..777dd27ca26 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp @@ -25,6 +25,7 @@ #include "clangdiagnosticconfigsmodel.h" +#include "cpptoolsreuse.h" #include "cpptoolsconstants.h" #include @@ -73,7 +74,6 @@ constexpr const char *DEFAULT_TIDY_CHECKS = "-*," "-readability-braces-around-statements," "-readability-implicit-bool-conversion," "-readability-named-parameter"; -constexpr const char *DEFAULT_CLAZY_CHECKS = "level0"; static void addConfigForAlmostEveryWarning(ClangDiagnosticConfigsModel &model) { @@ -141,7 +141,7 @@ static void addConfigForClazy(ClangDiagnosticConfigsModel &model) "Clazy level0 checks")); config.setIsReadOnly(true); config.setClangOptions(QStringList{QStringLiteral("-w")}); - config.setClazyChecks(QString::fromUtf8(DEFAULT_CLAZY_CHECKS)); + config.setClazyChecks(clazyChecksForLevel(0)); model.appendOrUpdate(config); } @@ -157,7 +157,7 @@ static void addConfigForTidyAndClazy(ClangDiagnosticConfigsModel &model) config.setClangTidyMode(ClangDiagnosticConfig::TidyMode::ChecksPrefixList); config.setClangTidyChecks(QString::fromUtf8(DEFAULT_TIDY_CHECKS)); - config.setClazyChecks(QString::fromUtf8(DEFAULT_CLAZY_CHECKS)); + config.setClazyChecks(clazyChecksForLevel(0)); model.appendOrUpdate(config); } diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp index a9a32a77bd1..6d45b428be5 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp @@ -319,16 +319,6 @@ public: m_root->checked = Qt::Unchecked; propagateDown(index(0, 0, QModelIndex())); - // <= Qt Creator 4.8 settings provide specific levels: {"level0"} - if (checks.size() == 1 && checks.first().startsWith("level")) { - bool ok = false; - const int level = checks.first().mid(5).toInt(&ok); - QTC_ASSERT(ok, return); - enableChecksByLevel(level); - return; - } - - // >= Qt Creator 4.9 settings provide specific checks: {c1, c2, ...} for (const QString &check : checks) { const QModelIndex index = indexForCheck(check); if (!index.isValid()) @@ -437,23 +427,6 @@ private: } } - void enableChecksByLevel(int level) - { - if (level < 0) - return; - - ClazyChecksTree *node = m_levelNodes.value(level); - QTC_ASSERT(node, return); - const QModelIndex index = indexForTree(node); - QTC_ASSERT(index.isValid(), return); - - node->checked = Qt::Checked; - propagateUp(index); - propagateDown(index); - - enableChecksByLevel(--level); - } - QModelIndex indexForCheck(const QString &check) const { if (check == "*") return index(0, 0, QModelIndex()); diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index 235066e6a04..35b81a9b7a5 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -27,6 +27,7 @@ #include "clangdiagnosticconfigsmodel.h" #include "cpptoolsconstants.h" +#include "cpptoolsreuse.h" #include @@ -79,6 +80,24 @@ static QString skipIndexingBigFilesKey() static QString indexerFileSizeLimitKey() { return QLatin1String(Constants::CPPTOOLS_INDEXER_FILE_SIZE_LIMIT); } +static QString convertToNewClazyChecksFormat(const QString &checks) +{ + // Before Qt Creator 4.9 valid values for checks were: "", "levelN". + // Starting with Qt Creator 4.9, checks are a comma-separated string of checks: "x,y,z". + + if (checks.isEmpty()) + return checks; + + if (checks.size() == 6 && checks.startsWith("level")) { + bool ok = false; + const int level = checks.mid(5).toInt(&ok); + QTC_ASSERT(ok, return QString()); + return clazyChecksForLevel(level); + } + + return checks; +} + static ClangDiagnosticConfigs customDiagnosticConfigsFromSettings(QSettings *s) { QTC_ASSERT(s->group() == QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP), @@ -98,7 +117,9 @@ static ClangDiagnosticConfigs customDiagnosticConfigsFromSettings(QSettings *s) s->value(clangDiagnosticConfigsArrayClangTidyModeKey()).toInt())); config.setClangTidyChecks( s->value(clangDiagnosticConfigsArrayClangTidyChecksKey()).toString()); - config.setClazyChecks(s->value(clangDiagnosticConfigsArrayClazyChecksKey()).toString()); + + const QString clazyChecks = s->value(clangDiagnosticConfigsArrayClazyChecksKey()).toString(); + config.setClazyChecks(convertToNewClazyChecksFormat(clazyChecks)); configs.append(config); } s->endArray(); diff --git a/src/plugins/cpptools/cpptoolsreuse.cpp b/src/plugins/cpptools/cpptoolsreuse.cpp index 63d0a0bbc8b..c56bfb9160c 100644 --- a/src/plugins/cpptools/cpptoolsreuse.cpp +++ b/src/plugins/cpptools/cpptoolsreuse.cpp @@ -27,6 +27,7 @@ #include "cppcodemodelsettings.h" #include "cpptoolsplugin.h" +#include "cpptools_clazychecks.h" #include #include @@ -287,4 +288,14 @@ UsePrecompiledHeaders getPchUsage() return UsePrecompiledHeaders::Yes; } +QString clazyChecksForLevel(int level) +{ + QStringList checks; + for (const Constants::ClazyCheckInfo &check : Constants::CLAZY_CHECKS) { + if (check.level == level) + checks << check.name; + } + return checks.join(','); +} + } // CppTools diff --git a/src/plugins/cpptools/cpptoolsreuse.h b/src/plugins/cpptools/cpptoolsreuse.h index cb45fe89580..d1a1956be31 100644 --- a/src/plugins/cpptools/cpptoolsreuse.h +++ b/src/plugins/cpptools/cpptoolsreuse.h @@ -80,4 +80,6 @@ UsePrecompiledHeaders CPPTOOLS_EXPORT getPchUsage(); int indexerFileSizeLimitInMb(); bool fileSizeExceedsLimit(const QFileInfo &fileInfo, int sizeLimitInMb); +QString clazyChecksForLevel(int level); + } // CppTools