[clang-tools-extra] [clang-tidy] Add new check bugprone-tagged-union-member-count (PR #89925)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 30 07:09:53 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>,
=?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 01/11] [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 2931325d8b5798..d9e094e11bdf09 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 081ba67efe1538..5e7fa08aece02e 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 00000000000000..6a7b5a92902c86
--- /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 00000000000000..bde42da23ff38f
--- /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 78b09d23d4427f..9f9069a8d08923 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 00000000000000..7eac93c6c96021
--- /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 79e81dd174e4f3..3058bee1357fc9 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 00000000000000..91165f1bd64e08
--- /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 02/11] 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 6a7b5a92902c86..786198b011f308 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 bde42da23ff38f..2c6ec3b05358a9 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 9f9069a8d08923..b287b99da5a011 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 7eac93c6c96021..cd7de571041e4b 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 3058bee1357fc9..5b2fb2bfbfc301 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 00000000000000..69b44c10b7c315
--- /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 00000000000000..cddbb003a01f9b
--- /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 00000000000000..7d6c5f385c8e1d
--- /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 91165f1bd64e08..05ae457f57410c 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 03/11] 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 786198b011f308..756ecb586fdf43 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 2c6ec3b05358a9..0a13ead0102dbe 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 cd7de571041e4b..369fc8bdc19c11 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 00000000000000..f02aa94959e211
--- /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 00000000000000..672787869b53a2
--- /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 00000000000000..af655e4f4c8487
--- /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 00000000000000..f61a48f111a30b
--- /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 00000000000000..4815a7361b8193
--- /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 69b44c10b7c315..00000000000000
--- 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 cddbb003a01f9b..00000000000000
--- 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 00000000000000..5db5965c568803
--- /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 00000000000000..eed1bc1bdc35cb
--- /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 7d6c5f385c8e1d..00000000000000
--- 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 00000000000000..7e19e9753e6604
--- /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 05ae457f57410c..286e69070cc15e 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 00000000000000..a84e9b2db30ea5
--- /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 00000000000000..f8cdf92d784376
--- /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 04/11] 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 756ecb586fdf43..ea12f255900057 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 0a13ead0102dbe..b70c6144d14d2a 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 369fc8bdc19c11..03008628ed2eee 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 00000000000000..73bfb7acbc4643
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-bad-config.cpp
@@ -0,0 +1,11 @@
+// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t \
+// RUN: -config='{CheckOptions: { \
+// RUN: bugprone-tagged-union-member-count.EnableCountingEnumHeuristic: false, \
+// RUN: bugprone-tagged-union-member-count.CountingEnumSuffixes: "count", \
+// RUN: bugprone-tagged-union-member-count.CountingEnumPrefixes: "last", \
+// RUN: }}'
+
+// Warn when the heuristic is disabled and a suffix or a prefix is set explicitly.
+
+// CHECK-MESSAGES: warning: bugprone-tagged-union-member-count: Counting enum heuristic is disabled but CountingEnumPrefixes is set
+// CHECK-MESSAGES: warning: bugprone-tagged-union-member-count: Counting enum heuristic is disabled but CountingEnumSuffixes is set
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-heuristic-is-disabled.cpp
index f02aa94959e211..dca52170a749a3 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 672787869b53a2..b55d5a510a1493 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 af655e4f4c8487..a0d38a31650abd 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 f61a48f111a30b..c287b1953a3336 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 4815a7361b8193..f248f2efaa5adc 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 5db5965c568803..c39683c3c40f60 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 eed1bc1bdc35cb..10d376d7919685 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 7e19e9753e6604..d2bfac65f33727 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 286e69070cc15e..89d4b6ae04d707 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 a84e9b2db30ea5..4193bd787a9eec 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 f8cdf92d784376..b0fe28eea9c5e2 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 05/11] 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 ea12f255900057..32f616530ddce0 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 06/11] 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 32f616530ddce0..bfd09166fbee0f 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 b70c6144d14d2a..0b6eb80597551b 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 07/11] 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 bfd09166fbee0f..03b50292a85ba3 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, "")),
>From d5eedcd4ac34612635245c194a95cd8c41348155 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, 5 Aug 2024 10:25:00 +0200
Subject: [PATCH 08/11] Implemented the suggestions of latest review comments.
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 213 ++++++++++--------
.../bugprone/TaggedUnionMemberCountCheck.h | 17 +-
.../bugprone/tagged-union-member-count.rst | 97 +++++---
.../bugprone/tagged-union-member-count.c | 60 +++--
.../bugprone/tagged-union-member-count.cpp | 13 ++
.../bugprone/tagged-union-member-count.m | 15 ++
.../bugprone/tagged-union-member-count.mm | 13 ++
7 files changed, 273 insertions(+), 155 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index 03b50292a85ba3..07ede8178d6418 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -16,39 +16,74 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
-const char StrictModeOptionName[] = "StrictMode";
-const char EnableCountingEnumHeuristicOptionName[] =
+static constexpr llvm::StringLiteral StrictModeOptionName = "StrictMode";
+static constexpr llvm::StringLiteral EnableCountingEnumHeuristicOptionName =
"EnableCountingEnumHeuristic";
-const char CountingEnumPrefixesOptionName[] = "CountingEnumPrefixes";
-const char CountingEnumSuffixesOptionName[] = "CountingEnumSuffixes";
+static constexpr llvm::StringLiteral CountingEnumPrefixesOptionName =
+ "CountingEnumPrefixes";
+static constexpr llvm::StringLiteral CountingEnumSuffixesOptionName =
+ "CountingEnumSuffixes";
+
+static constexpr bool StrictModeOptionDefaultValue = false;
+static constexpr bool EnableCountingEnumHeuristicOptionDefaultValue = true;
+static constexpr llvm::StringLiteral CountingEnumPrefixesOptionDefaultValue =
+ "";
+static constexpr llvm::StringLiteral CountingEnumSuffixesOptionDefaultValue =
+ "count";
+
+static constexpr llvm::StringLiteral RootMatchBindName = "root";
+static constexpr llvm::StringLiteral UnionMatchBindName = "union";
+static constexpr llvm::StringLiteral TagMatchBindName = "tags";
+
+namespace {
+AST_MATCHER(FieldDecl, isUnion) {
+ const Type *T = Node.getType().getCanonicalType().getTypePtr();
+ assert(T);
+ return T->isUnionType();
+}
+
+AST_MATCHER(FieldDecl, isEnum) {
+ const Type *T = Node.getType().getCanonicalType().getTypePtr();
+ assert(T);
+ return T->isEnumeralType();
+}
+
+AST_MATCHER_P2(RecordDecl, fieldCountOfKindIsGT,
+ ast_matchers::internal::Matcher<FieldDecl>, InnerMatcher,
+ unsigned, N) {
+ unsigned matchCount = 0;
+ for (const auto field : Node.fields()) {
+ if (InnerMatcher.matches(*field, Finder, Builder)) {
+ matchCount += 1;
+ }
+ }
+ return matchCount > N;
+}
+}; // namespace
TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- StrictMode(Options.get(StrictModeOptionName, false)),
+ StrictMode(
+ Options.get(StrictModeOptionName, StrictModeOptionDefaultValue)),
EnableCountingEnumHeuristic(
- Options.get(EnableCountingEnumHeuristicOptionName, true)),
- RawCountingEnumPrefixes(Options.get(CountingEnumPrefixesOptionName, "")),
- RawCountingEnumSuffixes(
- Options.get(CountingEnumSuffixesOptionName, "count")),
- ParsedCountingEnumPrefixes(
- utils::options::parseStringList(RawCountingEnumPrefixes)),
- ParsedCountingEnumSuffixes(
- utils::options::parseStringList(RawCountingEnumSuffixes)),
- CountingEnumPrefixesSet(
- Options.get(CountingEnumPrefixesOptionName).has_value()),
- CountingEnumSuffixesSet(
- Options.get(CountingEnumSuffixesOptionName).has_value()),
- CountingEnumConstantDecl(nullptr) {
+ Options.get(EnableCountingEnumHeuristicOptionName,
+ EnableCountingEnumHeuristicOptionDefaultValue)),
+ CountingEnumPrefixes(utils::options::parseStringList(
+ Options.get(CountingEnumPrefixesOptionName,
+ CountingEnumPrefixesOptionDefaultValue))),
+ CountingEnumSuffixes(utils::options::parseStringList(
+ Options.get(CountingEnumSuffixesOptionName,
+ CountingEnumSuffixesOptionDefaultValue))) {
if (!EnableCountingEnumHeuristic) {
- if (CountingEnumPrefixesSet)
+ if (Options.get(CountingEnumPrefixesOptionName).has_value())
configurationDiag("%0: Counting enum heuristic is disabled but "
- "CountingEnumPrefixes is set")
- << Name;
- if (CountingEnumSuffixesSet)
+ "%1 is set")
+ << Name << CountingEnumPrefixesOptionName;
+ if (Options.get(CountingEnumSuffixesOptionName).has_value())
configurationDiag("%0: Counting enum heuristic is disabled but "
- "CountingEnumSuffixes is set")
- << Name;
+ "%1 is set")
+ << Name << CountingEnumSuffixesOptionName;
}
}
@@ -57,39 +92,30 @@ void TaggedUnionMemberCountCheck::storeOptions(
Options.store(Opts, StrictModeOptionName, StrictMode);
Options.store(Opts, EnableCountingEnumHeuristicOptionName,
EnableCountingEnumHeuristic);
- Options.store(Opts, CountingEnumPrefixesOptionName, RawCountingEnumPrefixes);
- Options.store(Opts, CountingEnumSuffixesOptionName, RawCountingEnumSuffixes);
+ Options.store(Opts, CountingEnumPrefixesOptionName,
+ utils::options::serializeStringList(CountingEnumPrefixes));
+ Options.store(Opts, CountingEnumSuffixesOptionName,
+ utils::options::serializeStringList(CountingEnumSuffixes));
}
void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(
- recordDecl(
- 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();
-}
+ static const auto hasMultipleUnionsOrEnums = anyOf(
+ fieldCountOfKindIsGT(isUnion(), 1), fieldCountOfKindIsGT(isEnum(), 1));
-static bool isEnum(const FieldDecl *R) {
- 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;
+ Finder->addMatcher(
+ recordDecl(anyOf(isStruct(), isClass()),
+ unless(anyOf(isImplicit(), hasMultipleUnionsOrEnums)),
+ has(fieldDecl(isUnion()).bind(UnionMatchBindName)),
+ has(fieldDecl(isEnum()).bind(TagMatchBindName)))
+ .bind(RootMatchBindName),
+ this);
}
static bool signEquals(const llvm::APSInt &A, const llvm::APSInt &B) {
- return (A.isNegative() && B.isNegative()) ||
- (A.isStrictlyPositive() && B.isStrictlyPositive()) ||
- (A.isZero() && B.isZero());
+ return (A.isNegative() == B.isNegative()) &&
+ (A.isStrictlyPositive() == B.isStrictlyPositive()) &&
+ (A.isZero() == B.isZero());
}
static bool greaterBySign(const llvm::APSInt &A, const llvm::APSInt &B) {
@@ -98,22 +124,21 @@ 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);
- }))
+ StringRef Name) const {
+ if (llvm::any_of(CountingEnumPrefixes, [Name](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);
- }))
+ if (llvm::any_of(CountingEnumSuffixes, [Name](StringRef Suffix) -> bool {
+ return Name.ends_with_insensitive(Suffix);
+ }))
return true;
return false;
}
-size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
- const EnumDecl *Ed) noexcept {
+std::size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
+ const EnumDecl *ED,
+ const EnumConstantDecl *&OutCountingEnumConstantDecl) {
bool FoundMax = false;
llvm::APSInt MaxTagValue;
llvm::SmallSet<llvm::APSInt, 32> EnumValues;
@@ -132,11 +157,11 @@ size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
// 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;
+ std::size_t CeCount = 0;
bool CeIsLast = false;
llvm::APSInt CeValue = llvm::APSInt::get(0);
- for (const auto &Enumerator : Ed->enumerators()) {
+ for (const auto &Enumerator : ED->enumerators()) {
const llvm::APSInt Val = Enumerator->getInitVal();
EnumValues.insert(Val);
if (FoundMax) {
@@ -153,18 +178,18 @@ size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
CeIsLast = true;
CeValue = Val;
CeCount += 1;
- CountingEnumConstantDecl = Enumerator;
+ OutCountingEnumConstantDecl = Enumerator;
} else {
CeIsLast = false;
}
}
}
- size_t ValidValuesCount = EnumValues.size();
+ std::size_t ValidValuesCount = EnumValues.size();
if (CeCount == 1 && CeIsLast && CeValue == MaxTagValue) {
ValidValuesCount -= 1;
} else {
- CountingEnumConstantDecl = nullptr;
+ OutCountingEnumConstantDecl = nullptr;
}
return ValidValuesCount;
@@ -172,42 +197,38 @@ size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
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");
-
- // The matcher can only narrow down the type to recordType()
- if (!isUnion(UnionField))
- return;
-
- if (hasMultipleUnionsOrEnums(Root))
- return;
+ const auto *Root = Result.Nodes.getNodeAs<RecordDecl>(RootMatchBindName);
+ const auto *UnionField =
+ Result.Nodes.getNodeAs<FieldDecl>(UnionMatchBindName);
+ const auto *TagField = Result.Nodes.getNodeAs<FieldDecl>(TagMatchBindName);
+ assert(Root && UnionField && TagField);
const auto *UnionDef =
UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
- const auto *EnumDef = llvm::dyn_cast_or_null<EnumDecl>(
+ const auto *EnumDef = llvm::dyn_cast<EnumDecl>(
TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
+ assert(UnionDef && EnumDef && "Declarations missing!");
+
+ const EnumConstantDecl *CountingEnumConstantDecl = nullptr;
+ const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
+ const std::size_t TagCount =
+ getNumberOfValidEnumValues(EnumDef, CountingEnumConstantDecl);
+
+ 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 (EnumDef) {
- const size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
- const size_t TagCount = getNumberOfValidEnumValues(EnumDef);
-
- if (UnionMemberCount > TagCount) {
- diag(Root->getLocation(),
- "tagged union has more data members (%0) than tags (%1)!")
- << UnionMemberCount << TagCount;
- } else if (StrictMode && UnionMemberCount < TagCount) {
- diag(Root->getLocation(),
- "tagged union has fewer data members (%0) than tags (%1)!")
- << UnionMemberCount << TagCount;
- }
-
- if (CountingEnumConstantDecl) {
- diag(CountingEnumConstantDecl->getLocation(),
- "assuming that this constant is just an auxiliary value and not "
- "used for indicating a valid union data member",
- DiagnosticIDs::Note);
- }
+ 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 0b6eb80597551b..74f45a4929d8e5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -28,16 +28,13 @@ class TaggedUnionMemberCountCheck : public ClangTidyCheck {
private:
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;
- EnumConstantDecl *CountingEnumConstantDecl;
-
- size_t getNumberOfValidEnumValues(const EnumDecl *Ed) noexcept;
- bool isCountingEnumLikeName(StringRef Name) const noexcept;
+ const std::vector<StringRef> CountingEnumPrefixes;
+ const std::vector<StringRef> CountingEnumSuffixes;
+
+ std::size_t getNumberOfValidEnumValues(
+ const EnumDecl *ED,
+ const EnumConstantDecl *&OutCountingEnumConstantDecl);
+ bool isCountingEnumLikeName(StringRef Name) const;
};
} // 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 03008628ed2eee..8de6ffc151b918 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,6 +1,5 @@
.. title:: clang-tidy - bugprone-tagged-union-member-count
-==================================
bugprone-tagged-union-member-count
==================================
@@ -11,8 +10,7 @@ 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
-=======
+Example:
.. code-block:: c++
@@ -30,14 +28,26 @@ Example
} Data;
};
-Counting enum constant heuristic
-================================
+How enum constants are counted
+------------------------------
+
+The main complicating factor when counting the number of enum constants is that
+some of them might be auxiliary values that purposely don't have a corresponding union
+data member and are used for something else. For example the last enum constant
+sometimes explicitly "points to" the last declared valid enum constant or
+tracks how many enum constants have been declared.
-Sometimes the last enum constant in an enum is used to keep track of how many
-enum constants have been declared. For an illustration:
+For an illsutration:
.. code-block:: c++
+ enum TagWithLast {
+ Tag1 = 0,
+ Tag2 = 1,
+ Tag3 = 2,
+ LastTag = 2
+ };
+
enum TagWithCounter {
Tag1, // is 0
Tag2, // is 1
@@ -45,22 +55,47 @@ enum constants have been declared. For an illustration:
TagCount, // is 3
};
-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.
+The check counts the number of distinct values among the enum constants and not the enum
+constants themselves. This way enum constants that are essentially just aliases of other
+enum constants are not included in the final 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.
+Counting enum constants are detected using the following heuristic. The counting enum
+constant has to be the last enum constant to be declared. It's value must be the largest
+out of every enum constant. It's name must start with a prefix or must end with a suffix
+from :option:`CountingEnumPrefixes/Suffixes`. If the heuristic can be applied to multiple
+enum constants, then the enum count is not modified, otherwise when only one counting
+enum constant is found, then the final count is decreased by one. When the final count is decremented
+based on this heuristic, a diagnostic note is emitted, that shows which enum constant
+matched the criteria.
-This heuristic can be disabled entirely (:option:`EnableCountingEnumHeuristic`) or
+The heuristic can be disabled entirely (:option:`EnableCountingEnumHeuristic`) or
configured to follow your naming convention (:option:`CountingEnumPrefixes/Suffixes`).
The strings specified in (:option:`CountingEnumPrefixes/Suffixes`) are matched
case insensitively.
+Example counts:
+
+.. code-block:: c++
+
+ // Enum count is 3, because the value 2 is counted only once
+ enum TagWithLast {
+ Tag1 = 0,
+ Tag2 = 1,
+ Tag3 = 2,
+ LastTag = 2
+ };
+
+ // Enum count is 3, because TagCount is heuristically excluded
+ enum TagWithCounter {
+ Tag1, // is 0
+ Tag2, // is 1
+ Tag3, // is 2
+ TagCount, // is 3
+ };
+
+
Options
-=======
+-------
.. option:: EnableCountingEnumHeuristic
@@ -84,14 +119,14 @@ When :option:`EnableCountingEnumHeuristic` is false:
struct TaggedUnion {
TagWithCounter Kind;
- union Data {
+ union {
int A;
long B;
char *Str;
float F;
- };
+ } Data;
};
-
+
When :option:`EnableCountingEnumHeuristic` is true:
.. code-block:: c++
@@ -105,12 +140,12 @@ When :option:`EnableCountingEnumHeuristic` is true:
struct TaggedUnion { // warning: tagged union has more data members (4) than tags (3)
TagWithCounter Kind;
- union Data {
+ union {
int A;
long B;
char *Str;
float F;
- };
+ } Data;
};
.. option:: CountingEnumPrefixes/Suffixes
@@ -138,12 +173,12 @@ is "count;size":
struct TaggedUnionCount { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterCount Kind;
- union Data {
+ union {
int A;
long B;
char *Str;
float F;
- };
+ } Data;
};
enum TagWithCounterSize {
@@ -155,12 +190,12 @@ is "count;size":
struct TaggedUnionSize { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterSize Kind;
- union Data {
+ union {
int A;
long B;
char *Str;
float F;
- };
+ } Data;
};
When :option:`EnableCountingEnumHeuristic` is true and CountingEnumPrefixes is "maxsize;last_"
@@ -176,12 +211,12 @@ When :option:`EnableCountingEnumHeuristic` is true and CountingEnumPrefixes is "
struct TaggedUnionLast { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterLast tag;
- union Data {
+ union {
int I;
short S;
char *C;
float F;
- };
+ } Data;
};
enum TagWithCounterMaxSize {
@@ -193,12 +228,12 @@ When :option:`EnableCountingEnumHeuristic` is true and CountingEnumPrefixes is "
struct TaggedUnionMaxSize { // warning: tagged union has more data members (4) than tags (3)
TagWithCounterMaxSize tag;
- union Data {
+ union {
int I;
short S;
char *C;
float F;
- };
+ } Data;
};
.. option:: StrictMode
@@ -221,7 +256,7 @@ When :option:`StrictMode` is false:
union {
int I;
float F;
- };
+ } Data;
};
When :option:`StrictMode` is true:
@@ -237,5 +272,5 @@ When :option:`StrictMode` is true:
union {
int I;
float F;
- };
+ } 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 d2bfac65f33727..60c93c553bacaf 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
@@ -1,43 +1,43 @@
-// RUN: %check_clang_tidy -std=c89-or-later %s bugprone-tagged-union-member-count %t
+// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
typedef enum Tags3 {
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,16 +55,31 @@ struct maybeTaggedUnion3 { // No warnings expected.
};
};
+// No warnings expected, because LastATag is just an alias
+struct TaggedUnionWithAliasedEnumConstant {
+ enum {
+ ATag1,
+ ATag2,
+ ATag3,
+ LastATag = ATag3,
+ } Tag;
+ union {
+ float F;
+ int *Ints;
+ char Key[8];
+ } Data;
+};
+
// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithPredefinedTagAndPredefinedUnion {
enum Tags3 Tag;
- union union4 Data;
+ 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 {
+ union {
int *Ints;
char Characters[13];
struct {
@@ -72,7 +87,7 @@ struct TaggedUnionStructWithPredefinedTagAndInlineUnion {
double Im;
} Complex;
long L;
- } Data;
+ } Data;
};
// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
@@ -82,7 +97,7 @@ struct TaggedUnionStructWithInlineTagAndPredefinedUnion {
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)
@@ -113,13 +128,22 @@ struct TaggedUnionStructNesting {
// 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 TaggedUnionStructWithTypedefedTagAndTypedefedUnion {
- tags3 Tag;
- union4 Data;
-};
+ Tags3 Tag;
+ Union4 Data;
+};
+
+#define DECLARE_TAGGED_UNION_STRUCT(Tag, Union, Name)\
+struct Name {\
+ Tag Kind;\
+ Union Data;\
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
+DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
index 89d4b6ae04d707..25827e8c8de0cf 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
@@ -295,3 +295,16 @@ class Name {\
// CHECK-MESSAGES: :[[@LINE+1]]:43: warning: tagged union has more data members (4) than tags (3)
DECLARE_TAGGED_UNION_CLASS(Tags3, Union4, TaggedUnionClassFromMacro);
+
+// Lambdas implicitly compile down to an unnamed CXXRecordDecl and if they have captures,
+// then those become unnamed fields.
+void DoNotMatchLambdas() {
+ enum {
+ A
+ } e;
+ union {
+ long A;
+ char B;
+ } u;
+ auto L = [e, u] () {};
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m
index 4193bd787a9eec..60c93c553bacaf 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
@@ -55,6 +55,21 @@
};
};
+// No warnings expected, because LastATag is just an alias
+struct TaggedUnionWithAliasedEnumConstant {
+ enum {
+ ATag1,
+ ATag2,
+ ATag3,
+ LastATag = ATag3,
+ } Tag;
+ union {
+ float F;
+ int *Ints;
+ char Key[8];
+ } Data;
+};
+
// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionStructWithPredefinedTagAndPredefinedUnion {
enum Tags3 Tag;
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 b0fe28eea9c5e2..8b308555281c5c 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
@@ -294,3 +294,16 @@
// CHECK-MESSAGES: :[[@LINE+1]]:43: warning: tagged union has more data members (4) than tags (3)
DECLARE_TAGGED_UNION_CLASS(Tags3, Union4, TaggedUnionClassFromMacro);
+
+// Lambdas implicitly compile down to an unnamed CXXRecordDecl and if they have captures,
+// then those become unnamed fields.
+void DoNotMatchLambdas() {
+ enum {
+ A
+ } e;
+ union {
+ long A;
+ char B;
+ } u;
+ auto L = [e, u] () {};
+}
>From 01120fc1d914fb2e8a5330d5465cf25065a0c2a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Thu, 8 Aug 2024 10:29:03 +0200
Subject: [PATCH 09/11] Implement the requested changes of isuckatcs.
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 97 ++++++-------------
.../bugprone/TaggedUnionMemberCountCheck.h | 5 +-
.../bugprone/tagged-union-member-count.rst | 21 ++--
...unt-counting-enum-heuristic-is-enabled.cpp | 64 ++++++++++++
...nt-counting-enum-prefixes-and-suffixes.cpp | 4 +-
5 files changed, 105 insertions(+), 86 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index 07ede8178d6418..e0283ca25e471d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -76,11 +76,11 @@ TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
Options.get(CountingEnumSuffixesOptionName,
CountingEnumSuffixesOptionDefaultValue))) {
if (!EnableCountingEnumHeuristic) {
- if (Options.get(CountingEnumPrefixesOptionName).has_value())
+ if (Options.get(CountingEnumPrefixesOptionName))
configurationDiag("%0: Counting enum heuristic is disabled but "
"%1 is set")
<< Name << CountingEnumPrefixesOptionName;
- if (Options.get(CountingEnumSuffixesOptionName).has_value())
+ if (Options.get(CountingEnumSuffixesOptionName))
configurationDiag("%0: Counting enum heuristic is disabled but "
"%1 is set")
<< Name << CountingEnumSuffixesOptionName;
@@ -112,19 +112,7 @@ void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
this);
}
-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 {
+bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name) const {
if (llvm::any_of(CountingEnumPrefixes, [Name](StringRef Prefix) -> bool {
return Name.starts_with_insensitive(Prefix);
}))
@@ -136,63 +124,25 @@ bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(
return false;
}
-std::size_t TaggedUnionMemberCountCheck::getNumberOfValidEnumValues(
- const EnumDecl *ED,
- const EnumConstantDecl *&OutCountingEnumConstantDecl) {
- bool FoundMax = false;
- llvm::APSInt MaxTagValue;
+std::pair<const std::size_t, const EnumConstantDecl *>
+TaggedUnionMemberCountCheck::getNumberOfEnumValues(const EnumDecl *ED) {
llvm::SmallSet<llvm::APSInt, 32> EnumValues;
- // Heuristic for counter enum constants.
- //
- // enum tag_with_counter {
- // tag1,
- // tag2,
- // tag_count, <-- Searching for these enum constants
- // };
- //
- // The final tag count is decreased by 1 if and only if:
- // 1. There is only one counting enum constant,
- // 2. The counting enum constant is the last enum constant that is defined,
- // 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'.
- std::size_t CeCount = 0;
- bool CeIsLast = false;
- llvm::APSInt CeValue = llvm::APSInt::get(0);
-
- 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))
- MaxTagValue = Val;
- } else {
- MaxTagValue = Val;
- FoundMax = true;
- }
+ const EnumConstantDecl *LastEnumConstant = nullptr;
+ for (const auto Enumerator : ED->enumerators()) {
+ EnumValues.insert(Enumerator->getInitVal());
+ LastEnumConstant = Enumerator;
+ }
- if (EnableCountingEnumHeuristic) {
- if (isCountingEnumLikeName(Enumerator->getName())) {
- CeIsLast = true;
- CeValue = Val;
- CeCount += 1;
- OutCountingEnumConstantDecl = Enumerator;
- } else {
- CeIsLast = false;
+ if (EnableCountingEnumHeuristic && LastEnumConstant) {
+ if (isCountingEnumLikeName(LastEnumConstant->getName())) {
+ if (LastEnumConstant->getInitVal() == (EnumValues.size() - 1)) {
+ return {EnumValues.size() - 1, LastEnumConstant};
}
}
}
- std::size_t ValidValuesCount = EnumValues.size();
- if (CeCount == 1 && CeIsLast && CeValue == MaxTagValue) {
- ValidValuesCount -= 1;
- } else {
- OutCountingEnumConstantDecl = nullptr;
- }
-
- return ValidValuesCount;
+ return {EnumValues.size(), nullptr};
}
void TaggedUnionMemberCountCheck::check(
@@ -201,18 +151,25 @@ void TaggedUnionMemberCountCheck::check(
const auto *UnionField =
Result.Nodes.getNodeAs<FieldDecl>(UnionMatchBindName);
const auto *TagField = Result.Nodes.getNodeAs<FieldDecl>(TagMatchBindName);
- assert(Root && UnionField && TagField);
+
+ assert(Root && "Root is missing!");
+ assert(UnionField && "UnionField is missing!");
+ assert(TagField && "TagField is missing!");
+ if (!Root || !UnionField || !TagField)
+ return;
const auto *UnionDef =
UnionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
const auto *EnumDef = llvm::dyn_cast<EnumDecl>(
TagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
- assert(UnionDef && EnumDef && "Declarations missing!");
- const EnumConstantDecl *CountingEnumConstantDecl = nullptr;
+ assert(UnionDef && "UnionDef is missing!");
+ assert(EnumDef && "EnumDef is missing!");
+ if (!UnionDef || !EnumDef)
+ return;
+
const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());
- const std::size_t TagCount =
- getNumberOfValidEnumValues(EnumDef, CountingEnumConstantDecl);
+ auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef);
if (UnionMemberCount > TagCount) {
diag(Root->getLocation(),
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
index 74f45a4929d8e5..8b9d677d00b40a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -31,9 +31,8 @@ class TaggedUnionMemberCountCheck : public ClangTidyCheck {
const std::vector<StringRef> CountingEnumPrefixes;
const std::vector<StringRef> CountingEnumSuffixes;
- std::size_t getNumberOfValidEnumValues(
- const EnumDecl *ED,
- const EnumConstantDecl *&OutCountingEnumConstantDecl);
+ std::pair<const std::size_t, const EnumConstantDecl *>
+ getNumberOfEnumValues(const EnumDecl *ED);
bool isCountingEnumLikeName(StringRef Name) const;
};
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 8de6ffc151b918..ae596ba34891fb 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
@@ -32,12 +32,12 @@ How enum constants are counted
------------------------------
The main complicating factor when counting the number of enum constants is that
-some of them might be auxiliary values that purposely don't have a corresponding union
+some of them might be auxiliary values that purposefully don't have a corresponding union
data member and are used for something else. For example the last enum constant
sometimes explicitly "points to" the last declared valid enum constant or
tracks how many enum constants have been declared.
-For an illsutration:
+For an illustration:
.. code-block:: c++
@@ -56,17 +56,16 @@ For an illsutration:
};
The check counts the number of distinct values among the enum constants and not the enum
-constants themselves. This way enum constants that are essentially just aliases of other
+constants themselves. This way the enum constants that are essentially just aliases of other
enum constants are not included in the final count.
-Counting enum constants are detected using the following heuristic. The counting enum
-constant has to be the last enum constant to be declared. It's value must be the largest
-out of every enum constant. It's name must start with a prefix or must end with a suffix
-from :option:`CountingEnumPrefixes/Suffixes`. If the heuristic can be applied to multiple
-enum constants, then the enum count is not modified, otherwise when only one counting
-enum constant is found, then the final count is decreased by one. When the final count is decremented
-based on this heuristic, a diagnostic note is emitted, that shows which enum constant
-matched the criteria.
+Handling of counting enum constants (ones like :code:`TagCount` in the previous code example)
+is done by decreasing the number of enum values by one if the name of the last enum constant
+starts with a prefix or ends with a suffix specified in :option:`CountingEnumPrefixes/Suffixes`
+and it's value is one less than the total number of distinct values in the enum.
+
+When the final count is adjusted based on this heuristic then a diagnostic note is emitted
+that shows which enum constant matched the criteria.
The heuristic can be disabled entirely (:option:`EnableCountingEnumHeuristic`) or
configured to follow your naming convention (:option:`CountingEnumPrefixes/Suffixes`).
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 b55d5a510a1493..96aef122e85ef6 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
@@ -90,3 +90,67 @@ struct CountingEnumCaseInsensitivityTest2 {
} Kind;
union Union4 Data;
};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TagWhereCountingEnumIsAliased {
+ enum {
+ tag_alias_counter1 = 1,
+ tag_alias_counter2 = 2,
+ tag_alias_counter3 = 3,
+ tag_alias_other_count = 3,
+ } Kind;
+ union {
+ char C;
+ short S;
+ int I;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (2)
+struct TagWithCountingEnumButOtherValueIsAliased {
+ enum {
+ tag_alias_other1 = 1,
+ tag_alias_other2 = 1,
+ tag_alias_other3 = 3,
+ tag_alias_other_count = 2,
+ } Kind;
+ union {
+ char C;
+ short S;
+ int I;
+ long L;
+ } Data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
+struct TagWhereCounterIsTheSmallest {
+ enum {
+ tag_large1 = 1000,
+ tag_large2 = 1001,
+ tag_large3 = 1002,
+ tag_large_count = 3,
+ } Kind;
+ union {
+ char C;
+ short S;
+ int I;
+ long L;
+ } Data;
+};
+
+// No warnings expected, only the last enum constant can be a counting enum constant
+struct TagWhereCounterLikeNameIsNotLast {
+ enum {
+ kind_count,
+ kind2,
+ last_kind1,
+ kind3,
+ } Kind;
+ union {
+ char C;
+ short S;
+ int I;
+ long L;
+ } Data;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-counting-enum-prefixes-and-suffixes.cpp
index a0d38a31650abd..c0e33ac6f6f36f 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
@@ -19,8 +19,8 @@ union Union4 {
float *Floats;
};
-// No warning expected, because Kind has multiple counting enum candidates,
-// therefore the enum count is left unchanged.
+// The heuristic only considers the last enum constant
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: tagged union has more data members (4) than tags (3)
struct TaggedUnionPrefixAndSuffixMatch {
enum {
tags1,
>From 96a8e65539b23b587a06cfc4b2b45e40616f7ff0 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, 30 Aug 2024 14:07:30 +0200
Subject: [PATCH 10/11] Replace AST_MATCHER macros and fix coding style in a
few places.
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 46 ++++++++-----------
1 file changed, 20 insertions(+), 26 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index e0283ca25e471d..4279a354738c17 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -36,30 +36,20 @@ static constexpr llvm::StringLiteral UnionMatchBindName = "union";
static constexpr llvm::StringLiteral TagMatchBindName = "tags";
namespace {
-AST_MATCHER(FieldDecl, isUnion) {
- const Type *T = Node.getType().getCanonicalType().getTypePtr();
- assert(T);
- return T->isUnionType();
-}
-
-AST_MATCHER(FieldDecl, isEnum) {
- const Type *T = Node.getType().getCanonicalType().getTypePtr();
- assert(T);
- return T->isEnumeralType();
-}
AST_MATCHER_P2(RecordDecl, fieldCountOfKindIsGT,
ast_matchers::internal::Matcher<FieldDecl>, InnerMatcher,
unsigned, N) {
- unsigned matchCount = 0;
- for (const auto field : Node.fields()) {
- if (InnerMatcher.matches(*field, Finder, Builder)) {
- matchCount += 1;
+ unsigned MatchCount = 0;
+ for (const auto Field : Node.fields()) {
+ if (InnerMatcher.matches(*Field, Finder, Builder)) {
+ MatchCount += 1;
}
}
- return matchCount > N;
+ return MatchCount > N;
}
-}; // namespace
+
+} // namespace
TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(
StringRef Name, ClangTidyContext *Context)
@@ -100,14 +90,20 @@ void TaggedUnionMemberCountCheck::storeOptions(
void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
+ static const auto UnionField = fieldDecl(hasType(qualType(
+ hasCanonicalType(recordType(hasDeclaration(recordDecl(isUnion())))))));
+
+ static const auto EnumField = fieldDecl(hasType(
+ qualType(hasCanonicalType(enumType(hasDeclaration(enumDecl()))))));
+
static const auto hasMultipleUnionsOrEnums = anyOf(
- fieldCountOfKindIsGT(isUnion(), 1), fieldCountOfKindIsGT(isEnum(), 1));
+ fieldCountOfKindIsGT(UnionField, 1), fieldCountOfKindIsGT(EnumField, 1));
Finder->addMatcher(
recordDecl(anyOf(isStruct(), isClass()),
unless(anyOf(isImplicit(), hasMultipleUnionsOrEnums)),
- has(fieldDecl(isUnion()).bind(UnionMatchBindName)),
- has(fieldDecl(isEnum()).bind(TagMatchBindName)))
+ has(UnionField.bind(UnionMatchBindName)),
+ has(EnumField.bind(TagMatchBindName)))
.bind(RootMatchBindName),
this);
}
@@ -134,12 +130,10 @@ TaggedUnionMemberCountCheck::getNumberOfEnumValues(const EnumDecl *ED) {
LastEnumConstant = Enumerator;
}
- if (EnableCountingEnumHeuristic && LastEnumConstant) {
- if (isCountingEnumLikeName(LastEnumConstant->getName())) {
- if (LastEnumConstant->getInitVal() == (EnumValues.size() - 1)) {
- return {EnumValues.size() - 1, LastEnumConstant};
- }
- }
+ if (EnableCountingEnumHeuristic && LastEnumConstant &&
+ isCountingEnumLikeName(LastEnumConstant->getName()) &&
+ (LastEnumConstant->getInitVal() == (EnumValues.size() - 1))) {
+ return {EnumValues.size() - 1, LastEnumConstant};
}
return {EnumValues.size(), nullptr};
>From 2454aac1881dc54f0a1edb3830dcfce714358606 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, 30 Aug 2024 16:09:01 +0200
Subject: [PATCH 11/11] Change starting size of SmallSet from 32 to 16.
---
.../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 4279a354738c17..ae73e908f53e1d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -122,7 +122,7 @@ bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name) const {
std::pair<const std::size_t, const EnumConstantDecl *>
TaggedUnionMemberCountCheck::getNumberOfEnumValues(const EnumDecl *ED) {
- llvm::SmallSet<llvm::APSInt, 32> EnumValues;
+ llvm::SmallSet<llvm::APSInt, 16> EnumValues;
const EnumConstantDecl *LastEnumConstant = nullptr;
for (const auto Enumerator : ED->enumerators()) {
More information about the cfe-commits
mailing list