12#include "clang/AST/ASTContext.h"
13#include "clang/AST/TypeVisitor.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
25bool isCompleteAndHasNoZeroValue(
const EnumDecl *D) {
30 [](
const EnumConstantDecl *Value) {
31 return Value->getInitVal().isZero();
36 return isCompleteAndHasNoZeroValue(&Node);
42 if (isa<CXXScalarValueInitExpr, ImplicitValueInitExpr>(&Node))
44 if (
const auto *Init = dyn_cast<InitListExpr>(&Node)) {
45 if (Init->getNumInits() == 0)
51AST_MATCHER(InitListExpr, hasArrayFiller) {
return Node.hasArrayFiller(); }
57class FindEnumMember :
public TypeVisitor<FindEnumMember, bool> {
59 const EnumType *FoundEnum =
nullptr;
61 bool VisitType(
const Type *T) {
62 const Type *DesT =
T->getUnqualifiedDesugaredType();
67 bool VisitArrayType(
const ArrayType *T) {
68 return Visit(
T->getElementType().getTypePtr());
70 bool VisitConstantArrayType(
const ConstantArrayType *T) {
71 return Visit(
T->getElementType().getTypePtr());
73 bool VisitEnumType(
const EnumType *T) {
74 if (isCompleteAndHasNoZeroValue(
T->getDecl())) {
80 bool VisitRecordType(
const RecordType *T) {
81 const RecordDecl *RD =
T->getDecl()->getDefinition();
82 if (!RD || RD->isUnion())
84 auto VisitField = [
this](
const FieldDecl *F) {
85 return Visit(F->getType().getTypePtr());
87 return llvm::any_of(RD->fields(), VisitField);
97 utils::options::parseStringList(Options.get(
"IgnoredEnums",
""))) {
98 IgnoredEnums.emplace_back(
"::std::errc");
103 Options.store(Opts,
"IgnoredEnums",
108 MatchFinder *Finder) {
109 auto EnumWithoutZeroValue = enumType(hasDeclaration(
110 enumDecl(isCompleteAndHasNoZeroValue(),
113 auto EnumOrArrayOfEnum = qualType(hasUnqualifiedDesugaredType(
114 anyOf(EnumWithoutZeroValue,
115 arrayType(hasElementType(qualType(
116 hasUnqualifiedDesugaredType(EnumWithoutZeroValue)))))));
118 expr(isEmptyInit(), hasType(EnumOrArrayOfEnum)).bind(
"expr"),
this);
126 Finder->addMatcher(initListExpr(hasArrayFiller()).bind(
"array_filler_expr"),
131 const MatchFinder::MatchResult &Result) {
132 const auto *InitExpr = Result.Nodes.getNodeAs<Expr>(
"expr");
133 const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"enum");
135 const auto *InitList =
136 Result.Nodes.getNodeAs<InitListExpr>(
"array_filler_expr");
140 FindEnumMember Finder;
141 if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr()))
144 Enum = Finder.FoundEnum->getDecl();
147 if (!InitExpr || !Enum)
150 ASTContext &ACtx = Enum->getASTContext();
151 SourceLocation Loc = InitExpr->getExprLoc();
152 if (Loc.isInvalid()) {
153 if (isa<ImplicitValueInitExpr, InitListExpr>(InitExpr)) {
154 const DynTypedNodeList Parents = ACtx.getParents(*InitExpr);
158 if (
const auto *Ctor = Parents[0].get<CXXConstructorDecl>()) {
161 CXXCtorInitializer *
const *CtorInit = std::find_if(
162 Ctor->init_begin(), Ctor->init_end(),
163 [InitExpr](
const CXXCtorInitializer *Init) {
164 return Init->isMemberInitializer() && Init->getInit() == InitExpr;
168 Loc = (*CtorInit)->getLParenLoc();
169 }
else if (
const auto *InitList = Parents[0].get<InitListExpr>()) {
172 while (InitList->getExprLoc().isInvalid()) {
173 const DynTypedNodeList Parents = ACtx.getParents(*InitList);
176 InitList = Parents[0].get<InitListExpr>();
180 Loc = InitList->getExprLoc();
189 diag(Loc,
"enum value of type %0 initialized with invalid value of 0, "
190 "enum doesn't have a zero-value enumerator")
192 diag(Enum->getLocation(),
"enum is defined here", DiagnosticIDs::Note);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
InvalidEnumDefaultInitializationCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
@ Type
An inlay hint that for a type annotation.
AST_MATCHER(BinaryOperator, isRelationalOperator)
inline ::clang::ast_matchers::internal::Matcher< NamedDecl > matchesAnyListedName(llvm::ArrayRef< StringRef > NameList)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap