File indexing completed on 2024-05-12 04:39:20

0001 /*
0002     SPDX-FileCopyrightText: 2018 Friedrich W. H. Kossebau <kossebau@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "checkgroup.h"
0008 
0009 // plugin
0010 #include <debug.h>
0011 // Qt
0012 #include <QRegularExpression>
0013 
0014 namespace ClangTidy
0015 {
0016 
0017 CheckGroup* CheckGroup::fromPlainList(const QStringList& checks)
0018 {
0019     auto* result = new CheckGroup;
0020     // root group cannot inherit
0021     result->m_groupEnabledState = Disabled;
0022 
0023     for (const auto& checkName : checks) {
0024         result->addCheck(checkName);
0025     }
0026 
0027     return result;
0028 }
0029 
0030 CheckGroup::CheckGroup(const QString& name, CheckGroup* superGroup)
0031     : m_superGroup(superGroup)
0032     , m_prefix(name)
0033 {
0034 }
0035 
0036 CheckGroup::~CheckGroup()
0037 {
0038     qDeleteAll(m_subGroups);
0039 }
0040 
0041 
0042 void CheckGroup::addCheck(const QString& checkName)
0043 {
0044     const int nextSplitOffset = checkName.indexOf(QRegularExpression(QStringLiteral("[-.]")), m_prefix.length()); 
0045 
0046     // 1. check if looking at last section, if so add to current group
0047     if (nextSplitOffset < 0) {
0048         m_checks.append(checkName);
0049         m_checksEnabledStates.append(EnabledInherited);
0050         return;
0051     }
0052 
0053     // 2. check if existing subgroup for prefix, if so add to that
0054     // include separator into subgroup name
0055     const QStringRef subGroupName = checkName.leftRef(nextSplitOffset + 1); 
0056     for (auto* subGroup : qAsConst(m_subGroups)) {
0057         if (subGroup->prefix() == subGroupName) {
0058             subGroup->addCheck(checkName);
0059             return;
0060         }
0061     }
0062 
0063     // 3. check if existing check with same prefix, if so create subgroup for them
0064     for (int i = 0; i < m_checks.size(); ++i) {
0065         const auto& listedCheckName = m_checks[i];
0066         if (listedCheckName.startsWith(subGroupName)) {
0067             auto* newSubGroup = new CheckGroup(subGroupName.toString(), this);
0068             newSubGroup->addCheck(listedCheckName);
0069             newSubGroup->addCheck(checkName);
0070             m_subGroups.append(newSubGroup);
0071             m_checks.removeAt(i);
0072             m_checksEnabledStates.removeAt(i);
0073             return;
0074         }
0075     }
0076 
0077     // 4. add to current group
0078     m_checks.append(checkName);
0079     m_checksEnabledStates.append(EnabledInherited);
0080 }
0081 
0082 void CheckGroup::setEnabledChecks(const QStringList& rules)
0083 {
0084     // TODO: optimize & check first rule if not matching all
0085     resetEnabledState(Disabled);
0086 
0087     for (const auto& rule : rules) {
0088         int matchStartPos = 0;
0089         EnabledState enabledState = Enabled;
0090         if (rule.startsWith(QLatin1Char('-'))) {
0091             matchStartPos = 1;
0092             enabledState = Disabled;
0093         }
0094         applyEnabledRule(rule.midRef(matchStartPos), enabledState);
0095     }
0096 
0097     m_enabledChecksCountDirty = true;
0098     setEnabledChecksCountDirtyInSubGroups();
0099 }
0100 
0101 void CheckGroup::applyEnabledRule(const QStringRef& rule, EnabledState enabledState)
0102 {
0103     // this group?
0104     if (rule == wildCardText()) {
0105         resetEnabledState(enabledState);
0106         return;
0107     }
0108 
0109     for (auto* subGroup : qAsConst(m_subGroups)) {
0110         if (rule.startsWith(subGroup->prefix())) {
0111             subGroup->applyEnabledRule(rule, enabledState);
0112             return;
0113         }
0114     }
0115 
0116     for (int i = 0; i < m_checks.size(); ++i) {
0117         if (m_checks[i] == rule) {
0118             m_checksEnabledStates[i] = enabledState;
0119             return;
0120         }
0121     }
0122 }
0123 
0124 void CheckGroup::resetEnabledState(EnabledState enabledState)
0125 {
0126     m_groupEnabledState = enabledState;
0127 
0128     for (auto* subGroup : qAsConst(m_subGroups)) {
0129         subGroup->resetEnabledState(EnabledInherited);
0130     }
0131     m_checksEnabledStates.fill(EnabledInherited);
0132 }
0133 
0134 QStringList CheckGroup::enabledChecksRules() const
0135 {
0136     QStringList result;
0137     collectEnabledChecks(result);
0138     return result;
0139 }
0140 
0141 void CheckGroup::collectEnabledChecks(QStringList& enabledChecks) const
0142 {
0143     const auto effectiveGroupEnabledState = this->effectiveGroupEnabledState();
0144 
0145     const bool appendGroupRule =
0146         (!m_superGroup) ||
0147         (m_superGroup->effectiveGroupEnabledState() != effectiveGroupEnabledState);
0148     if (appendGroupRule) {
0149         QString rule = wildCardText();
0150         if (effectiveGroupEnabledState == CheckGroup::Disabled) {
0151             rule.prepend(QLatin1Char('-'));
0152         }
0153         enabledChecks.append(rule);
0154     }
0155 
0156     for (const auto* subGroup : m_subGroups) {
0157         subGroup->collectEnabledChecks(enabledChecks);
0158     }
0159 
0160     for (int i = 0; i < m_checks.size(); ++i) {
0161         const auto effectiveCheckEnabledState = this->effectiveCheckEnabledState(i);
0162         if (effectiveGroupEnabledState != effectiveCheckEnabledState) {
0163             QString rule = m_checks.at(i);
0164             if (effectiveCheckEnabledState == CheckGroup::Disabled) {
0165                 rule.prepend(QLatin1Char('-'));
0166             }
0167             enabledChecks.append(rule);
0168         }
0169     }
0170 }
0171 
0172 const QString& CheckGroup::prefix() const
0173 {
0174     return m_prefix;
0175 }
0176 
0177 QString CheckGroup::wildCardText() const
0178 {
0179     return m_prefix + QLatin1Char('*');
0180 }
0181 
0182 const QStringList& CheckGroup::checkNames() const
0183 {
0184     return m_checks;
0185 }
0186 
0187 const QVector<CheckGroup*>& CheckGroup::subGroups() const
0188 {
0189     return m_subGroups;
0190 }
0191 
0192 CheckGroup * CheckGroup::superGroup() const
0193 {
0194     return m_superGroup;
0195 }
0196 
0197 CheckGroup::EnabledState CheckGroup::groupEnabledState() const
0198 {
0199     return m_groupEnabledState;
0200 }
0201 
0202 CheckGroup::EnabledState CheckGroup::effectiveGroupEnabledState() const
0203 {
0204     EnabledState result = m_groupEnabledState;
0205     if (result == EnabledInherited) {
0206         Q_ASSERT(m_superGroup);
0207         result = m_superGroup->effectiveGroupEnabledState();
0208     }
0209     return result;
0210 }
0211 
0212 CheckGroup::EnabledState CheckGroup::checkEnabledState(int index) const
0213 {
0214     return m_checksEnabledStates.at(index);
0215 }
0216 
0217 CheckGroup::EnabledState CheckGroup::effectiveCheckEnabledState(int index) const
0218 {
0219     EnabledState result = m_checksEnabledStates.at(index);
0220     if (result == EnabledInherited) {
0221         result = effectiveGroupEnabledState();
0222     }
0223     return result;
0224 }
0225 
0226 void CheckGroup::setGroupEnabledState(CheckGroup::EnabledState groupEnabledState)
0227 {
0228     const int oldEffectiveGroupEnabledState = effectiveGroupEnabledState();
0229 
0230     m_groupEnabledState = groupEnabledState;
0231 
0232     if (oldEffectiveGroupEnabledState != effectiveGroupEnabledState()) {
0233         setEnabledChecksCountDirtyInSuperGroups();
0234         setEnabledChecksCountDirtyInSubGroups();
0235     }
0236 }
0237 
0238 
0239 void CheckGroup::setCheckEnabledState(int index, CheckGroup::EnabledState checkEnabledState)
0240 {
0241     const int oldEffectiveCheckEnabledState = effectiveCheckEnabledState(index);
0242 
0243     m_checksEnabledStates[index] = checkEnabledState;
0244 
0245     if (oldEffectiveCheckEnabledState != effectiveCheckEnabledState(index)) {
0246         setEnabledChecksCountDirtyInSuperGroups();
0247     }
0248 }
0249 
0250 void CheckGroup::updateData() const
0251 {
0252     if (m_enabledChecksCountDirty) {
0253         m_enabledChecksCount = 0;
0254         m_hasSubGroupWithExplicitEnabledState = false;
0255 
0256         for (auto* subGroup : m_subGroups) {
0257             m_enabledChecksCount += subGroup->enabledChecksCount();
0258             m_hasSubGroupWithExplicitEnabledState |= subGroup->hasSubGroupWithExplicitEnabledState();
0259             m_hasSubGroupWithExplicitEnabledState |= (subGroup->groupEnabledState() != EnabledInherited);
0260         }
0261 
0262         for (int i = 0; i < m_checks.size(); ++i) {
0263             if (effectiveCheckEnabledState(i) == Enabled) {
0264                 ++m_enabledChecksCount;
0265             }
0266             m_hasSubGroupWithExplicitEnabledState |= (m_checksEnabledStates[i] != EnabledInherited);
0267         }
0268         m_enabledChecksCountDirty = false;
0269     }
0270 }
0271 
0272 int CheckGroup::enabledChecksCount() const
0273 {
0274     updateData();
0275     return m_enabledChecksCount;
0276 }
0277 
0278 bool CheckGroup::hasSubGroupWithExplicitEnabledState() const
0279 {
0280     updateData();
0281     return m_hasSubGroupWithExplicitEnabledState;
0282 }
0283 
0284 void CheckGroup::setEnabledChecksCountDirtyInSuperGroups()
0285 {
0286     auto* checkGroup = this;
0287     while (checkGroup) {
0288         checkGroup->m_enabledChecksCountDirty = true;
0289         checkGroup = checkGroup->superGroup();
0290     }
0291 }
0292 
0293 void CheckGroup::setEnabledChecksCountDirtyInSubGroups()
0294 {
0295     for (auto* subGroup : qAsConst(m_subGroups)) {
0296         subGroup->m_enabledChecksCountDirty = true;
0297         subGroup->setEnabledChecksCountDirtyInSubGroups();
0298     }
0299 }
0300 
0301 }