[clang-tools-extra] [clang-tidy] Add new check bugprone-tagged-union-member-count (PR #89925)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 30 09:12:11 PDT 2024
=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrcode at protonmail.com>,
=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrcode at protonmail.com>,
=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrcode at protonmail.com>,
=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrcode at protonmail.com>,
=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrcode at protonmail.com>,
=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrcode at protonmail.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/89925 at github.com>
https://github.com/tigbr updated https://github.com/llvm/llvm-project/pull/89925
>From 0dff511a0f63480dea527b6e823dcf230755f312 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Sun, 3 Mar 2024 22:30:32 +0100
Subject: [PATCH 1/7] [clang-tidy] Add new check
bugprone-tagged-union-member-count
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.
I am still collecting data on how many bugs this check can catch on open
source software and I will update the pull-request with the results.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../bugprone/TaggedUnionMemberCountCheck.cpp | 125 ++++++++++++
.../bugprone/TaggedUnionMemberCountCheck.h | 31 +++
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../bugprone/tagged-union-member-count.rst | 66 +++++++
.../docs/clang-tidy/checks/list.rst | 1 +
.../bugprone/tagged-union-member-count.cpp | 181 ++++++++++++++++++
8 files changed, 414 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 2931325d8b579..d9e094e11bdf0 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -76,6 +76,7 @@
#include "SuspiciousStringviewDataUsageCheck.h"
#include "SwappedArgumentsCheck.h"
#include "SwitchMissingDefaultCaseCheck.h"
+#include "TaggedUnionMemberCountCheck.h"
#include "TerminatingContinueCheck.h"
#include "ThrowKeywordMissingCheck.h"
#include "TooSmallLoopVariableCheck.h"
@@ -223,6 +224,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 081ba67efe153..5e7fa08aece02 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -71,6 +71,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 0000000000000..6a7b5a92902c8
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -0,0 +1,125 @@
+//===--- 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 "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+#include "clang/AST/PrettyPrinter.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Casting.h"
+#include <limits>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ recordDecl(
+ allOf(isStruct(),
+ has(fieldDecl(hasType(recordDecl(isUnion()).bind("union")))),
+ has(fieldDecl(hasType(enumDecl().bind("tags"))))))
+ .bind("root"),
+ this);
+}
+
+static bool hasMultipleUnionsOrEnums(const RecordDecl *rec) {
+ int tags = 0;
+ int unions = 0;
+ for (const FieldDecl *r : rec->fields()) {
+ TypeSourceInfo *info = r->getTypeSourceInfo();
+ QualType qualtype = info->getType();
+ const Type *type = qualtype.getTypePtr();
+ if (type->isUnionType())
+ unions += 1;
+ else if (type->isEnumeralType())
+ tags += 1;
+ if (tags > 1 || unions > 1)
+ return true;
+ }
+ return false;
+}
+
+static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
+ int64_t maxTagValue = std::numeric_limits<int64_t>::min();
+ int64_t minTagValue = std::numeric_limits<int64_t>::max();
+
+ // Heuristic for counter enum constants.
+ //
+ // enum tag_with_counter {
+ // tag1,
+ // tag2,
+ // tag_count, <-- Searching for these enum constants
+ // };
+ //
+ // The 'ce' prefix is used to abbreviate counterEnum.
+ // The final tag count is decreased by 1 if and only if:
+ // 1. The number of counting enum constants = 1,
+ int ceCount = 0;
+ // 2. The counting enum constant is the last enum constant that is defined,
+ int ceFirstIndex = 0;
+ // 3. The value of the counting enum constant is the largest out of every enum constant.
+ int64_t ceValue = 0;
+
+ int64_t enumConstantsCount = 0;
+ for (auto En : llvm::enumerate(ed->enumerators())) {
+ enumConstantsCount += 1;
+
+ int64_t enumValue = En.value()->getInitVal().getExtValue();
+ StringRef enumName = En.value()->getName();
+
+ if (enumValue > maxTagValue)
+ maxTagValue = enumValue;
+ if (enumValue < minTagValue)
+ minTagValue = enumValue;
+
+ if (enumName.ends_with_insensitive("count")) {
+ if (ceCount == 0) {
+ ceFirstIndex = En.index();
+ }
+ ceValue = enumValue;
+ ceCount += 1;
+ }
+ }
+
+ int64_t validValuesCount = maxTagValue - minTagValue + 1;
+ if (ceCount == 1 &&
+ ceFirstIndex == enumConstantsCount - 1 &&
+ ceValue == maxTagValue) {
+ validValuesCount -= 1;
+ }
+ return validValuesCount;
+}
+
+void TaggedUnionMemberCountCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *root = Result.Nodes.getNodeAs<RecordDecl>("root");
+ const auto *unionMatch = Result.Nodes.getNodeAs<RecordDecl>("union");
+ const auto *tagMatch = Result.Nodes.getNodeAs<EnumDecl>("tags");
+
+ if (hasMultipleUnionsOrEnums(root))
+ return;
+
+ int64_t unionMemberCount = llvm::range_size(unionMatch->fields());
+ int64_t tagCount = getNumberOfValidEnumValues(tagMatch);
+
+ // FIXME: Maybe a emit a note when a counter enum constant was found.
+ if (unionMemberCount > tagCount) {
+ diag(root->getLocation(), "Tagged union has more data members than tags! "
+ "Data members: %0 Tags: %1")
+ << unionMemberCount << tagCount;
+ } else if (unionMemberCount < tagCount) {
+ diag(root->getLocation(), "Tagged union has fewer data members than tags! "
+ "Data members: %0 Tags: %1")
+ << unionMemberCount << tagCount;
+ }
+}
+
+} // 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 0000000000000..bde42da23ff38
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -0,0 +1,31 @@
+//===--- 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)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // 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 78b09d23d4427..9f9069a8d0892 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -129,6 +129,12 @@ New checks
Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.
+- New :doc:`bugprone-tagged-union-member-count
+ <clang-tidy/checks/bugprone/tagged-union-member-count>` check.
+
+ Warns about suspicious looking tagged unions where the number of enum
+ constants and the number of union data members are not equal.
+
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 0000000000000..7eac93c6c9602
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
@@ -0,0 +1,66 @@
+.. 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 tags2 {
+ tag1,
+ tag2,
+ };
+
+ struct taggedunion { // warning: Tagged union has more data members than tags! Data members: 3 Tags: 2 [bugprone-tagged-union-member-count]
+ enum tags2 kind;
+ union {
+ int i;
+ float f;
+ char *str;
+ } data;
+ };
+
+ enum tags4 {
+ tag1,
+ tag2,
+ tag3,
+ tag4,
+ };
+
+ struct taggedunion { // warning: Tagged union has more fewer members than tags! Data members: 3 Tags: 4 [bugprone-tagged-union-member-count]
+ enum tags4 kind;
+ union {
+ int i;
+ float f;
+ char *str;
+ } data;
+ };
+
+Counting enum constant heuristic
+--------------------------------
+
+Some programmers use enums in such a way, where the value of the last enum
+constant is used to keep track of how many enum constants have been declared.
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1, // is 0
+ tag2, // is 1
+ tag3, // is 2
+ tags_count, // is 3
+ };
+
+The checker detects this usage pattern heuristically and does not include
+the counter enum constant in the final tag count, since the counter is not
+meant to indicate the valid variant member.
+
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 79e81dd174e4f..3058bee1357fc 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -142,6 +142,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>`, "Yes"
: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.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
new file mode 100644
index 0000000000000..91165f1bd64e0
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
@@ -0,0 +1,181 @@
+// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
+
+enum tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+};
+
+enum tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+};
+
+enum tags5 {
+ tags5_1,
+ tags5_2,
+ tags5_3,
+ tags5_4,
+ tags5_5,
+};
+
+union union3 {
+ short *shorts;
+ int *ints;
+ float *floats;
+};
+
+union union4 {
+ short *shorts;
+ double *doubles;
+ int *ints;
+ float *floats;
+};
+
+// It is not obvious which enum is the tag for the union.
+struct taggedunion2 { // No warnings expected.
+ enum tags3 tagA;
+ enum tags4 tagB;
+ union union4 data;
+};
+
+// It is not obvious which union does the tag belong to.
+struct taggedunion4 { // No warnings expected.
+ enum tags3 tag;
+ union union3 dataB;
+ union union3 dataA;
+};
+
+struct taggedunion1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union union4 data;
+};
+
+struct taggedunion5 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union {
+ int *ints;
+ char characters[13];
+ struct {
+ double re;
+ double im;
+ } complex;
+ long l;
+ } data;
+};
+
+struct taggedunion7 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } tag;
+ union union4 data;
+};
+
+struct taggedunion8 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } tag;
+ union {
+ int *ints;
+ char characters[13];
+ struct {
+ double re;
+ double im;
+ } complex;
+ long l;
+ } data;
+};
+
+struct nested1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union {
+ char c;
+ short s;
+ int i;
+ struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has fewer data members than tags! Data members: 4 Tags: 5 [bugprone-tagged-union-member-count]
+ enum tags5 tag;
+ union union4 data;
+ } inner;
+ } data;
+};
+
+struct nested2 {
+ enum tags3 tag;
+ union {
+ float f;
+ int i;
+ struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union union4 data;
+ } inner;
+ } data;
+};
+
+struct nested3 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 2 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union {
+ float f;
+ int i;
+ struct innerdecl { // CHECK-MESSAGES: :[[@LINE]]:10: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union union4 data;
+ };
+ } data;
+};
+
+enum tag_with_counter_lowercase {
+ node_type_loop,
+ node_type_branch,
+ node_type_function,
+ node_type_count,
+};
+
+enum tag_with_counter_uppercase {
+ NODE_TYPE_LOOP,
+ NODE_TYPE_BRANCH,
+ NODE_TYPE_FUNCTION,
+ NODE_TYPE_COUNT,
+};
+
+struct taggedunion10 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tag_with_counter_lowercase tag;
+ union union4 data;
+};
+
+struct taggedunion11 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tag_with_counter_uppercase tag;
+ union union4 data;
+};
+
+// Without the counter enum constant the number of tags
+// and the number data members are equal.
+struct taggedunion12 { // No warnings expected.
+ enum tag_with_counter_uppercase tag;
+ union union3 data;
+};
+
+// Technically this means that every enum value is defined from 0-256 and therefore a warning is given.
+enum mycolor {
+ mycolor_black = 0x00,
+ mycolor_gray = 0xcc,
+ mycolor_white = 0xff,
+};
+
+struct taggedunion9 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 3 Tags: 256 [bugprone-tagged-union-member-count]
+ enum mycolor tag;
+ union {
+ int a;
+ float b;
+ struct {
+ double re;
+ double im;
+ } complex;
+ } data;
+};
+
>From a8eadb10876dded0deb3fbbbb6ad5b13ce9861fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Tue, 21 May 2024 13:51:28 +0200
Subject: [PATCH 2/7] pull request fixes
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 80 +++++---
.../bugprone/TaggedUnionMemberCountCheck.h | 10 +-
clang-tools-extra/docs/ReleaseNotes.rst | 12 +-
.../bugprone/tagged-union-member-count.rst | 190 ++++++++++++++++--
.../docs/clang-tidy/checks/list.rst | 2 +-
...er-count-enumcounterheuristicisenabled.cpp | 38 ++++
...d-union-member-count-enumcountersuffix.cpp | 47 +++++
.../tagged-union-member-count-strictmode.cpp | 150 ++++++++++++++
.../bugprone/tagged-union-member-count.cpp | 176 ++++++++++------
9 files changed, 589 insertions(+), 116 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index 6a7b5a92902c8..786198b011f30 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -10,44 +10,53 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
-
#include "clang/AST/PrettyPrinter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include <limits>
+#include <iostream>
using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
+TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ EnumCounterHeuristicIsEnabled(Options.get("EnumCounterHeuristicIsEnabled", true)),
+ EnumCounterSuffix(Options.get("EnumCounterSuffix", "count")),
+ StrictMode(Options.get("StrictMode", true)) { }
+
+void TaggedUnionMemberCountCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "StrictMode", StrictMode);
+ Options.store(Opts, "EnumCounterHeuristicIsEnabled", EnumCounterHeuristicIsEnabled);
+ Options.store(Opts, "EnumCounterSuffix", EnumCounterSuffix);
+}
+
void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(
+Finder->addMatcher(
recordDecl(
allOf(isStruct(),
- has(fieldDecl(hasType(recordDecl(isUnion()).bind("union")))),
- has(fieldDecl(hasType(enumDecl().bind("tags"))))))
+ has(fieldDecl(hasType(qualType(hasCanonicalType(recordType())))).bind("union")),
+ has(fieldDecl(hasType(qualType(hasCanonicalType(enumType())))).bind("tags"))))
.bind("root"),
this);
}
+static bool isUnion(const FieldDecl *R) {
+ return R->getType().getCanonicalType().getTypePtr()->isUnionType();
+}
+
+static bool isEnum(const FieldDecl *R) {
+ return R->getType().getCanonicalType().getTypePtr()->isEnumeralType();
+}
+
static bool hasMultipleUnionsOrEnums(const RecordDecl *rec) {
- int tags = 0;
- int unions = 0;
- for (const FieldDecl *r : rec->fields()) {
- TypeSourceInfo *info = r->getTypeSourceInfo();
- QualType qualtype = info->getType();
- const Type *type = qualtype.getTypePtr();
- if (type->isUnionType())
- unions += 1;
- else if (type->isEnumeralType())
- tags += 1;
- if (tags > 1 || unions > 1)
- return true;
- }
- return false;
+ return llvm::count_if(rec->fields(), isUnion) > 1 ||
+ llvm::count_if(rec->fields(), isEnum) > 1;
}
-static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
+static size_t getNumberOfValidEnumValues(const EnumDecl *ed, bool EnumCounterHeuristicIsEnabled, StringRef EnumCounterSuffix) {
int64_t maxTagValue = std::numeric_limits<int64_t>::min();
int64_t minTagValue = std::numeric_limits<int64_t>::max();
@@ -80,7 +89,7 @@ static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
if (enumValue < minTagValue)
minTagValue = enumValue;
- if (enumName.ends_with_insensitive("count")) {
+ if (enumName.ends_with_insensitive(EnumCounterSuffix)) {
if (ceCount == 0) {
ceFirstIndex = En.index();
}
@@ -90,7 +99,8 @@ static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
}
int64_t validValuesCount = maxTagValue - minTagValue + 1;
- if (ceCount == 1 &&
+ if (EnumCounterHeuristicIsEnabled &&
+ ceCount == 1 &&
ceFirstIndex == enumConstantsCount - 1 &&
ceValue == maxTagValue) {
validValuesCount -= 1;
@@ -98,26 +108,36 @@ static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
return validValuesCount;
}
+// Feladatok:
+// - typedef tesztelés
+// - template tesztelés
+// - névtelen union tesztelés
+// - "count" paraméterezése
void TaggedUnionMemberCountCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *root = Result.Nodes.getNodeAs<RecordDecl>("root");
- const auto *unionMatch = Result.Nodes.getNodeAs<RecordDecl>("union");
- const auto *tagMatch = Result.Nodes.getNodeAs<EnumDecl>("tags");
+ const auto *unionField = Result.Nodes.getNodeAs<FieldDecl>("union");
+ const auto *tagField = Result.Nodes.getNodeAs<FieldDecl>("tags");
+
+ // The matcher can only narrow down the type to recordType()
+ if (!isUnion(unionField))
+ return;
if (hasMultipleUnionsOrEnums(root))
return;
- int64_t unionMemberCount = llvm::range_size(unionMatch->fields());
- int64_t tagCount = getNumberOfValidEnumValues(tagMatch);
+ const auto *unionDef = unionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
+ const auto *enumDef = static_cast<EnumDecl*>(tagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
+
+ size_t unionMemberCount = llvm::range_size(unionDef->fields());
+ size_t tagCount = getNumberOfValidEnumValues(enumDef, EnumCounterHeuristicIsEnabled, EnumCounterSuffix);
// FIXME: Maybe a emit a note when a counter enum constant was found.
if (unionMemberCount > tagCount) {
- diag(root->getLocation(), "Tagged union has more data members than tags! "
- "Data members: %0 Tags: %1")
+ diag(root->getLocation(), "Tagged union has more data members (%0) than tags (%1)!")
<< unionMemberCount << tagCount;
- } else if (unionMemberCount < tagCount) {
- diag(root->getLocation(), "Tagged union has fewer data members than tags! "
- "Data members: %0 Tags: %1")
+ } else if (StrictMode && unionMemberCount < tagCount) {
+ diag(root->getLocation(), "Tagged union has fewer data members (%0) than tags (%1)!")
<< unionMemberCount << tagCount;
}
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
index bde42da23ff38..2c6ec3b05358a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
#include "../ClangTidyCheck.h"
+#include "llvm/ADT/StringRef.h"
namespace clang::tidy::bugprone {
@@ -20,10 +21,15 @@ namespace clang::tidy::bugprone {
// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/tagged-union-member-count.html
class TaggedUnionMemberCountCheck : public ClangTidyCheck {
public:
- TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context) {}
+ 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:
+ bool EnumCounterHeuristicIsEnabled;
+ StringRef EnumCounterSuffix;
+ bool StrictMode;
};
} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9f9069a8d0892..b287b99da5a01 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -117,6 +117,12 @@ New checks
to reading out-of-bounds data due to inadequate or incorrect string null
termination.
+- 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
+ different from the number of data members inside the union.
+
- New :doc:`modernize-use-designated-initializers
<clang-tidy/checks/modernize/use-designated-initializers>` check.
@@ -129,12 +135,6 @@ New checks
Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.
-- New :doc:`bugprone-tagged-union-member-count
- <clang-tidy/checks/bugprone/tagged-union-member-count>` check.
-
- Warns about suspicious looking tagged unions where the number of enum
- constants and the number of union data members are not equal.
-
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
index 7eac93c6c9602..cd7de571041e4 100644
--- 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
@@ -15,29 +15,13 @@ Example
.. code-block:: c++
- enum tags2 {
+ enum tags {
tag1,
tag2,
};
- struct taggedunion { // warning: Tagged union has more data members than tags! Data members: 3 Tags: 2 [bugprone-tagged-union-member-count]
- enum tags2 kind;
- union {
- int i;
- float f;
- char *str;
- } data;
- };
-
- enum tags4 {
- tag1,
- tag2,
- tag3,
- tag4,
- };
-
- struct taggedunion { // warning: Tagged union has more fewer members than tags! Data members: 3 Tags: 4 [bugprone-tagged-union-member-count]
- enum tags4 kind;
+ struct taggedUnion { // warning: Tagged union has more data members (3) than tags (2)! [bugprone-tagged-union-member-count]
+ enum tags kind;
union {
int i;
float f;
@@ -60,7 +44,173 @@ constant is used to keep track of how many enum constants have been declared.
tags_count, // is 3
};
-The checker detects this usage pattern heuristically and does not include
+The check detects this usage pattern heuristically and does not include
the counter enum constant in the final tag count, since the counter is not
meant to indicate the valid variant member.
+Options
+-------
+
+.. option:: EnumCounterHeuristicIsEnabled
+
+ This option enables or disables the counting enum heuristic.
+ To find possible counting enum constants this option uses the value of
+ the string :option:`EnumCounterSuffix`.
+
+ This option is enabled by default.
+
+Example of :option:`EnumCounterHeuristicIsEnabled`:
+
+When :option:`EnumCounterHeuristicIsEnabled` is false:
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ struct taggedUnion {
+ tags_with_counter tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+When :option:`EnumCounterHeuristicIsEnabled` is true:
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ struct taggedUnion { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ tags_with_counter tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+.. option:: EnumCounterSuffix
+
+ When defined, the check will use the given string to search for counting
+ enum constants. This option does not alter the check's behavior when
+ :option:`EnumCounterHeuristicIsEnabled` is disabled.
+
+ The default value is "count".
+
+Example of :option:`EnumCounterSuffix`:
+
+When :option:`EnumCounterHeuristicIsEnabled` is true and
+:option:`EnumCounterSuffix` is "size":
+
+.. code-block:: c++
+
+ enum tags_with_counter_count {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ enum tags_with_counter_size {
+ tag4,
+ tag5,
+ tag6,
+ tags_size,
+ };
+
+ struct taggedUnion1 {
+ tags_with_counter_count tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+ struct taggedUnion2 { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ tags_with_counter_size tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+When :option:`EnumCounterSuffix` is true:
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ struct taggedUnion { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ tags_with_counter tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+.. 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 not enabled by default.
+
+Example of :option:`StrictMode`:
+
+When :option:`StrictMode` is false:
+
+.. code-block:: c++
+
+ struct taggedUnion {
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ } tags;
+ union {
+ int i;
+ float f;
+ };
+ };
+
+When :option:`StrictMode` is true:
+
+.. code-block:: c++
+
+ struct taggedunion1 { // warning: Tagged union has fewer data members (2) than tags (3)! [bugprone-tagged-union-member-count]
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ } tags;
+ union {
+ int i;
+ float f;
+ };
+ };
+
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 3058bee1357fc..5b2fb2bfbfc30 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -142,7 +142,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>`, "Yes"
+ :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-enumcounterheuristicisenabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
new file mode 100644
index 0000000000000..69b44c10b7c31
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
@@ -0,0 +1,38 @@
+// 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: 1, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "count", \
+// RUN: }}' --
+
+// Without the heuristic the tags and the data members match
+struct taggedUnion1 { // No warnings expected
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ tags_count,
+ } tags;
+ union {
+ int a;
+ int b;
+ int c;
+ int d;
+ } data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct taggedUnion2 {
+ enum {
+ tags11,
+ tags22,
+ tags33,
+ tags_count,
+ } tags;
+ union {
+ int a;
+ int b;
+ int c;
+ } data;
+};
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
new file mode 100644
index 0000000000000..cddbb003a01f9
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
@@ -0,0 +1,47 @@
+// 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: 0, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 1, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "size", \
+// RUN: }}' --
+
+typedef union union3 {
+ short *shorts;
+ int *ints;
+ float *floats;
+} union3;
+
+typedef union union4 {
+ short *shorts;
+ double *doubles;
+ int *ints;
+ float *floats;
+} union4;
+
+enum tag_with_counter_count {
+ twc1,
+ twc2,
+ twc3,
+ twc_count,
+};
+
+enum tag_with_counter_size {
+ twc11,
+ twc22,
+ twc33,
+ twc_size,
+};
+
+// The heuristic is configured with the "size" suffix so
+// twc_count will not be considered a counting enum constant
+struct taggedUnion1 { // No warning expected
+ enum tag_with_counter_count tag;
+ union union4 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedUnion2 {
+ enum tag_with_counter_size tag;
+ union union4 data;
+};
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
new file mode 100644
index 0000000000000..7d6c5f385c8e1
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
@@ -0,0 +1,150 @@
+// 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: 1, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "count", \
+// RUN: }}' --
+
+typedef enum tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+} tags3;
+
+typedef enum tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+} tags4;
+
+typedef enum tags5 {
+ tags5_1,
+ tags5_2,
+ tags5_3,
+ tags5_4,
+ tags5_5,
+} tags5;
+
+enum class classtags3 {
+ classtags3_1,
+ classtags3_2,
+ classtags3_3,
+};
+
+enum class classtags4 {
+ classtags4_1,
+ classtags4_2,
+ classtags4_3,
+ classtags4_4,
+};
+
+enum class classtags5 {
+ classtags5_1,
+ classtags5_2,
+ classtags5_3,
+ classtags5_4,
+ classtags5_5,
+};
+
+enum class typedtags3 : unsigned int {
+ typedtags3_1,
+ typedtags3_2,
+ typedtags3_3,
+};
+
+enum class typedtags4 : long {
+ typedtags4_1,
+ typedtags4_2,
+ typedtags4_3,
+ typedtags4_4,
+};
+
+enum class typedtags5 {
+ typedtags5_1,
+ typedtags5_2,
+ typedtags5_3,
+ typedtags5_4,
+ typedtags5_5,
+};
+
+typedef union union3 {
+ short *shorts;
+ int *ints;
+ float *floats;
+} union3;
+
+typedef union union4 {
+ short *shorts;
+ double *doubles;
+ int *ints;
+ float *floats;
+} union4;
+
+// Technically this means that every enum value is defined from 0-256 and therefore a warning is given.
+enum mycolor {
+ mycolor_black = 0x00,
+ mycolor_gray = 0xcc,
+ mycolor_white = 0xff,
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (256)! [bugprone-tagged-union-member-count]
+struct taggedunion9 {
+ enum mycolor tag;
+ union {
+ int a;
+ float b;
+ struct {
+ double re;
+ double im;
+ } complex;
+ } data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withanonymous {
+ enum tags4 tag;
+ union {
+ int a;
+ float b;
+ char *c;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withTypedef1 {
+ tags4 tag;
+ union3 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withEnumClass1 {
+ enum classtags4 tag;
+ union3 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withTypedEnum2 {
+ typedtags4 tag;
+ union3 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (2) than tags (3)! [bugprone-tagged-union-member-count]
+struct anonymous2 {
+ tags3 tag;
+ union {
+ int a;
+ float f;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has fewer data members (3) than tags (5)! [bugprone-tagged-union-member-count]
+template <typename Union, typename Tag>
+struct templated {
+ Tag tag;
+ Union data;
+};
+
+templated<union3, tags3> t1; // No warning expected
+templated<union3, tags5> t2;
+
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
index 91165f1bd64e0..05ae457f57410 100644
--- 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
@@ -1,38 +1,81 @@
-// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
+// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t
+// Test cases for the default configuration
-enum tags3 {
+typedef enum tags3 {
tags3_1,
tags3_2,
tags3_3,
-};
+} tags3;
-enum tags4 {
+typedef enum tags4 {
tags4_1,
tags4_2,
tags4_3,
tags4_4,
-};
+} tags4;
-enum tags5 {
+typedef enum tags5 {
tags5_1,
tags5_2,
tags5_3,
tags5_4,
tags5_5,
+} tags5;
+
+enum class classtags3 {
+ classtags3_1,
+ classtags3_2,
+ classtags3_3,
+};
+
+enum class classtags4 {
+ classtags4_1,
+ classtags4_2,
+ classtags4_3,
+ classtags4_4,
+};
+
+enum class classtags5 {
+ classtags5_1,
+ classtags5_2,
+ classtags5_3,
+ classtags5_4,
+ classtags5_5,
+};
+
+enum class typedtags3 : unsigned int {
+ typedtags3_1,
+ typedtags3_2,
+ typedtags3_3,
+};
+
+enum class typedtags4 : long {
+ typedtags4_1,
+ typedtags4_2,
+ typedtags4_3,
+ typedtags4_4,
+};
+
+enum class typedtags5 {
+ typedtags5_1,
+ typedtags5_2,
+ typedtags5_3,
+ typedtags5_4,
+ typedtags5_5,
};
-union union3 {
+typedef union union3 {
short *shorts;
int *ints;
float *floats;
-};
+} union3;
-union union4 {
+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 taggedunion2 { // No warnings expected.
@@ -48,12 +91,29 @@ struct taggedunion4 { // No warnings expected.
union union3 dataA;
};
-struct taggedunion1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// It is not obvious which union does the tag belong to.
+struct taggedunion6 { // No warnings expected.
+ enum tags3 tag;
+ union {
+ int i1;
+ int i2;
+ int i3;
+ };
+ union {
+ float f1;
+ float f2;
+ float f3;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion1 {
enum tags3 tag;
union union4 data;
};
-struct taggedunion5 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion5 {
enum tags3 tag;
union {
int *ints;
@@ -66,7 +126,8 @@ struct taggedunion5 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has
} data;
};
-struct taggedunion7 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion7 {
enum {
tag1,
tag2,
@@ -75,7 +136,8 @@ struct taggedunion7 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has
union union4 data;
};
-struct taggedunion8 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion8 {
enum {
tag1,
tag2,
@@ -92,40 +154,18 @@ struct taggedunion8 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has
} data;
};
-struct nested1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
- enum tags3 tag;
- union {
- char c;
- short s;
- int i;
- struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has fewer data members than tags! Data members: 4 Tags: 5 [bugprone-tagged-union-member-count]
- enum tags5 tag;
- union union4 data;
- } inner;
- } data;
-};
-
-struct nested2 {
- enum tags3 tag;
- union {
- float f;
- int i;
- struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
- enum tags3 tag;
- union union4 data;
- } inner;
- } data;
-};
-
-struct nested3 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 2 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct nested4 {
enum tags3 tag;
union {
float f;
int i;
- struct innerdecl { // CHECK-MESSAGES: :[[@LINE]]:10: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ long l;
+ // CHECK-MESSAGES: :[[@LINE+1]]:10: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ struct innerdecl {
enum tags3 tag;
union union4 data;
- };
+ } inner;
} data;
};
@@ -143,12 +183,14 @@ enum tag_with_counter_uppercase {
NODE_TYPE_COUNT,
};
-struct taggedunion10 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion10 {
enum tag_with_counter_lowercase tag;
union union4 data;
};
-struct taggedunion11 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion11 {
enum tag_with_counter_uppercase tag;
union union4 data;
};
@@ -160,22 +202,42 @@ struct taggedunion12 { // No warnings expected.
union union3 data;
};
-// Technically this means that every enum value is defined from 0-256 and therefore a warning is given.
-enum mycolor {
- mycolor_black = 0x00,
- mycolor_gray = 0xcc,
- mycolor_white = 0xff,
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct withTypedef2 {
+ tags3 tag;
+ union4 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct withEnumClass2 {
+ enum classtags3 tag;
+ union4 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct withTypedEnum1 {
+ typedtags3 tag;
+ union4 data;
};
-struct taggedunion9 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 3 Tags: 256 [bugprone-tagged-union-member-count]
- enum mycolor tag;
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct anonymous1 {
+ tags3 tag;
union {
int a;
- float b;
- struct {
- double re;
- double im;
- } complex;
- } data;
+ int b;
+ int c;
+ int d;
+ };
};
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+template <typename Union, typename Tag>
+struct templated {
+ Tag tag;
+ Union data;
+};
+
+templated<union3, tags3> t1; // No warning expected
+templated<union4, tags3> t3;
+
>From be3ec1a4e9ac3309da8f2037334de68bd478883f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Fri, 19 Jul 2024 17:37:49 +0200
Subject: [PATCH 3/7] Major iteration based on review comments
Feature changes:
* Added C++ classes to matcher
* Added multiple prefix/suffix option support to the counting enum heuristic
Implementation changes:
* Distinct enum values are counted with a set instead of the previous max - min + 1 approach
* Use APSInt to avoid truncation or overflow
Testing changes:
* Tests use more descriptive names
* Removed redundant suffix from test case messages
* Separated test files for C, C++, Objective-C and Objective-C++
* C++ tests are ran with the -std=c98-or-later flag
* C++ tests are extended with more language feature test cases
* Created tests for the Prefix/suffix feature of the counting enum heuristic
* StrictMode and heuristic enabling/disabling is more thoroughly tested
Also made plenty of small improvements as well.
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 189 +++++---
.../bugprone/TaggedUnionMemberCountCheck.h | 31 +-
.../bugprone/tagged-union-member-count.rst | 239 +++++-----
...nt-counting-enum-heuristic-is-disabled.cpp | 68 +++
...unt-counting-enum-heuristic-is-enabled.cpp | 63 +++
...nt-counting-enum-prefixes-and-suffixes.cpp | 53 +++
...on-member-count-counting-enum-prefixes.cpp | 36 ++
...on-member-count-counting-enum-suffixes.cpp | 36 ++
...er-count-enumcounterheuristicisenabled.cpp | 38 --
...d-union-member-count-enumcountersuffix.cpp | 47 --
...on-member-count-strictmode-is-disabled.cpp | 28 ++
...ion-member-count-strictmode-is-enabled.cpp | 31 ++
.../tagged-union-member-count-strictmode.cpp | 150 ------
.../bugprone/tagged-union-member-count.c | 148 ++++++
.../bugprone/tagged-union-member-count.cpp | 434 +++++++++---------
.../bugprone/tagged-union-member-count.m | 147 ++++++
.../bugprone/tagged-union-member-count.mm | 262 +++++++++++
17 files changed, 1354 insertions(+), 646 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-enabled.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-suffixes.cpp
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-disabled.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-enabled.cpp
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index 786198b011f30..756ecb586fdf4 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -7,58 +7,94 @@
//===----------------------------------------------------------------------===//
#include "TaggedUnionMemberCountCheck.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Expr.h"
+#include "../utils/OptionsUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/AST/PrettyPrinter.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/Support/Casting.h"
-#include <limits>
-#include <iostream>
+#include "llvm/ADT/SmallSet.h"
using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
-TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- EnumCounterHeuristicIsEnabled(Options.get("EnumCounterHeuristicIsEnabled", true)),
- EnumCounterSuffix(Options.get("EnumCounterSuffix", "count")),
- StrictMode(Options.get("StrictMode", true)) { }
+TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ StrictModeIsEnabled(Options.get(StrictModeIsEnabledOptionName, true)),
+ CountingEnumHeuristicIsEnabled(
+ Options.get(CountingEnumHeuristicIsEnabledOptionName, true)),
+ RawCountingEnumPrefixes(Options.get(CountingEnumPrefixesOptionName, "")),
+ RawCountingEnumSuffixes(
+ Options.get(CountingEnumSuffixesOptionName, "count")),
+ ParsedCountingEnumPrefixes(
+ utils::options::parseStringList(RawCountingEnumPrefixes)),
+ ParsedCountingEnumSuffixes(
+ utils::options::parseStringList(RawCountingEnumSuffixes)) {}
void TaggedUnionMemberCountCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "StrictMode", StrictMode);
- Options.store(Opts, "EnumCounterHeuristicIsEnabled", EnumCounterHeuristicIsEnabled);
- Options.store(Opts, "EnumCounterSuffix", EnumCounterSuffix);
+ Options.store(Opts, StrictModeIsEnabledOptionName, StrictModeIsEnabled);
+ Options.store(Opts, CountingEnumHeuristicIsEnabledOptionName,
+ CountingEnumHeuristicIsEnabled);
+ Options.store(Opts, CountingEnumPrefixesOptionName, RawCountingEnumPrefixes);
+ Options.store(Opts, CountingEnumSuffixesOptionName, RawCountingEnumSuffixes);
}
void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
-Finder->addMatcher(
+ Finder->addMatcher(
recordDecl(
- allOf(isStruct(),
- has(fieldDecl(hasType(qualType(hasCanonicalType(recordType())))).bind("union")),
- has(fieldDecl(hasType(qualType(hasCanonicalType(enumType())))).bind("tags"))))
+ allOf(anyOf(isStruct(), isClass()),
+ has(fieldDecl(hasType(qualType(hasCanonicalType(recordType()))))
+ .bind("union")),
+ has(fieldDecl(hasType(qualType(hasCanonicalType(enumType()))))
+ .bind("tags"))))
.bind("root"),
this);
}
static bool isUnion(const FieldDecl *R) {
- return R->getType().getCanonicalType().getTypePtr()->isUnionType();
+ return R->getType().getCanonicalType().getTypePtr()->isUnionType();
}
static bool isEnum(const FieldDecl *R) {
- return R->getType().getCanonicalType().getTypePtr()->isEnumeralType();
+ return R->getType().getCanonicalType().getTypePtr()->isEnumeralType();
}
-static bool hasMultipleUnionsOrEnums(const RecordDecl *rec) {
- return llvm::count_if(rec->fields(), isUnion) > 1 ||
- llvm::count_if(rec->fields(), isEnum) > 1;
+static bool hasMultipleUnionsOrEnums(const RecordDecl *Rec) {
+ return llvm::count_if(Rec->fields(), isUnion) > 1 ||
+ llvm::count_if(Rec->fields(), isEnum) > 1;
}
-static size_t getNumberOfValidEnumValues(const EnumDecl *ed, bool EnumCounterHeuristicIsEnabled, StringRef EnumCounterSuffix) {
- int64_t maxTagValue = std::numeric_limits<int64_t>::min();
- int64_t minTagValue = std::numeric_limits<int64_t>::max();
+static bool signEquals(const llvm::APSInt &A, const llvm::APSInt &B) {
+ return (A.isNegative() && B.isNegative()) ||
+ (A.isStrictlyPositive() && B.isStrictlyPositive()) ||
+ (A.isZero() && B.isZero());
+}
+
+static bool greaterBySign(const llvm::APSInt &A, const llvm::APSInt &B) {
+ return (A.isNonNegative() && B.isNegative()) ||
+ (A.isStrictlyPositive() && B.isNonPositive());
+}
+
+bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(
+ StringRef Name) const noexcept {
+ if (llvm::any_of(ParsedCountingEnumPrefixes,
+ [&Name](const StringRef &prefix) -> bool {
+ return Name.starts_with_insensitive(prefix);
+ }))
+ return true;
+ if (llvm::any_of(ParsedCountingEnumSuffixes,
+ [&Name](const StringRef &suffix) -> bool {
+ return Name.ends_with_insensitive(suffix);
+ }))
+ return true;
+ return false;
+}
+
+size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
+ const EnumDecl *Ed) const noexcept {
+ bool FoundMax = false;
+ llvm::APSInt MaxTagValue;
+ llvm::SmallSet<llvm::APSInt, 32> EnumValues;
// Heuristic for counter enum constants.
//
@@ -68,77 +104,78 @@ static size_t getNumberOfValidEnumValues(const EnumDecl *ed, bool EnumCounterHeu
// tag_count, <-- Searching for these enum constants
// };
//
- // The 'ce' prefix is used to abbreviate counterEnum.
// The final tag count is decreased by 1 if and only if:
- // 1. The number of counting enum constants = 1,
- int ceCount = 0;
+ // 1. There is only one counting enum constant,
// 2. The counting enum constant is the last enum constant that is defined,
- int ceFirstIndex = 0;
- // 3. The value of the counting enum constant is the largest out of every enum constant.
- int64_t ceValue = 0;
-
- int64_t enumConstantsCount = 0;
- for (auto En : llvm::enumerate(ed->enumerators())) {
- enumConstantsCount += 1;
-
- int64_t enumValue = En.value()->getInitVal().getExtValue();
- StringRef enumName = En.value()->getName();
-
- if (enumValue > maxTagValue)
- maxTagValue = enumValue;
- if (enumValue < minTagValue)
- minTagValue = enumValue;
+ // 3. The value of the counting enum constant is the largest out of every enum
+ // constant.
+ // The 'ce' prefix is a shorthand for 'counting enum'.
+ size_t CeCount = 0;
+ bool IsLast = false;
+ llvm::APSInt CeValue = llvm::APSInt::get(0);
+
+ for (const auto &&[index, enumerator] : llvm::enumerate(Ed->enumerators())) {
+ const llvm::APSInt Val = enumerator->getInitVal();
+ EnumValues.insert(Val);
+ if (FoundMax) {
+ if (greaterBySign(Val, MaxTagValue) ||
+ (signEquals(Val, MaxTagValue) && Val > MaxTagValue)) {
+ MaxTagValue = Val;
+ }
+ } else {
+ MaxTagValue = Val;
+ FoundMax = true;
+ }
- if (enumName.ends_with_insensitive(EnumCounterSuffix)) {
- if (ceCount == 0) {
- ceFirstIndex = En.index();
+ if (CountingEnumHeuristicIsEnabled) {
+ if (isCountingEnumLikeName(enumerator->getName())) {
+ IsLast = true;
+ CeValue = Val;
+ CeCount += 1;
+ } else {
+ IsLast = false;
}
- ceValue = enumValue;
- ceCount += 1;
}
}
- int64_t validValuesCount = maxTagValue - minTagValue + 1;
- if (EnumCounterHeuristicIsEnabled &&
- ceCount == 1 &&
- ceFirstIndex == enumConstantsCount - 1 &&
- ceValue == maxTagValue) {
- validValuesCount -= 1;
+ size_t ValidValuesCount = EnumValues.size();
+ if (CeCount == 1 && IsLast && CeValue == MaxTagValue) {
+ ValidValuesCount -= 1;
}
- return validValuesCount;
+
+ return ValidValuesCount;
}
-// Feladatok:
-// - typedef tesztelés
-// - template tesztelés
-// - névtelen union tesztelés
-// - "count" paraméterezése
void TaggedUnionMemberCountCheck::check(
const MatchFinder::MatchResult &Result) {
- const auto *root = Result.Nodes.getNodeAs<RecordDecl>("root");
- const auto *unionField = Result.Nodes.getNodeAs<FieldDecl>("union");
- const auto *tagField = Result.Nodes.getNodeAs<FieldDecl>("tags");
+ const auto *Root = Result.Nodes.getNodeAs<RecordDecl>("root");
+ const auto *UnionField = Result.Nodes.getNodeAs<FieldDecl>("union");
+ const auto *TagField = Result.Nodes.getNodeAs<FieldDecl>("tags");
// The matcher can only narrow down the type to recordType()
- if (!isUnion(unionField))
+ if (!isUnion(UnionField))
return;
- if (hasMultipleUnionsOrEnums(root))
+ if (hasMultipleUnionsOrEnums(Root))
return;
- const auto *unionDef = unionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
- const auto *enumDef = static_cast<EnumDecl*>(tagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
+ const auto *UnionDef =
+ UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
+ const auto *EnumDef = static_cast<EnumDecl *>(
+ TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
- size_t unionMemberCount = llvm::range_size(unionDef->fields());
- size_t tagCount = getNumberOfValidEnumValues(enumDef, EnumCounterHeuristicIsEnabled, EnumCounterSuffix);
+ const size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
+ const size_t TagCount = getNumberOfValidEnumValues(EnumDef);
// FIXME: Maybe a emit a note when a counter enum constant was found.
- 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 (UnionMemberCount > TagCount) {
+ diag(Root->getLocation(),
+ "Tagged union has more data members (%0) than tags (%1)!")
+ << UnionMemberCount << TagCount;
+ } else if (StrictModeIsEnabled && UnionMemberCount < TagCount) {
+ diag(Root->getLocation(),
+ "Tagged union has fewer data members (%0) than tags (%1)!")
+ << UnionMemberCount << TagCount;
}
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
index 2c6ec3b05358a..0a13ead0102db 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -10,15 +10,14 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
#include "../ClangTidyCheck.h"
-#include "llvm/ADT/StringRef.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
+/// 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);
@@ -27,9 +26,23 @@ class TaggedUnionMemberCountCheck : public ClangTidyCheck {
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
- bool EnumCounterHeuristicIsEnabled;
- StringRef EnumCounterSuffix;
- bool StrictMode;
+ static constexpr char StrictModeIsEnabledOptionName[] = "StrictModeIsEnabled";
+ static constexpr char CountingEnumHeuristicIsEnabledOptionName[] =
+ "CountingEnumHeuristicIsEnabled";
+ static constexpr char CountingEnumPrefixesOptionName[] =
+ "CountingEnumPrefixes";
+ static constexpr char CountingEnumSuffixesOptionName[] =
+ "CountingEnumSuffixes";
+
+ const bool StrictModeIsEnabled;
+ const bool CountingEnumHeuristicIsEnabled;
+ const StringRef RawCountingEnumPrefixes;
+ const StringRef RawCountingEnumSuffixes;
+ std::vector<StringRef> ParsedCountingEnumPrefixes;
+ std::vector<StringRef> ParsedCountingEnumSuffixes;
+
+ size_t getNumberOfValidEnumValues(const EnumDecl *ed) const noexcept;
+ bool isCountingEnumLikeName(StringRef name) const noexcept;
};
} // namespace clang::tidy::bugprone
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
index cd7de571041e4..369fc8bdc19c1 100644
--- 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
@@ -1,5 +1,6 @@
.. title:: clang-tidy - bugprone-tagged-union-member-count
+==================================
bugprone-tagged-union-member-count
==================================
@@ -11,190 +12,190 @@ 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,
+ enum Tags {
+ Tag1,
+ Tag2,
};
- struct taggedUnion { // warning: Tagged union has more data members (3) than tags (2)! [bugprone-tagged-union-member-count]
- enum tags kind;
+ struct TaggedUnion { // warning: Tagged union has more data members (3) than tags (2)
+ enum Tags Kind;
union {
- int i;
- float f;
- char *str;
- } data;
+ int I;
+ float F;
+ char *Str;
+ } Data;
};
Counting enum constant heuristic
---------------------------------
+================================
Some programmers use enums in such a way, where the value of the last enum
constant is used to keep track of how many enum constants have been declared.
.. code-block:: c++
- enum tags_with_counter {
- tag1, // is 0
- tag2, // is 1
- tag3, // is 2
- tags_count, // is 3
+ enum TagWithCounter {
+ Tag1, // is 0
+ Tag2, // is 1
+ Tag3, // is 2
+ TagCount, // is 3
};
-The check detects this usage pattern heuristically and does not include
+This usage pattern is detected heuristically and the check does not include
the counter enum constant in the final tag count, since the counter is not
-meant to indicate the valid variant member.
+meant to indicate the valid union data member.
-Options
--------
+When the check finds multiple possible counting enums, then it does not change the enum count.
-.. option:: EnumCounterHeuristicIsEnabled
+This heuristic can be disabled entirely (:option:`CountingEnumHeuristicIsEnabled`) or
+configured to follow your naming convention (:option:`CountingEnumPrefixes/Suffixes`).
+String matching is done case insensitively.
- This option enables or disables the counting enum heuristic.
- To find possible counting enum constants this option uses the value of
- the string :option:`EnumCounterSuffix`.
+Options
+=======
- This option is enabled by default.
+.. option:: CountingEnumHeuristicIsEnabled
-Example of :option:`EnumCounterHeuristicIsEnabled`:
+This option enables or disables the counting enum heuristic.
+To find possible counting enum constants this option uses the prefixes
+and suffixes specified
+the string :option:`CountingEnumSuffixes`.
-When :option:`EnumCounterHeuristicIsEnabled` is false:
+This option is enabled by default.
+
+When :option:`CountingEnumHeuristicIsEnabled` is false:
.. code-block:: c++
- enum tags_with_counter {
- tag1,
- tag2,
- tag3,
- tags_count,
+ enum TagWithCounter {
+ Tag1,
+ Tag2,
+ Tag3,
+ TagCount,
};
- struct taggedUnion {
- tags_with_counter tag;
- union data {
- int a;
- int b;
- char *str;
- float f;
+ struct TaggedUnion {
+ TagWithCounter Kind;
+ union Data {
+ int A;
+ long B;
+ char *Str;
+ float F;
};
};
-When :option:`EnumCounterHeuristicIsEnabled` is true:
+When :option:`CountingEnumHeuristicIsEnabled` is true:
.. code-block:: c++
- enum tags_with_counter {
- tag1,
- tag2,
- tag3,
- tags_count,
+ enum TagWithCounter {
+ Tag1,
+ Tag2,
+ Tag3,
+ TagCount,
};
- struct taggedUnion { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
- tags_with_counter tag;
- union data {
- int a;
- int b;
- char *str;
- float f;
+ struct TaggedUnion { // warning: Tagged union has more data members (4) than tags (3)
+ TagWithCounter Kind;
+ union Data {
+ int A;
+ long B;
+ char *Str;
+ float F;
};
};
-.. option:: EnumCounterSuffix
-
- When defined, the check will use the given string to search for counting
- enum constants. This option does not alter the check's behavior when
- :option:`EnumCounterHeuristicIsEnabled` is disabled.
+.. option:: CountingEnumPrefixes/Suffixes
- The default value is "count".
+When defined, the check will use the list of the semicolon separated strings
+in CountingEnumPrefixes or CountingEnumSuffixes for the identification of possible counting enum constants.
+These options do not alter the check's behavior when :option:`CountingEnumHeuristicIsEnabled` is set to false.
-Example of :option:`EnumCounterSuffix`:
+The default value for CountingEnumSuffixes is "count" and for CountingEnumPrefixes is "" (empty string).
-When :option:`EnumCounterHeuristicIsEnabled` is true and
-:option:`EnumCounterSuffix` is "size":
+When :option:`CountingEnumHeuristicIsEnabled` is true and CountingEnumSuffixes is "count;size":
.. code-block:: c++
- enum tags_with_counter_count {
- tag1,
- tag2,
- tag3,
- tags_count,
+ enum TagWithCounterCount {
+ Tag1,
+ Tag2,
+ Tag3,
+ TagCount,
};
- enum tags_with_counter_size {
- tag4,
- tag5,
- tag6,
- tags_size,
+ struct TaggedUnionCount { // warning: Tagged union has more data members (4) than tags (3)
+ TagWithCounterCount Kind;
+ union Data {
+ int A;
+ long B;
+ char *Str;
+ float F;
+ };
};
- struct taggedUnion1 {
- tags_with_counter_count tag;
- union data {
- int a;
- int b;
- char *str;
- float f;
- };
+ enum TagWithCounterSize {
+ Tag11,
+ Tag22,
+ Tag33,
+ TagSize,
};
- struct taggedUnion2 { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
- tags_with_counter_size tag;
- union data {
- int a;
- int b;
- char *str;
- float f;
+ struct TaggedUnionSize { // warning: Tagged union has more data members (4) than tags (3)
+ TagWithCounterSize Kind;
+ union Data {
+ int A;
+ long B;
+ char *Str;
+ float F;
};
};
-
-When :option:`EnumCounterSuffix` is true:
+
+When :option:`CountingEnumHeuristicIsEnabled` is true and CountingEnumPrefixes is "maxsize;last_"
.. code-block:: c++
- enum tags_with_counter {
- tag1,
- tag2,
- tag3,
- tags_count,
+ enum TagWithCounter {
+ Tag1,
+ Tag2,
+ Tag3,
+ TagCount,
};
- struct taggedUnion { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
- tags_with_counter tag;
- union data {
- int a;
- int b;
- char *str;
- float f;
+ struct TaggedUnion { // warning: Tagged union has more data members (4) than tags (3)
+ TagWithCounter tag;
+ union Data {
+ int I;
+ short S;
+ char *C;
+ float F;
};
};
.. 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 not enabled by default.
+When enabled, the check will also give a warning, when the number of tags
+is greater than the number of union data members.
-Example of :option:`StrictMode`:
+This option is disabled by default.
When :option:`StrictMode` is false:
.. code-block:: c++
- struct taggedUnion {
+ struct TaggedUnion {
enum {
- tags1,
- tags2,
- tags3,
- } tags;
+ Tag1,
+ Tag2,
+ Tag3,
+ } Tags;
union {
- int i;
- float f;
+ int I;
+ float F;
};
};
@@ -202,15 +203,15 @@ When :option:`StrictMode` is true:
.. code-block:: c++
- struct taggedunion1 { // warning: Tagged union has fewer data members (2) than tags (3)! [bugprone-tagged-union-member-count]
+ struct TaggedUnion { // warning: Tagged union has fewer data members (2) than tags (3)
enum {
- tags1,
- tags2,
- tags3,
- } tags;
+ Tag1,
+ Tag2,
+ Tag3,
+ } Tags;
union {
- int i;
- float f;
+ int I;
+ float F;
};
};
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 0000000000000..f02aa94959e21
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp
@@ -0,0 +1,68 @@
+// 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.StrictModeIsEnabled: 1, \
+// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 0, \
+// 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 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 0000000000000..672787869b53a
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-enabled.cpp
@@ -0,0 +1,63 @@
+// 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.StrictModeIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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;
+};
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 0000000000000..af655e4f4c848
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp
@@ -0,0 +1,53 @@
+// 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.StrictModeIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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;
+};
+
+// No warning expected, because Kind has multiple counting enum candidates,
+// therefore the enum count is left unchanged.
+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 0000000000000..f61a48f111a30
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes.cpp
@@ -0,0 +1,36 @@
+// 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.StrictModeIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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 0000000000000..4815a7361b819
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-suffixes.cpp
@@ -0,0 +1,36 @@
+// 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.StrictModeIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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-enumcounterheuristicisenabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
deleted file mode 100644
index 69b44c10b7c31..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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: 1, \
-// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 0, \
-// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "count", \
-// RUN: }}' --
-
-// Without the heuristic the tags and the data members match
-struct taggedUnion1 { // No warnings expected
- enum {
- tags1,
- tags2,
- tags3,
- tags_count,
- } tags;
- union {
- int a;
- int b;
- int c;
- int d;
- } data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
-struct taggedUnion2 {
- enum {
- tags11,
- tags22,
- tags33,
- tags_count,
- } tags;
- union {
- int a;
- int b;
- int c;
- } data;
-};
-
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
deleted file mode 100644
index cddbb003a01f9..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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: 0, \
-// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 1, \
-// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "size", \
-// RUN: }}' --
-
-typedef union union3 {
- short *shorts;
- int *ints;
- float *floats;
-} union3;
-
-typedef union union4 {
- short *shorts;
- double *doubles;
- int *ints;
- float *floats;
-} union4;
-
-enum tag_with_counter_count {
- twc1,
- twc2,
- twc3,
- twc_count,
-};
-
-enum tag_with_counter_size {
- twc11,
- twc22,
- twc33,
- twc_size,
-};
-
-// The heuristic is configured with the "size" suffix so
-// twc_count will not be considered a counting enum constant
-struct taggedUnion1 { // No warning expected
- enum tag_with_counter_count tag;
- union union4 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct taggedUnion2 {
- enum tag_with_counter_size tag;
- 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 0000000000000..5db5965c56880
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-disabled.cpp
@@ -0,0 +1,28 @@
+// 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.StrictModeIsEnabled: 0, \
+// 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 0000000000000..eed1bc1bdc35c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode-is-enabled.cpp
@@ -0,0 +1,31 @@
+// 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.StrictModeIsEnabled: 1, \
+// 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-strictmode.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
deleted file mode 100644
index 7d6c5f385c8e1..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-// 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: 1, \
-// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 0, \
-// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "count", \
-// RUN: }}' --
-
-typedef enum tags3 {
- tags3_1,
- tags3_2,
- tags3_3,
-} tags3;
-
-typedef enum tags4 {
- tags4_1,
- tags4_2,
- tags4_3,
- tags4_4,
-} tags4;
-
-typedef enum tags5 {
- tags5_1,
- tags5_2,
- tags5_3,
- tags5_4,
- tags5_5,
-} tags5;
-
-enum class classtags3 {
- classtags3_1,
- classtags3_2,
- classtags3_3,
-};
-
-enum class classtags4 {
- classtags4_1,
- classtags4_2,
- classtags4_3,
- classtags4_4,
-};
-
-enum class classtags5 {
- classtags5_1,
- classtags5_2,
- classtags5_3,
- classtags5_4,
- classtags5_5,
-};
-
-enum class typedtags3 : unsigned int {
- typedtags3_1,
- typedtags3_2,
- typedtags3_3,
-};
-
-enum class typedtags4 : long {
- typedtags4_1,
- typedtags4_2,
- typedtags4_3,
- typedtags4_4,
-};
-
-enum class typedtags5 {
- typedtags5_1,
- typedtags5_2,
- typedtags5_3,
- typedtags5_4,
- typedtags5_5,
-};
-
-typedef union union3 {
- short *shorts;
- int *ints;
- float *floats;
-} union3;
-
-typedef union union4 {
- short *shorts;
- double *doubles;
- int *ints;
- float *floats;
-} union4;
-
-// Technically this means that every enum value is defined from 0-256 and therefore a warning is given.
-enum mycolor {
- mycolor_black = 0x00,
- mycolor_gray = 0xcc,
- mycolor_white = 0xff,
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (256)! [bugprone-tagged-union-member-count]
-struct taggedunion9 {
- enum mycolor tag;
- union {
- int a;
- float b;
- struct {
- double re;
- double im;
- } complex;
- } data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
-struct withanonymous {
- enum tags4 tag;
- union {
- int a;
- float b;
- char *c;
- };
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
-struct withTypedef1 {
- tags4 tag;
- union3 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
-struct withEnumClass1 {
- enum classtags4 tag;
- union3 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
-struct withTypedEnum2 {
- typedtags4 tag;
- union3 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (2) than tags (3)! [bugprone-tagged-union-member-count]
-struct anonymous2 {
- tags3 tag;
- union {
- int a;
- float f;
- };
-};
-
-// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has fewer data members (3) than tags (5)! [bugprone-tagged-union-member-count]
-template <typename Union, typename Tag>
-struct templated {
- Tag tag;
- Union data;
-};
-
-templated<union3, tags3> t1; // No warning expected
-templated<union3, tags5> t2;
-
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 0000000000000..7e19e9753e660
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c
@@ -0,0 +1,148 @@
+// RUN: %check_clang_tidy -std=c89-or-later %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;
+ };
+};
+
+// 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 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 TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
+ tags3 Tag;
+ union4 Data;
+};
+
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
index 05ae457f57410..286e69070cc15 100644
--- 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
@@ -1,243 +1,263 @@
// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t
-// Test cases for the default configuration
+// Test check with C++ features
-typedef enum tags3 {
- tags3_1,
- tags3_2,
- tags3_3,
+typedef enum Tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
} tags3;
-typedef enum tags4 {
- tags4_1,
- tags4_2,
- tags4_3,
- tags4_4,
+typedef enum Tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
} tags4;
-typedef enum tags5 {
- tags5_1,
- tags5_2,
- tags5_3,
- tags5_4,
- tags5_5,
-} tags5;
-
-enum class classtags3 {
- classtags3_1,
- classtags3_2,
- classtags3_3,
+enum class Classtags3 {
+ classtags3_1,
+ classtags3_2,
+ classtags3_3,
};
-enum class classtags4 {
- classtags4_1,
- classtags4_2,
- classtags4_3,
- classtags4_4,
+enum class Typedtags3 : unsigned int {
+ typedtags3_1,
+ typedtags3_2,
+ typedtags3_3,
};
-enum class classtags5 {
- classtags5_1,
- classtags5_2,
- classtags5_3,
- classtags5_4,
- classtags5_5,
+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;
};
-enum class typedtags3 : unsigned int {
- typedtags3_1,
- typedtags3_2,
- typedtags3_3,
+// 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;
};
-enum class typedtags4 : long {
- typedtags4_1,
- typedtags4_2,
- typedtags4_3,
- typedtags4_4,
+// 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;
+ };
};
-enum class typedtags5 {
- typedtags5_1,
- typedtags5_2,
- typedtags5_3,
- typedtags5_4,
- typedtags5_5,
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+class TaggedUnionClassPredefinedTagAndPredefinedUnion {
+ enum Tags3 Tag;
+ union Union4 Data;
};
-typedef union union3 {
- short *shorts;
- int *ints;
- float *floats;
-} union3;
+// 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;
+};
-typedef union union4 {
- short *shorts;
- double *doubles;
- int *ints;
- float *floats;
-} union4;
+// 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;
+};
-// It is not obvious which enum is the tag for the union.
-struct taggedunion2 { // No warnings expected.
- enum tags3 tagA;
- enum tags4 tagB;
- 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;
};
-// It is not obvious which union does the tag belong to.
-struct taggedunion4 { // No warnings expected.
- enum tags3 tag;
- union union3 dataB;
- union union3 dataA;
+// 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;
};
-// It is not obvious which union does the tag belong to.
-struct taggedunion6 { // No warnings expected.
- enum tags3 tag;
- union {
- int i1;
- int i2;
- int i3;
- };
- union {
- float f1;
- float f2;
- float f3;
- };
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct taggedunion1 {
- enum tags3 tag;
- union union4 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct taggedunion5 {
- 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)! [bugprone-tagged-union-member-count]
-struct taggedunion7 {
- enum {
- tag1,
- tag2,
- tag3,
- } tag;
- union union4 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct taggedunion8 {
- enum {
- tag1,
- tag2,
- tag3,
- } 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)! [bugprone-tagged-union-member-count]
-struct nested4 {
- enum tags3 tag;
- union {
- float f;
- int i;
- long l;
- // CHECK-MESSAGES: :[[@LINE+1]]:10: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
- struct innerdecl {
- enum tags3 tag;
- union union4 data;
- } inner;
- } data;
-};
-
-enum tag_with_counter_lowercase {
- node_type_loop,
- node_type_branch,
- node_type_function,
- node_type_count,
-};
-
-enum tag_with_counter_uppercase {
- NODE_TYPE_LOOP,
- NODE_TYPE_BRANCH,
- NODE_TYPE_FUNCTION,
- NODE_TYPE_COUNT,
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct taggedunion10 {
- enum tag_with_counter_lowercase tag;
- union union4 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct taggedunion11 {
- enum tag_with_counter_uppercase tag;
- union union4 data;
-};
-
-// Without the counter enum constant the number of tags
-// and the number data members are equal.
-struct taggedunion12 { // No warnings expected.
- enum tag_with_counter_uppercase tag;
- union union3 data;
-};
-
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct withTypedef2 {
- tags3 tag;
- union4 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]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct withEnumClass2 {
- 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)! [bugprone-tagged-union-member-count]
-struct withTypedEnum1 {
- typedtags3 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 Union, typename Tag>
+struct TemplatedStructWithNamespacedTagAndUnion {
+ Tag Kind;
+ Union Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
-struct anonymous1 {
- tags3 tag;
- union {
- int a;
- int b;
- int c;
- int d;
- };
+TemplatedStructWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedStruct3;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: Tagged union has more data members (4) than tags (3)
+template <typename Union, typename Tag>
+class TemplatedClassWithNamespacedTagAndUnion {
+ Tag Kind;
+ Union Data;
};
-// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+TemplatedClassWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedClass3;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has more data members (4) than tags (3)
template <typename Union, typename Tag>
-struct templated {
- Tag tag;
- Union data;
+struct TemplatedStruct {
+ Tag Kind;
+ Union Data;
};
-templated<union3, tags3> t1; // No warning expected
-templated<union4, tags3> t3;
+TemplatedStruct<union3, tags3> TemplatedStruct1; // No warning expected
+TemplatedStruct<union4, tags3> TemplatedStruct2;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: Tagged union has more data members (4) than tags (3)
+template <typename Union, typename Tag>
+class TemplatedClass {
+ Tag Kind;
+ Union Data;
+};
+TemplatedClass<union3, tags3> TemplatedClass1; // No warning expected
+TemplatedClass<union4, tags3> TemplatedClass2;
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 0000000000000..a84e9b2db30ea
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m
@@ -0,0 +1,147 @@
+// 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;
+ };
+};
+
+// 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 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 TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
+ tags3 Tag;
+ union4 Data;
+};
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 0000000000000..f8cdf92d78437
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm
@@ -0,0 +1,262 @@
+// 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 Union, typename Tag>
+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 Union, typename Tag>
+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 Union, typename Tag>
+struct TemplatedStruct {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedStruct<union3, tags3> TemplatedStruct1; // No warning expected
+TemplatedStruct<union4, tags3> TemplatedStruct2;
+
+// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: Tagged union has more data members (4) than tags (3)
+template <typename Union, typename Tag>
+class TemplatedClass {
+ Tag Kind;
+ Union Data;
+};
+
+TemplatedClass<union3, tags3> TemplatedClass1; // No warning expected
+TemplatedClass<union4, tags3> TemplatedClass2;
>From 319cb0d673e4c814c14b7d029accef7f149c44b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Mon, 22 Jul 2024 10:17:13 +0200
Subject: [PATCH 4/7] Next batch of review comment fixes
Implementation:
* Removed redundant allOf from matcher.
* Removed unused llvm::enumerate from loop.
* Changed static_cast to llvm::dyn_cast_or_null.
* Update diagnostic message so that is starts with lower case.
Documentation:
* Rephrased confusing parts of the documentation.
* Updated the diagnostic messages in the documentation examples to start with lowercase.
* Fixed the example for the prefixes in the documentation.
New configuration diagnostic:
Implemented a diagnostic warning for the configuration of the counting enum heuristic.
When the heuristic is disabled and a prefix or a suffix is explicitly set
in the configuration file a warning is emitted.
Testing:
* Created test case for macros.
* Created test case for templates where the union and tag are not templated,
but some other data member is.
* Moved enum counter heuristic tests from .c and .m files into its
deticated test file.
* Created test for the new configuration diagnostic.
* Updated tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp so
that the new configuration diagnostic does not trigger on this file
Style and formatting:
* Removed braces from single statement if statements.
* Changed a couple variables to use the CamelCase naming style.
* Changed diagnostic message to start with lower case.
* Moved the option name string constants to the .cpp file.
* Renamed StrictModeIsEnabled option to just StrictMode.
* Use true and false instead of 1 and 0 in the tests of the configuration options
* Renamed CountingEnumHeuristicIsEnabled to EnableCountingEnumHeuristic (code, docs, tests).
* Keep text in 80 column limit in docs .rst file.
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 96 ++++++++------
.../bugprone/TaggedUnionMemberCountCheck.h | 18 +--
.../bugprone/tagged-union-member-count.rst | 84 ++++++++-----
...unt-counting-enum-heuristic-bad-config.cpp | 11 ++
...nt-counting-enum-heuristic-is-disabled.cpp | 11 +-
...unt-counting-enum-heuristic-is-enabled.cpp | 37 +++++-
...nt-counting-enum-prefixes-and-suffixes.cpp | 9 +-
...on-member-count-counting-enum-prefixes.cpp | 9 +-
...on-member-count-counting-enum-suffixes.cpp | 9 +-
...on-member-count-strictmode-is-disabled.cpp | 5 +-
...ion-member-count-strictmode-is-enabled.cpp | 5 +-
.../bugprone/tagged-union-member-count.c | 37 ++----
.../bugprone/tagged-union-member-count.cpp | 118 +++++++++++-------
.../bugprone/tagged-union-member-count.m | 71 +++++------
.../bugprone/tagged-union-member-count.mm | 116 +++++++++++------
15 files changed, 370 insertions(+), 266 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-bad-config.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index 756ecb586fdf4..ea12f25590005 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -16,25 +16,47 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
+const char StrictModeOptionName[] = "StrictMode";
+const char EnableCountingEnumHeuristicOptionName[] =
+ "EnableCountingEnumHeuristic";
+const char CountingEnumPrefixesOptionName[] = "CountingEnumPrefixes";
+const char CountingEnumSuffixesOptionName[] = "CountingEnumSuffixes";
+
TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- StrictModeIsEnabled(Options.get(StrictModeIsEnabledOptionName, true)),
- CountingEnumHeuristicIsEnabled(
- Options.get(CountingEnumHeuristicIsEnabledOptionName, true)),
+ StrictMode(Options.get(StrictModeOptionName, true)),
+ EnableCountingEnumHeuristic(
+ Options.get(EnableCountingEnumHeuristicOptionName, true)),
RawCountingEnumPrefixes(Options.get(CountingEnumPrefixesOptionName, "")),
RawCountingEnumSuffixes(
Options.get(CountingEnumSuffixesOptionName, "count")),
ParsedCountingEnumPrefixes(
utils::options::parseStringList(RawCountingEnumPrefixes)),
ParsedCountingEnumSuffixes(
- utils::options::parseStringList(RawCountingEnumSuffixes)) {}
+ utils::options::parseStringList(RawCountingEnumSuffixes)),
+ CountingEnumPrefixesSet(
+ Options.get(CountingEnumPrefixesOptionName).has_value()),
+ CountingEnumSuffixesSet(
+ Options.get(CountingEnumSuffixesOptionName).has_value()) {
+ // TODO: Create test case for this diagnostic
+ if (!EnableCountingEnumHeuristic) {
+ if (CountingEnumPrefixesSet)
+ configurationDiag("%0: Counting enum heuristic is disabled but "
+ "CountingEnumPrefixes is set")
+ << Name;
+ if (CountingEnumSuffixesSet)
+ configurationDiag("%0: Counting enum heuristic is disabled but "
+ "CountingEnumSuffixes is set")
+ << Name;
+ }
+}
void TaggedUnionMemberCountCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, StrictModeIsEnabledOptionName, StrictModeIsEnabled);
- Options.store(Opts, CountingEnumHeuristicIsEnabledOptionName,
- CountingEnumHeuristicIsEnabled);
+ Options.store(Opts, StrictModeOptionName, StrictMode);
+ Options.store(Opts, EnableCountingEnumHeuristicOptionName,
+ EnableCountingEnumHeuristic);
Options.store(Opts, CountingEnumPrefixesOptionName, RawCountingEnumPrefixes);
Options.store(Opts, CountingEnumSuffixesOptionName, RawCountingEnumSuffixes);
}
@@ -42,11 +64,11 @@ void TaggedUnionMemberCountCheck::storeOptions(
void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
recordDecl(
- allOf(anyOf(isStruct(), isClass()),
- has(fieldDecl(hasType(qualType(hasCanonicalType(recordType()))))
- .bind("union")),
- has(fieldDecl(hasType(qualType(hasCanonicalType(enumType()))))
- .bind("tags"))))
+ anyOf(isStruct(), isClass()),
+ has(fieldDecl(hasType(qualType(hasCanonicalType(recordType()))))
+ .bind("union")),
+ has(fieldDecl(hasType(qualType(hasCanonicalType(enumType()))))
+ .bind("tags")))
.bind("root"),
this);
}
@@ -78,13 +100,13 @@ static bool greaterBySign(const llvm::APSInt &A, const llvm::APSInt &B) {
bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(
StringRef Name) const noexcept {
if (llvm::any_of(ParsedCountingEnumPrefixes,
- [&Name](const StringRef &prefix) -> bool {
- return Name.starts_with_insensitive(prefix);
+ [&Name](const StringRef &Prefix) -> bool {
+ return Name.starts_with_insensitive(Prefix);
}))
return true;
if (llvm::any_of(ParsedCountingEnumSuffixes,
- [&Name](const StringRef &suffix) -> bool {
- return Name.ends_with_insensitive(suffix);
+ [&Name](const StringRef &Suffix) -> bool {
+ return Name.ends_with_insensitive(Suffix);
}))
return true;
return false;
@@ -114,21 +136,20 @@ size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
bool IsLast = false;
llvm::APSInt CeValue = llvm::APSInt::get(0);
- for (const auto &&[index, enumerator] : llvm::enumerate(Ed->enumerators())) {
- const llvm::APSInt Val = enumerator->getInitVal();
+ for (const auto &Enumerator : Ed->enumerators()) {
+ const llvm::APSInt Val = Enumerator->getInitVal();
EnumValues.insert(Val);
if (FoundMax) {
if (greaterBySign(Val, MaxTagValue) ||
- (signEquals(Val, MaxTagValue) && Val > MaxTagValue)) {
+ (signEquals(Val, MaxTagValue) && Val > MaxTagValue))
MaxTagValue = Val;
- }
} else {
MaxTagValue = Val;
FoundMax = true;
}
- if (CountingEnumHeuristicIsEnabled) {
- if (isCountingEnumLikeName(enumerator->getName())) {
+ if (EnableCountingEnumHeuristic) {
+ if (isCountingEnumLikeName(Enumerator->getName())) {
IsLast = true;
CeValue = Val;
CeCount += 1;
@@ -139,9 +160,8 @@ size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
}
size_t ValidValuesCount = EnumValues.size();
- if (CeCount == 1 && IsLast && CeValue == MaxTagValue) {
+ if (CeCount == 1 && IsLast && CeValue == MaxTagValue)
ValidValuesCount -= 1;
- }
return ValidValuesCount;
}
@@ -161,21 +181,23 @@ void TaggedUnionMemberCountCheck::check(
const auto *UnionDef =
UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
- const auto *EnumDef = static_cast<EnumDecl *>(
+ const auto *EnumDef = llvm::dyn_cast_or_null<EnumDecl>(
TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
- const size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
- const size_t TagCount = getNumberOfValidEnumValues(EnumDef);
-
- // FIXME: Maybe a emit a note when a counter enum constant was found.
- if (UnionMemberCount > TagCount) {
- diag(Root->getLocation(),
- "Tagged union has more data members (%0) than tags (%1)!")
- << UnionMemberCount << TagCount;
- } else if (StrictModeIsEnabled && UnionMemberCount < TagCount) {
- diag(Root->getLocation(),
- "Tagged union has fewer data members (%0) than tags (%1)!")
- << UnionMemberCount << TagCount;
+ if (EnumDef) {
+ const size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
+ const size_t TagCount = getNumberOfValidEnumValues(EnumDef);
+
+ // FIXME: Maybe a emit a note when a counter enum constant was found.
+ 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;
+ }
}
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
index 0a13ead0102db..b70c6144d14d2 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -26,23 +26,17 @@ class TaggedUnionMemberCountCheck : public ClangTidyCheck {
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
- static constexpr char StrictModeIsEnabledOptionName[] = "StrictModeIsEnabled";
- static constexpr char CountingEnumHeuristicIsEnabledOptionName[] =
- "CountingEnumHeuristicIsEnabled";
- static constexpr char CountingEnumPrefixesOptionName[] =
- "CountingEnumPrefixes";
- static constexpr char CountingEnumSuffixesOptionName[] =
- "CountingEnumSuffixes";
-
- const bool StrictModeIsEnabled;
- const bool CountingEnumHeuristicIsEnabled;
+ const bool StrictMode;
+ const bool EnableCountingEnumHeuristic;
const StringRef RawCountingEnumPrefixes;
const StringRef RawCountingEnumSuffixes;
std::vector<StringRef> ParsedCountingEnumPrefixes;
std::vector<StringRef> ParsedCountingEnumSuffixes;
+ const bool CountingEnumPrefixesSet;
+ const bool CountingEnumSuffixesSet;
- size_t getNumberOfValidEnumValues(const EnumDecl *ed) const noexcept;
- bool isCountingEnumLikeName(StringRef name) const noexcept;
+ size_t getNumberOfValidEnumValues(const EnumDecl *Ed) const noexcept;
+ bool isCountingEnumLikeName(StringRef Name) const noexcept;
};
} // namespace clang::tidy::bugprone
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
index 369fc8bdc19c1..03008628ed2ee 100644
--- 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
@@ -21,7 +21,7 @@ Example
Tag2,
};
- struct TaggedUnion { // warning: Tagged union has more data members (3) than tags (2)
+ struct TaggedUnion { // warning: tagged union has more data members (3) than tags (2)
enum Tags Kind;
union {
int I;
@@ -33,8 +33,8 @@ Example
Counting enum constant heuristic
================================
-Some programmers use enums in such a way, where the value of the last enum
-constant is used to keep track of how many enum constants have been declared.
+Sometimes the last enum constant in an enum is used to keep track of how many
+enum constants have been declared. For an illustration:
.. code-block:: c++
@@ -45,29 +45,33 @@ constant is used to keep track of how many enum constants have been declared.
TagCount, // is 3
};
-This usage pattern is detected heuristically and the check does not include
-the counter enum constant in the final tag count, since the counter is not
-meant to indicate the valid union data member.
+When an enum like this is used as the tag for a tagged union then the last
+"counting" enum constant will intentionally not have a corresponding union
+data member.
-When the check finds multiple possible counting enums, then it does not change the enum count.
+This usage pattern is detected heuristically and the check
+does not include the counter enum constant in the final tag count.
+If the heuristic can be applied to multiple enum constants, then the check will
+just count the enum constants normally and not modify the final count.
-This heuristic can be disabled entirely (:option:`CountingEnumHeuristicIsEnabled`) or
+This heuristic can be disabled entirely (:option:`EnableCountingEnumHeuristic`) or
configured to follow your naming convention (:option:`CountingEnumPrefixes/Suffixes`).
-String matching is done case insensitively.
+The strings specified in (:option:`CountingEnumPrefixes/Suffixes`) are matched
+case insensitively.
Options
=======
-.. option:: CountingEnumHeuristicIsEnabled
+.. option:: EnableCountingEnumHeuristic
This option enables or disables the counting enum heuristic.
-To find possible counting enum constants this option uses the prefixes
-and suffixes specified
-the string :option:`CountingEnumSuffixes`.
+It uses the prefixes and suffixes specified in the options
+:option:`CountingEnumPrefixes/Suffixes` to find counting enum constants by
+using them for prefix and suffix matching.
This option is enabled by default.
-When :option:`CountingEnumHeuristicIsEnabled` is false:
+When :option:`EnableCountingEnumHeuristic` is false:
.. code-block:: c++
@@ -88,7 +92,7 @@ When :option:`CountingEnumHeuristicIsEnabled` is false:
};
};
-When :option:`CountingEnumHeuristicIsEnabled` is true:
+When :option:`EnableCountingEnumHeuristic` is true:
.. code-block:: c++
@@ -99,7 +103,7 @@ When :option:`CountingEnumHeuristicIsEnabled` is true:
TagCount,
};
- struct TaggedUnion { // warning: Tagged union has more data members (4) than tags (3)
+ struct TaggedUnion { // warning: tagged union has more data members (4) than tags (3)
TagWithCounter Kind;
union Data {
int A;
@@ -111,13 +115,17 @@ When :option:`CountingEnumHeuristicIsEnabled` is true:
.. option:: CountingEnumPrefixes/Suffixes
-When defined, the check will use the list of the semicolon separated strings
-in CountingEnumPrefixes or CountingEnumSuffixes for the identification of possible counting enum constants.
-These options do not alter the check's behavior when :option:`CountingEnumHeuristicIsEnabled` is set to false.
+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 for CountingEnumSuffixes is "count" and for CountingEnumPrefixes is "" (empty string).
+The default value of CountingEnumSuffixes is "count" and of
+CountingEnumPrefixes is "" (empty string).
-When :option:`CountingEnumHeuristicIsEnabled` is true and CountingEnumSuffixes is "count;size":
+When :option:`EnableCountingEnumHeuristic` is true and CountingEnumSuffixes
+is "count;size":
.. code-block:: c++
@@ -128,7 +136,7 @@ When :option:`CountingEnumHeuristicIsEnabled` is true and CountingEnumSuffixes i
TagCount,
};
- struct TaggedUnionCount { // warning: Tagged union has more data members (4) than tags (3)
+ struct TaggedUnionCount { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterCount Kind;
union Data {
int A;
@@ -145,7 +153,7 @@ When :option:`CountingEnumHeuristicIsEnabled` is true and CountingEnumSuffixes i
TagSize,
};
- struct TaggedUnionSize { // warning: Tagged union has more data members (4) than tags (3)
+ struct TaggedUnionSize { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterSize Kind;
union Data {
int A;
@@ -155,19 +163,36 @@ When :option:`CountingEnumHeuristicIsEnabled` is true and CountingEnumSuffixes i
};
};
-When :option:`CountingEnumHeuristicIsEnabled` is true and CountingEnumPrefixes is "maxsize;last_"
+When :option:`EnableCountingEnumHeuristic` is true and CountingEnumPrefixes is "maxsize;last_"
.. code-block:: c++
- enum TagWithCounter {
+ enum TagWithCounterLast {
Tag1,
Tag2,
Tag3,
- TagCount,
+ last_tag,
+ };
+
+ struct TaggedUnionLast { // warning: tagged union has more data members (4) than tags (3)
+ TagWithCounterLast tag;
+ union Data {
+ int I;
+ short S;
+ char *C;
+ float F;
+ };
+ };
+
+ enum TagWithCounterMaxSize {
+ Tag1,
+ Tag2,
+ Tag3,
+ MaxSizeTag,
};
- struct TaggedUnion { // warning: Tagged union has more data members (4) than tags (3)
- TagWithCounter tag;
+ struct TaggedUnionMaxSize { // warning: tagged union has more data members (4) than tags (3)
+ TagWithCounterMaxSize tag;
union Data {
int I;
short S;
@@ -203,7 +228,7 @@ When :option:`StrictMode` is true:
.. code-block:: c++
- struct TaggedUnion { // warning: Tagged union has fewer data members (2) than tags (3)
+ struct TaggedUnion { // warning: tagged union has fewer data members (2) than tags (3)
enum {
Tag1,
Tag2,
@@ -214,4 +239,3 @@ When :option:`StrictMode` is true:
float F;
};
};
-
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 0000000000000..73bfb7acbc464
--- /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
index f02aa94959e21..dca52170a749a 100644
--- 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
@@ -1,12 +1,10 @@
// 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.StrictModeIsEnabled: 1, \
-// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 0, \
-// RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \
-// RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (3) than tags (4)
struct IncorrectBecauseHeuristicIsDisabledPrefixCase {
enum {
tags11,
@@ -36,7 +34,7 @@ struct CorrectBecauseHeuristicIsDisabledPrefixCase { // No warnings expected
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (3) than tags (4)
struct IncorrectBecauseHeuristicIsDisabledSuffixCase {
enum {
tags11,
@@ -65,4 +63,3 @@ struct CorrectBecauseHeuristicIsDisabledSuffixCase { // No warnings expected
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
index 672787869b53a..b55d5a510a149 100644
--- 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
@@ -1,12 +1,12 @@
// 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.StrictModeIsEnabled: 0, \
-// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2)
struct IncorrectBecauseHeuristicIsEnabledPrefixCase {
enum {
tags1,
@@ -34,7 +34,7 @@ struct CorrectBecauseHeuristicIsEnabledPrefixCase { // No warnings expected
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (3) than tags (2)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2)
struct IncorrectBecauseHeuristicIsEnabledSuffixCase {
enum {
tags1,
@@ -61,3 +61,32 @@ struct CorrectBecauseHeuristicIsEnabledSuffixCase { // No warnings expected
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;
+};
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
index af655e4f4c848..a0d38a31650ab 100644
--- 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
@@ -1,7 +1,7 @@
// 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.StrictModeIsEnabled: 0, \
-// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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: }}' --
@@ -31,7 +31,7 @@ struct TaggedUnionPrefixAndSuffixMatch {
Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (3) than tags (2)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2)
struct TaggedUnionOnlyPrefixMatch {
enum {
prefixtag1,
@@ -41,7 +41,7 @@ struct TaggedUnionOnlyPrefixMatch {
Union3 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (3) than tags (2)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (3) than tags (2)
struct TaggedUnionOnlySuffixMatch {
enum {
suffixtag1,
@@ -50,4 +50,3 @@ struct TaggedUnionOnlySuffixMatch {
} 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
index f61a48f111a30..c287b1953a333 100644
--- 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
@@ -1,7 +1,7 @@
// 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.StrictModeIsEnabled: 0, \
-// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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: }}' --
@@ -12,7 +12,7 @@ union Union4 {
float *Floats;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionWithMaxsizeAsCounterPrefix {
enum {
twc1,
@@ -23,7 +23,7 @@ struct TaggedUnionWithMaxsizeAsCounterPrefix {
Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionWithLastAsCounterPrefix {
enum {
twc11,
@@ -33,4 +33,3 @@ struct TaggedUnionWithLastAsCounterPrefix {
} 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
index 4815a7361b819..f248f2efaa5ad 100644
--- 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
@@ -1,7 +1,7 @@
// 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.StrictModeIsEnabled: 0, \
-// RUN: bugprone-tagged-union-member-count.CountingEnumHeuristicIsEnabled: 1, \
+// 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: }}' --
@@ -12,7 +12,7 @@ typedef union Union4 {
float *Floats;
} union4;
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionWithCounterCountSuffix {
enum {
twc1,
@@ -23,7 +23,7 @@ struct TaggedUnionWithCounterCountSuffix {
union Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionWithCounterSizeSuffix {
enum {
twc11,
@@ -33,4 +33,3 @@ struct TaggedUnionWithCounterSizeSuffix {
} 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
index 5db5965c56880..c39683c3c40f6 100644
--- 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
@@ -1,9 +1,9 @@
// 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.StrictModeIsEnabled: 0, \
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (2) than tags (1)
struct Incorrect {
enum {
tags1,
@@ -25,4 +25,3 @@ struct CorrectBecauseStrictModeIsDisabled { // No warnings expected
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
index eed1bc1bdc35c..10d376d791968 100644
--- 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
@@ -1,9 +1,9 @@
// 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.StrictModeIsEnabled: 1, \
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has fewer data members (2) than tags (3)
struct IncorrectBecauseStrictmodeIsEnabled {
enum {
tags1,
@@ -28,4 +28,3 @@ struct Correct { // No warnings expected
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
index 7e19e9753e660..d2bfac65f3372 100644
--- 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
@@ -55,13 +55,13 @@ struct maybeTaggedUnion3 { // No warnings expected.
};
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithPredefinedTagAndInlineUnion {
enum Tags3 Tag;
union {
@@ -75,7 +75,7 @@ struct TaggedUnionStructWithPredefinedTagAndInlineUnion {
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithInlineTagAndPredefinedUnion {
enum {
TaggedUnion7tag1,
@@ -85,7 +85,7 @@ struct TaggedUnionStructWithInlineTagAndPredefinedUnion {
union union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithInlineTagAndInlineUnion {
enum {
TaggedUnion8tag1,
@@ -103,14 +103,14 @@ struct TaggedUnionStructWithInlineTagAndInlineUnion {
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+ // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: tagged union has more data members (4) than tags (3)
struct innerdecl {
enum Tags3 Tag;
union union4 Data;
@@ -118,31 +118,8 @@ struct TaggedUnionStructNesting {
} Data;
};
-// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
tags3 Tag;
union4 Data;
};
-
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
index 286e69070cc15..89d4b6ae04d70 100644
--- 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
@@ -5,14 +5,14 @@ typedef enum Tags3 {
tags3_1,
tags3_2,
tags3_3,
-} tags3;
+} Tags3;
typedef enum Tags4 {
tags4_1,
tags4_2,
tags4_3,
tags4_4,
-} tags4;
+} Tags4;
enum class Classtags3 {
classtags3_1,
@@ -30,14 +30,14 @@ typedef union Union3 {
short *Shorts;
int *Ints;
float *Floats;
-} union3;
+} Union3;
typedef union Union4 {
short *Shorts;
double *Doubles;
int *Ints;
float *Floats;
-} union4;
+} Union4;
// It is not obvious which enum is the tag for the union.
class MaybeTaggedUnion1 { // No warnings expected.
@@ -68,13 +68,13 @@ class MaybeTaggedUnion3 { // No warnings expected.
};
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassPredefinedTagAndPredefinedUnion {
enum Tags3 Tag;
- union Union4 Data;
+ union Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassPredefinedTagAndInlineUnion {
enum Tags3 Tag;
union {
@@ -88,7 +88,7 @@ class TaggedUnionClassPredefinedTagAndInlineUnion {
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassInlineTagAndPredefinedUnion {
enum {
tag1,
@@ -98,7 +98,7 @@ class TaggedUnionClassInlineTagAndPredefinedUnion {
union Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassInlineTagAndInlineUnion {
enum {
tag1,
@@ -116,14 +116,14 @@ class TaggedUnionClassInlineTagAndInlineUnion {
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+ // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: tagged union has more data members (4) than tags (3)
class Innerdecl {
enum Tags3 Tag;
union Union4 Data;
@@ -131,39 +131,39 @@ class TaggedUnionClassWithNestedTaggedUnionClass {
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassWithTypedefedTag {
- tags3 Tag;
- union4 Data;
+ Tags3 Tag;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithEnumClass {
enum Classtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClasswithEnumClass {
enum Classtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithTypedEnum {
Typedtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassWithTypedEnum {
Typedtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct AnonymousTaggedUnionStruct {
- tags3 Tag;
+ Tags3 Tag;
union {
char A;
short B;
@@ -172,9 +172,9 @@ struct AnonymousTaggedUnionStruct {
};
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassWithAnonymousUnion {
- tags3 Tag;
+ Tags3 Tag;
union {
char A;
short B;
@@ -198,13 +198,13 @@ union Union4 {
float *Floats;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassInNamespace {
Tags3 Tags;
Union4 Data;
@@ -212,20 +212,20 @@ class TaggedUnionClassInNamespace {
} // namespace testnamespace
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+// 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 Union, typename Tag>
+// 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;
@@ -233,8 +233,8 @@ struct TemplatedStructWithNamespacedTagAndUnion {
TemplatedStructWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedStruct3;
-// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: Tagged union has more data members (4) than tags (3)
-template <typename Union, typename Tag>
+// 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;
@@ -242,22 +242,56 @@ class TemplatedClassWithNamespacedTagAndUnion {
TemplatedClassWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedClass3;
-// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has more data members (4) than tags (3)
-template <typename Union, typename Tag>
+// 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<union3, tags3> TemplatedStruct1; // No warning expected
-TemplatedStruct<union4, tags3> TemplatedStruct2;
+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 Union, typename Tag>
+// 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<union3, tags3> TemplatedClass1; // No warning expected
-TemplatedClass<union4, tags3> TemplatedClass2;
+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);
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
index a84e9b2db30ea..4193bd787a9ee 100644
--- 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
@@ -4,40 +4,40 @@
tags3_1,
tags3_2,
tags3_3,
-} tags3;
+} Tags3;
typedef enum Tags4 {
tags4_1,
tags4_2,
tags4_3,
tags4_4,
-} tags4;
+} Tags4;
-typedef union union3 {
+typedef union Union3 {
short *Shorts;
int *Ints;
float *Floats;
-} union3;
+} Union3;
-typedef union union4 {
+typedef union Union4 {
short *Shorts;
double *Doubles;
int *Ints;
float *Floats;
-} union4;
+} 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;
+ 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;
+ union Union3 DataB;
+ union Union3 DataA;
};
// It is not obvious which union does the tag belong to.
@@ -55,13 +55,13 @@
};
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithPredefinedTagAndPredefinedUnion {
enum Tags3 Tag;
- union union4 Data;
+ union Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithPredefinedTagAndInlineUnion {
enum Tags3 Tag;
union {
@@ -75,17 +75,17 @@
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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;
+ union Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithInlineTagAndInlineUnion {
enum {
TaggedUnion8tag1,
@@ -103,45 +103,32 @@
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+ // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: tagged union has more data members (4) than tags (3)
struct innerdecl {
enum Tags3 Tag;
- union union4 Data;
+ union Union4 Data;
} Inner;
} Data;
};
-// 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 TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
+ Tags3 Tag;
+ 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;
-};
+#define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\
+struct Name {\
+ Tag Kind;\
+ Union Data;\
+}
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
-struct TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
- tags3 Tag;
- union4 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
index f8cdf92d78437..b0fe28eea9c5e 100644
--- 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
@@ -4,14 +4,14 @@
tags3_1,
tags3_2,
tags3_3,
-} tags3;
+} Tags3;
typedef enum Tags4 {
tags4_1,
tags4_2,
tags4_3,
tags4_4,
-} tags4;
+} Tags4;
enum class Classtags3 {
classtags3_1,
@@ -29,14 +29,14 @@
short *Shorts;
int *Ints;
float *Floats;
-} union3;
+} Union3;
typedef union Union4 {
short *Shorts;
double *Doubles;
int *Ints;
float *Floats;
-} union4;
+} Union4;
// It is not obvious which enum is the tag for the union.
class MaybeTaggedUnion1 { // No warnings expected.
@@ -67,13 +67,13 @@
};
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassPredefinedTagAndInlineUnion {
enum Tags3 Tag;
union {
@@ -87,7 +87,7 @@
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassInlineTagAndPredefinedUnion {
enum {
tag1,
@@ -97,7 +97,7 @@
union Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassInlineTagAndInlineUnion {
enum {
tag1,
@@ -115,14 +115,14 @@
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+ // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: tagged union has more data members (4) than tags (3)
class Innerdecl {
enum Tags3 Tag;
union Union4 Data;
@@ -130,39 +130,39 @@
} Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassWithTypedefedTag {
- tags3 Tag;
- union4 Data;
+ Tags3 Tag;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithEnumClass {
enum Classtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClasswithEnumClass {
enum Classtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithTypedEnum {
Typedtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassWithTypedEnum {
Typedtags3 Tag;
- union4 Data;
+ Union4 Data;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct AnonymousTaggedUnionStruct {
- tags3 Tag;
+ Tags3 Tag;
union {
char A;
short B;
@@ -171,9 +171,9 @@
};
};
-// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: Tagged union has more data members (4) than tags (3)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassWithAnonymousUnion {
- tags3 Tag;
+ Tags3 Tag;
union {
char A;
short B;
@@ -197,13 +197,13 @@
float *Floats;
};
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: tagged union has more data members (4) than tags (3)
class TaggedUnionClassInNamespace {
Tags3 Tags;
Union4 Data;
@@ -211,20 +211,20 @@
} // namespace testnamespace
-// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)
+// 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)
+// 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 Union, typename Tag>
+// 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;
@@ -232,8 +232,8 @@
TemplatedStructWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedStruct3;
-// CHECK-MESSAGES: :[[@LINE+2]]:7: warning: Tagged union has more data members (4) than tags (3)
-template <typename Union, typename Tag>
+// 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;
@@ -241,22 +241,56 @@
TemplatedClassWithNamespacedTagAndUnion<testnamespace::Union4, testnamespace::Tags3> TemplatedClass3;
-// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has more data members (4) than tags (3)
-template <typename Union, typename Tag>
+// 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<union3, tags3> TemplatedStruct1; // No warning expected
-TemplatedStruct<union4, tags3> TemplatedStruct2;
+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 Union, typename Tag>
+// 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<union3, tags3> TemplatedClass1; // No warning expected
-TemplatedClass<union4, tags3> TemplatedClass2;
+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);
>From f69708b245ac2713e372666df89d3c25a8618300 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Tue, 23 Jul 2024 14:39:41 +0200
Subject: [PATCH 5/7] Remove outdated TODO comment.
---
.../clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index ea12f25590005..32f616530ddce 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -39,7 +39,6 @@ TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
Options.get(CountingEnumPrefixesOptionName).has_value()),
CountingEnumSuffixesSet(
Options.get(CountingEnumSuffixesOptionName).has_value()) {
- // TODO: Create test case for this diagnostic
if (!EnableCountingEnumHeuristic) {
if (CountingEnumPrefixesSet)
configurationDiag("%0: Counting enum heuristic is disabled but "
>From 5348cf2d8f4e7025d1b80bee991a77115059335f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Tue, 23 Jul 2024 16:45:18 +0200
Subject: [PATCH 6/7] Emit a note, when the final enum count is modified
because of the counting enum heuristic.
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 25 +++++++++++++------
.../bugprone/TaggedUnionMemberCountCheck.h | 3 ++-
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index 32f616530ddce..bfd09166fbee0 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -38,7 +38,8 @@ TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
CountingEnumPrefixesSet(
Options.get(CountingEnumPrefixesOptionName).has_value()),
CountingEnumSuffixesSet(
- Options.get(CountingEnumSuffixesOptionName).has_value()) {
+ Options.get(CountingEnumSuffixesOptionName).has_value()),
+ CountingEnumConstantDecl(nullptr) {
if (!EnableCountingEnumHeuristic) {
if (CountingEnumPrefixesSet)
configurationDiag("%0: Counting enum heuristic is disabled but "
@@ -112,7 +113,7 @@ bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(
}
size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
- const EnumDecl *Ed) const noexcept {
+ const EnumDecl *Ed) noexcept {
bool FoundMax = false;
llvm::APSInt MaxTagValue;
llvm::SmallSet<llvm::APSInt, 32> EnumValues;
@@ -132,7 +133,7 @@ size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
// constant.
// The 'ce' prefix is a shorthand for 'counting enum'.
size_t CeCount = 0;
- bool IsLast = false;
+ bool CeIsLast = false;
llvm::APSInt CeValue = llvm::APSInt::get(0);
for (const auto &Enumerator : Ed->enumerators()) {
@@ -149,18 +150,22 @@ size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
if (EnableCountingEnumHeuristic) {
if (isCountingEnumLikeName(Enumerator->getName())) {
- IsLast = true;
+ CeIsLast = true;
CeValue = Val;
CeCount += 1;
+ CountingEnumConstantDecl = Enumerator;
} else {
- IsLast = false;
+ CeIsLast = false;
}
}
}
size_t ValidValuesCount = EnumValues.size();
- if (CeCount == 1 && IsLast && CeValue == MaxTagValue)
+ if (CeCount == 1 && CeIsLast && CeValue == MaxTagValue) {
ValidValuesCount -= 1;
+ } else {
+ CountingEnumConstantDecl = nullptr;
+ }
return ValidValuesCount;
}
@@ -187,7 +192,6 @@ void TaggedUnionMemberCountCheck::check(
const size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
const size_t TagCount = getNumberOfValidEnumValues(EnumDef);
- // FIXME: Maybe a emit a note when a counter enum constant was found.
if (UnionMemberCount > TagCount) {
diag(Root->getLocation(),
"tagged union has more data members (%0) than tags (%1)!")
@@ -197,6 +201,13 @@ void TaggedUnionMemberCountCheck::check(
"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);
+ }
}
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
index b70c6144d14d2..0b6eb80597551 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -34,8 +34,9 @@ class TaggedUnionMemberCountCheck : public ClangTidyCheck {
std::vector<StringRef> ParsedCountingEnumSuffixes;
const bool CountingEnumPrefixesSet;
const bool CountingEnumSuffixesSet;
+ EnumConstantDecl *CountingEnumConstantDecl;
- size_t getNumberOfValidEnumValues(const EnumDecl *Ed) const noexcept;
+ size_t getNumberOfValidEnumValues(const EnumDecl *Ed) noexcept;
bool isCountingEnumLikeName(StringRef Name) const noexcept;
};
>From 831133af8e88c8c04e17cd297241a5183c365b02 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Tue, 30 Jul 2024 18:11:26 +0200
Subject: [PATCH 7/7] Fix bug that StrictMode was enabled by default.
---
.../clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index bfd09166fbee0..03b50292a85ba 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -25,7 +25,7 @@ const char CountingEnumSuffixesOptionName[] = "CountingEnumSuffixes";
TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- StrictMode(Options.get(StrictModeOptionName, true)),
+ StrictMode(Options.get(StrictModeOptionName, false)),
EnableCountingEnumHeuristic(
Options.get(EnableCountingEnumHeuristicOptionName, true)),
RawCountingEnumPrefixes(Options.get(CountingEnumPrefixesOptionName, "")),
More information about the cfe-commits
mailing list