11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 using namespace clang::ast_matchers;
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 E1->getInitVal() < E2->getInitVal();
47 MinVal = MinMaxVal.first->getInitVal();
48 MaxVal = MinMaxVal.second->getInitVal();
54 return std::distance(EnumDec->enumerator_begin(), EnumDec->enumerator_end());
58 const EnumDecl *Enum2) {
60 return (Range1.MaxVal < Range2.
MinVal) || (Range2.
MaxVal < Range1.MinVal);
64 llvm::APSInt Val = EnumConst->getInitVal();
65 if (Val.isPowerOf2() || !Val.getBoolValue())
67 const Expr *InitExpr = EnumConst->getInitExpr();
70 return isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
74 auto EnumConst = std::max_element(
75 EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
76 [](
const EnumConstantDecl *E1,
const EnumConstantDecl *E2) {
77 return E1->getInitVal() < E2->getInitVal();
80 if (
const Expr *InitExpr = EnumConst->getInitExpr()) {
81 return EnumConst->getInitVal().countTrailingOnes() ==
82 EnumConst->getInitVal().getActiveBits() &&
83 isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
90 EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
103 return NonPowOfTwoCounter >= 1 && NonPowOfTwoCounter <= 2 &&
104 NonPowOfTwoCounter < EnumLen / 2 &&
109 SuspiciousEnumUsageCheck::SuspiciousEnumUsageCheck(StringRef
Name,
111 :
ClangTidyCheck(Name, Context), StrictMode(Options.get(
"StrictMode", 0)) {}
118 const auto enumExpr = [](StringRef RefName, StringRef DeclName) {
119 return allOf(ignoringImpCasts(expr().bind(RefName)),
120 ignoringImpCasts(hasType(enumDecl().bind(DeclName))));
124 binaryOperator(hasOperatorName(
"|"), hasLHS(enumExpr(
"",
"enumDecl")),
125 hasRHS(allOf(enumExpr(
"",
"otherEnumDecl"),
126 ignoringImpCasts(hasType(enumDecl(
127 unless(equalsBoundNode(
"enumDecl"))))))))
132 binaryOperator(anyOf(hasOperatorName(
"+"), hasOperatorName(
"|")),
133 hasLHS(enumExpr(
"lhsExpr",
"enumDecl")),
134 hasRHS(allOf(enumExpr(
"rhsExpr",
""),
135 ignoringImpCasts(hasType(enumDecl(
136 equalsBoundNode(
"enumDecl"))))))),
140 binaryOperator(anyOf(hasOperatorName(
"+"), hasOperatorName(
"|")),
142 allOf(hasType(isInteger()), unless(enumExpr(
"",
"")))),
143 hasEitherOperand(enumExpr(
"enumExpr",
"enumDecl"))),
147 binaryOperator(anyOf(hasOperatorName(
"|="), hasOperatorName(
"+=")),
148 hasRHS(enumExpr(
"enumExpr",
"enumDecl"))),
152 void SuspiciousEnumUsageCheck::checkSuspiciousBitmaskUsage(
153 const Expr *NodeExpr,
const EnumDecl *EnumDec) {
154 const auto *EnumExpr = dyn_cast<DeclRefExpr>(NodeExpr);
155 const auto *EnumConst =
156 EnumExpr ? dyn_cast<EnumConstantDecl>(EnumExpr->getDecl()) :
nullptr;
171 if (
const auto *DiffEnumOp =
172 Result.Nodes.getNodeAs<BinaryOperator>(
"diffEnumOp")) {
173 const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>(
"enumDecl");
174 const auto *OtherEnumDec =
175 Result.Nodes.getNodeAs<EnumDecl>(
"otherEnumDecl");
179 if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
180 OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
193 const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>(
"enumDecl");
200 if (
const auto *EnumExpr = Result.Nodes.getNodeAs<Expr>(
"enumExpr")) {
201 checkSuspiciousBitmaskUsage(EnumExpr, EnumDec);
207 const auto *LhsExpr = Result.Nodes.getNodeAs<Expr>(
"lhsExpr");
208 checkSuspiciousBitmaskUsage(LhsExpr, EnumDec);
210 const auto *RhsExpr = Result.Nodes.getNodeAs<Expr>(
"rhsExpr");
211 checkSuspiciousBitmaskUsage(RhsExpr, EnumDec);
static int countNonPowOfTwoLiteralNum(const EnumDecl *EnumDec)
static const char DifferentEnumErrorMessage[]
static const char BitmaskVarErrorMessage[]
std::unique_ptr< ast_matchers::MatchFinder > Finder
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)
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.
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.
ClangTidyContext & Context
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.