[clang-tools-extra] 7b8f7be - [clang-tidy] Add new check bugprone-tagged-union-member-count (#89925)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 1 04:24:36 PDT 2024
Author: tigbr
Date: 2024-10-01T13:24:32+02:00
New Revision: 7b8f7beadcf1814b1f1aa985d344ca17747531a7
URL: https://github.com/llvm/llvm-project/commit/7b8f7beadcf1814b1f1aa985d344ca17747531a7
DIFF: https://github.com/llvm/llvm-project/commit/7b8f7beadcf1814b1f1aa985d344ca17747531a7.diff
LOG: [clang-tidy] Add new check bugprone-tagged-union-member-count (#89925)
This patch introduces a new check to find mismatches between the number
of data members in a union and the number enum values present in
variant-like structures.
Variant-like types can look something like this:
```c++
struct variant {
enum {
tag1,
tag2,
} kind;
union {
int i;
char c;
} data;
};
```
The kind data member of the variant is supposed to tell which data
member of the union is valid, however if there are fewer enum values
than union members, then it is likely a mistake.
The opposite is not that obvious, because it might be fine to have more
enum values than union data members, but for the time being I am curious
how many real bugs can be caught if we give a warning regardless.
This patch also contains a heuristic where we try to guess whether the
last enum constant is actually supposed to be a tag value for the
variant or whether it is just holding how many enum constants have been
created.
Patch by Gábor Tóthvári!
Added:
clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-bad-config.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-enabled.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-suffixes.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-disabled.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-enabled.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m
clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm
Modified:
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 689eb92a3d8d17..642f025359b1d1 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -78,6 +78,7 @@
#include "SuspiciousStringviewDataUsageCheck.h"
#include "SwappedArgumentsCheck.h"
#include "SwitchMissingDefaultCaseCheck.h"
+#include "TaggedUnionMemberCountCheck.h"
#include "TerminatingContinueCheck.h"
#include "ThrowKeywordMissingCheck.h"
#include "TooSmallLoopVariableCheck.h"
@@ -229,6 +230,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-suspicious-stringview-data-usage");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
"bugprone-swapped-arguments");
+ CheckFactories.registerCheck<TaggedUnionMemberCountCheck>(
+ "bugprone-tagged-union-member-count");
CheckFactories.registerCheck<TerminatingContinueCheck>(
"bugprone-terminating-continue");
CheckFactories.registerCheck<ThrowKeywordMissingCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index cb0d8ae98bac58..9f7ecb9623c539 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -73,6 +73,7 @@ add_clang_library(clangTidyBugproneModule
SuspiciousSemicolonCheck.cpp
SuspiciousStringCompareCheck.cpp
SwappedArgumentsCheck.cpp
+ TaggedUnionMemberCountCheck.cpp
TerminatingContinueCheck.cpp
ThrowKeywordMissingCheck.cpp
TooSmallLoopVariableCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
new file mode 100644
index 00000000000000..db99ef3786e5f5
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -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
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
new file mode 100644
index 00000000000000..8b9d677d00b40a
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -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
+///
diff erent 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
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 7d37a4b03222cf..e34e296b5a096d 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -103,6 +103,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
+- New :doc:`bugprone-tagged-union-member-count
+ <clang-tidy/checks/bugprone/tagged-union-member-count>` check.
+
+ Gives warnings for tagged unions, where the number of tags is
+
diff erent from the number of data members inside the union.
+
New check aliases
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
new file mode 100644
index 00000000000000..2f1036c10345e9
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
@@ -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
+
diff erent 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;
+ };
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 1909d7b8d8e246..e3dfabba8fad14 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -145,6 +145,7 @@ Clang-Tidy Checks
:doc:`bugprone-suspicious-stringview-data-usage <bugprone/suspicious-stringview-data-usage>`,
:doc:`bugprone-swapped-arguments <bugprone/swapped-arguments>`, "Yes"
:doc:`bugprone-switch-missing-default-case <bugprone/switch-missing-default-case>`,
+ :doc:`bugprone-tagged-union-member-count <bugprone/tagged-union-member-count>`,
:doc:`bugprone-terminating-continue <bugprone/terminating-continue>`, "Yes"
:doc:`bugprone-throw-keyword-missing <bugprone/throw-keyword-missing>`,
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-bad-config.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-bad-config.cpp
new file mode 100644
index 00000000000000..73bfb7acbc4643
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-bad-config.cpp
@@ -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
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp
new file mode 100644
index 00000000000000..dca52170a749a3
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp
@@ -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;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-enabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-enabled.cpp
new file mode 100644
index 00000000000000..96aef122e85ef6
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-enabled.cpp
@@ -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;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp
new file mode 100644
index 00000000000000..c0e33ac6f6f36f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp
@@ -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;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes.cpp
new file mode 100644
index 00000000000000..c287b1953a3336
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes.cpp
@@ -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;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-suffixes.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-suffixes.cpp
new file mode 100644
index 00000000000000..f248f2efaa5adc
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-suffixes.cpp
@@ -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;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-disabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-disabled.cpp
new file mode 100644
index 00000000000000..c39683c3c40f60
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-disabled.cpp
@@ -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;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-enabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-enabled.cpp
new file mode 100644
index 00000000000000..10d376d7919685
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-enabled.cpp
@@ -0,0 +1,30 @@
+// 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: }}' --
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (2) than tags (3)
+struct IncorrectBecauseStrictmodeIsEnabled {
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ } Tags;
+ union {
+ char A;
+ short B;
+ } Data;
+};
+
+struct Correct { // No warnings expected
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ } Tags;
+ union {
+ char A;
+ short B;
+ int C;
+ } Data;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c
new file mode 100644
index 00000000000000..60c93c553bacaf
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c
@@ -0,0 +1,149 @@
+// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
+
+typedef enum Tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+} Tags3;
+
+typedef enum Tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+} Tags4;
+
+typedef union Union3 {
+ short *Shorts;
+ int *Ints;
+ float *Floats;
+} Union3;
+
+typedef union Union4 {
+ short *Shorts;
+ double *Doubles;
+ int *Ints;
+ float *Floats;
+} Union4;
+
+// It is not obvious which enum is the tag for the union.
+struct maybeTaggedUnion1 { // No warnings expected.
+ enum Tags3 TagA;
+ enum Tags4 TagB;
+ union Union4 Data;
+};
+
+// It is not obvious which union does the tag belong to.
+struct maybeTaggedUnion2 { // No warnings expected.
+ enum Tags3 Tag;
+ union Union3 DataB;
+ union Union3 DataA;
+};
+
+// It is not obvious which union does the tag belong to.
+struct maybeTaggedUnion3 { // No warnings expected.
+ enum Tags3 Tag;
+ union {
+ int I1;
+ int I2;
+ int I3;
+ };
+ union {
+ float F1;
+ float F2;
+ float F3;
+ };
+};
+
+// No warnings expected, because LastATag is just an alias
+struct TaggedUnionWithAliasedEnumConstant {
+ enum {
+ ATag1,
+ ATag2,
+ ATag3,
+ LastATag = ATag3,
+ } Tag;
+ union {
+ float F;
+ int *Ints;
+ char Key[8];
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithPredefinedTagAndPredefinedUnion {
+ enum Tags3 Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithPredefinedTagAndInlineUnion {
+ enum Tags3 Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ struct {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithInlineTagAndPredefinedUnion {
+ enum {
+ TaggedUnion7tag1,
+ TaggedUnion7tag2,
+ TaggedUnion7tag3,
+ } Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithInlineTagAndInlineUnion {
+ enum {
+ TaggedUnion8tag1,
+ TaggedUnion8tag2,
+ TaggedUnion8tag3,
+ } Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ struct {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructNesting {
+ enum Tags3 Tag;
+ union {
+ float F;
+ int I;
+ long L;
+ // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: tagged union has more data members (4) than tags (3)
+ struct innerdecl {
+ enum Tags3 Tag;
+ union Union4 Data;
+ } Inner;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
+ Tags3 Tag;
+ Union4 Data;
+};
+
+#define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\
+struct Name {\
+ Tag Kind;\
+ Union Data;\
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
+DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
new file mode 100644
index 00000000000000..25827e8c8de0cf
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
@@ -0,0 +1,310 @@
+// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t
+// Test check with C++ features
+
+typedef enum Tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+} Tags3;
+
+typedef enum Tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+} Tags4;
+
+enum class Classtags3 {
+ classtags3_1,
+ classtags3_2,
+ classtags3_3,
+};
+
+enum class Typedtags3 : unsigned int {
+ typedtags3_1,
+ typedtags3_2,
+ typedtags3_3,
+};
+
+typedef union Union3 {
+ short *Shorts;
+ int *Ints;
+ float *Floats;
+} Union3;
+
+typedef union Union4 {
+ short *Shorts;
+ double *Doubles;
+ int *Ints;
+ float *Floats;
+} Union4;
+
+// It is not obvious which enum is the tag for the union.
+class MaybeTaggedUnion1 { // No warnings expected.
+ enum Tags3 TagA;
+ enum Tags4 TagB;
+ union Union4 Data;
+};
+
+// It is not obvious which union does the tag belong to.
+class MaybeTaggedUnion2 { // No warnings expected.
+ enum Tags3 Tag;
+ union Union3 DataB;
+ union Union3 DataA;
+};
+
+// It is not obvious which union does the tag belong to.
+class MaybeTaggedUnion3 { // No warnings expected.
+ enum Tags3 Tag;
+ union {
+ int I1;
+ int I2;
+ int I3;
+ };
+ union {
+ float F1;
+ float F2;
+ float F3;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassPredefinedTagAndPredefinedUnion {
+ enum Tags3 Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassPredefinedTagAndInlineUnion {
+ enum Tags3 Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ class {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassInlineTagAndPredefinedUnion {
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassInlineTagAndInlineUnion {
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ class {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithNestedTaggedUnionClass {
+ enum Tags3 Tag;
+ union {
+ float F;
+ int I;
+ long L;
+ // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: tagged union has more data members (4) than tags (3)
+ class Innerdecl {
+ enum Tags3 Tag;
+ union Union4 Data;
+ } Inner;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithTypedefedTag {
+ Tags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithEnumClass {
+ enum Classtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClasswithEnumClass {
+ enum Classtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithTypedEnum {
+ Typedtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithTypedEnum {
+ Typedtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct AnonymousTaggedUnionStruct {
+ Tags3 Tag;
+ union {
+ char A;
+ short B;
+ int C;
+ long D;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithAnonymousUnion {
+ Tags3 Tag;
+ union {
+ char A;
+ short B;
+ int C;
+ long D;
+ };
+};
+
+namespace testnamespace {
+
+enum Tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+};
+
+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 TaggedUnionStructInNamespace {
+ Tags3 Tags;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassInNamespace {
+ Tags3 Tags;
+ Union4 Data;
+};
+
+} // namespace testnamespace
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithNamespacedTagAndUnion {
+ testnamespace::Tags3 Tags;
+ testnamespace::Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithNamespacedTagAndUnion {
+ testnamespace::Tags3 Tags;
+ testnamespace::Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+struct TemplatedStructWithNamespacedTagAndUnion {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedStructWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedStruct3;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+class TemplatedClassWithNamespacedTagAndUnion {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedClassWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedClass3;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+struct TemplatedStruct {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedStruct<Tags3, Union3> TemplatedStruct1; // No warning expected
+TemplatedStruct<Tags3, Union4> TemplatedStruct2;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+class TemplatedClass {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedClass<Tags3, Union3> TemplatedClass1; // No warning expected
+TemplatedClass<Tags3, Union4> TemplatedClass2;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3)
+template <typename T>
+struct TemplatedStructButTaggedUnionPartIsNotTemplated {
+ Tags3 Kind;
+ Union4 Data;
+ T SomethingElse;
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3)
+template <typename T>
+class TemplatedClassButTaggedUnionPartIsNotTemplated {
+ Tags3 Kind;
+ Union4 Data;
+ T SomethingElse;
+};
+
+#define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\
+struct Name {\
+ Tag Kind;\
+ Union Data;\
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
+DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
+
+#define DECLARE_TAGGED_UNION_CLASS(Tag, Union, Name)\
+class Name {\
+ Tag Kind;\
+ Union Data;\
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:43: warning: tagged union has more data members (4) than tags (3)
+DECLARE_TAGGED_UNION_CLASS(Tags3, Union4, TaggedUnionClassFromMacro);
+
+// Lambdas implicitly compile down to an unnamed CXXRecordDecl and if they have captures,
+// then those become unnamed fields.
+void DoNotMatchLambdas() {
+ enum {
+ A
+ } e;
+ union {
+ long A;
+ char B;
+ } u;
+ auto L = [e, u] () {};
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m
new file mode 100644
index 00000000000000..60c93c553bacaf
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m
@@ -0,0 +1,149 @@
+// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
+
+typedef enum Tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+} Tags3;
+
+typedef enum Tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+} Tags4;
+
+typedef union Union3 {
+ short *Shorts;
+ int *Ints;
+ float *Floats;
+} Union3;
+
+typedef union Union4 {
+ short *Shorts;
+ double *Doubles;
+ int *Ints;
+ float *Floats;
+} Union4;
+
+// It is not obvious which enum is the tag for the union.
+struct maybeTaggedUnion1 { // No warnings expected.
+ enum Tags3 TagA;
+ enum Tags4 TagB;
+ union Union4 Data;
+};
+
+// It is not obvious which union does the tag belong to.
+struct maybeTaggedUnion2 { // No warnings expected.
+ enum Tags3 Tag;
+ union Union3 DataB;
+ union Union3 DataA;
+};
+
+// It is not obvious which union does the tag belong to.
+struct maybeTaggedUnion3 { // No warnings expected.
+ enum Tags3 Tag;
+ union {
+ int I1;
+ int I2;
+ int I3;
+ };
+ union {
+ float F1;
+ float F2;
+ float F3;
+ };
+};
+
+// No warnings expected, because LastATag is just an alias
+struct TaggedUnionWithAliasedEnumConstant {
+ enum {
+ ATag1,
+ ATag2,
+ ATag3,
+ LastATag = ATag3,
+ } Tag;
+ union {
+ float F;
+ int *Ints;
+ char Key[8];
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithPredefinedTagAndPredefinedUnion {
+ enum Tags3 Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithPredefinedTagAndInlineUnion {
+ enum Tags3 Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ struct {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithInlineTagAndPredefinedUnion {
+ enum {
+ TaggedUnion7tag1,
+ TaggedUnion7tag2,
+ TaggedUnion7tag3,
+ } Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithInlineTagAndInlineUnion {
+ enum {
+ TaggedUnion8tag1,
+ TaggedUnion8tag2,
+ TaggedUnion8tag3,
+ } Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ struct {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructNesting {
+ enum Tags3 Tag;
+ union {
+ float F;
+ int I;
+ long L;
+ // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: tagged union has more data members (4) than tags (3)
+ struct innerdecl {
+ enum Tags3 Tag;
+ union Union4 Data;
+ } Inner;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
+ Tags3 Tag;
+ Union4 Data;
+};
+
+#define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\
+struct Name {\
+ Tag Kind;\
+ Union Data;\
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
+DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm
new file mode 100644
index 00000000000000..8b308555281c5c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm
@@ -0,0 +1,309 @@
+// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
+
+typedef enum Tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+} Tags3;
+
+typedef enum Tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+} Tags4;
+
+enum class Classtags3 {
+ classtags3_1,
+ classtags3_2,
+ classtags3_3,
+};
+
+enum class Typedtags3 : unsigned int {
+ typedtags3_1,
+ typedtags3_2,
+ typedtags3_3,
+};
+
+typedef union Union3 {
+ short *Shorts;
+ int *Ints;
+ float *Floats;
+} Union3;
+
+typedef union Union4 {
+ short *Shorts;
+ double *Doubles;
+ int *Ints;
+ float *Floats;
+} Union4;
+
+// It is not obvious which enum is the tag for the union.
+class MaybeTaggedUnion1 { // No warnings expected.
+ enum Tags3 TagA;
+ enum Tags4 TagB;
+ union Union4 Data;
+};
+
+// It is not obvious which union does the tag belong to.
+class MaybeTaggedUnion2 { // No warnings expected.
+ enum Tags3 Tag;
+ union Union3 DataB;
+ union Union3 DataA;
+};
+
+// It is not obvious which union does the tag belong to.
+class MaybeTaggedUnion3 { // No warnings expected.
+ enum Tags3 Tag;
+ union {
+ int I1;
+ int I2;
+ int I3;
+ };
+ union {
+ float F1;
+ float F2;
+ float F3;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassPredefinedTagAndPredefinedUnion {
+ enum Tags3 Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassPredefinedTagAndInlineUnion {
+ enum Tags3 Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ class {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassInlineTagAndPredefinedUnion {
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } Tag;
+ union Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassInlineTagAndInlineUnion {
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } Tag;
+ union {
+ int *Ints;
+ char Characters[13];
+ class {
+ double Re;
+ double Im;
+ } Complex;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithNestedTaggedUnionClass {
+ enum Tags3 Tag;
+ union {
+ float F;
+ int I;
+ long L;
+ // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: tagged union has more data members (4) than tags (3)
+ class Innerdecl {
+ enum Tags3 Tag;
+ union Union4 Data;
+ } Inner;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithTypedefedTag {
+ Tags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithEnumClass {
+ enum Classtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClasswithEnumClass {
+ enum Classtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithTypedEnum {
+ Typedtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithTypedEnum {
+ Typedtags3 Tag;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct AnonymousTaggedUnionStruct {
+ Tags3 Tag;
+ union {
+ char A;
+ short B;
+ int C;
+ long D;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithAnonymousUnion {
+ Tags3 Tag;
+ union {
+ char A;
+ short B;
+ int C;
+ long D;
+ };
+};
+
+namespace testnamespace {
+
+enum Tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+};
+
+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 TaggedUnionStructInNamespace {
+ Tags3 Tags;
+ Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassInNamespace {
+ Tags3 Tags;
+ Union4 Data;
+};
+
+} // namespace testnamespace
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TaggedUnionStructWithNamespacedTagAndUnion {
+ testnamespace::Tags3 Tags;
+ testnamespace::Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
+class TaggedUnionClassWithNamespacedTagAndUnion {
+ testnamespace::Tags3 Tags;
+ testnamespace::Union4 Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+struct TemplatedStructWithNamespacedTagAndUnion {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedStructWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedStruct3;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+class TemplatedClassWithNamespacedTagAndUnion {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedClassWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedClass3;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+struct TemplatedStruct {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedStruct<Tags3, Union3> TemplatedStruct1; // No warning expected
+TemplatedStruct<Tags3, Union4> TemplatedStruct2;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3)
+template <typename Tag, typename Union>
+class TemplatedClass {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedClass<Tags3, Union3> TemplatedClass1; // No warning expected
+TemplatedClass<Tags3, Union4> TemplatedClass2;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: tagged union has more data members (4) than tags (3)
+template <typename T>
+struct TemplatedStructButTaggedUnionPartIsNotTemplated {
+ Tags3 Kind;
+ Union4 Data;
+ T SomethingElse;
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: tagged union has more data members (4) than tags (3)
+template <typename T>
+class TemplatedClassButTaggedUnionPartIsNotTemplated {
+ Tags3 Kind;
+ Union4 Data;
+ T SomethingElse;
+};
+
+#define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\
+struct Name {\
+ Tag Kind;\
+ Union Data;\
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
+DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
+
+#define DECLARE_TAGGED_UNION_CLASS(Tag, Union, Name)\
+class Name {\
+ Tag Kind;\
+ Union Data;\
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:43: warning: tagged union has more data members (4) than tags (3)
+DECLARE_TAGGED_UNION_CLASS(Tags3, Union4, TaggedUnionClassFromMacro);
+
+// Lambdas implicitly compile down to an unnamed CXXRecordDecl and if they have captures,
+// then those become unnamed fields.
+void DoNotMatchLambdas() {
+ enum {
+ A
+ } e;
+ union {
+ long A;
+ char B;
+ } u;
+ auto L = [e, u] () {};
+}
More information about the cfe-commits
mailing list