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;
131 AllowExplicitZeroFirstInitialValue(
132 Options.get(
"AllowExplicitZeroFirstInitialValue", true)),
133 AllowExplicitSequentialInitialValues(
134 Options.get(
"AllowExplicitSequentialInitialValues", true)) {}
137 Options.
store(Opts,
"AllowExplicitZeroFirstInitialValue",
138 AllowExplicitZeroFirstInitialValue);
139 Options.
store(Opts,
"AllowExplicitSequentialInitialValues",
140 AllowExplicitSequentialInitialValues);
145 enumDecl(unless(isMacro()), unless(hasConsistentInitialValues()))
146 .bind(
"inconsistent"),
148 if (!AllowExplicitZeroFirstInitialValue)
150 enumDecl(hasZeroInitialValueForFirstEnumerator()).bind(
"zero_first"),
152 if (!AllowExplicitSequentialInitialValues)
153 Finder->addMatcher(enumDecl(unless(isMacro()), hasSequentialInitialValues())
159 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"inconsistent")) {
160 DiagnosticBuilder Diag =
161 diag(Enum->getBeginLoc(),
162 "inital values in enum %0 are not consistent, consider explicit "
163 "initialization of all, none or only the first enumerator")
165 for (
const EnumConstantDecl *ECD : Enum->enumerators())
166 if (ECD->getInitExpr() ==
nullptr) {
167 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
168 ECD->getLocation(), 0, *Result.SourceManager,
getLangOpts());
169 if (EndLoc.isMacroID())
171 llvm::SmallString<8> Str{
" = "};
172 ECD->getInitVal().toString(Str);
173 Diag << FixItHint::CreateInsertion(EndLoc, Str);
178 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"zero_first")) {
179 const EnumConstantDecl *ECD = *Enum->enumerator_begin();
180 const SourceLocation
Loc = ECD->getLocation();
181 if (
Loc.isInvalid() ||
Loc.isMacroID())
183 DiagnosticBuilder Diag =
diag(
Loc,
"zero initial value for the first "
184 "enumerator in %0 can be disregarded")
189 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"sequential")) {
190 DiagnosticBuilder Diag =
191 diag(Enum->getBeginLoc(),
192 "sequential initial value in %0 can be ignored")
194 for (
const EnumConstantDecl *ECD : llvm::drop_begin(Enum->enumerators()))
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