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"
26 const auto *CE = dyn_cast_if_present<ConstantExpr>(ECD->getInitExpr());
28 dyn_cast_if_present<DeclRefExpr>(CE ? CE->getSubExpr() :
nullptr);
30 dyn_cast_if_present<EnumConstantDecl>(DRE ? DRE->getDecl() :
nullptr);
31 return RefECD && RefECD->getDeclContext() == ECD->getDeclContext();
41 return llvm::all_of(Node.enumerators(),
42 [AllowSelfRefs](
const EnumConstantDecl *ECD) {
43 return isAllowedSelfReference(ECD, AllowSelfRefs) ||
44 ECD->getInitExpr() == nullptr;
51 for (
const EnumConstantDecl *ECD : Node.enumerators()) {
54 if ((IsFirst && ECD->getInitExpr() ==
nullptr) ||
55 (!IsFirst && ECD->getInitExpr() !=
nullptr))
63 return llvm::all_of(Node.enumerators(), [](
const EnumConstantDecl *ECD) {
64 return ECD->getInitExpr() != nullptr;
71 const Expr *
const Init = Enumerator->getInitExpr();
74 return Init->isIntegerConstantExpr(Enumerator->getASTContext());
78 const EnumConstantDecl *ECD,
79 const SourceManager &SM,
80 const LangOptions &LangOpts) {
81 const SourceRange InitExprRange = ECD->getInitExpr()->getSourceRange();
82 if (InitExprRange.isInvalid() || InitExprRange.getBegin().isMacroID() ||
83 InitExprRange.getEnd().isMacroID())
86 ECD->getLocation(), SM, LangOpts);
87 if (!EqualToken.has_value() ||
88 EqualToken.value().getKind() != tok::TokenKind::equal)
90 const SourceLocation EqualLoc{EqualToken->getLocation()};
91 if (EqualLoc.isInvalid() || EqualLoc.isMacroID())
93 Diag << FixItHint::CreateRemoval(EqualLoc)
94 << FixItHint::CreateRemoval(InitExprRange);
99AST_MATCHER(EnumDecl, isMacro) {
100 const SourceLocation Loc = Node.getBeginLoc();
101 return Loc.isMacroID();
104AST_MATCHER_P(EnumDecl, hasConsistentInitialValues,
bool, AllowSelfRefs) {
110AST_MATCHER_P(EnumDecl, hasZeroInitialValueForFirstEnumerator,
bool,
112 const EnumDecl::enumerator_range Enumerators = Node.enumerators();
113 if (Enumerators.empty())
115 const EnumConstantDecl *ECD = *Enumerators.begin();
127AST_MATCHER_P(EnumDecl, hasSequentialInitialValues,
bool, AllowSelfRefs) {
128 const EnumDecl::enumerator_range Enumerators = Node.enumerators();
129 if (Enumerators.empty())
131 const EnumConstantDecl *
const FirstEnumerator = *Node.enumerator_begin();
132 llvm::APSInt PrevValue = FirstEnumerator->getInitVal();
135 bool AllEnumeratorsArePowersOfTwo =
true;
136 for (
const EnumConstantDecl *Enumerator : llvm::drop_begin(Enumerators)) {
139 const llvm::APSInt NewValue = Enumerator->getInitVal();
140 if (NewValue != ++PrevValue)
144 PrevValue = NewValue;
145 AllEnumeratorsArePowersOfTwo &= NewValue.isPowerOf2();
147 return !AllEnumeratorsArePowersOfTwo;
152static std::string
getName(
const EnumDecl *Decl) {
153 if (!Decl->getDeclName())
156 return Decl->getQualifiedNameAsString();
162 AllowExplicitZeroFirstInitialValue(
163 Options.get(
"AllowExplicitZeroFirstInitialValue", true)),
164 AllowExplicitSequentialInitialValues(
165 Options.get(
"AllowExplicitSequentialInitialValues", true)),
166 AllowReferencedInitialValues(
167 Options.get(
"AllowReferencedInitialValues", false)) {}
170 Options.store(Opts,
"AllowExplicitZeroFirstInitialValue",
171 AllowExplicitZeroFirstInitialValue);
172 Options.store(Opts,
"AllowExplicitSequentialInitialValues",
173 AllowExplicitSequentialInitialValues);
174 Options.store(Opts,
"AllowReferencedInitialValues",
175 AllowReferencedInitialValues);
179 const bool AllowSelfRefs = AllowReferencedInitialValues;
180 Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
181 unless(hasConsistentInitialValues(AllowSelfRefs)))
182 .bind(
"inconsistent"),
184 if (!AllowExplicitZeroFirstInitialValue)
186 enumDecl(isDefinition(),
187 hasZeroInitialValueForFirstEnumerator(AllowSelfRefs))
190 if (!AllowExplicitSequentialInitialValues)
191 Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
192 hasSequentialInitialValues(AllowSelfRefs))
198 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"inconsistent")) {
202 const DiagnosticBuilder Diag =
203 diag(Enum->getBeginLoc(),
"initial values in enum '%0' are not "
204 "consistent, consider explicit "
205 "initialization of all, none or only the "
209 for (
const EnumConstantDecl *ECD : Enum->enumerators()) {
210 if (ECD->getInitExpr() ==
nullptr) {
211 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
212 ECD->getLocation(), 0, *Result.SourceManager, getLangOpts());
213 if (EndLoc.isMacroID())
215 SmallString<8> Str{
" = "};
216 ECD->getInitVal().toString(Str);
217 Diag << FixItHint::CreateInsertion(EndLoc, Str);
222 for (
const EnumConstantDecl *ECD : Enum->enumerators()) {
223 if (ECD->getInitExpr() ==
nullptr) {
224 diag(ECD->getLocation(),
"uninitialized enumerator '%0' defined here",
232 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"zero_first")) {
233 const EnumConstantDecl *ECD = *Enum->enumerator_begin();
234 const SourceLocation Loc = ECD->getLocation();
235 if (Loc.isInvalid() || Loc.isMacroID())
237 const DiagnosticBuilder Diag =
238 diag(Loc,
"zero initial value for the first "
239 "enumerator in '%0' can be disregarded")
244 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"sequential")) {
245 const DiagnosticBuilder Diag =
246 diag(Enum->getBeginLoc(),
247 "sequential initial value in '%0' can be ignored")
253 const EnumConstantDecl *PrevECD =
nullptr;
254 for (
const EnumConstantDecl *ECD : Enum->enumerators()) {
255 if (PrevECD !=
nullptr &&
257 llvm::APSInt Expected = PrevECD->getInitVal();
259 if (llvm::APSInt::isSameValue(Expected, ECD->getInitVal()))
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_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
static void cleanInitialValue(const DiagnosticBuilder &Diag, const EnumConstantDecl *ECD, const SourceManager &SM, const LangOptions &LangOpts)
static bool isInitializedByLiteral(const EnumConstantDecl *Enumerator)
Check if Enumerator is initialized with a (potentially negated) IntegerLiteral.
static std::string getName(const EnumDecl *Decl)
static bool areAllEnumeratorsInitialized(const EnumDecl &Node)
static bool isNoneEnumeratorsInitialized(const EnumDecl &Node, bool AllowSelfRefs)
static bool isOnlyFirstEnumeratorInitialized(const EnumDecl &Node, bool AllowSelfRefs)
static bool isAllowedSelfReference(const EnumConstantDecl *ECD, bool AllowSelfRefs)
static bool isSelfReference(const EnumConstantDecl *ECD)
Check if ECD is initialized by referencing another enumerator in the same enum (e....
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
llvm::StringMap< ClangTidyValue > OptionMap