=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com>, =?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrc...@protonmail.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/89...@github.com>
================ @@ -0,0 +1,186 @@ +//===--- TaggedUnionMemberCountCheck.cpp - clang-tidy ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TaggedUnionMemberCountCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +static constexpr llvm::StringLiteral StrictModeOptionName = "StrictMode"; +static constexpr llvm::StringLiteral EnableCountingEnumHeuristicOptionName = + "EnableCountingEnumHeuristic"; +static constexpr llvm::StringLiteral CountingEnumPrefixesOptionName = + "CountingEnumPrefixes"; +static constexpr llvm::StringLiteral CountingEnumSuffixesOptionName = + "CountingEnumSuffixes"; + +static constexpr bool StrictModeOptionDefaultValue = false; +static constexpr bool EnableCountingEnumHeuristicOptionDefaultValue = true; +static constexpr llvm::StringLiteral CountingEnumPrefixesOptionDefaultValue = + ""; +static constexpr llvm::StringLiteral CountingEnumSuffixesOptionDefaultValue = + "count"; + +static constexpr llvm::StringLiteral RootMatchBindName = "root"; +static constexpr llvm::StringLiteral UnionMatchBindName = "union"; +static constexpr llvm::StringLiteral TagMatchBindName = "tags"; + +namespace { + +AST_MATCHER_P2(RecordDecl, fieldCountOfKindIsGT, + ast_matchers::internal::Matcher<FieldDecl>, InnerMatcher, + unsigned, N) { + unsigned MatchCount = 0; + for (const auto Field : Node.fields()) { + if (InnerMatcher.matches(*Field, Finder, Builder)) { + MatchCount += 1; + } + } + return MatchCount > N; +} + +} // namespace + +TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + StrictMode( + Options.get(StrictModeOptionName, StrictModeOptionDefaultValue)), + EnableCountingEnumHeuristic( + Options.get(EnableCountingEnumHeuristicOptionName, + EnableCountingEnumHeuristicOptionDefaultValue)), + CountingEnumPrefixes(utils::options::parseStringList( + Options.get(CountingEnumPrefixesOptionName, + CountingEnumPrefixesOptionDefaultValue))), + CountingEnumSuffixes(utils::options::parseStringList( + Options.get(CountingEnumSuffixesOptionName, + CountingEnumSuffixesOptionDefaultValue))) { + if (!EnableCountingEnumHeuristic) { + if (Options.get(CountingEnumPrefixesOptionName)) + configurationDiag("%0: Counting enum heuristic is disabled but " + "%1 is set") + << Name << CountingEnumPrefixesOptionName; + if (Options.get(CountingEnumSuffixesOptionName)) + configurationDiag("%0: Counting enum heuristic is disabled but " + "%1 is set") + << Name << CountingEnumSuffixesOptionName; + } +} + +void TaggedUnionMemberCountCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, StrictModeOptionName, StrictMode); + Options.store(Opts, EnableCountingEnumHeuristicOptionName, + EnableCountingEnumHeuristic); + Options.store(Opts, CountingEnumPrefixesOptionName, + utils::options::serializeStringList(CountingEnumPrefixes)); + Options.store(Opts, CountingEnumSuffixesOptionName, + utils::options::serializeStringList(CountingEnumSuffixes)); +} + +void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) { + + static const auto UnionField = fieldDecl(hasType(qualType( + hasCanonicalType(recordType(hasDeclaration(recordDecl(isUnion()))))))); + + static const auto EnumField = fieldDecl(hasType( + qualType(hasCanonicalType(enumType(hasDeclaration(enumDecl())))))); + + static const auto hasMultipleUnionsOrEnums = anyOf( + fieldCountOfKindIsGT(UnionField, 1), fieldCountOfKindIsGT(EnumField, 1)); + + Finder->addMatcher( + recordDecl(anyOf(isStruct(), isClass()), + unless(anyOf(isImplicit(), hasMultipleUnionsOrEnums)), + has(UnionField.bind(UnionMatchBindName)), + has(EnumField.bind(TagMatchBindName))) + .bind(RootMatchBindName), + this); +} + +bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name) const { + if (llvm::any_of(CountingEnumPrefixes, [Name](StringRef Prefix) -> bool { + return Name.starts_with_insensitive(Prefix); + })) + return true; + if (llvm::any_of(CountingEnumSuffixes, [Name](StringRef Suffix) -> bool { + return Name.ends_with_insensitive(Suffix); + })) + return true; + return false; +} + +std::pair<const std::size_t, const EnumConstantDecl *> +TaggedUnionMemberCountCheck::getNumberOfEnumValues(const EnumDecl *ED) { + llvm::SmallSet<llvm::APSInt, 16> EnumValues; + + const EnumConstantDecl *LastEnumConstant = nullptr; + for (const auto Enumerator : ED->enumerators()) { + EnumValues.insert(Enumerator->getInitVal()); + LastEnumConstant = Enumerator; + } + + if (EnableCountingEnumHeuristic && LastEnumConstant && + isCountingEnumLikeName(LastEnumConstant->getName()) && + (LastEnumConstant->getInitVal() == (EnumValues.size() - 1))) { ---------------- HerrCai0907 wrote: I am not sure whether it is to strict to mark last element as count. Consider this case, I want to enum start with 256 and have a count enum (will sub 256 before each using) at the end. Then I have no idea how to mark it as a counter even if prefix or postfix are configured. But it is also fine for me if you think more strict check can have more benefit. https://github.com/llvm/llvm-project/pull/89925 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits