File indexing completed on 2024-05-12 05:41:03
0001 /* 0002 SPDX-FileCopyrightText: 2021 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com 0003 SPDX-FileContributor: Waqar Ahmed <waqar.ahmed@kdab.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "unexpected-flag-enumerator-value.h" 0009 #include "HierarchyUtils.h" 0010 #include "SourceCompatibilityHelpers.h" 0011 0012 #include <clang/AST/AST.h> 0013 0014 #include <algorithm> 0015 0016 using namespace clang; 0017 0018 UnexpectedFlagEnumeratorValue::UnexpectedFlagEnumeratorValue(const std::string &name, ClazyContext *context) 0019 : CheckBase(name, context) 0020 { 0021 } 0022 0023 static ConstantExpr *getConstantExpr(EnumConstantDecl *enCD) 0024 { 0025 auto *cexpr = dyn_cast_or_null<ConstantExpr>(enCD->getInitExpr()); 0026 if (cexpr) { 0027 return cexpr; 0028 } 0029 return clazy::getFirstChildOfType<ConstantExpr>(enCD->getInitExpr()); 0030 } 0031 0032 static bool isBinaryOperatorExpression(ConstantExpr *cexpr) 0033 { 0034 return clazy::getFirstChildOfType<BinaryOperator>(cexpr); 0035 } 0036 0037 static bool isReferenceToEnumerator(ConstantExpr *cexpr) 0038 { 0039 return dyn_cast_or_null<DeclRefExpr>(cexpr->getSubExpr()); 0040 } 0041 0042 static bool isIntentionallyNotPowerOf2(EnumConstantDecl *en) 0043 { 0044 constexpr unsigned MinOnesToQualifyAsMask = 3; 0045 0046 const auto val = en->getInitVal(); 0047 if (val.isMask() && val.countTrailingOnes() >= MinOnesToQualifyAsMask) { 0048 return true; 0049 } 0050 0051 #if LLVM_VERSION_MAJOR >= 17 0052 if (val.isShiftedMask() && val.popcount() >= MinOnesToQualifyAsMask) { 0053 return true; 0054 } 0055 #else 0056 if (val.isShiftedMask() && val.countPopulation() >= MinOnesToQualifyAsMask) { 0057 return true; 0058 } 0059 #endif 0060 0061 if (clazy::contains_lower(en->getName(), "mask")) { 0062 return true; 0063 } 0064 0065 auto *cexpr = getConstantExpr(en); 0066 if (!cexpr) { 0067 return false; 0068 } 0069 0070 if (isBinaryOperatorExpression(cexpr)) { 0071 return true; 0072 } 0073 0074 if (isReferenceToEnumerator(cexpr)) { 0075 return true; 0076 } 0077 0078 return false; 0079 } 0080 0081 static SmallVector<EnumConstantDecl *, 16> getEnumerators(EnumDecl *enDecl) 0082 { 0083 SmallVector<EnumConstantDecl *, 16> ret; 0084 for (auto *enumerator : enDecl->enumerators()) { 0085 ret.push_back(enumerator); 0086 } 0087 return ret; 0088 } 0089 0090 static uint64_t getIntegerValue(EnumConstantDecl *e) 0091 { 0092 return e->getInitVal().getLimitedValue(); 0093 } 0094 0095 static bool hasConsecutiveValues(const SmallVector<EnumConstantDecl *, 16> &enumerators) 0096 { 0097 auto val = getIntegerValue(enumerators.front()); 0098 const size_t until = std::min<size_t>(4, enumerators.size()); 0099 for (size_t i = 1; i < until; ++i) { 0100 val++; 0101 if (getIntegerValue(enumerators[i]) != val) { 0102 return false; 0103 } 0104 } 0105 return true; 0106 } 0107 0108 static bool hasInitExprs(const SmallVector<EnumConstantDecl *, 16> &enumerators) 0109 { 0110 size_t enumeratorsWithInitExpr = 0; 0111 for (auto *enumerator : enumerators) { 0112 if (enumerator->getInitExpr()) { 0113 enumeratorsWithInitExpr++; 0114 } 0115 } 0116 0117 return enumeratorsWithInitExpr == enumerators.size(); 0118 } 0119 0120 static bool isFlagEnum(const SmallVector<EnumConstantDecl *, 16> &enumerators) 0121 { 0122 if (enumerators.size() < 4) { 0123 return false; 0124 } 0125 0126 // For an enum to be considered "flag like", all enumerators 0127 // must have an explicit init value / expr 0128 if (!hasInitExprs(enumerators)) { 0129 return false; 0130 } 0131 0132 if (hasConsecutiveValues(enumerators)) { 0133 return false; 0134 } 0135 0136 llvm::SmallVector<bool, 16> enumValues; 0137 for (auto *enumerator : enumerators) { 0138 enumValues.push_back(enumerator->getInitVal().isPowerOf2()); 0139 } 0140 0141 const size_t count = std::count(enumValues.begin(), enumValues.end(), false); 0142 0143 // If half of our values were power-of-2, this is probably a flag enum 0144 return count <= (enumerators.size() / 2); 0145 } 0146 0147 void UnexpectedFlagEnumeratorValue::VisitDecl(clang::Decl *decl) 0148 { 0149 auto *enDecl = dyn_cast_or_null<EnumDecl>(decl); 0150 if (!enDecl || !enDecl->hasNameForLinkage()) { 0151 return; 0152 } 0153 0154 const SmallVector<EnumConstantDecl *, 16> enumerators = getEnumerators(enDecl); 0155 0156 if (!isFlagEnum(enumerators)) { 0157 return; 0158 } 0159 0160 for (EnumConstantDecl *enumerator : enumerators) { 0161 const auto &initVal = enumerator->getInitVal(); 0162 #if LLVM_VERSION_MAJOR >= 17 0163 if (!initVal.isPowerOf2() && !initVal.isZero() && !initVal.isNegative()) { 0164 #else 0165 if (!initVal.isPowerOf2() && !initVal.isNullValue() && !initVal.isNegative()) { 0166 #endif 0167 if (isIntentionallyNotPowerOf2(enumerator)) { 0168 continue; 0169 } 0170 const auto value = enumerator->getInitVal().getLimitedValue(); 0171 Expr *initExpr = enumerator->getInitExpr(); 0172 emitWarning(initExpr ? initExpr->getBeginLoc() : enumerator->getBeginLoc(), "Unexpected non power-of-2 enumerator value: " + std::to_string(value)); 0173 } 0174 } 0175 }