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 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, 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 const auto NotFromSystemHeaderOrStdNamespace =
105 unless(anyOf(isExpansionInSystemHeader(), isInStdNamespace()));
107 const auto UnionField =
108 fieldDecl(hasType(qualType(hasCanonicalType(recordType(hasDeclaration(
109 recordDecl(isUnion(), NotFromSystemHeaderOrStdNamespace)))))));
111 const auto EnumField = fieldDecl(hasType(qualType(hasCanonicalType(
112 enumType(hasDeclaration(enumDecl(NotFromSystemHeaderOrStdNamespace)))))));
114 const auto HasOneUnionField =
116 const auto HasOneEnumField =
119 Finder->addMatcher(recordDecl(anyOf(isStruct(), isClass()), HasOneUnionField,
120 HasOneEnumField, unless(isImplicit()))
125bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name)
const {
126 if (llvm::any_of(CountingEnumPrefixes, [Name](StringRef Prefix) ->
bool {
127 return Name.starts_with_insensitive(Prefix);
130 if (llvm::any_of(CountingEnumSuffixes, [Name](StringRef Suffix) ->
bool {
131 return Name.ends_with_insensitive(Suffix);
137std::pair<const std::size_t, const EnumConstantDecl *>
138TaggedUnionMemberCountCheck::getNumberOfEnumValues(
const EnumDecl *ED) {
139 llvm::SmallSet<llvm::APSInt, 16> EnumValues;
141 const EnumConstantDecl *LastEnumConstant =
nullptr;
142 for (
const EnumConstantDecl *Enumerator : ED->enumerators()) {
143 EnumValues.insert(Enumerator->getInitVal());
144 LastEnumConstant = Enumerator;
147 if (EnableCountingEnumHeuristic && LastEnumConstant &&
148 isCountingEnumLikeName(LastEnumConstant->getName()) &&
149 llvm::APSInt::isSameValue(LastEnumConstant->getInitVal(),
150 llvm::APSInt::get(EnumValues.size() - 1))) {
151 return {EnumValues.size() - 1, LastEnumConstant};
154 return {EnumValues.size(),
nullptr};
158 const MatchFinder::MatchResult &Result) {
160 const auto *UnionField =
164 assert(Root &&
"Root is missing!");
165 assert(UnionField &&
"UnionField is missing!");
166 assert(TagField &&
"TagField is missing!");
167 if (!Root || !UnionField || !TagField)
170 const auto *UnionDef = UnionField->getType()->castAsRecordDecl();
171 const auto *EnumDef = TagField->getType()->castAsEnumDecl();
173 const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
174 auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef);
176 if (UnionMemberCount > TagCount) {
177 diag(Root->getLocation(),
178 "tagged union has more data members (%0) than tags (%1)!")
179 << UnionMemberCount << TagCount;
180 }
else if (StrictMode && UnionMemberCount < TagCount) {
181 diag(Root->getLocation(),
182 "tagged union has fewer data members (%0) than tags (%1)!")
183 << UnionMemberCount << TagCount;
186 if (CountingEnumConstantDecl) {
187 diag(CountingEnumConstantDecl->getLocation(),
188 "assuming that this constant is just an auxiliary value and not "
189 "used for indicating a valid union data member",
190 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