11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 22 "enum values are from different enum types";
25 "enum type seems like a bitmask (contains mostly " 26 "power-of-2 literals), but this literal is not a " 30 "enum type seems like a bitmask (contains mostly " 31 "power-of-2 literals) but %plural{1:a literal is|:some literals are}0 not " 42 const auto MinMaxVal = std::minmax_element(
43 EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
44 [](
const EnumConstantDecl *E1,
const EnumConstantDecl *E2) {
45 return llvm::APSInt::compareValues(E1->getInitVal(),
46 E2->getInitVal()) < 0;
48 MinVal = MinMaxVal.first->getInitVal();
49 MaxVal = MinMaxVal.second->getInitVal();
55 return std::distance(EnumDec->enumerator_begin(), EnumDec->enumerator_end());
59 const EnumDecl *Enum2) {
61 return llvm::APSInt::compareValues(Range1.MaxVal, Range2.
MinVal) < 0 ||
62 llvm::APSInt::compareValues(Range2.
MaxVal, Range1.MinVal) < 0;
66 llvm::APSInt Val = EnumConst->getInitVal();
67 if (Val.isPowerOf2() || !Val.getBoolValue())
69 const Expr *InitExpr = EnumConst->getInitExpr();
72 return isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
76 auto EnumConst = std::max_element(
77 EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
78 [](
const EnumConstantDecl *E1,
const EnumConstantDecl *E2) {
79 return E1->getInitVal() < E2->getInitVal();
82 if (
const Expr *InitExpr = EnumConst->getInitExpr()) {
83 return EnumConst->getInitVal().countTrailingOnes() ==
84 EnumConst->getInitVal().getActiveBits() &&
85 isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
92 EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
105 return NonPowOfTwoCounter >= 1 && NonPowOfTwoCounter <= 2 &&
106 NonPowOfTwoCounter < EnumLen / 2 &&
111 SuspiciousEnumUsageCheck::SuspiciousEnumUsageCheck(StringRef
Name,
114 StrictMode(Options.getLocalOrGlobal(
"StrictMode", 0)) {}
121 const auto enumExpr = [](StringRef RefName, StringRef DeclName) {
122 return allOf(ignoringImpCasts(expr().bind(RefName)),
123 ignoringImpCasts(hasType(enumDecl().bind(DeclName))));
127 binaryOperator(hasOperatorName(
"|"), hasLHS(enumExpr(
"",
"enumDecl")),
128 hasRHS(allOf(enumExpr(
"",
"otherEnumDecl"),
129 ignoringImpCasts(hasType(enumDecl(
130 unless(equalsBoundNode(
"enumDecl"))))))))
135 binaryOperator(anyOf(hasOperatorName(
"+"), hasOperatorName(
"|")),
136 hasLHS(enumExpr(
"lhsExpr",
"enumDecl")),
137 hasRHS(allOf(enumExpr(
"rhsExpr",
""),
138 ignoringImpCasts(hasType(enumDecl(
139 equalsBoundNode(
"enumDecl"))))))),
143 binaryOperator(anyOf(hasOperatorName(
"+"), hasOperatorName(
"|")),
145 allOf(hasType(isInteger()), unless(enumExpr(
"",
"")))),
146 hasEitherOperand(enumExpr(
"enumExpr",
"enumDecl"))),
150 binaryOperator(anyOf(hasOperatorName(
"|="), hasOperatorName(
"+=")),
151 hasRHS(enumExpr(
"enumExpr",
"enumDecl"))),
155 void SuspiciousEnumUsageCheck::checkSuspiciousBitmaskUsage(
156 const Expr *NodeExpr,
const EnumDecl *EnumDec) {
157 const auto *EnumExpr = dyn_cast<DeclRefExpr>(NodeExpr);
158 const auto *EnumConst =
159 EnumExpr ? dyn_cast<EnumConstantDecl>(EnumExpr->getDecl()) :
nullptr;
174 if (
const auto *DiffEnumOp =
175 Result.Nodes.getNodeAs<BinaryOperator>(
"diffEnumOp")) {
176 const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>(
"enumDecl");
177 const auto *OtherEnumDec =
178 Result.Nodes.getNodeAs<EnumDecl>(
"otherEnumDecl");
182 if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
183 OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
196 const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>(
"enumDecl");
203 if (
const auto *EnumExpr = Result.Nodes.getNodeAs<Expr>(
"enumExpr")) {
204 checkSuspiciousBitmaskUsage(EnumExpr, EnumDec);
210 const auto *LhsExpr = Result.Nodes.getNodeAs<Expr>(
"lhsExpr");
211 checkSuspiciousBitmaskUsage(LhsExpr, EnumDec);
213 const auto *RhsExpr = Result.Nodes.getNodeAs<Expr>(
"rhsExpr");
214 checkSuspiciousBitmaskUsage(RhsExpr, EnumDec);
static int countNonPowOfTwoLiteralNum(const EnumDecl *EnumDec)
static const char DifferentEnumErrorMessage[]
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
static const char BitmaskVarErrorMessage[]
ValueRange(const EnumDecl *EnumDec)
Base class for all clang-tidy checks.
static bool isPossiblyBitMask(const EnumDecl *EnumDec)
Check if there is one or two enumerators that are not a power of 2 and are initialized by a literal i...
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static bool hasDisjointValueRange(const EnumDecl *Enum1, const EnumDecl *Enum2)
std::map< std::string, std::string > OptionMap
static bool isNonPowerOf2NorNullLiteral(const EnumConstantDecl *EnumConst)
static const char BitmaskNoteMessage[]
static const char BitmaskErrorMessage[]
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Stores a min and a max value which describe an interval.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static int enumLength(const EnumDecl *EnumDec)
Return the number of EnumConstantDecls in an EnumDecl.
static bool isMaxValAllBitSetLiteral(const EnumDecl *EnumDec)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.