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)) {
58 Builder->setBinding(BindName, clang::DynTypedNode::create(*FirstMatch));
71 EnableCountingEnumHeuristic(
74 CountingEnumPrefixes(
utils::options::parseStringList(
77 CountingEnumSuffixes(
utils::options::parseStringList(
80 if (!EnableCountingEnumHeuristic) {
82 configurationDiag(
"%0: Counting enum heuristic is disabled but "
86 configurationDiag(
"%0: Counting enum heuristic is disabled but "
96 EnableCountingEnumHeuristic);
104 auto NotFromSystemHeaderOrStdNamespace =
105 unless(anyOf(isExpansionInSystemHeader(), isInStdNamespace()));
108 fieldDecl(hasType(qualType(hasCanonicalType(recordType(hasDeclaration(
109 recordDecl(isUnion(), NotFromSystemHeaderOrStdNamespace)))))));
111 auto EnumField = fieldDecl(hasType(qualType(hasCanonicalType(
112 enumType(hasDeclaration(enumDecl(NotFromSystemHeaderOrStdNamespace)))))));
117 Finder->addMatcher(recordDecl(anyOf(isStruct(), isClass()), HasOneUnionField,
118 HasOneEnumField, unless(isImplicit()))
123bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name)
const {
124 if (llvm::any_of(CountingEnumPrefixes, [Name](StringRef Prefix) ->
bool {
125 return Name.starts_with_insensitive(Prefix);
128 if (llvm::any_of(CountingEnumSuffixes, [Name](StringRef Suffix) ->
bool {
129 return Name.ends_with_insensitive(Suffix);
135std::pair<const std::size_t, const EnumConstantDecl *>
136TaggedUnionMemberCountCheck::getNumberOfEnumValues(
const EnumDecl *ED) {
137 llvm::SmallSet<llvm::APSInt, 16> EnumValues;
139 const EnumConstantDecl *LastEnumConstant =
nullptr;
140 for (
const EnumConstantDecl *Enumerator : ED->enumerators()) {
141 EnumValues.insert(Enumerator->getInitVal());
142 LastEnumConstant = Enumerator;
145 if (EnableCountingEnumHeuristic && LastEnumConstant &&
146 isCountingEnumLikeName(LastEnumConstant->getName()) &&
147 llvm::APSInt::isSameValue(LastEnumConstant->getInitVal(),
148 llvm::APSInt::get(EnumValues.size() - 1))) {
149 return {EnumValues.size() - 1, LastEnumConstant};
152 return {EnumValues.size(),
nullptr};
156 const MatchFinder::MatchResult &Result) {
158 const auto *UnionField =
162 assert(Root &&
"Root is missing!");
163 assert(UnionField &&
"UnionField is missing!");
164 assert(TagField &&
"TagField is missing!");
165 if (!Root || !UnionField || !TagField)
168 const auto *UnionDef = UnionField->getType()->castAsRecordDecl();
169 const auto *EnumDef = TagField->getType()->castAsEnumDecl();
171 const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
172 auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef);
174 if (UnionMemberCount > TagCount) {
175 diag(Root->getLocation(),
176 "tagged union has more data members (%0) than tags (%1)!")
177 << UnionMemberCount << TagCount;
178 }
else if (StrictMode && UnionMemberCount < TagCount) {
179 diag(Root->getLocation(),
180 "tagged union has fewer data members (%0) than tags (%1)!")
181 << UnionMemberCount << TagCount;
184 if (CountingEnumConstantDecl) {
185 diag(CountingEnumConstantDecl->getLocation(),
186 "assuming that this constant is just an auxiliary value and not "
187 "used for indicating a valid union data member",
188 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