diff options
author | Nikolai Kosjar <nikolai.kosjar@qt.io> | 2019-01-14 15:06:28 +0100 |
---|---|---|
committer | Nikolai Kosjar <nikolai.kosjar@qt.io> | 2019-01-24 07:39:07 +0000 |
commit | 7315d9a47c432b68f295f3d4fb72e7e34d76770a (patch) | |
tree | 85c83e670dcc6e85492d35a94cb1e6a943d17f1e | |
parent | 4acf2a1df1c149c66c1ba6903b5708c7c1adaa66 (diff) |
Clang: Make clazy UI more fine-grained
...so that specific checks can be enabled/disabled.
This replaces the level radio buttons in Tools > Options > C++ > Code
Model > "Manage..." > Tab: Clazy.
Task-number: QTCREATORBUG-21120
Change-Id: If468d79d3c309b287b4105d83ac31f0b1489c71c
Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
-rwxr-xr-x | scripts/generateClazyChecks.py | 131 | ||||
-rw-r--r-- | src/plugins/cpptools/clangdiagnosticconfigsselectionwidget.cpp | 6 | ||||
-rw-r--r-- | src/plugins/cpptools/clangdiagnosticconfigswidget.cpp | 604 | ||||
-rw-r--r-- | src/plugins/cpptools/clangdiagnosticconfigswidget.h | 11 | ||||
-rw-r--r-- | src/plugins/cpptools/clangdiagnosticconfigswidget.ui | 9 | ||||
-rw-r--r-- | src/plugins/cpptools/clazychecks.ui | 132 | ||||
-rw-r--r-- | src/plugins/cpptools/cppcodemodelsettings.cpp | 16 | ||||
-rw-r--r-- | src/plugins/cpptools/cppcodemodelsettings.h | 4 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptools.pro | 3 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptools.qbs | 1 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptools_clazychecks.h | 130 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptoolsconstants.h | 3 |
12 files changed, 877 insertions, 173 deletions
diff --git a/scripts/generateClazyChecks.py b/scripts/generateClazyChecks.py new file mode 100755 index 0000000000..373512de75 --- /dev/null +++ b/scripts/generateClazyChecks.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +############################################################################ +# +# Copyright (C) 2019 The Qt Company Ltd. +# Contact: https://www.qt.io/licensing/ +# +# This file is part of Qt Creator. +# +# Commercial License Usage +# Licensees holding valid commercial Qt licenses may use this file in +# accordance with the commercial license agreement provided with the +# Software or, alternatively, in accordance with the terms contained in +# a written agreement between you and The Qt Company. For licensing terms +# and conditions see https://www.qt.io/terms-conditions. For further +# information use the contact form at https://www.qt.io/contact-us. +# +# GNU General Public License Usage +# Alternatively, this file may be used under the terms of the GNU +# General Public License version 3 as published by the Free Software +# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +# included in the packaging of this file. Please review the following +# information to ensure the GNU General Public License requirements will +# be met: https://www.gnu.org/licenses/gpl-3.0.html. +# +############################################################################ + +import argparse +import json +import os + +def full_header_file_content(checks_initializer_as_string): + return '''/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <vector> + +namespace CppTools { +namespace Constants { + +class ClazyCheckInfo +{ +public: + bool isValid() const { return !name.isEmpty() && level >= -1; } + + QString name; + int level = -1; // "Manual level" + QStringList topics; +}; +using ClazyCheckInfos = std::vector<ClazyCheckInfo>; + +// CLANG-UPGRADE-CHECK: Run 'scripts/generateClazyChecks.py' after Clang upgrade to +// update this header. +static const ClazyCheckInfos CLAZY_CHECKS = { +''' + checks_initializer_as_string + ''' +}; + +} // namespace Constants +} // namespace CppTools +''' + +def parse_arguments(): + parser = argparse.ArgumentParser(description='Clazy checks header file generator') + parser.add_argument('--clazy-checks-json-path', help='path to clazy\'s checks.json', + default=None, dest='checks_json_path') + return parser.parse_args() + +def quoted(text): + return '"%s"' % (text) + +def categories_as_initializer_string(check): + if 'categories' not in check: + return '{}' + out = '' + for category in check['categories']: + out += quoted(category) + ',' + if out.endswith(','): + out = out[:-1] + return '{' + out + '}' + +def check_as_initializer_string(check): + return '{%s, %d, %s}' %(quoted(check['name']), + check['level'], + categories_as_initializer_string(check)) + +def checks_as_initializer_string(checks): + out = '' + for check in checks: + out += ' ' + check_as_initializer_string(check) + ',\n' + if out.endswith(',\n'): + out = out[:-2] + return out + +def main(): + arguments = parse_arguments() + + content = file(arguments.checks_json_path).read() + checks = json.loads(content)['checks'] + + current_path = os.path.dirname(os.path.abspath(__file__)) + header_path = os.path.abspath(os.path.join(current_path, '..', 'src', + 'plugins', 'cpptools', 'cpptools_clazychecks.h')) + + with open(header_path, 'w') as f: + f.write(full_header_file_content(checks_as_initializer_string(checks))) + +if __name__ == "__main__": + main() diff --git a/src/plugins/cpptools/clangdiagnosticconfigsselectionwidget.cpp b/src/plugins/cpptools/clangdiagnosticconfigsselectionwidget.cpp index 06d8478b50..eb89d7d87e 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigsselectionwidget.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfigsselectionwidget.cpp @@ -120,12 +120,14 @@ void ClangDiagnosticConfigsSelectionWidget::connectToClangDiagnosticConfigsDialo connect(buttonsBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(buttonsBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); - connect(&dialog, &QDialog::accepted, [widget]() { + const bool previousEnableLowerClazyLevels = codeModelSettings()->enableLowerClazyLevels(); + connect(&dialog, &QDialog::accepted, [widget, previousEnableLowerClazyLevels]() { QSharedPointer<CppCodeModelSettings> settings = codeModelSettings(); const ClangDiagnosticConfigs oldDiagnosticConfigs = settings->clangCustomDiagnosticConfigs(); const ClangDiagnosticConfigs currentDiagnosticConfigs = widget->customConfigs(); - if (oldDiagnosticConfigs != currentDiagnosticConfigs) { + if (oldDiagnosticConfigs != currentDiagnosticConfigs + || previousEnableLowerClazyLevels != codeModelSettings()->enableLowerClazyLevels()) { const ClangDiagnosticConfigsModel configsModel(currentDiagnosticConfigs); if (!configsModel.hasConfigWithId(settings->clangDiagnosticConfigId())) settings->resetClangDiagnosticConfigId(); diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp index fb683a07ae..fc157352ee 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp @@ -27,6 +27,7 @@ #include "cppcodemodelsettings.h" #include "cpptools_clangtidychecks.h" +#include "cpptools_clazychecks.h" #include "cpptoolsconstants.h" #include "cpptoolsreuse.h" #include "ui_clangdiagnosticconfigswidget.h" @@ -45,10 +46,14 @@ #include <QDialogButtonBox> #include <QInputDialog> #include <QPushButton> +#include <QSortFilterProxyModel> +#include <QStringListModel> #include <QUuid> namespace CppTools { +using namespace Constants; + static constexpr const char CLANG_STATIC_ANALYZER_URL[] = "https://clang-analyzer.llvm.org/available_checks.html"; @@ -75,13 +80,99 @@ static bool needsLink(ProjectExplorer::Tree *node) { return !node->isDir && !node->fullPath.toString().startsWith("clang-analyzer-"); } -class TidyChecksTreeModel final : public ProjectExplorer::SelectableFilesModel +static void selectAll(QAbstractItemView *view) +{ + view->setSelectionMode(QAbstractItemView::MultiSelection); + view->selectAll(); + view->setSelectionMode(QAbstractItemView::SingleSelection); +} + +class BaseChecksTreeModel : public ProjectExplorer::SelectableFilesModel { Q_OBJECT public: - TidyChecksTreeModel() + enum Roles { LinkRole = Qt::UserRole + 1 }; + enum Columns { NameColumn, LinkColumn }; + + BaseChecksTreeModel() : ProjectExplorer::SelectableFilesModel(nullptr) + {} + + int columnCount(const QModelIndex &) const override { return 2; } + + QVariant data(const QModelIndex &fullIndex, int role = Qt::DisplayRole) const override + { + if (fullIndex.column() == LinkColumn) { + switch (role) { + case Qt::DisplayRole: + return tr("Web Page"); + case Qt::FontRole: { + QFont font = QApplication::font(); + font.setUnderline(true); + return font; + } + case Qt::ForegroundRole: + return QApplication::palette().link().color(); + } + return QVariant(); + } + return QVariant(); + } + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override + { + if (role == Qt::CheckStateRole && !m_enabled) + return false; + ProjectExplorer::SelectableFilesModel::setData(index, value, role); + return true; + } + + void setEnabled(bool enabled) + { + m_enabled = enabled; + } + + // TODO: Remove/replace this method after base class refactoring is done. + void traverse(const QModelIndex &index, + const std::function<bool(const QModelIndex &)> &visit) const + { + if (!index.isValid()) + return; + + if (!visit(index)) + return; + + if (!hasChildren(index)) + return; + + const int rows = rowCount(index); + const int cols = columnCount(index); + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) + traverse(this->index(i, j, index), visit); + } + } + +protected: + bool m_enabled = true; +}; + +static void openUrl(QAbstractItemModel *model, const QModelIndex &index) +{ + const QString link = model->data(index, BaseChecksTreeModel::LinkRole).toString(); + if (link.isEmpty()) + return; + + QDesktopServices::openUrl(QUrl(link)); +}; + +class TidyChecksTreeModel final : public BaseChecksTreeModel +{ + Q_OBJECT + +public: + TidyChecksTreeModel() { buildTree(nullptr, m_root, Constants::CLANG_TIDY_CHECKS_ROOT); } @@ -119,11 +210,7 @@ public: } } - int columnCount(const QModelIndex &/*parent*/) const override - { - return 2; - } - +private: QVariant data(const QModelIndex &fullIndex, int role = Qt::DisplayRole) const final { if (!fullIndex.isValid() || role == Qt::DecorationRole) @@ -134,25 +221,16 @@ public: if (fullIndex.column() == 1) { if (!needsLink(node)) return QVariant(); - switch (role) { - case Qt::DisplayRole: - return tr("Web Page"); - case Qt::FontRole: { - QFont font = QApplication::font(); - font.setUnderline(true); - return font; - } - case Qt::ForegroundRole: - return QApplication::palette().link().color(); - case Qt::UserRole: { + + if (role == LinkRole) { // 'clang-analyzer-' group if (node->isDir) return QString::fromUtf8(CLANG_STATIC_ANALYZER_URL); return QString::fromUtf8(Constants::TIDY_DOCUMENTATION_URL_TEMPLATE) .arg(node->fullPath.toString()); } - } - return QVariant(); + + return BaseChecksTreeModel::data(fullIndex, role); } if (role == Qt::DisplayRole) @@ -161,39 +239,218 @@ public: return ProjectExplorer::SelectableFilesModel::data(index, role); } - void setEnabled(bool enabled) + QModelIndex indexForCheck(const QString &check) const { + if (check == "*") + return index(0, 0, QModelIndex()); + + QModelIndex result; + traverse(index(0, 0, QModelIndex()), [&](const QModelIndex &index){ + using ProjectExplorer::Tree; + if (result.isValid()) + return false; + + auto *node = static_cast<Tree *>(index.internalPointer()); + const QString nodeName = node->fullPath.toString(); + if ((check.endsWith("*") && nodeName.startsWith(check.left(check.length() - 1))) + || (!node->isDir && nodeName == check)) { + result = index; + return false; + } + + return check.startsWith(nodeName); + }); + return result; + } + + static void collectChecks(const ProjectExplorer::Tree *root, QString &checks) { - m_enabled = enabled; + if (root->checked == Qt::Unchecked) + return; + if (root->checked == Qt::Checked) { + checks += "," + root->fullPath.toString(); + if (root->isDir) + checks += "*"; + return; + } + for (const ProjectExplorer::Tree *t : root->childDirectories) + collectChecks(t, checks); } +}; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override +class ClazyChecksTree : public ProjectExplorer::Tree +{ +public: + enum Kind { TopLevelNode, LevelNode, CheckNode }; + ClazyChecksTree(const QString &name, Kind kind) { - if (role == Qt::CheckStateRole && !m_enabled) - return false; - return ProjectExplorer::SelectableFilesModel::setData(index, value, role); + this->name = name; + this->kind = kind; + this->isDir = kind == TopLevelNode || kind == LevelNode; } -private: + static ClazyChecksTree *fromIndex(const QModelIndex &index) + { + return static_cast<ClazyChecksTree *>(index.internalPointer()); + } - // TODO: Remove/replace this method after base class refactoring is done. - void traverse(const QModelIndex &index, - const std::function<bool(const QModelIndex &)> &visit) const +public: + ClazyCheckInfo checkInfo; + Kind kind = TopLevelNode; +}; + +class ClazyChecksTreeModel final : public BaseChecksTreeModel +{ + Q_OBJECT + +public: + ClazyChecksTreeModel() { buildTree(); } + + QStringList enabledChecks() const { - if (!index.isValid()) - return; + QStringList checks; + collectChecks(m_root, checks); + return checks; + } - if (!visit(index)) - return; + void enableChecks(const QStringList &checks) + { + // Unselect all + m_root->checked = Qt::Unchecked; + propagateDown(index(0, 0, QModelIndex())); - if (!hasChildren(index)) + // <= 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; + } - const int rows = rowCount(index); - const int cols = columnCount(index); - for (int i = 0; i < rows; ++i) { - for (int j = 0; j < cols; ++j) - traverse(this->index(i, j, index), visit); + // >= Qt Creator 4.9 settings provide specific checks: {c1, c2, ...} + for (const QString &check : checks) { + const QModelIndex index = indexForCheck(check); + if (!index.isValid()) + continue; + ClazyChecksTree::fromIndex(index)->checked = Qt::Checked; + propagateUp(index); + propagateDown(index); + } + } + + bool hasEnabledButNotVisibleChecks( + const std::function<bool(const QModelIndex &index)> &isHidden) const + { + bool enabled = false; + traverse(index(0, 0, QModelIndex()), [&](const QModelIndex &index){ + if (enabled) + return false; + const auto *node = ClazyChecksTree::fromIndex(index); + if (node->kind == ClazyChecksTree::CheckNode && index.column() == NameColumn) { + const bool isChecked = data(index, Qt::CheckStateRole).toInt() == Qt::Checked; + const bool isVisible = isHidden(index); + if (isChecked && isVisible) { + enabled = true; + return false; + } + } + return true; + }); + + return enabled; + } + + bool enableLowerLevels() const { return m_enableLowerLevels; } + void setEnableLowerLevels(bool enable) { m_enableLowerLevels = enable; } + + QSet<QString> topics() const { return m_topics; } + +private: + void buildTree() + { + // Top level node + m_root = new ClazyChecksTree("*", ClazyChecksTree::TopLevelNode); + + for (const ClazyCheckInfo &check : CLAZY_CHECKS) { + // Level node + ClazyChecksTree *&levelNode = m_levelNodes[check.level]; + if (!levelNode) { + levelNode = new ClazyChecksTree(levelDescription(check.level), ClazyChecksTree::LevelNode); + levelNode->parent = m_root; + levelNode->checkInfo.level = check.level; // Pass on the level for sorting + m_root->childDirectories << levelNode; + } + + // Check node + auto checkNode = new ClazyChecksTree(check.name, ClazyChecksTree::CheckNode); + checkNode->parent = levelNode; + checkNode->checkInfo = check; + + levelNode->childDirectories.append(checkNode); + + m_topics.unite(check.topics.toSet()); + } + } + + QVariant data(const QModelIndex &fullIndex, int role = Qt::DisplayRole) const override final + { + if (!fullIndex.isValid() || role == Qt::DecorationRole) + return QVariant(); + const QModelIndex index = this->index(fullIndex.row(), 0, fullIndex.parent()); + const auto *node = ClazyChecksTree::fromIndex(index); + + if (fullIndex.column() == LinkColumn) { + if (role == LinkRole) { + if (node->checkInfo.name.isEmpty()) + return QVariant(); + return QString::fromUtf8(Constants::CLAZY_DOCUMENTATION_URL_TEMPLATE).arg(node->name); + } + if (role == Qt::DisplayRole && node->kind != ClazyChecksTree::CheckNode) + return QVariant(); + + return BaseChecksTreeModel::data(fullIndex, role); } + + if (role == Qt::DisplayRole) + return node->name; + + return ProjectExplorer::SelectableFilesModel::data(index, role); + } + + static QString levelDescription(int level) + { + switch (level) { + case -1: + return tr("Manual Level: Very few false positives"); + case 0: + return tr("Level 0: No false positives"); + case 1: + return tr("Level 1: Very few false positives"); + case 2: + return tr("Level 2: More false positives"); + case 3: + return tr("Level 3: Experimental checks"); + default: + QTC_CHECK(false && "No clazy level description"); + return tr("Level %1").arg(QString::number(level)); + } + } + + 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 { @@ -202,38 +459,137 @@ private: QModelIndex result; traverse(index(0, 0, QModelIndex()), [&](const QModelIndex &index){ - using ProjectExplorer::Tree; if (result.isValid()) return false; - - auto *node = static_cast<Tree *>(index.internalPointer()); - const QString nodeName = node->fullPath.toString(); - if ((check.endsWith("*") && nodeName.startsWith(check.left(check.length() - 1))) - || (!node->isDir && nodeName == check)) { + const auto *node = ClazyChecksTree::fromIndex(index); + if (node->kind == ClazyChecksTree::CheckNode && node->checkInfo.name == check) { result = index; return false; } + return true; + }); + return result; + } - return check.startsWith(nodeName); + QModelIndex indexForTree(const ClazyChecksTree *tree) const { + if (!tree) + return QModelIndex(); + + QModelIndex result; + traverse(index(0, 0, QModelIndex()), [&](const QModelIndex &index){ + if (result.isValid()) + return false; + if (index.internalPointer() == tree) { + result = index; + return false; + } + return true; }); return result; } - static void collectChecks(const ProjectExplorer::Tree *root, QString &checks) + static void collectChecks(const ProjectExplorer::Tree *root, QStringList &checks) { if (root->checked == Qt::Unchecked) return; - if (root->checked == Qt::Checked) { - checks += "," + root->fullPath.toString(); - if (root->isDir) - checks += "*"; + if (root->checked == Qt::Checked && !root->isDir) { + checks.append(root->name); return; } for (const ProjectExplorer::Tree *t : root->childDirectories) collectChecks(t, checks); } - bool m_enabled = true; + static QStringList toStringList(const QVariantList &variantList) + { + QStringList list; + for (auto &item : variantList) + list.append(item.toString()); + return list; + } + +private: + QHash<int, ClazyChecksTree *> m_levelNodes; + QSet<QString> m_topics; + bool m_enableLowerLevels = true; +}; + +class ClazyChecksSortFilterModel : public QSortFilterProxyModel +{ +public: + ClazyChecksSortFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) + {} + + void setTopics(const QStringList &value) + { + m_topics = value; + invalidateFilter(); + } + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override + { + const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + if (!index.isValid()) + return false; + + const auto *node = ClazyChecksTree::fromIndex(index); + if (node->kind == ClazyChecksTree::CheckNode) { + const QStringList topics = node->checkInfo.topics; + return Utils::anyOf(m_topics, [this, topics](const QString &topic) { + return topics.contains(topic); + }); + } + + return true; + } + +private: + // Note that sort order of levels is important for "enableLowerLevels" mode, see setData(). + bool lessThan(const QModelIndex &l, const QModelIndex &r) const override + { + const int leftLevel = adaptLevel(ClazyChecksTree::fromIndex(l)->checkInfo.level); + const int rightLevel = adaptLevel(ClazyChecksTree::fromIndex(r)->checkInfo.level); + + if (leftLevel == rightLevel) + return sourceModel()->data(l).toString() < sourceModel()->data(r).toString(); + return leftLevel < rightLevel; + } + + static bool adaptLevel(int level) + { + if (level == -1) // "Manual Level" + return 1000; + return level; + } + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override + { + if (!index.isValid()) + return false; + + if (role == Qt::CheckStateRole + && static_cast<ClazyChecksTreeModel *>(sourceModel())->enableLowerLevels() + && QSortFilterProxyModel::setData(index, value, role)) { + const auto *node = ClazyChecksTree::fromIndex(mapToSource(index)); + if (node->kind == ClazyChecksTree::LevelNode && node->checkInfo.level >= 0) { + // Rely on the sort order to find the lower level index/node + const auto previousIndex = this->index(index.row() - 1, + index.column(), + index.parent()); + if (previousIndex.isValid() + && ClazyChecksTree::fromIndex(mapToSource(previousIndex))->checkInfo.level + >= 0) { + setData(previousIndex, value, role); + } + } + } + + return QSortFilterProxyModel::setData(index, value, role); + } + +private: + QStringList m_topics; }; ClangDiagnosticConfigsWidget::ClangDiagnosticConfigsWidget(const Core::Id &configToSelect, @@ -241,6 +597,7 @@ ClangDiagnosticConfigsWidget::ClangDiagnosticConfigsWidget(const Core::Id &confi : QWidget(parent) , m_ui(new Ui::ClangDiagnosticConfigsWidget) , m_diagnosticConfigsModel(codeModelSettings()->clangCustomDiagnosticConfigs()) + , m_clazyTreeModel(new ClazyChecksTreeModel()) , m_tidyTreeModel(new TidyChecksTreeModel()) { m_ui->setupUi(this); @@ -256,14 +613,12 @@ ClangDiagnosticConfigsWidget::ClangDiagnosticConfigsWidget(const Core::Id &confi this, &ClangDiagnosticConfigsWidget::onRemoveButtonClicked); connectDiagnosticOptionsChanged(); - connect(m_tidyChecks->checksPrefixesTree, &QTreeView::clicked, - [this](const QModelIndex &index) { - const QString link = m_tidyTreeModel->data(index, Qt::UserRole).toString(); - if (link.isEmpty()) - return; - - QDesktopServices::openUrl(QUrl(link)); - }); + connect(m_tidyChecks->checksPrefixesTree, + &QTreeView::clicked, + [model = m_tidyTreeModel.get()](const QModelIndex &index) { openUrl(model, index); }); + connect(m_clazyChecks->checksView, + &QTreeView::clicked, + [model = m_clazySortFilterProxyModel](const QModelIndex &index) { openUrl(model, index); }); syncWidgetsToModel(configToSelect); } @@ -344,25 +699,12 @@ void ClangDiagnosticConfigsWidget::onClangTidyTreeChanged() updateConfig(config); } -void ClangDiagnosticConfigsWidget::onClazyRadioButtonChanged(bool checked) +void ClangDiagnosticConfigsWidget::onClazyTreeChanged() { - if (!checked) - return; - - QString checks; - if (m_clazyChecks->clazyRadioDisabled->isChecked()) - checks = QString(); - else if (m_clazyChecks->clazyRadioLevel0->isChecked()) - checks = "level0"; - else if (m_clazyChecks->clazyRadioLevel1->isChecked()) - checks = "level1"; - else if (m_clazyChecks->clazyRadioLevel2->isChecked()) - checks = "level2"; - else if (m_clazyChecks->clazyRadioLevel3->isChecked()) - checks = "level3"; + syncClazyChecksGroupBox(); ClangDiagnosticConfig config = selectedConfig(); - config.setClazyChecks(checks); + config.setClazyChecks(m_clazyTreeModel->enabledChecks().join(",")); updateConfig(config); } @@ -518,22 +860,35 @@ void ClangDiagnosticConfigsWidget::syncTidyChecksToTree(const ClangDiagnosticCon void ClangDiagnosticConfigsWidget::syncClazyWidgets(const ClangDiagnosticConfig &config) { + disconnectClazyItemChanged(); + const QString clazyChecks = config.clazyChecks(); - QRadioButton *button = m_clazyChecks->clazyRadioDisabled; - if (clazyChecks.isEmpty()) - button = m_clazyChecks->clazyRadioDisabled; - else if (clazyChecks == "level0") - button = m_clazyChecks->clazyRadioLevel0; - else if (clazyChecks == "level1") - button = m_clazyChecks->clazyRadioLevel1; - else if (clazyChecks == "level2") - button = m_clazyChecks->clazyRadioLevel2; - else if (clazyChecks == "level3") - button = m_clazyChecks->clazyRadioLevel3; + m_clazyTreeModel->enableChecks(clazyChecks.split(',', QString::SkipEmptyParts)); + + syncClazyChecksGroupBox(); - button->setChecked(true); - m_clazyChecksWidget->setEnabled(!config.isReadOnly()); + const bool enabled = !config.isReadOnly(); + m_clazyChecks->topicsResetButton->setEnabled(enabled); + m_clazyChecks->enableLowerLevelsCheckBox->setEnabled(enabled); + selectAll(m_clazyChecks->topicsView); + m_clazyChecks->topicsView->setEnabled(enabled); + m_clazyTreeModel->setEnabled(enabled); + + connectClazyItemChanged(); +} + +void ClangDiagnosticConfigsWidget::syncClazyChecksGroupBox() +{ + const auto isHidden = [this](const QModelIndex &index) { + return !m_clazySortFilterProxyModel->filterAcceptsRow(index.row(), index.parent()); + }; + const bool hasEnabledButHidden = m_clazyTreeModel->hasEnabledButNotVisibleChecks(isHidden); + const QString title = hasEnabledButHidden ? tr("Checks (%1 enabled, some are filtered out)") + : tr("Checks (%1 enabled)"); + + const QStringList checks = m_clazyTreeModel->enabledChecks(); + m_clazyChecks->checksGroupBox->setTitle(title.arg(checks.count())); } void ClangDiagnosticConfigsWidget::updateConfig(const ClangDiagnosticConfig &config) @@ -599,12 +954,16 @@ void ClangDiagnosticConfigsWidget::disconnectClangTidyItemChanged() this, &ClangDiagnosticConfigsWidget::onClangTidyTreeChanged); } -void ClangDiagnosticConfigsWidget::connectClazyRadioButtonClicked(QRadioButton *button) +void ClangDiagnosticConfigsWidget::connectClazyItemChanged() { - connect(button, - &QRadioButton::clicked, - this, - &ClangDiagnosticConfigsWidget::onClazyRadioButtonChanged); + connect(m_clazyTreeModel.get(), &ClazyChecksTreeModel::dataChanged, + this, &ClangDiagnosticConfigsWidget::onClazyTreeChanged); +} + +void ClangDiagnosticConfigsWidget::disconnectClazyItemChanged() +{ + disconnect(m_clazyTreeModel.get(), &ClazyChecksTreeModel::dataChanged, + this, &ClangDiagnosticConfigsWidget::onClazyTreeChanged); } void ClangDiagnosticConfigsWidget::connectConfigChooserCurrentIndex() @@ -644,6 +1003,15 @@ ClangDiagnosticConfigs ClangDiagnosticConfigsWidget::customConfigs() const }); } +static void setupTreeView(QTreeView *view, QAbstractItemModel *model, int expandToDepth = 0) +{ + view->setModel(model); + view->expandToDepth(expandToDepth); + view->header()->setStretchLastSection(false); + view->header()->setSectionResizeMode(0, QHeaderView::Stretch); + view->setHeaderHidden(true); +} + void ClangDiagnosticConfigsWidget::setupTabs() { m_clangBaseChecks.reset(new CppTools::Ui::ClangBaseChecks); @@ -653,20 +1021,45 @@ void ClangDiagnosticConfigsWidget::setupTabs() m_clazyChecks.reset(new CppTools::Ui::ClazyChecks); m_clazyChecksWidget = new QWidget(); m_clazyChecks->setupUi(m_clazyChecksWidget); - - connectClazyRadioButtonClicked(m_clazyChecks->clazyRadioDisabled); - connectClazyRadioButtonClicked(m_clazyChecks->clazyRadioLevel0); - connectClazyRadioButtonClicked(m_clazyChecks->clazyRadioLevel1); - connectClazyRadioButtonClicked(m_clazyChecks->clazyRadioLevel2); - connectClazyRadioButtonClicked(m_clazyChecks->clazyRadioLevel3); + m_clazySortFilterProxyModel = new ClazyChecksSortFilterModel(this); + m_clazySortFilterProxyModel->setSourceModel(m_clazyTreeModel.get()); + setupTreeView(m_clazyChecks->checksView, m_clazySortFilterProxyModel, 2); + m_clazyChecks->checksView->setSortingEnabled(true); + m_clazyChecks->checksView->sortByColumn(0, Qt::AscendingOrder); + auto topicsModel = new QStringListModel(m_clazyTreeModel->topics().toList(), this); + topicsModel->sort(0); + m_clazyChecks->topicsView->setModel(topicsModel); + connect(m_clazyChecks->topicsResetButton, &QPushButton::clicked, [this](){ + selectAll(m_clazyChecks->topicsView); + }); + connect(m_clazyChecks->topicsView->selectionModel(), + &QItemSelectionModel::selectionChanged, + [this, topicsModel](const QItemSelection &, const QItemSelection &) { + const auto indexes = m_clazyChecks->topicsView->selectionModel()->selectedIndexes(); + const QStringList topics + = Utils::transform(indexes, [topicsModel](const QModelIndex &index) { + return topicsModel->data(index).toString(); + }); + m_clazySortFilterProxyModel->setTopics(topics); + this->syncClazyChecksGroupBox(); + }); + + selectAll(m_clazyChecks->topicsView); + connect(m_clazyChecks->enableLowerLevelsCheckBox, &QCheckBox::stateChanged, [this](int) { + const bool enable = m_clazyChecks->enableLowerLevelsCheckBox->isChecked(); + m_clazyTreeModel->setEnableLowerLevels(enable); + codeModelSettings()->setEnableLowerClazyLevels( + m_clazyChecks->enableLowerLevelsCheckBox->isChecked()); + }); + const Qt::CheckState checkEnableLowerClazyLevels + = codeModelSettings()->enableLowerClazyLevels() ? Qt::Checked : Qt::Unchecked; + m_clazyChecks->enableLowerLevelsCheckBox->setCheckState(checkEnableLowerClazyLevels); m_tidyChecks.reset(new CppTools::Ui::TidyChecks); m_tidyChecksWidget = new QWidget(); m_tidyChecks->setupUi(m_tidyChecksWidget); - m_tidyChecks->checksPrefixesTree->setModel(m_tidyTreeModel.get()); - m_tidyChecks->checksPrefixesTree->expandToDepth(0); - m_tidyChecks->checksPrefixesTree->header()->setStretchLastSection(false); - m_tidyChecks->checksPrefixesTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + setupTreeView(m_tidyChecks->checksPrefixesTree, m_tidyTreeModel.get()); + connect(m_tidyChecks->plainTextEditButton, &QPushButton::clicked, this, [this]() { const bool readOnly = selectedConfig().isReadOnly(); @@ -702,6 +1095,7 @@ void ClangDiagnosticConfigsWidget::setupTabs() }); connectClangTidyItemChanged(); + connectClazyItemChanged(); m_ui->tabWidget->addTab(m_clangBaseChecksWidget, tr("Clang")); m_ui->tabWidget->addTab(m_tidyChecksWidget, tr("Clang-Tidy")); diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.h b/src/plugins/cpptools/clangdiagnosticconfigswidget.h index 39753a74c6..502f5a62d5 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.h +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.h @@ -38,7 +38,6 @@ QT_BEGIN_NAMESPACE class QListWidgetItem; class QPushButton; -class QRadioButton; QT_END_NAMESPACE namespace CppTools { @@ -51,6 +50,8 @@ class TidyChecks; } class TidyChecksTreeModel; +class ClazyChecksTreeModel; +class ClazyChecksSortFilterModel; class CPPTOOLS_EXPORT ClangDiagnosticConfigsWidget : public QWidget { @@ -73,8 +74,8 @@ private: void onRemoveButtonClicked(); void onClangTidyModeChanged(int index); void onClangTidyTreeChanged(); + void onClazyTreeChanged(); void onClangTidyTreeItemClicked(const QModelIndex &index); - void onClazyRadioButtonChanged(bool checked); void onDiagnosticOptionsEdited(); @@ -83,6 +84,7 @@ private: void syncOtherWidgetsToComboBox(); void syncClangTidyWidgets(const ClangDiagnosticConfig &config); void syncClazyWidgets(const ClangDiagnosticConfig &config); + void syncClazyChecksGroupBox(); void syncTidyChecksToTree(const ClangDiagnosticConfig &config); void updateConfig(const CppTools::ClangDiagnosticConfig &config); @@ -97,7 +99,8 @@ private: void connectClangTidyItemChanged(); void disconnectClangTidyItemChanged(); - void connectClazyRadioButtonClicked(QRadioButton *button); + void connectClazyItemChanged(); + void disconnectClazyItemChanged(); void connectConfigChooserCurrentIndex(); void disconnectConfigChooserCurrentIndex(); @@ -114,6 +117,8 @@ private: std::unique_ptr<CppTools::Ui::ClazyChecks> m_clazyChecks; QWidget *m_clazyChecksWidget = nullptr; + std::unique_ptr<ClazyChecksTreeModel> m_clazyTreeModel; + ClazyChecksSortFilterModel *m_clazySortFilterProxyModel = nullptr; std::unique_ptr<CppTools::Ui::TidyChecks> m_tidyChecks; QWidget *m_tidyChecksWidget = nullptr; diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.ui b/src/plugins/cpptools/clangdiagnosticconfigswidget.ui index e80ccef4fe..af4824ae22 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.ui +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.ui @@ -105,7 +105,14 @@ </layout> </item> <item> - <widget class="QTabWidget" name="tabWidget"/> + <widget class="QTabWidget" name="tabWidget"> + <property name="minimumSize"> + <size> + <width>700</width> + <height>500</height> + </size> + </property> + </widget> </item> </layout> </item> diff --git a/src/plugins/cpptools/clazychecks.ui b/src/plugins/cpptools/clazychecks.ui index 6502323360..34d9aaf0c8 100644 --- a/src/plugins/cpptools/clazychecks.ui +++ b/src/plugins/cpptools/clazychecks.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>609</width> - <height>220</height> + <width>700</width> + <height>500</height> </rect> </property> <property name="sizePolicy"> @@ -23,7 +23,7 @@ <item> <widget class="QLabel" name="label"> <property name="text"> - <string>Each level adds checks to the previous level. For more information, see <a href="https://github.com/KDE/clazy">clazy's homepage</a>.</string> + <string>See <a href="https://github.com/KDE/clazy">clazy's homepage</a> for more information.</string> </property> <property name="openExternalLinks"> <bool>true</bool> @@ -31,64 +31,74 @@ </widget> </item> <item> - <widget class="QRadioButton" name="clazyRadioDisabled"> - <property name="text"> - <string>Disabled</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="clazyRadioLevel0"> - <property name="toolTip"> - <string/> - </property> - <property name="text"> - <string>Level 0: No false positives</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="clazyRadioLevel1"> - <property name="toolTip"> - <string/> - </property> - <property name="text"> - <string>Level 1: Very few false positives</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="clazyRadioLevel2"> - <property name="toolTip"> - <string/> - </property> - <property name="text"> - <string>Level 2: More false positives</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="clazyRadioLevel3"> - <property name="toolTip"> - <string>Not always correct, possibly very noisy, might require a knowledgeable developer to review, might have a very big rate of false-positives, might have bugs.</string> - </property> - <property name="text"> - <string>Level 3: Experimental checks</string> - </property> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>34</height> - </size> - </property> - </spacer> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Topic Filter</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QPushButton" name="topicsResetButton"> + <property name="text"> + <string>Reset to All</string> + </property> + </widget> + </item> + <item> + <widget class="QListView" name="topicsView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>150</width> + <height>16777215</height> + </size> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="checksGroupBox"> + <property name="title"> + <string>Checks</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="enableLowerLevelsCheckBox"> + <property name="toolTip"> + <string>When enabling a level explicitly, also enable lower levels (Clazy semantic).</string> + </property> + <property name="text"> + <string>Enable lower levels automatically</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTreeView" name="checksView"/> + </item> + </layout> + </widget> + </item> + </layout> </item> </layout> </widget> diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index c5aee2089b..235066e6a0 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -64,6 +64,9 @@ static QString clangDiagnosticConfigsArrayClangTidyModeKey() static QString clangDiagnosticConfigsArrayClazyChecksKey() { return QLatin1String("clazyChecks"); } +static QString enableLowerClazyLevelsKey() +{ return QLatin1String("enableLowerClazyLevels"); } + static QString pchUsageKey() { return QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_PCH_USAGE); } @@ -124,6 +127,8 @@ void CppCodeModelSettings::fromSettings(QSettings *s) setClangDiagnosticConfigId(initialClangDiagnosticConfigId()); } + setEnableLowerClazyLevels(s->value(enableLowerClazyLevelsKey(), true).toBool()); + const QVariant pchUsageVariant = s->value(pchUsageKey(), initialPchUsage()); setPCHUsage(static_cast<PCHUsage>(pchUsageVariant.toInt())); @@ -165,6 +170,7 @@ void CppCodeModelSettings::toSettings(QSettings *s) s->endArray(); s->setValue(clangDiagnosticConfigKey(), clangDiagnosticConfigId().toSetting()); + s->setValue(enableLowerClazyLevelsKey(), enableLowerClazyLevels()); s->setValue(pchUsageKey(), pchUsage()); s->setValue(interpretAmbiguousHeadersAsCHeadersKey(), interpretAmbigiousHeadersAsCHeaders()); @@ -256,3 +262,13 @@ void CppCodeModelSettings::setIndexerFileSizeLimitInMb(int sizeInMB) { m_indexerFileSizeLimitInMB = sizeInMB; } + +bool CppCodeModelSettings::enableLowerClazyLevels() const +{ + return m_enableLowerClazyLevels; +} + +void CppCodeModelSettings::setEnableLowerClazyLevels(bool yesno) +{ + m_enableLowerClazyLevels = yesno; +} diff --git a/src/plugins/cpptools/cppcodemodelsettings.h b/src/plugins/cpptools/cppcodemodelsettings.h index d1783711f1..b19d6e3e62 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.h +++ b/src/plugins/cpptools/cppcodemodelsettings.h @@ -61,6 +61,9 @@ public: ClangDiagnosticConfigs clangCustomDiagnosticConfigs() const; void setClangCustomDiagnosticConfigs(const ClangDiagnosticConfigs &configs); + bool enableLowerClazyLevels() const; + void setEnableLowerClazyLevels(bool yesno); + PCHUsage pchUsage() const; void setPCHUsage(PCHUsage pchUsage); @@ -84,6 +87,7 @@ private: int m_indexerFileSizeLimitInMB = 5; ClangDiagnosticConfigs m_clangCustomDiagnosticConfigs; Core::Id m_clangDiagnosticConfigId; + bool m_enableLowerClazyLevels = true; // For UI behavior only }; } // namespace CppTools diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 8e81066123..a55e805af4 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -103,7 +103,8 @@ HEADERS += \ cppmodelmanagerinterface.h \ cppbuiltinmodelmanagersupport.h \ headerpathfilter.h \ - cppkitinfo.h + cppkitinfo.h \ + cpptools_clazychecks.h SOURCES += \ abstracteditorsupport.cpp \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index d1d30296db..89062d3a21 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -158,6 +158,7 @@ Project { "cppsourceprocessor.h", "cpptools.qrc", "cpptools_clangtidychecks.h", + "cpptools_clazychecks.h", "cpptools_global.h", "cpptools_utils.h", "cpptoolsbridge.cpp", diff --git a/src/plugins/cpptools/cpptools_clazychecks.h b/src/plugins/cpptools/cpptools_clazychecks.h new file mode 100644 index 0000000000..1c60e4f893 --- /dev/null +++ b/src/plugins/cpptools/cpptools_clazychecks.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <vector> + +namespace CppTools { +namespace Constants { + +class ClazyCheckInfo +{ +public: + bool isValid() const { return !name.isEmpty() && level >= -1; } + + QString name; + int level = -1; // "Manual level" + QStringList topics; +}; +using ClazyCheckInfos = std::vector<ClazyCheckInfo>; + +// CLANG-UPGRADE-CHECK: Run 'scripts/generateClazyChecks.py' after Clang upgrade to +// update this header. +static const ClazyCheckInfos CLAZY_CHECKS = { + {"qt-keywords", -1, {}}, + {"ifndef-define-typo", -1, {"bug"}}, + {"inefficient-qlist", -1, {"containers","performance"}}, + {"isempty-vs-count", -1, {"readability"}}, + {"qrequiredresult-candidates", -1, {"bug"}}, + {"qstring-varargs", -1, {"bug"}}, + {"qt4-qstring-from-array", -1, {"qt4","qstring"}}, + {"tr-non-literal", -1, {"bug"}}, + {"raw-environment-function", -1, {"bug"}}, + {"container-inside-loop", -1, {"containers","performance"}}, + {"qhash-with-char-pointer-key", -1, {"cpp","bug"}}, + {"connect-by-name", 0, {"bug","readability"}}, + {"connect-non-signal", 0, {"bug"}}, + {"wrong-qevent-cast", 0, {"bug"}}, + {"lambda-in-connect", 0, {"bug"}}, + {"lambda-unique-connection", 0, {"bug"}}, + {"qdatetime-utc", 0, {"performance"}}, + {"qgetenv", 0, {"performance"}}, + {"qstring-insensitive-allocation", 0, {"performance","qstring"}}, + {"fully-qualified-moc-types", 0, {"bug","qml"}}, + {"qvariant-template-instantiation", 0, {"performance"}}, + {"unused-non-trivial-variable", 0, {"readability"}}, + {"connect-not-normalized", 0, {"performance"}}, + {"mutable-container-key", 0, {"containers","bug"}}, + {"qenums", 0, {"deprecation"}}, + {"qmap-with-pointer-key", 0, {"containers","performance"}}, + {"qstring-ref", 0, {"performance","qstring"}}, + {"strict-iterators", 0, {"containers","performance","bug"}}, + {"writing-to-temporary", 0, {"bug"}}, + {"container-anti-pattern", 0, {"containers","performance"}}, + {"qcolor-from-literal", 0, {"performance"}}, + {"qfileinfo-exists", 0, {"performance"}}, + {"qstring-arg", 0, {"performance","qstring"}}, + {"empty-qstringliteral", 0, {"performance"}}, + {"qt-macros", 0, {"bug"}}, + {"temporary-iterator", 0, {"containers","bug"}}, + {"wrong-qglobalstatic", 0, {"performance"}}, + {"lowercase-qml-type-name", 0, {"qml","bug"}}, + {"auto-unexpected-qstringbuilder", 1, {"bug","qstring"}}, + {"connect-3arg-lambda", 1, {"bug"}}, + {"const-signal-or-slot", 1, {"readability","bug"}}, + {"detaching-temporary", 1, {"containers","performance"}}, + {"foreach", 1, {"containers","performance"}}, + {"incorrect-emit", 1, {"readability"}}, + {"inefficient-qlist-soft", 1, {"containers","performance"}}, + {"install-event-filter", 1, {"bug"}}, + {"non-pod-global-static", 1, {"performance"}}, + {"post-event", 1, {"bug"}}, + {"qdeleteall", 1, {"containers","performance"}}, + {"qlatin1string-non-ascii", 1, {"bug","qstring"}}, + {"qproperty-without-notify", 1, {"bug"}}, + {"qstring-left", 1, {"bug","performance","qstring"}}, + {"range-loop", 1, {"containers","performance"}}, + {"returning-data-from-temporary", 1, {"bug"}}, + {"rule-of-two-soft", 1, {"cpp","bug"}}, + {"child-event-qobject-cast", 1, {"bug"}}, + {"virtual-signal", 1, {"bug","readability"}}, + {"overridden-signal", 1, {"bug","readability"}}, + {"qhash-namespace", 1, {"bug"}}, + {"skipped-base-method", 1, {"bug","cpp"}}, + {"unneeded-cast", 3, {"cpp","readability"}}, + {"ctor-missing-parent-argument", 2, {"bug"}}, + {"base-class-event", 2, {"bug"}}, + {"copyable-polymorphic", 2, {"cpp","bug"}}, + {"function-args-by-ref", 2, {"cpp","performance"}}, + {"function-args-by-value", 2, {"cpp","performance"}}, + {"global-const-char-pointer", 2, {"cpp","performance"}}, + {"implicit-casts", 2, {"cpp","bug"}}, + {"missing-qobject-macro", 2, {"bug"}}, + {"missing-typeinfo", 2, {"containers","performance"}}, + {"old-style-connect", 2, {"performance"}}, + {"qstring-allocations", 2, {"performance","qstring"}}, + {"returning-void-expression", 2, {"readability","cpp"}}, + {"rule-of-three", 2, {"cpp","bug"}}, + {"virtual-call-ctor", 2, {"cpp","bug"}}, + {"static-pmf", 2, {"bug"}}, + {"assert-with-side-effects", 3, {"bug"}}, + {"detaching-member", 3, {"containers","performance"}}, + {"thread-with-slots", 3, {"bug"}}, + {"reserve-candidates", 3, {"containers"}} +}; + +} // namespace Constants +} // namespace CppTools diff --git a/src/plugins/cpptools/cpptoolsconstants.h b/src/plugins/cpptools/cpptoolsconstants.h index 645b59af02..07c6c64e7d 100644 --- a/src/plugins/cpptools/cpptoolsconstants.h +++ b/src/plugins/cpptools/cpptoolsconstants.h @@ -101,5 +101,8 @@ const char SYMBOLS_FIND_FILTER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("CppTools", "C constexpr const char TIDY_DOCUMENTATION_URL_TEMPLATE[] = "https://releases.llvm.org/7.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/%1.html"; +constexpr const char CLAZY_DOCUMENTATION_URL_TEMPLATE[] + = "https://github.com/KDE/clazy/blob/master/docs/checks/README-%1.md"; + } // namespace Constants } // namespace CppTools |