Clang-tidy: add configurable predefined checkset selections

parent dcbd9f90
......@@ -24,6 +24,9 @@ set(kdevclangtidy_PART_SRCS
job.cpp
plugin.cpp
checkset.cpp
checksetselection.cpp
checksetselectionlock.cpp
checksetselectionmanager.cpp
config/clangtidyprojectconfigpage.cpp
config/clangtidypreferences.cpp
......@@ -32,6 +35,9 @@ set(kdevclangtidy_PART_SRCS
config/checklistitemproxystyle.cpp
config/checklistmodel.cpp
config/checkgroup.cpp
config/checksetselectioncombobox.cpp
config/checksetselectionlistmodel.cpp
config/checksetmanagewidget.cpp
parsers/clangtidyparser.cpp
# disable for now:
......@@ -40,6 +46,7 @@ set(kdevclangtidy_PART_SRCS
)
ki18n_wrap_ui(kdevclangtidy_PART_SRCS
config/checksetmanagewidget.ui
config/clangtidypreferences.ui
config/clangtidyprojectconfigpage.ui
)
......
......@@ -25,6 +25,7 @@
// plugin
#include "plugin.h"
#include "job.h"
#include "checksetselectionmanager.h"
#include <clangtidyconfig.h>
#include <clangtidyprojectconfig.h>
// KDevPlatform
......@@ -38,7 +39,7 @@
namespace ClangTidy
{
Analyzer::Analyzer(Plugin* plugin, QObject* parent)
Analyzer::Analyzer(Plugin* plugin, CheckSetSelectionManager* checkSetSelectionManager, QObject* parent)
: KDevelop::CompileAnalyzer(plugin,
i18n("Clang-Tidy"), QStringLiteral("dialog-ok"),
QStringLiteral("clangtidy_file"), QStringLiteral("clangtidy_project"),
......@@ -50,6 +51,7 @@ Analyzer::Analyzer(Plugin* plugin, QObject* parent)
KDevelop::ProblemModel::CanByPassScopeFilter,
parent)
, m_plugin(plugin)
, m_checkSetSelectionManager(checkSetSelectionManager)
{
}
......@@ -77,7 +79,11 @@ KDevelop::CompileAnalyzeJob * Analyzer::createJob(KDevelop::IProject* project,
params.additionalParameters = projectSettings.additionalParameters();
const auto enabledChecks = projectSettings.enabledChecks();
QString checkSetSelectionId = projectSettings.checkSetSelection();
if (checkSetSelectionId == QLatin1String("Default")) {
checkSetSelectionId = m_checkSetSelectionManager->defaultCheckSetSelectionId();
}
const auto enabledChecks = checkSetSelectionId.isEmpty() ? projectSettings.enabledChecks() : m_checkSetSelectionManager->checkSetSelection(checkSetSelectionId).selectionAsString();
if (!enabledChecks.isEmpty()) {
params.enabledChecks = enabledChecks;
} else {
......
......@@ -29,13 +29,14 @@ namespace ClangTidy
{
class Plugin;
class CheckSetSelectionManager;
class Analyzer : public KDevelop::CompileAnalyzer
{
Q_OBJECT
public:
Analyzer(Plugin* plugin, QObject* parent);
Analyzer(Plugin* plugin, CheckSetSelectionManager* checkSetSelectionManager, QObject* parent);
~Analyzer();
protected:
......@@ -44,6 +45,7 @@ protected:
private:
Plugin* const m_plugin;
CheckSetSelectionManager* const m_checkSetSelectionManager;
};
}
......
/*
* This file is part of KDevelop
*
* Copyright 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "checksetselection.h"
// Qt
#include <QString>
namespace ClangTidy
{
class CheckSetSelectionPrivate : public QSharedData
{
public:
QString id;
QString name;
QString selection;
};
CheckSetSelection::CheckSetSelection()
: d(new CheckSetSelectionPrivate)
{
}
CheckSetSelection::CheckSetSelection(const CheckSetSelection& other) = default;
CheckSetSelection::~CheckSetSelection() = default;
CheckSetSelection& CheckSetSelection::operator=(const CheckSetSelection& other) = default;
QString CheckSetSelection::selectionAsString() const
{
return d->selection;
}
QString CheckSetSelection::id() const
{
return d->id;
}
QString CheckSetSelection::name() const
{
return d->name;
}
void CheckSetSelection::setId(const QString& id)
{
d->id = id;
}
void CheckSetSelection::setSelection(const QString& selection)
{
d->selection = selection;
}
void CheckSetSelection::setName(const QString& name)
{
d->name = name;
}
}
/*
* This file is part of KDevelop
*
* Copyright 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef CLANGTIDY_CHECKSETSELECTION_H
#define CLANGTIDY_CHECKSETSELECTION_H
// Qt
#include <QSharedDataPointer>
class QString;
namespace ClangTidy
{
class CheckSetSelectionPrivate;
class CheckSetSelection
{
public:
CheckSetSelection();
CheckSetSelection(const CheckSetSelection& other);
~CheckSetSelection();
public:
CheckSetSelection& operator=(const CheckSetSelection& other);
public:
void setId(const QString& id);
QString id() const;
public:
void setName(const QString& name);
QString name() const;
public:
QString selectionAsString() const;
void setSelection(const QString& selection);
private:
QSharedDataPointer<CheckSetSelectionPrivate> d;
};
}
#endif
/*
* This file is part of KDevelop
*
* Copyright 2010-2012,2020 Friedrich W. H. Kossebau <kossebau@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "checksetselectionlock.h"
// plugin
#include <debug.h>
// Qt
#include <QLockFile>
#include <QSharedPointer>
#include <QString>
namespace ClangTidy {
class CheckSetSelectionLockPrivate : public QSharedData
{
public:
CheckSetSelectionLockPrivate(const QString& fileName,
const QString& checkSetSelectionId);
public:
QSharedPointer<QLockFile> lockFile;
QString checkSetSelectionId;
};
static QString checkSetSelectionFileLockPath(const QString& checkSetSelectionFilePath)
{
// TODO: just ".lock" conflicts with KConfig(?) using the same
return checkSetSelectionFilePath + QLatin1String(".kdevlock");
}
CheckSetSelectionLockPrivate::CheckSetSelectionLockPrivate(const QString& fileName,
const QString& id)
: lockFile(new QLockFile(fileName.isEmpty() ? fileName : checkSetSelectionFileLockPath(fileName)))
, checkSetSelectionId(id)
{
if (!fileName.isEmpty()) {
if (!lockFile->tryLock(1000)) {
qCWarning(KDEV_CLANGTIDY)
<< "Failed to acquire lock file" << fileName
<< "error =" << lockFile->error();
}
}
}
CheckSetSelectionLock::CheckSetSelectionLock(const QString& fileName,
const QString& checkSetSelectionId)
: d(new CheckSetSelectionLockPrivate(fileName, checkSetSelectionId))
{
}
CheckSetSelectionLock::CheckSetSelectionLock(const CheckSetSelectionLock& other) = default;
CheckSetSelectionLock::~CheckSetSelectionLock() = default;
CheckSetSelectionLock& CheckSetSelectionLock::operator=(const CheckSetSelectionLock& other) = default;
void CheckSetSelectionLock::unlock()
{
d->lockFile->unlock();
}
bool CheckSetSelectionLock::isLocked() const
{
return d->lockFile->isLocked();
}
QString CheckSetSelectionLock::checkSetSelectionId() const
{
return d->checkSetSelectionId;
}
}
/*
* This file is part of KDevelop
*
* Copyright 2010-2012,2020 Friedrich W. H. Kossebau <kossebau@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef CLANGTIDY_CHECKSETSELECTIONLOCK_H
#define CLANGTIDY_CHECKSETSELECTIONLOCK_H
// Qt
#include <QSharedDataPointer>
class QString;
namespace ClangTidy {
class CheckSetSelectionLockPrivate;
class CheckSetSelectionLock
{
friend class CheckSetSelectionManager;
protected:
CheckSetSelectionLock(const QString& fileName,
const QString& checkSetSelectionId);
public:
CheckSetSelectionLock(const CheckSetSelectionLock& other);
~CheckSetSelectionLock();
public:
CheckSetSelectionLock& operator=(const CheckSetSelectionLock& other);
public:
void unlock();
bool isLocked() const;
QString checkSetSelectionId() const;
private:
QSharedDataPointer<CheckSetSelectionLockPrivate> d;
};
}
#endif
/*
* This file is part of KDevelop
*
* Copyright 2010-2012,2020 Friedrich W. H. Kossebau <kossebau@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "checksetselectionmanager.h"
// plugin
#include "checksetselectionlock.h"
#include "debug.h"
// KF
#include <KConfigGroup>
#include <KConfig>
#include <KDirWatch>
// Qt
#include <QFileInfo>
#include <QDir>
#include <QUuid>
namespace ClangTidy {
QStringList checkSetSelectionFileNameFilter() { return QStringList { QStringLiteral("*.kdevctcs"), QStringLiteral("*.kdevlock") }; }
inline QLatin1String checkSetSelectionFileSuffix() { return QLatin1String(".kdevctcs"); }
inline QLatin1String checkSetSelectionDirSubPath() { return QLatin1String("/kdevclangtidy/checksetselections"); }
inline QLatin1String defaultCheckSetSelectionFileSubPath() { return QLatin1String("/kdevclangtidy/defaultchecksetselection"); }
QVector<QString> lockedCheckSetSelectionIds(const CheckSetSelectionFileInfoLookup& checkSetSelectionFileInfoLookup)
{
QVector<QString> result;
for (auto it = checkSetSelectionFileInfoLookup.constBegin(), end = checkSetSelectionFileInfoLookup.constEnd();
it != end; ++it) {
if (it.value().isLocked()) {
result.append(it.key());
}
}
return result;
}
void updateLockStatus(CheckSetSelectionFileInfoLookup& checkSetSelectionFileInfoLookup,
const QVector<QString>& lockedCheckSetSelectionIds,
const QVector<QString>& unlockedCheckSetSelectionIds)
{
if (lockedCheckSetSelectionIds.isEmpty() && unlockedCheckSetSelectionIds.isEmpty()) {
return;
}
for (auto it = checkSetSelectionFileInfoLookup.begin(), end = checkSetSelectionFileInfoLookup.end();
it != end; ++it) {
bool isLocked;
if (lockedCheckSetSelectionIds.contains(it.key())) {
isLocked = true;
} else if (unlockedCheckSetSelectionIds.contains(it.key())) {
isLocked = false;
} else {
continue;
}
it.value().setLocked(isLocked);
}
}
QString defaultCheckSetSelectionFilePath()
{
return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + defaultCheckSetSelectionFileSubPath();
}
QString checkSetSelectionFilePath(const QString& checkSetSelectionId)
{
return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
+ checkSetSelectionDirSubPath() + QLatin1Char('/') + checkSetSelectionId + checkSetSelectionFileSuffix();
}
QString checkSetSelectionFileName(const QString& checkSetSelectionId)
{
return checkSetSelectionId + checkSetSelectionFileSuffix();
}
// TODO: add global lock
// TODO: make calls async
// TODO: only load checkset setlections on demand
CheckSetSelectionManager::CheckSetSelectionManager()
: m_checkSetSelectionFileWatcher(new KDirWatch(this))
{
connect(m_checkSetSelectionFileWatcher, &KDirWatch::dirty,
this, &CheckSetSelectionManager::onCheckSetSelectionsFolderChanged);
// get all folder where checkSetSelections could be stored
const QStringList dataFolderPaths =
QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
for (const QString& dataFolderPath : dataFolderPaths) {
const QString checkSetSelectionFolderPath = dataFolderPath + checkSetSelectionDirSubPath();
// watch folder for changes
m_checkSetSelectionFileWatcher->addDir(checkSetSelectionFolderPath, KDirWatch::WatchDirOnly);
// read current files
onCheckSetSelectionsFolderChanged(checkSetSelectionFolderPath);
}
// default checkset selection
// While there is no proper config syncing offer in the used frameworks, use a
// single file with the id as content as workaround and watch for it changing
auto* defaultCheckSetSelectionWatcher = new KDirWatch(this);
connect(defaultCheckSetSelectionWatcher, &KDirWatch::created,
this, &CheckSetSelectionManager::onDefaultCheckSetSelectionChanged);
connect(defaultCheckSetSelectionWatcher, &KDirWatch::dirty,
this, &CheckSetSelectionManager::onDefaultCheckSetSelectionChanged);
const QString _defaultCheckSetSelectionFilePath = defaultCheckSetSelectionFilePath();
defaultCheckSetSelectionWatcher->addFile(_defaultCheckSetSelectionFilePath);
onDefaultCheckSetSelectionChanged(_defaultCheckSetSelectionFilePath);
// report any problems with existing checkset selections?
}
CheckSetSelectionManager::~CheckSetSelectionManager() = default;
int CheckSetSelectionManager::checkSetSelectionsCount() const
{
return m_checkSetSelections.count();
}
QVector<CheckSetSelection> CheckSetSelectionManager::checkSetSelections() const
{
return m_checkSetSelections;
}
CheckSetSelection CheckSetSelectionManager::checkSetSelection(const QString& checkSetSelectionId) const
{
CheckSetSelection result;
for (const CheckSetSelection& checkSetSelection : m_checkSetSelections) {
if (checkSetSelection.id() == checkSetSelectionId) {
result = checkSetSelection;
break;
}
}
return result;
}
QString CheckSetSelectionManager::defaultCheckSetSelectionId() const
{
return m_defaultCheckSetSelectionId;
}
CheckSetSelection CheckSetSelectionManager::defaultCheckSetSelection() const
{
return checkSetSelection(m_defaultCheckSetSelectionId);
}
bool CheckSetSelectionManager::isCheckSetSelectionLocked(const QString& checkSetSelectionId) const
{
bool result = false;
// search in all folders for the info
for (const CheckSetSelectionFileInfoLookup& checkSetSelectionFileInfoLookup : m_checkSetSelectionFileInfoLookupPerFolder) {
CheckSetSelectionFileInfoLookup::ConstIterator it =
checkSetSelectionFileInfoLookup.find(checkSetSelectionId);
if (it != checkSetSelectionFileInfoLookup.constEnd()) {
result = it->isLocked();
break;
}
}
return result;
}
void CheckSetSelectionManager::saveCheckSetSelections(QVector<CheckSetSelection>& checkSetSelections)
{
// TODO: do not save if locked by someone else -> needs passing of our lock? or just registering our own and check?
// create and set unique id
std::for_each(checkSetSelections.begin(), checkSetSelections.end(), [this](CheckSetSelection& checkSetSelection) {
const QString checkSetSelectionId = checkSetSelection.id();
bool needsId = true;
if (!checkSetSelectionId.isEmpty()) {
// already existing?
auto hasCheckSetSelectionId = [&checkSetSelectionId] (const CheckSetSelection& existingProfile) {
return (checkSetSelectionId == existingProfile.id());
};
if (std::any_of(m_checkSetSelections.constBegin(), m_checkSetSelections.constEnd(), hasCheckSetSelectionId)) {
needsId = false;
}
}
// set new uuid for non-existing
if (needsId) {
checkSetSelection.setId(QUuid::createUuid().toString());
}
saveCheckSetSelection(checkSetSelection);
});
}
void CheckSetSelectionManager::removeCheckSetSelections(const QVector<QString>& checkSetSelectionIds)
{
for (const QString& checkSetSelectionId : checkSetSelectionIds) {
removeCheckSetSelection(checkSetSelectionId);