11#include "clang/AST/Decl.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Basic/Diagnostic.h"
15#include "clang/Basic/SourceLocation.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/SmallString.h"
24 return llvm::all_of(Node.enumerators(), [](
const EnumConstantDecl *ECD) {
25 return ECD->getInitExpr() == nullptr;
31 for (
const EnumConstantDecl *ECD : Node.enumerators()) {
32 if ((IsFirst && ECD->getInitExpr() ==
nullptr) ||
33 (!IsFirst && ECD->getInitExpr() !=
nullptr))
41 return llvm::all_of(Node.enumerators(), [](
const EnumConstantDecl *ECD) {
42 return ECD->getInitExpr() != nullptr;
49 const Expr *
const Init = Enumerator->getInitExpr();
52 return Init->isIntegerConstantExpr(Enumerator->getASTContext());
56 const EnumConstantDecl *ECD,
57 const SourceManager &SM,
58 const LangOptions &LangOpts) {
59 const SourceRange InitExprRange = ECD->getInitExpr()->getSourceRange();
60 if (InitExprRange.isInvalid() || InitExprRange.getBegin().isMacroID() ||
61 InitExprRange.getEnd().isMacroID())
64 ECD->getLocation(), SM, LangOpts);
65 if (!EqualToken.has_value() ||
66 EqualToken.value().getKind() != tok::TokenKind::equal)
68 const SourceLocation EqualLoc{EqualToken->getLocation()};
69 if (EqualLoc.isInvalid() || EqualLoc.isMacroID())
71 Diag << FixItHint::CreateRemoval(EqualLoc)
72 << FixItHint::CreateRemoval(InitExprRange);
77AST_MATCHER(EnumDecl, isMacro) {
78 SourceLocation Loc = Node.getBeginLoc();
79 return Loc.isMacroID();
82AST_MATCHER(EnumDecl, hasConsistentInitialValues) {
88AST_MATCHER(EnumDecl, hasZeroInitialValueForFirstEnumerator) {
89 const EnumDecl::enumerator_range Enumerators = Node.enumerators();
90 if (Enumerators.empty())
92 const EnumConstantDecl *ECD = *Enumerators.begin();
105 const EnumDecl::enumerator_range Enumerators = Node.enumerators();
106 if (Enumerators.empty())
108 const EnumConstantDecl *
const FirstEnumerator = *Node.enumerator_begin();
109 llvm::APSInt PrevValue = FirstEnumerator->getInitVal();
112 bool AllEnumeratorsArePowersOfTwo =
true;
113 for (
const EnumConstantDecl *Enumerator : llvm::drop_begin(Enumerators)) {
114 const llvm::APSInt NewValue = Enumerator->getInitVal();
115 if (NewValue != ++PrevValue)
119 PrevValue = NewValue;
120 AllEnumeratorsArePowersOfTwo &= NewValue.isPowerOf2();
122 return !AllEnumeratorsArePowersOfTwo;
127static std::string
getName(
const EnumDecl *Decl) {
128 if (!Decl->getDeclName())
131 return Decl->getQualifiedNameAsString();
137 AllowExplicitZeroFirstInitialValue(
138 Options.get(
"AllowExplicitZeroFirstInitialValue", true)),
139 AllowExplicitSequentialInitialValues(
140 Options.get(
"AllowExplicitSequentialInitialValues", true)) {}
143 Options.store(Opts,
"AllowExplicitZeroFirstInitialValue",
144 AllowExplicitZeroFirstInitialValue);
145 Options.store(Opts,
"AllowExplicitSequentialInitialValues",
146 AllowExplicitSequentialInitialValues);
150 Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
151 unless(hasConsistentInitialValues()))
152 .bind(
"inconsistent"),
154 if (!AllowExplicitZeroFirstInitialValue)
156 enumDecl(isDefinition(), hasZeroInitialValueForFirstEnumerator())
159 if (!AllowExplicitSequentialInitialValues)
160 Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
161 hasSequentialInitialValues())
167 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"inconsistent")) {
168 DiagnosticBuilder Diag =
171 "initial values in enum '%0' are not consistent, consider explicit "
172 "initialization of all, none or only the first enumerator")
174 for (
const EnumConstantDecl *ECD : Enum->enumerators())
175 if (ECD->getInitExpr() ==
nullptr) {
176 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
177 ECD->getLocation(), 0, *Result.SourceManager, getLangOpts());
178 if (EndLoc.isMacroID())
180 llvm::SmallString<8> Str{
" = "};
181 ECD->getInitVal().toString(Str);
182 Diag << FixItHint::CreateInsertion(EndLoc, Str);
187 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"zero_first")) {
188 const EnumConstantDecl *ECD = *Enum->enumerator_begin();
189 const SourceLocation Loc = ECD->getLocation();
190 if (Loc.isInvalid() || Loc.isMacroID())
192 DiagnosticBuilder Diag = diag(Loc,
"zero initial value for the first "
193 "enumerator in '%0' can be disregarded")
198 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"sequential")) {
199 DiagnosticBuilder Diag =
200 diag(Enum->getBeginLoc(),
201 "sequential initial value in '%0' can be ignored")
203 for (
const EnumConstantDecl *ECD : llvm::drop_begin(Enum->enumerators()))
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
EnumInitialValueCheck(StringRef Name, ClangTidyContext *Context)
AST_MATCHER(BinaryOperator, isRelationalOperator)
static bool isInitializedByLiteral(const EnumConstantDecl *Enumerator)
Check if Enumerator is initialized with a (potentially negated) IntegerLiteral.
static bool isNoneEnumeratorsInitialized(const EnumDecl &Node)
static std::string getName(const EnumDecl *Decl)
static bool areAllEnumeratorsInitialized(const EnumDecl &Node)
static void cleanInitialValue(DiagnosticBuilder &Diag, const EnumConstantDecl *ECD, const SourceManager &SM, const LangOptions &LangOpts)
static bool isOnlyFirstEnumeratorInitialized(const EnumDecl &Node)
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
llvm::StringMap< ClangTidyValue > OptionMap