aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@qt.io>2019-01-14 15:06:28 +0100
committerNikolai Kosjar <nikolai.kosjar@qt.io>2019-01-24 07:39:07 +0000
commit7315d9a47c432b68f295f3d4fb72e7e34d76770a (patch)
tree85c83e670dcc6e85492d35a94cb1e6a943d17f1e
parent4acf2a1df1c149c66c1ba6903b5708c7c1adaa66 (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-xscripts/generateClazyChecks.py131
-rw-r--r--src/plugins/cpptools/clangdiagnosticconfigsselectionwidget.cpp6
-rw-r--r--src/plugins/cpptools/clangdiagnosticconfigswidget.cpp604
-rw-r--r--src/plugins/cpptools/clangdiagnosticconfigswidget.h11
-rw-r--r--src/plugins/cpptools/clangdiagnosticconfigswidget.ui9
-rw-r--r--src/plugins/cpptools/clazychecks.ui132
-rw-r--r--src/plugins/cpptools/cppcodemodelsettings.cpp16
-rw-r--r--src/plugins/cpptools/cppcodemodelsettings.h4
-rw-r--r--src/plugins/cpptools/cpptools.pro3
-rw-r--r--src/plugins/cpptools/cpptools.qbs1
-rw-r--r--src/plugins/cpptools/cpptools_clazychecks.h130
-rw-r--r--src/plugins/cpptools/cpptoolsconstants.h3
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 &lt;a href=&quot;https://github.com/KDE/clazy&quot;&gt;clazy's homepage&lt;/a&gt;.</string>
+ <string>See &lt;a href=&quot;https://github.com/KDE/clazy&quot;&gt;clazy's homepage&lt;/a&gt; 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