11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/ADT/STLExtras.h"
13#include "llvm/ADT/SmallSet.h"
21 "EnableCountingEnumHeuristic";
23 "CountingEnumPrefixes";
25 "CountingEnumSuffixes";
38AST_MATCHER_P2(RecordDecl, fieldCountOfKindIsOne,
39 ast_matchers::internal::Matcher<FieldDecl>, InnerMatcher,
40 StringRef, BindName) {
46 clang::ast_matchers::internal::BoundNodesTreeBuilder TempBuilder;
48 const FieldDecl *FirstMatch =
nullptr;
49 for (
const FieldDecl *Field : Node.fields()) {
50 if (InnerMatcher.matches(*Field, Finder, &TempBuilder)) {
59 Builder->setBinding(BindName, clang::DynTypedNode::create(*FirstMatch));
72 EnableCountingEnumHeuristic(
75 CountingEnumPrefixes(
utils::options::parseStringList(
78 CountingEnumSuffixes(
utils::options::parseStringList(
81 if (!EnableCountingEnumHeuristic) {
83 configurationDiag(
"%0: Counting enum heuristic is disabled but "
87 configurationDiag(
"%0: Counting enum heuristic is disabled but "
97 EnableCountingEnumHeuristic);
105 auto NotFromSystemHeaderOrStdNamespace =
106 unless(anyOf(isExpansionInSystemHeader(), isInStdNamespace()));
109 fieldDecl(hasType(qualType(hasCanonicalType(recordType(hasDeclaration(
110 recordDecl(isUnion(), NotFromSystemHeaderOrStdNamespace)))))));
112 auto EnumField = fieldDecl(hasType(qualType(hasCanonicalType(
113 enumType(hasDeclaration(enumDecl(NotFromSystemHeaderOrStdNamespace)))))));
118 Finder->addMatcher(recordDecl(anyOf(isStruct(), isClass()), HasOneUnionField,
119 HasOneEnumField, unless(isImplicit()))
124bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name)
const {
125 if (llvm::any_of(CountingEnumPrefixes, [Name](StringRef Prefix) ->
bool {
126 return Name.starts_with_insensitive(Prefix);
129 if (llvm::any_of(CountingEnumSuffixes, [Name](StringRef Suffix) ->
bool {
130 return Name.ends_with_insensitive(Suffix);
136std::pair<const std::size_t, const EnumConstantDecl *>
137TaggedUnionMemberCountCheck::getNumberOfEnumValues(
const EnumDecl *ED) {
138 llvm::SmallSet<llvm::APSInt, 16> EnumValues;
140 const EnumConstantDecl *LastEnumConstant =
nullptr;
141 for (
const EnumConstantDecl *Enumerator : ED->enumerators()) {
142 EnumValues.insert(Enumerator->getInitVal());
143 LastEnumConstant = Enumerator;
146 if (EnableCountingEnumHeuristic && LastEnumConstant &&
147 isCountingEnumLikeName(LastEnumConstant->getName()) &&
148 llvm::APSInt::isSameValue(LastEnumConstant->getInitVal(),
149 llvm::APSInt::get(EnumValues.size() - 1))) {
150 return {EnumValues.size() - 1, LastEnumConstant};
153 return {EnumValues.size(),
nullptr};
157 const MatchFinder::MatchResult &Result) {
159 const auto *UnionField =
163 assert(Root &&
"Root is missing!");
164 assert(UnionField &&
"UnionField is missing!");
165 assert(TagField &&
"TagField is missing!");
166 if (!Root || !UnionField || !TagField)
169 const auto *UnionDef = UnionField->getType()->castAsRecordDecl();
170 const auto *EnumDef = TagField->getType()->castAsEnumDecl();
172 const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
173 auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef);
175 if (UnionMemberCount > TagCount) {
176 diag(Root->getLocation(),
177 "tagged union has more data members (%0) than tags (%1)!")
178 << UnionMemberCount << TagCount;
179 }
else if (StrictMode && UnionMemberCount < TagCount) {
180 diag(Root->getLocation(),
181 "tagged union has fewer data members (%0) than tags (%1)!")
182 << UnionMemberCount << TagCount;
185 if (CountingEnumConstantDecl) {
186 diag(CountingEnumConstantDecl->getLocation(),
187 "assuming that this constant is just an auxiliary value and not "
188 "used for indicating a valid union data member",
189 DiagnosticIDs::Note);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static constexpr StringRef EnableCountingEnumHeuristicOptionName
static constexpr StringRef CountingEnumSuffixesOptionDefaultValue
static constexpr bool EnableCountingEnumHeuristicOptionDefaultValue
static constexpr StringRef UnionMatchBindName
static constexpr StringRef TagMatchBindName
static constexpr StringRef CountingEnumPrefixesOptionDefaultValue
static constexpr bool StrictModeOptionDefaultValue
static constexpr StringRef StrictModeOptionName
static constexpr StringRef RootMatchBindName
static constexpr StringRef CountingEnumSuffixesOptionName
static constexpr StringRef CountingEnumPrefixesOptionName
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap