| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| //===--- 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, fieldCountOfKindIsOne, | ||
| ast_matchers::internal::Matcher<FieldDecl>, InnerMatcher, | ||
| StringRef, BindName) { | ||
| // BoundNodesTreeBuilder resets itself when a match occurs. | ||
| // So to avoid losing previously saved binds, a temporary instance | ||
| // is used for matching. | ||
| // | ||
| // For precedence, see commit: 5b07de1a5faf4a22ae6fd982b877c5e7e3a76559 | ||
| clang::ast_matchers::internal::BoundNodesTreeBuilder TempBuilder; | ||
|
|
||
| const FieldDecl *FirstMatch = nullptr; | ||
| for (const FieldDecl *Field : Node.fields()) { | ||
| if (InnerMatcher.matches(*Field, Finder, &TempBuilder)) { | ||
| if (FirstMatch) { | ||
| return false; | ||
| } else { | ||
| FirstMatch = Field; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (FirstMatch) { | ||
| Builder->setBinding(BindName, clang::DynTypedNode::create(*FirstMatch)); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| } // 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) { | ||
|
|
||
| auto UnionField = fieldDecl(hasType(qualType( | ||
| hasCanonicalType(recordType(hasDeclaration(recordDecl(isUnion()))))))); | ||
|
|
||
| auto EnumField = fieldDecl(hasType( | ||
| qualType(hasCanonicalType(enumType(hasDeclaration(enumDecl())))))); | ||
|
|
||
| auto hasOneUnionField = fieldCountOfKindIsOne(UnionField, UnionMatchBindName); | ||
| auto hasOneEnumField = fieldCountOfKindIsOne(EnumField, TagMatchBindName); | ||
|
|
||
| Finder->addMatcher(recordDecl(anyOf(isStruct(), isClass()), hasOneUnionField, | ||
| hasOneEnumField, unless(isImplicit())) | ||
| .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 EnumConstantDecl *Enumerator : ED->enumerators()) { | ||
| EnumValues.insert(Enumerator->getInitVal()); | ||
| LastEnumConstant = Enumerator; | ||
| } | ||
|
|
||
| if (EnableCountingEnumHeuristic && LastEnumConstant && | ||
| isCountingEnumLikeName(LastEnumConstant->getName()) && | ||
| (LastEnumConstant->getInitVal() == (EnumValues.size() - 1))) { | ||
| return {EnumValues.size() - 1, LastEnumConstant}; | ||
| } | ||
|
|
||
| return {EnumValues.size(), nullptr}; | ||
| } | ||
|
|
||
| void TaggedUnionMemberCountCheck::check( | ||
| const MatchFinder::MatchResult &Result) { | ||
| const auto *Root = Result.Nodes.getNodeAs<RecordDecl>(RootMatchBindName); | ||
| const auto *UnionField = | ||
| Result.Nodes.getNodeAs<FieldDecl>(UnionMatchBindName); | ||
| const auto *TagField = Result.Nodes.getNodeAs<FieldDecl>(TagMatchBindName); | ||
|
|
||
| assert(Root && "Root is missing!"); | ||
| assert(UnionField && "UnionField is missing!"); | ||
| assert(TagField && "TagField is missing!"); | ||
| if (!Root || !UnionField || !TagField) | ||
| return; | ||
|
|
||
| const auto *UnionDef = | ||
| UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl(); | ||
| const auto *EnumDef = llvm::dyn_cast<EnumDecl>( | ||
| TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl()); | ||
|
|
||
| assert(UnionDef && "UnionDef is missing!"); | ||
| assert(EnumDef && "EnumDef is missing!"); | ||
| if (!UnionDef || !EnumDef) | ||
| return; | ||
|
|
||
| const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields()); | ||
| auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef); | ||
|
|
||
| if (UnionMemberCount > TagCount) { | ||
| diag(Root->getLocation(), | ||
| "tagged union has more data members (%0) than tags (%1)!") | ||
| << UnionMemberCount << TagCount; | ||
| } else if (StrictMode && UnionMemberCount < TagCount) { | ||
| diag(Root->getLocation(), | ||
| "tagged union has fewer data members (%0) than tags (%1)!") | ||
| << UnionMemberCount << TagCount; | ||
| } | ||
|
|
||
| if (CountingEnumConstantDecl) { | ||
| diag(CountingEnumConstantDecl->getLocation(), | ||
| "assuming that this constant is just an auxiliary value and not " | ||
| "used for indicating a valid union data member", | ||
| DiagnosticIDs::Note); | ||
| } | ||
| } | ||
|
|
||
| } // namespace clang::tidy::bugprone |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| //===--- TaggedUnionMemberCountCheck.h - clang-tidy -------------*- C++ -*-===// | ||
| // | ||
| // 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 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::bugprone { | ||
|
|
||
| /// Gives warnings for tagged unions, where the number of tags is | ||
| /// different from the number of data members inside the union. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/tagged-union-member-count.html | ||
| class TaggedUnionMemberCountCheck : public ClangTidyCheck { | ||
| public: | ||
| TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context); | ||
| void storeOptions(ClangTidyOptions::OptionMap &Opts) override; | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
|
|
||
| private: | ||
| const bool StrictMode; | ||
| const bool EnableCountingEnumHeuristic; | ||
| const std::vector<StringRef> CountingEnumPrefixes; | ||
| const std::vector<StringRef> CountingEnumSuffixes; | ||
|
|
||
| std::pair<const std::size_t, const EnumConstantDecl *> | ||
| getNumberOfEnumValues(const EnumDecl *ED); | ||
| bool isCountingEnumLikeName(StringRef Name) const; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::bugprone | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -483,7 +483,6 @@ std::string convertGlobToRegex(llvm::StringRef Glob) { | |
| } | ||
| } | ||
| RegStream << '$'; | ||
| return RegText; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,7 +87,6 @@ std::string buildUmbrella(llvm::StringLiteral Mandatory, | |
| "#endif\n", | ||
| Header); | ||
| } | ||
| return Result; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,280 @@ | ||
| .. title:: clang-tidy - bugprone-tagged-union-member-count | ||
|
|
||
| bugprone-tagged-union-member-count | ||
| ================================== | ||
|
|
||
| Gives warnings for tagged unions, where the number of tags is | ||
| different from the number of data members inside the union. | ||
|
|
||
| A struct or a class is considered to be a tagged union if it has | ||
| exactly one union data member and exactly one enum data member and | ||
| any number of other data members that are neither unions or enums. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum Tags { | ||
| Tag1, | ||
| Tag2, | ||
| }; | ||
|
|
||
| struct TaggedUnion { // warning: tagged union has more data members (3) than tags (2) | ||
| enum Tags Kind; | ||
| union { | ||
| int I; | ||
| float F; | ||
| char *Str; | ||
| } Data; | ||
| }; | ||
| How enum constants are counted | ||
| ------------------------------ | ||
|
|
||
| The main complicating factor when counting the number of enum constants is that | ||
| some of them might be auxiliary values that purposefully don't have a corresponding union | ||
| data member and are used for something else. For example the last enum constant | ||
| sometimes explicitly "points to" the last declared valid enum constant or | ||
| tracks how many enum constants have been declared. | ||
|
|
||
| For an illustration: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithLast { | ||
| Tag1 = 0, | ||
| Tag2 = 1, | ||
| Tag3 = 2, | ||
| LastTag = 2 | ||
| }; | ||
|
|
||
| enum TagWithCounter { | ||
| Tag1, // is 0 | ||
| Tag2, // is 1 | ||
| Tag3, // is 2 | ||
| TagCount, // is 3 | ||
| }; | ||
|
|
||
| The check counts the number of distinct values among the enum constants and not the enum | ||
| constants themselves. This way the enum constants that are essentially just aliases of other | ||
| enum constants are not included in the final count. | ||
|
|
||
| Handling of counting enum constants (ones like :code:`TagCount` in the previous code example) | ||
| is done by decreasing the number of enum values by one if the name of the last enum constant | ||
| starts with a prefix or ends with a suffix specified in :option:`CountingEnumPrefixes`, | ||
| :option:`CountingEnumSuffixes` and it's value is one less than the total number of distinct | ||
| values in the enum. | ||
|
|
||
| When the final count is adjusted based on this heuristic then a diagnostic note is emitted | ||
| that shows which enum constant matched the criteria. | ||
|
|
||
| The heuristic can be disabled entirely (:option:`EnableCountingEnumHeuristic`) or | ||
| configured to follow your naming convention (:option:`CountingEnumPrefixes`, :option:`CountingEnumSuffixes`). | ||
| The strings specified in :option:`CountingEnumPrefixes`, :option:`CountingEnumSuffixes` are matched | ||
| case insensitively. | ||
|
|
||
| Example counts: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| // Enum count is 3, because the value 2 is counted only once | ||
| enum TagWithLast { | ||
| Tag1 = 0, | ||
| Tag2 = 1, | ||
| Tag3 = 2, | ||
| LastTag = 2 | ||
| }; | ||
|
|
||
| // Enum count is 3, because TagCount is heuristically excluded | ||
| enum TagWithCounter { | ||
| Tag1, // is 0 | ||
| Tag2, // is 1 | ||
| Tag3, // is 2 | ||
| TagCount, // is 3 | ||
| }; | ||
|
|
||
|
|
||
| Options | ||
| ------- | ||
|
|
||
| .. option:: EnableCountingEnumHeuristic | ||
|
|
||
| This option enables or disables the counting enum heuristic. | ||
| It uses the prefixes and suffixes specified in the options | ||
| :option:`CountingEnumPrefixes`, :option:`CountingEnumSuffixes` to find counting enum constants by | ||
| using them for prefix and suffix matching. | ||
|
|
||
| This option is enabled by default. | ||
|
|
||
| When :option:`EnableCountingEnumHeuristic` is `false`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounter { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| TagCount, | ||
| }; | ||
|
|
||
| struct TaggedUnion { | ||
| TagWithCounter Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| When :option:`EnableCountingEnumHeuristic` is `true`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounter { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| TagCount, | ||
| }; | ||
|
|
||
| struct TaggedUnion { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounter Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| .. option:: CountingEnumPrefixes | ||
|
|
||
| See :option:`CountingEnumSuffixes` below. | ||
|
|
||
| .. option:: CountingEnumSuffixes | ||
|
|
||
| CountingEnumPrefixes and CountingEnumSuffixes are lists of semicolon | ||
| separated strings that are used to search for possible counting enum constants. | ||
| These strings are matched case insensitively as prefixes and suffixes | ||
| respectively on the names of the enum constants. | ||
| If :option:`EnableCountingEnumHeuristic` is `false` then these options do nothing. | ||
|
|
||
| The default value of :option:`CountingEnumSuffixes` is `count` and of | ||
| :option:`CountingEnumPrefixes` is the empty string. | ||
|
|
||
| When :option:`EnableCountingEnumHeuristic` is `true` and :option:`CountingEnumSuffixes` | ||
| is `count;size`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounterCount { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| TagCount, | ||
| }; | ||
|
|
||
| struct TaggedUnionCount { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterCount Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| enum TagWithCounterSize { | ||
| Tag11, | ||
| Tag22, | ||
| Tag33, | ||
| TagSize, | ||
| }; | ||
|
|
||
| struct TaggedUnionSize { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterSize Kind; | ||
| union { | ||
| int A; | ||
| long B; | ||
| char *Str; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| When :option:`EnableCountingEnumHeuristic` is `true` and :option:`CountingEnumPrefixes` is `maxsize;last_` | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| enum TagWithCounterLast { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| last_tag, | ||
| }; | ||
|
|
||
| struct TaggedUnionLast { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterLast tag; | ||
| union { | ||
| int I; | ||
| short S; | ||
| char *C; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| enum TagWithCounterMaxSize { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| MaxSizeTag, | ||
| }; | ||
|
|
||
| struct TaggedUnionMaxSize { // warning: tagged union has more data members (4) than tags (3) | ||
| TagWithCounterMaxSize tag; | ||
| union { | ||
| int I; | ||
| short S; | ||
| char *C; | ||
| float F; | ||
| } Data; | ||
| }; | ||
| .. option:: StrictMode | ||
|
|
||
| When enabled, the check will also give a warning, when the number of tags | ||
| is greater than the number of union data members. | ||
|
|
||
| This option is disabled by default. | ||
|
|
||
| When :option:`StrictMode` is `false`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| struct TaggedUnion { | ||
| enum { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| } Tags; | ||
| union { | ||
| int I; | ||
| float F; | ||
| } Data; | ||
| }; | ||
|
|
||
| When :option:`StrictMode` is `true`: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| struct TaggedUnion { // warning: tagged union has fewer data members (2) than tags (3) | ||
| enum { | ||
| Tag1, | ||
| Tag2, | ||
| Tag3, | ||
| } Tags; | ||
| union { | ||
| int I; | ||
| float F; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_NULLABLEVALUE_H_ | ||
| #define LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_NULLABLEVALUE_H_ | ||
|
|
||
| #include "bsl_optional.h" | ||
|
|
||
| /// Mock of `bdlb::NullableValue`. | ||
| namespace BloombergLP::bdlb { | ||
|
|
||
| template <typename T> | ||
| class NullableValue : public bsl::optional<T> { | ||
| public: | ||
| constexpr NullableValue() noexcept; | ||
|
|
||
| constexpr NullableValue(bsl::nullopt_t) noexcept; | ||
|
|
||
| NullableValue(const NullableValue &) = default; | ||
|
|
||
| NullableValue(NullableValue &&) = default; | ||
|
|
||
| const T &value() const &; | ||
| T &value() &; | ||
|
|
||
| // 'operator bool' is inherited from bsl::optional | ||
|
|
||
| constexpr bool isNull() const noexcept; | ||
|
|
||
| template <typename U> | ||
| constexpr T valueOr(U &&v) const &; | ||
|
|
||
| // 'reset' is inherited from bsl::optional | ||
|
|
||
| template <typename U> NullableValue &operator=(const U &u); | ||
| }; | ||
|
|
||
|
|
||
| } // namespace BloombergLP::bdlb | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_NULLABLEVALUE_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_ | ||
| #define LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_ | ||
|
|
||
| /// Mock of `bsl::optional`. | ||
| namespace bsl { | ||
|
|
||
| // clang-format off | ||
| template <typename T> struct remove_reference { using type = T; }; | ||
| template <typename T> struct remove_reference<T&> { using type = T; }; | ||
| template <typename T> struct remove_reference<T&&> { using type = T; }; | ||
| // clang-format on | ||
|
|
||
| template <typename T> | ||
| using remove_reference_t = typename remove_reference<T>::type; | ||
|
|
||
| template <typename T> | ||
| constexpr T &&forward(remove_reference_t<T> &t) noexcept; | ||
|
|
||
| template <typename T> | ||
| constexpr T &&forward(remove_reference_t<T> &&t) noexcept; | ||
|
|
||
| template <typename T> | ||
| constexpr remove_reference_t<T> &&move(T &&x); | ||
|
|
||
| struct nullopt_t { | ||
| constexpr explicit nullopt_t() {} | ||
| }; | ||
|
|
||
| constexpr nullopt_t nullopt; | ||
|
|
||
| template <typename T> | ||
| class optional { | ||
| public: | ||
| constexpr optional() noexcept; | ||
|
|
||
| constexpr optional(nullopt_t) noexcept; | ||
|
|
||
| optional(const optional &) = default; | ||
|
|
||
| optional(optional &&) = default; | ||
|
|
||
| const T &operator*() const &; | ||
| T &operator*() &; | ||
| const T &&operator*() const &&; | ||
| T &&operator*() &&; | ||
|
|
||
| const T *operator->() const; | ||
| T *operator->(); | ||
|
|
||
| const T &value() const &; | ||
| T &value() &; | ||
| const T &&value() const &&; | ||
| T &&value() &&; | ||
|
|
||
| constexpr explicit operator bool() const noexcept; | ||
| constexpr bool has_value() const noexcept; | ||
|
|
||
| template <typename U> | ||
| constexpr T value_or(U &&v) const &; | ||
| template <typename U> | ||
| T value_or(U &&v) &&; | ||
|
|
||
| template <typename... Args> | ||
| T &emplace(Args &&...args); | ||
|
|
||
| void reset() noexcept; | ||
|
|
||
| void swap(optional &rhs) noexcept; | ||
|
|
||
| template <typename U> optional &operator=(const U &u); | ||
| }; | ||
|
|
||
| } // namespace bsl | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \ | ||
| // RUN: }}' | ||
|
|
||
| // Warn when the heuristic is disabled and a suffix or a prefix is set explicitly. | ||
|
|
||
| // CHECK-MESSAGES: warning: bugprone-tagged-union-member-count: Counting enum heuristic is disabled but CountingEnumPrefixes is set | ||
| // CHECK-MESSAGES: warning: bugprone-tagged-union-member-count: Counting enum heuristic is disabled but CountingEnumSuffixes is set |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: false, \ | ||
| // RUN: }}' -- | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (3) than tags (4) | ||
| struct IncorrectBecauseHeuristicIsDisabledPrefixCase { | ||
| enum { | ||
| tags11, | ||
| tags22, | ||
| tags33, | ||
| lasttag, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsDisabledPrefixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| lasttags, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| long D; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (3) than tags (4) | ||
| struct IncorrectBecauseHeuristicIsDisabledSuffixCase { | ||
| enum { | ||
| tags11, | ||
| tags22, | ||
| tags33, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsDisabledSuffixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| long D; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \ | ||
| // RUN: }}' -- | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct IncorrectBecauseHeuristicIsEnabledPrefixCase { | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| lasttag, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsEnabledPrefixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| lasttag, | ||
| } Tags; | ||
| union { | ||
| int A; | ||
| int B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct IncorrectBecauseHeuristicIsEnabledSuffixCase { | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseHeuristicIsEnabledSuffixCase { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| tags_count, | ||
| } Tags; | ||
| union { | ||
| int A; | ||
| int B; | ||
| int C; | ||
| } Data; | ||
| }; | ||
|
|
||
| union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct CountingEnumCaseInsensitivityTest1 { | ||
| enum { | ||
| node_type_loop, | ||
| node_type_branch, | ||
| node_type_function, | ||
| node_type_count, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct CountingEnumCaseInsensitivityTest2 { | ||
| enum { | ||
| NODE_TYPE_LOOP, | ||
| NODE_TYPE_BRANCH, | ||
| NODE_TYPE_FUNCTION, | ||
| NODE_TYPE_COUNT, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TagWhereCountingEnumIsAliased { | ||
| enum { | ||
| tag_alias_counter1 = 1, | ||
| tag_alias_counter2 = 2, | ||
| tag_alias_counter3 = 3, | ||
| tag_alias_other_count = 3, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (2) | ||
| struct TagWithCountingEnumButOtherValueIsAliased { | ||
| enum { | ||
| tag_alias_other1 = 1, | ||
| tag_alias_other2 = 1, | ||
| tag_alias_other3 = 3, | ||
| tag_alias_other_count = 2, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TagWhereCounterIsTheSmallest { | ||
| enum { | ||
| tag_large1 = 1000, | ||
| tag_large2 = 1001, | ||
| tag_large3 = 1002, | ||
| tag_large_count = 3, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; | ||
|
|
||
| // No warnings expected, only the last enum constant can be a counting enum constant | ||
| struct TagWhereCounterLikeNameIsNotLast { | ||
| enum { | ||
| kind_count, | ||
| kind2, | ||
| last_kind1, | ||
| kind3, | ||
| } Kind; | ||
| union { | ||
| char C; | ||
| short S; | ||
| int I; | ||
| long L; | ||
| } Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \ | ||
| // RUN: }}' -- | ||
|
|
||
| union Union3 { | ||
| short *Shorts; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| // The heuristic only considers the last enum constant | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionPrefixAndSuffixMatch { | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tagscount, | ||
| lasttags | ||
| } Kind; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct TaggedUnionOnlyPrefixMatch { | ||
| enum { | ||
| prefixtag1, | ||
| prefixtag2, | ||
| lastprefixtag | ||
| } Kind; | ||
| Union3 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2) | ||
| struct TaggedUnionOnlySuffixMatch { | ||
| enum { | ||
| suffixtag1, | ||
| suffixtag2, | ||
| suffixtagcount | ||
| } Kind; | ||
| Union3 Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "maxsize;last", \ | ||
| // RUN: }}' -- | ||
|
|
||
| union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithMaxsizeAsCounterPrefix { | ||
| enum { | ||
| twc1, | ||
| twc2, | ||
| twc3, | ||
| maxsizetwc, | ||
| } Kind; | ||
| Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithLastAsCounterPrefix { | ||
| enum { | ||
| twc11, | ||
| twc22, | ||
| twc33, | ||
| lasttwc, | ||
| } Kind; | ||
| Union4 Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: true, \ | ||
| // RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count;size", \ | ||
| // RUN: }}' -- | ||
|
|
||
| typedef union Union4 { | ||
| short *Shorts; | ||
| double *Doubles; | ||
| int *Ints; | ||
| float *Floats; | ||
| } union4; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithCounterCountSuffix { | ||
| enum { | ||
| twc1, | ||
| twc2, | ||
| twc3, | ||
| twc_count, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3) | ||
| struct TaggedUnionWithCounterSizeSuffix { | ||
| enum { | ||
| twc11, | ||
| twc22, | ||
| twc33, | ||
| twc_size, | ||
| } Kind; | ||
| union Union4 Data; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \ | ||
| // RUN: -config='{CheckOptions: { \ | ||
| // RUN: bugprone-tagged-union-member-count.StrictMode: false, \ | ||
| // RUN: }}' -- | ||
|
|
||
| // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (2) than tags (1) | ||
| struct Incorrect { | ||
| enum { | ||
| tags1, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| } Data; | ||
| }; | ||
|
|
||
| struct CorrectBecauseStrictModeIsDisabled { // No warnings expected | ||
| enum { | ||
| tags1, | ||
| tags2, | ||
| tags3, | ||
| } Tags; | ||
| union { | ||
| char A; | ||
| short B; | ||
| } Data; | ||
| }; |