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,
112 StrictMode(Options.getLocalOrGlobal(
"StrictMode", 0)) {}
119 const auto enumExpr = [](StringRef RefName, StringRef DeclName) {
120 return allOf(ignoringImpCasts(expr().bind(RefName)),
121 ignoringImpCasts(hasType(enumDecl().bind(DeclName))));
125 binaryOperator(hasOperatorName(
"|"), hasLHS(enumExpr(
"",
"enumDecl")),
126 hasRHS(allOf(enumExpr(
"",
"otherEnumDecl"),
127 ignoringImpCasts(hasType(enumDecl(
128 unless(equalsBoundNode(
"enumDecl"))))))))
133 binaryOperator(anyOf(hasOperatorName(
"+"), hasOperatorName(
"|")),
134 hasLHS(enumExpr(
"lhsExpr",
"enumDecl")),
135 hasRHS(allOf(enumExpr(
"rhsExpr",
""),
136 ignoringImpCasts(hasType(enumDecl(
137 equalsBoundNode(
"enumDecl"))))))),
141 binaryOperator(anyOf(hasOperatorName(
"+"), hasOperatorName(
"|")),
143 allOf(hasType(isInteger()), unless(enumExpr(
"",
"")))),
144 hasEitherOperand(enumExpr(
"enumExpr",
"enumDecl"))),
148 binaryOperator(anyOf(hasOperatorName(
"|="), hasOperatorName(
"+=")),
149 hasRHS(enumExpr(
"enumExpr",
"enumDecl"))),
153 void SuspiciousEnumUsageCheck::checkSuspiciousBitmaskUsage(
154 const Expr *NodeExpr,
const EnumDecl *EnumDec) {
155 const auto *EnumExpr = dyn_cast<DeclRefExpr>(NodeExpr);
156 const auto *EnumConst =
157 EnumExpr ? dyn_cast<EnumConstantDecl>(EnumExpr->getDecl()) :
nullptr;
172 if (
const auto *DiffEnumOp =
173 Result.Nodes.getNodeAs<BinaryOperator>(
"diffEnumOp")) {
174 const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>(
"enumDecl");
175 const auto *OtherEnumDec =
176 Result.Nodes.getNodeAs<EnumDecl>(
"otherEnumDecl");
180 if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
181 OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
194 const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>(
"enumDecl");
201 if (
const auto *EnumExpr = Result.Nodes.getNodeAs<Expr>(
"enumExpr")) {
202 checkSuspiciousBitmaskUsage(EnumExpr, EnumDec);
208 const auto *LhsExpr = Result.Nodes.getNodeAs<Expr>(
"lhsExpr");
209 checkSuspiciousBitmaskUsage(LhsExpr, EnumDec);
211 const auto *RhsExpr = Result.Nodes.getNodeAs<Expr>(
"rhsExpr");
212 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.