12#include "clang/AST/ASTContext.h"
13#include "clang/AST/TypeVisitor.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
23bool isCompleteAndHasNoZeroValue(
const EnumDecl *D) {
28 [](
const EnumConstantDecl *Value) {
29 return Value->getInitVal().isZero();
34 return isCompleteAndHasNoZeroValue(&Node);
40 if (isa<CXXScalarValueInitExpr, ImplicitValueInitExpr>(&Node))
42 if (
const auto *Init = dyn_cast<InitListExpr>(&Node)) {
43 if (Init->getNumInits() == 0)
49AST_MATCHER(InitListExpr, hasArrayFiller) {
return Node.hasArrayFiller(); }
55class FindEnumMember :
public TypeVisitor<FindEnumMember, bool> {
57 const EnumType *FoundEnum =
nullptr;
59 bool VisitType(
const Type *T) {
60 const Type *DesT =
T->getUnqualifiedDesugaredType();
65 bool VisitArrayType(
const ArrayType *T) {
66 return Visit(
T->getElementType().getTypePtr());
68 bool VisitConstantArrayType(
const ConstantArrayType *T) {
69 return Visit(
T->getElementType().getTypePtr());
71 bool VisitEnumType(
const EnumType *T) {
72 if (isCompleteAndHasNoZeroValue(
T->getOriginalDecl())) {
78 bool VisitRecordType(
const RecordType *T) {
79 const RecordDecl *RD =
T->getOriginalDecl()->getDefinition();
80 if (!RD || RD->isUnion())
82 auto VisitField = [
this](
const FieldDecl *F) {
83 return Visit(F->getType().getTypePtr());
85 return llvm::any_of(RD->fields(), VisitField);
95 utils::options::parseStringList(Options.get(
"IgnoredEnums",
""))) {
96 IgnoredEnums.emplace_back(
"::std::errc");
101 Options.store(Opts,
"IgnoredEnums",
106 MatchFinder *Finder) {
107 auto EnumWithoutZeroValue = enumType(hasDeclaration(
108 enumDecl(isCompleteAndHasNoZeroValue(),
111 auto EnumOrArrayOfEnum = qualType(hasUnqualifiedDesugaredType(
112 anyOf(EnumWithoutZeroValue,
113 arrayType(hasElementType(qualType(
114 hasUnqualifiedDesugaredType(EnumWithoutZeroValue)))))));
116 expr(isEmptyInit(), hasType(EnumOrArrayOfEnum)).bind(
"expr"),
this);
124 Finder->addMatcher(initListExpr(hasArrayFiller()).bind(
"array_filler_expr"),
129 const MatchFinder::MatchResult &Result) {
130 const auto *InitExpr = Result.Nodes.getNodeAs<Expr>(
"expr");
131 const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"enum");
133 const auto *InitList =
134 Result.Nodes.getNodeAs<InitListExpr>(
"array_filler_expr");
138 FindEnumMember Finder;
139 if (!Finder.Visit(InitList->getArrayFiller()->getType().getTypePtr()))
142 Enum = Finder.FoundEnum->getOriginalDecl();
145 if (!InitExpr || !Enum)
148 ASTContext &ACtx = Enum->getASTContext();
149 SourceLocation Loc = InitExpr->getExprLoc();
150 if (Loc.isInvalid()) {
151 if (isa<ImplicitValueInitExpr, InitListExpr>(InitExpr)) {
152 DynTypedNodeList Parents = ACtx.getParents(*InitExpr);
156 if (
const auto *Ctor = Parents[0].get<CXXConstructorDecl>()) {
159 CXXCtorInitializer *
const *CtorInit = std::find_if(
160 Ctor->init_begin(), Ctor->init_end(),
161 [InitExpr](
const CXXCtorInitializer *Init) {
162 return Init->isMemberInitializer() && Init->getInit() == InitExpr;
166 Loc = (*CtorInit)->getLParenLoc();
167 }
else if (
const auto *InitList = Parents[0].get<InitListExpr>()) {
170 while (InitList->getExprLoc().isInvalid()) {
171 DynTypedNodeList Parents = ACtx.getParents(*InitList);
174 InitList = Parents[0].get<InitListExpr>();
178 Loc = InitList->getExprLoc();
187 diag(Loc,
"enum value of type %0 initialized with invalid value of 0, "
188 "enum doesn't have a zero-value enumerator")
190 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