10#include "../utils/LexerUtils.h"
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);
79 SourceLocation
Loc =
Node.getBeginLoc();
80 return Loc.isMacroID();
89AST_MATCHER(EnumDecl, hasZeroInitialValueForFirstEnumerator) {
90 const EnumDecl::enumerator_range Enumerators =
Node.enumerators();
91 if (Enumerators.empty())
93 const EnumConstantDecl *ECD = *Enumerators.begin();
106 const EnumDecl::enumerator_range Enumerators =
Node.enumerators();
107 if (Enumerators.empty())
109 const EnumConstantDecl *
const FirstEnumerator = *
Node.enumerator_begin();
110 llvm::APSInt PrevValue = FirstEnumerator->getInitVal();
113 bool AllEnumeratorsArePowersOfTwo =
true;
114 for (
const EnumConstantDecl *Enumerator : llvm::drop_begin(Enumerators)) {
115 const llvm::APSInt NewValue = Enumerator->getInitVal();
116 if (NewValue != ++PrevValue)
120 PrevValue = NewValue;
121 AllEnumeratorsArePowersOfTwo &= NewValue.isPowerOf2();
123 return !AllEnumeratorsArePowersOfTwo;
126std::string getName(
const EnumDecl *
Decl) {
127 if (!
Decl->getDeclName())
130 return Decl->getQualifiedNameAsString();
138 AllowExplicitZeroFirstInitialValue(
139 Options.get(
"AllowExplicitZeroFirstInitialValue", true)),
140 AllowExplicitSequentialInitialValues(
141 Options.get(
"AllowExplicitSequentialInitialValues", true)) {}
144 Options.
store(Opts,
"AllowExplicitZeroFirstInitialValue",
145 AllowExplicitZeroFirstInitialValue);
146 Options.
store(Opts,
"AllowExplicitSequentialInitialValues",
147 AllowExplicitSequentialInitialValues);
151 Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
152 unless(hasConsistentInitialValues()))
153 .bind(
"inconsistent"),
155 if (!AllowExplicitZeroFirstInitialValue)
157 enumDecl(isDefinition(), hasZeroInitialValueForFirstEnumerator())
160 if (!AllowExplicitSequentialInitialValues)
161 Finder->addMatcher(enumDecl(isDefinition(), unless(isMacro()),
162 hasSequentialInitialValues())
168 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"inconsistent")) {
169 DiagnosticBuilder Diag =
172 "initial values in enum '%0' are not consistent, consider explicit "
173 "initialization of all, none or only the first enumerator")
175 for (
const EnumConstantDecl *ECD : Enum->enumerators())
176 if (ECD->getInitExpr() ==
nullptr) {
177 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
178 ECD->getLocation(), 0, *Result.SourceManager,
getLangOpts());
179 if (EndLoc.isMacroID())
181 llvm::SmallString<8> Str{
" = "};
182 ECD->getInitVal().toString(Str);
183 Diag << FixItHint::CreateInsertion(EndLoc, Str);
188 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"zero_first")) {
189 const EnumConstantDecl *ECD = *Enum->enumerator_begin();
190 const SourceLocation
Loc = ECD->getLocation();
191 if (
Loc.isInvalid() ||
Loc.isMacroID())
193 DiagnosticBuilder Diag =
diag(
Loc,
"zero initial value for the first "
194 "enumerator in '%0' can be disregarded")
199 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"sequential")) {
200 DiagnosticBuilder Diag =
201 diag(Enum->getBeginLoc(),
202 "sequential initial value in '%0' can be ignored")
204 for (
const EnumConstantDecl *ECD : llvm::drop_begin(Enum->enumerators()))
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
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.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
EnumInitialValueCheck(StringRef Name, ClangTidyContext *Context)
static bool isInitializedByLiteral(const EnumConstantDecl *Enumerator)
Check if Enumerator is initialized with a (potentially negated) IntegerLiteral.
static bool isNoneEnumeratorsInitialized(const EnumDecl &Node)
static bool areAllEnumeratorsInitialized(const EnumDecl &Node)
AST_MATCHER(CXXMethodDecl, isStatic)
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