[clang-tools-extra] [clang-tidy] Add new check bugprone-tagged-union-member-count (PR #89925)
via cfe-commits
cfe-commits at lists.llvm.org
Wed May 22 01:12:50 PDT 2024
=?utf-8?b?R8OhYm9yIFTDs3RodsOhcmk=?= <tigbrcode at protonmail.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/89925 at github.com>
https://github.com/tigbr updated https://github.com/llvm/llvm-project/pull/89925
>From 0dff511a0f63480dea527b6e823dcf230755f312 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Sun, 3 Mar 2024 22:30:32 +0100
Subject: [PATCH 1/2] [clang-tidy] Add new check
bugprone-tagged-union-member-count
This patch introduces a new check to find mismatches between the number
of data members in a union and the number enum values present in
variant-like structures.
Variant-like types can look something like this:
```c++
struct variant {
enum {
tag1,
tag2,
} kind;
union {
int i;
char c;
} data;
};
```
The kind data member of the variant is supposed to tell which data
member of the union is valid, however if there are fewer enum values
than union members, then it is likely a mistake.
The opposite is not that obvious, because it might be fine to have more
enum values than union data members, but for the time being I am curious
how many real bugs can be caught if we give a warning regardless.
This patch also contains a heuristic where we try to guess whether the
last enum constant is actually supposed to be a tag value for the
variant or whether it is just holding how many enum constants have been
created.
I am still collecting data on how many bugs this check can catch on open
source software and I will update the pull-request with the results.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../bugprone/TaggedUnionMemberCountCheck.cpp | 125 ++++++++++++
.../bugprone/TaggedUnionMemberCountCheck.h | 31 +++
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../bugprone/tagged-union-member-count.rst | 66 +++++++
.../docs/clang-tidy/checks/list.rst | 1 +
.../bugprone/tagged-union-member-count.cpp | 181 ++++++++++++++++++
8 files changed, 414 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 2931325d8b579..d9e094e11bdf0 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -76,6 +76,7 @@
#include "SuspiciousStringviewDataUsageCheck.h"
#include "SwappedArgumentsCheck.h"
#include "SwitchMissingDefaultCaseCheck.h"
+#include "TaggedUnionMemberCountCheck.h"
#include "TerminatingContinueCheck.h"
#include "ThrowKeywordMissingCheck.h"
#include "TooSmallLoopVariableCheck.h"
@@ -223,6 +224,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-suspicious-stringview-data-usage");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
"bugprone-swapped-arguments");
+ CheckFactories.registerCheck<TaggedUnionMemberCountCheck>(
+ "bugprone-tagged-union-member-count");
CheckFactories.registerCheck<TerminatingContinueCheck>(
"bugprone-terminating-continue");
CheckFactories.registerCheck<ThrowKeywordMissingCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 081ba67efe153..5e7fa08aece02 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -71,6 +71,7 @@ add_clang_library(clangTidyBugproneModule
SuspiciousSemicolonCheck.cpp
SuspiciousStringCompareCheck.cpp
SwappedArgumentsCheck.cpp
+ TaggedUnionMemberCountCheck.cpp
TerminatingContinueCheck.cpp
ThrowKeywordMissingCheck.cpp
TooSmallLoopVariableCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
new file mode 100644
index 0000000000000..6a7b5a92902c8
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -0,0 +1,125 @@
+//===--- TaggedUnionMemberCountCheck.cpp - clang-tidy ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TaggedUnionMemberCountCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+#include "clang/AST/PrettyPrinter.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Casting.h"
+#include <limits>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ recordDecl(
+ allOf(isStruct(),
+ has(fieldDecl(hasType(recordDecl(isUnion()).bind("union")))),
+ has(fieldDecl(hasType(enumDecl().bind("tags"))))))
+ .bind("root"),
+ this);
+}
+
+static bool hasMultipleUnionsOrEnums(const RecordDecl *rec) {
+ int tags = 0;
+ int unions = 0;
+ for (const FieldDecl *r : rec->fields()) {
+ TypeSourceInfo *info = r->getTypeSourceInfo();
+ QualType qualtype = info->getType();
+ const Type *type = qualtype.getTypePtr();
+ if (type->isUnionType())
+ unions += 1;
+ else if (type->isEnumeralType())
+ tags += 1;
+ if (tags > 1 || unions > 1)
+ return true;
+ }
+ return false;
+}
+
+static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
+ int64_t maxTagValue = std::numeric_limits<int64_t>::min();
+ int64_t minTagValue = std::numeric_limits<int64_t>::max();
+
+ // Heuristic for counter enum constants.
+ //
+ // enum tag_with_counter {
+ // tag1,
+ // tag2,
+ // tag_count, <-- Searching for these enum constants
+ // };
+ //
+ // The 'ce' prefix is used to abbreviate counterEnum.
+ // The final tag count is decreased by 1 if and only if:
+ // 1. The number of counting enum constants = 1,
+ int ceCount = 0;
+ // 2. The counting enum constant is the last enum constant that is defined,
+ int ceFirstIndex = 0;
+ // 3. The value of the counting enum constant is the largest out of every enum constant.
+ int64_t ceValue = 0;
+
+ int64_t enumConstantsCount = 0;
+ for (auto En : llvm::enumerate(ed->enumerators())) {
+ enumConstantsCount += 1;
+
+ int64_t enumValue = En.value()->getInitVal().getExtValue();
+ StringRef enumName = En.value()->getName();
+
+ if (enumValue > maxTagValue)
+ maxTagValue = enumValue;
+ if (enumValue < minTagValue)
+ minTagValue = enumValue;
+
+ if (enumName.ends_with_insensitive("count")) {
+ if (ceCount == 0) {
+ ceFirstIndex = En.index();
+ }
+ ceValue = enumValue;
+ ceCount += 1;
+ }
+ }
+
+ int64_t validValuesCount = maxTagValue - minTagValue + 1;
+ if (ceCount == 1 &&
+ ceFirstIndex == enumConstantsCount - 1 &&
+ ceValue == maxTagValue) {
+ validValuesCount -= 1;
+ }
+ return validValuesCount;
+}
+
+void TaggedUnionMemberCountCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *root = Result.Nodes.getNodeAs<RecordDecl>("root");
+ const auto *unionMatch = Result.Nodes.getNodeAs<RecordDecl>("union");
+ const auto *tagMatch = Result.Nodes.getNodeAs<EnumDecl>("tags");
+
+ if (hasMultipleUnionsOrEnums(root))
+ return;
+
+ int64_t unionMemberCount = llvm::range_size(unionMatch->fields());
+ int64_t tagCount = getNumberOfValidEnumValues(tagMatch);
+
+ // FIXME: Maybe a emit a note when a counter enum constant was found.
+ if (unionMemberCount > tagCount) {
+ diag(root->getLocation(), "Tagged union has more data members than tags! "
+ "Data members: %0 Tags: %1")
+ << unionMemberCount << tagCount;
+ } else if (unionMemberCount < tagCount) {
+ diag(root->getLocation(), "Tagged union has fewer data members than tags! "
+ "Data members: %0 Tags: %1")
+ << unionMemberCount << tagCount;
+ }
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
new file mode 100644
index 0000000000000..bde42da23ff38
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -0,0 +1,31 @@
+//===--- TaggedUnionMemberCountCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+// Gives warnings for tagged unions, where the number of tags is
+// different from the number of data members inside the union.
+//
+// For the user-facing documentation see:
+// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/tagged-union-member-count.html
+class TaggedUnionMemberCountCheck : public ClangTidyCheck {
+public:
+ TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 78b09d23d4427..9f9069a8d0892 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -129,6 +129,12 @@ New checks
Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.
+- New :doc:`bugprone-tagged-union-member-count
+ <clang-tidy/checks/bugprone/tagged-union-member-count>` check.
+
+ Warns about suspicious looking tagged unions where the number of enum
+ constants and the number of union data members are not equal.
+
New check aliases
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
new file mode 100644
index 0000000000000..7eac93c6c9602
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
@@ -0,0 +1,66 @@
+.. title:: clang-tidy - bugprone-tagged-union-member-count
+
+bugprone-tagged-union-member-count
+==================================
+
+Gives warnings for tagged unions, where the number of tags is
+different from the number of data members inside the union.
+
+A struct or a class is considered to be a tagged union if it has
+exactly one union data member and exactly one enum data member and
+any number of other data members that are neither unions or enums.
+
+Example
+-------
+
+.. code-block:: c++
+
+ enum tags2 {
+ tag1,
+ tag2,
+ };
+
+ struct taggedunion { // warning: Tagged union has more data members than tags! Data members: 3 Tags: 2 [bugprone-tagged-union-member-count]
+ enum tags2 kind;
+ union {
+ int i;
+ float f;
+ char *str;
+ } data;
+ };
+
+ enum tags4 {
+ tag1,
+ tag2,
+ tag3,
+ tag4,
+ };
+
+ struct taggedunion { // warning: Tagged union has more fewer members than tags! Data members: 3 Tags: 4 [bugprone-tagged-union-member-count]
+ enum tags4 kind;
+ union {
+ int i;
+ float f;
+ char *str;
+ } data;
+ };
+
+Counting enum constant heuristic
+--------------------------------
+
+Some programmers use enums in such a way, where the value of the last enum
+constant is used to keep track of how many enum constants have been declared.
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1, // is 0
+ tag2, // is 1
+ tag3, // is 2
+ tags_count, // is 3
+ };
+
+The checker detects this usage pattern heuristically and does not include
+the counter enum constant in the final tag count, since the counter is not
+meant to indicate the valid variant member.
+
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 79e81dd174e4f..3058bee1357fc 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -142,6 +142,7 @@ Clang-Tidy Checks
:doc:`bugprone-suspicious-stringview-data-usage <bugprone/suspicious-stringview-data-usage>`,
:doc:`bugprone-swapped-arguments <bugprone/swapped-arguments>`, "Yes"
:doc:`bugprone-switch-missing-default-case <bugprone/switch-missing-default-case>`,
+ :doc:`bugprone-tagged-union-member-count <bugprone/tagged-union-member-count>`, "Yes"
:doc:`bugprone-terminating-continue <bugprone/terminating-continue>`, "Yes"
:doc:`bugprone-throw-keyword-missing <bugprone/throw-keyword-missing>`,
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
new file mode 100644
index 0000000000000..91165f1bd64e0
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
@@ -0,0 +1,181 @@
+// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
+
+enum tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+};
+
+enum tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+};
+
+enum tags5 {
+ tags5_1,
+ tags5_2,
+ tags5_3,
+ tags5_4,
+ tags5_5,
+};
+
+union union3 {
+ short *shorts;
+ int *ints;
+ float *floats;
+};
+
+union union4 {
+ short *shorts;
+ double *doubles;
+ int *ints;
+ float *floats;
+};
+
+// It is not obvious which enum is the tag for the union.
+struct taggedunion2 { // No warnings expected.
+ enum tags3 tagA;
+ enum tags4 tagB;
+ union union4 data;
+};
+
+// It is not obvious which union does the tag belong to.
+struct taggedunion4 { // No warnings expected.
+ enum tags3 tag;
+ union union3 dataB;
+ union union3 dataA;
+};
+
+struct taggedunion1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union union4 data;
+};
+
+struct taggedunion5 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union {
+ int *ints;
+ char characters[13];
+ struct {
+ double re;
+ double im;
+ } complex;
+ long l;
+ } data;
+};
+
+struct taggedunion7 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } tag;
+ union union4 data;
+};
+
+struct taggedunion8 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum {
+ tag1,
+ tag2,
+ tag3,
+ } tag;
+ union {
+ int *ints;
+ char characters[13];
+ struct {
+ double re;
+ double im;
+ } complex;
+ long l;
+ } data;
+};
+
+struct nested1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union {
+ char c;
+ short s;
+ int i;
+ struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has fewer data members than tags! Data members: 4 Tags: 5 [bugprone-tagged-union-member-count]
+ enum tags5 tag;
+ union union4 data;
+ } inner;
+ } data;
+};
+
+struct nested2 {
+ enum tags3 tag;
+ union {
+ float f;
+ int i;
+ struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union union4 data;
+ } inner;
+ } data;
+};
+
+struct nested3 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 2 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union {
+ float f;
+ int i;
+ struct innerdecl { // CHECK-MESSAGES: :[[@LINE]]:10: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tags3 tag;
+ union union4 data;
+ };
+ } data;
+};
+
+enum tag_with_counter_lowercase {
+ node_type_loop,
+ node_type_branch,
+ node_type_function,
+ node_type_count,
+};
+
+enum tag_with_counter_uppercase {
+ NODE_TYPE_LOOP,
+ NODE_TYPE_BRANCH,
+ NODE_TYPE_FUNCTION,
+ NODE_TYPE_COUNT,
+};
+
+struct taggedunion10 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tag_with_counter_lowercase tag;
+ union union4 data;
+};
+
+struct taggedunion11 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ enum tag_with_counter_uppercase tag;
+ union union4 data;
+};
+
+// Without the counter enum constant the number of tags
+// and the number data members are equal.
+struct taggedunion12 { // No warnings expected.
+ enum tag_with_counter_uppercase tag;
+ union union3 data;
+};
+
+// Technically this means that every enum value is defined from 0-256 and therefore a warning is given.
+enum mycolor {
+ mycolor_black = 0x00,
+ mycolor_gray = 0xcc,
+ mycolor_white = 0xff,
+};
+
+struct taggedunion9 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 3 Tags: 256 [bugprone-tagged-union-member-count]
+ enum mycolor tag;
+ union {
+ int a;
+ float b;
+ struct {
+ double re;
+ double im;
+ } complex;
+ } data;
+};
+
>From a8eadb10876dded0deb3fbbbb6ad5b13ce9861fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20T=C3=B3thv=C3=A1ri?= <tigbrcode at protonmail.com>
Date: Tue, 21 May 2024 13:51:28 +0200
Subject: [PATCH 2/2] pull request fixes
---
.../bugprone/TaggedUnionMemberCountCheck.cpp | 80 +++++---
.../bugprone/TaggedUnionMemberCountCheck.h | 10 +-
clang-tools-extra/docs/ReleaseNotes.rst | 12 +-
.../bugprone/tagged-union-member-count.rst | 190 ++++++++++++++++--
.../docs/clang-tidy/checks/list.rst | 2 +-
...er-count-enumcounterheuristicisenabled.cpp | 38 ++++
...d-union-member-count-enumcountersuffix.cpp | 47 +++++
.../tagged-union-member-count-strictmode.cpp | 150 ++++++++++++++
.../bugprone/tagged-union-member-count.cpp | 176 ++++++++++------
9 files changed, 589 insertions(+), 116 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index 6a7b5a92902c8..786198b011f30 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -10,44 +10,53 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
-
#include "clang/AST/PrettyPrinter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include <limits>
+#include <iostream>
using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
+TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ EnumCounterHeuristicIsEnabled(Options.get("EnumCounterHeuristicIsEnabled", true)),
+ EnumCounterSuffix(Options.get("EnumCounterSuffix", "count")),
+ StrictMode(Options.get("StrictMode", true)) { }
+
+void TaggedUnionMemberCountCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "StrictMode", StrictMode);
+ Options.store(Opts, "EnumCounterHeuristicIsEnabled", EnumCounterHeuristicIsEnabled);
+ Options.store(Opts, "EnumCounterSuffix", EnumCounterSuffix);
+}
+
void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
- Finder->addMatcher(
+Finder->addMatcher(
recordDecl(
allOf(isStruct(),
- has(fieldDecl(hasType(recordDecl(isUnion()).bind("union")))),
- has(fieldDecl(hasType(enumDecl().bind("tags"))))))
+ has(fieldDecl(hasType(qualType(hasCanonicalType(recordType())))).bind("union")),
+ has(fieldDecl(hasType(qualType(hasCanonicalType(enumType())))).bind("tags"))))
.bind("root"),
this);
}
+static bool isUnion(const FieldDecl *R) {
+ return R->getType().getCanonicalType().getTypePtr()->isUnionType();
+}
+
+static bool isEnum(const FieldDecl *R) {
+ return R->getType().getCanonicalType().getTypePtr()->isEnumeralType();
+}
+
static bool hasMultipleUnionsOrEnums(const RecordDecl *rec) {
- int tags = 0;
- int unions = 0;
- for (const FieldDecl *r : rec->fields()) {
- TypeSourceInfo *info = r->getTypeSourceInfo();
- QualType qualtype = info->getType();
- const Type *type = qualtype.getTypePtr();
- if (type->isUnionType())
- unions += 1;
- else if (type->isEnumeralType())
- tags += 1;
- if (tags > 1 || unions > 1)
- return true;
- }
- return false;
+ return llvm::count_if(rec->fields(), isUnion) > 1 ||
+ llvm::count_if(rec->fields(), isEnum) > 1;
}
-static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
+static size_t getNumberOfValidEnumValues(const EnumDecl *ed, bool EnumCounterHeuristicIsEnabled, StringRef EnumCounterSuffix) {
int64_t maxTagValue = std::numeric_limits<int64_t>::min();
int64_t minTagValue = std::numeric_limits<int64_t>::max();
@@ -80,7 +89,7 @@ static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
if (enumValue < minTagValue)
minTagValue = enumValue;
- if (enumName.ends_with_insensitive("count")) {
+ if (enumName.ends_with_insensitive(EnumCounterSuffix)) {
if (ceCount == 0) {
ceFirstIndex = En.index();
}
@@ -90,7 +99,8 @@ static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
}
int64_t validValuesCount = maxTagValue - minTagValue + 1;
- if (ceCount == 1 &&
+ if (EnumCounterHeuristicIsEnabled &&
+ ceCount == 1 &&
ceFirstIndex == enumConstantsCount - 1 &&
ceValue == maxTagValue) {
validValuesCount -= 1;
@@ -98,26 +108,36 @@ static int64_t getNumberOfValidEnumValues(const EnumDecl *ed) {
return validValuesCount;
}
+// Feladatok:
+// - typedef tesztelés
+// - template tesztelés
+// - névtelen union tesztelés
+// - "count" paraméterezése
void TaggedUnionMemberCountCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *root = Result.Nodes.getNodeAs<RecordDecl>("root");
- const auto *unionMatch = Result.Nodes.getNodeAs<RecordDecl>("union");
- const auto *tagMatch = Result.Nodes.getNodeAs<EnumDecl>("tags");
+ const auto *unionField = Result.Nodes.getNodeAs<FieldDecl>("union");
+ const auto *tagField = Result.Nodes.getNodeAs<FieldDecl>("tags");
+
+ // The matcher can only narrow down the type to recordType()
+ if (!isUnion(unionField))
+ return;
if (hasMultipleUnionsOrEnums(root))
return;
- int64_t unionMemberCount = llvm::range_size(unionMatch->fields());
- int64_t tagCount = getNumberOfValidEnumValues(tagMatch);
+ const auto *unionDef = unionField->getType().getCanonicalType().getTypePtr()->getAsRecordDecl();
+ const auto *enumDef = static_cast<EnumDecl*>(tagField->getType().getCanonicalType().getTypePtr()->getAsTagDecl());
+
+ size_t unionMemberCount = llvm::range_size(unionDef->fields());
+ size_t tagCount = getNumberOfValidEnumValues(enumDef, EnumCounterHeuristicIsEnabled, EnumCounterSuffix);
// FIXME: Maybe a emit a note when a counter enum constant was found.
if (unionMemberCount > tagCount) {
- diag(root->getLocation(), "Tagged union has more data members than tags! "
- "Data members: %0 Tags: %1")
+ diag(root->getLocation(), "Tagged union has more data members (%0) than tags (%1)!")
<< unionMemberCount << tagCount;
- } else if (unionMemberCount < tagCount) {
- diag(root->getLocation(), "Tagged union has fewer data members than tags! "
- "Data members: %0 Tags: %1")
+ } else if (StrictMode && unionMemberCount < tagCount) {
+ diag(root->getLocation(), "Tagged union has fewer data members (%0) than tags (%1)!")
<< unionMemberCount << tagCount;
}
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
index bde42da23ff38..2c6ec3b05358a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TAGGEDUNIONMEMBERCOUNTCHECK_H
#include "../ClangTidyCheck.h"
+#include "llvm/ADT/StringRef.h"
namespace clang::tidy::bugprone {
@@ -20,10 +21,15 @@ namespace clang::tidy::bugprone {
// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/tagged-union-member-count.html
class TaggedUnionMemberCountCheck : public ClangTidyCheck {
public:
- TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context) {}
+ TaggedUnionMemberCountCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool EnumCounterHeuristicIsEnabled;
+ StringRef EnumCounterSuffix;
+ bool StrictMode;
};
} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9f9069a8d0892..b287b99da5a01 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -117,6 +117,12 @@ New checks
to reading out-of-bounds data due to inadequate or incorrect string null
termination.
+- New :doc:`bugprone-tagged-union-member-count
+ <clang-tidy/checks/bugprone/tagged-union-member-count>` check.
+
+ Gives warnings for tagged unions, where the number of tags is
+ different from the number of data members inside the union.
+
- New :doc:`modernize-use-designated-initializers
<clang-tidy/checks/modernize/use-designated-initializers>` check.
@@ -129,12 +135,6 @@ New checks
Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.
-- New :doc:`bugprone-tagged-union-member-count
- <clang-tidy/checks/bugprone/tagged-union-member-count>` check.
-
- Warns about suspicious looking tagged unions where the number of enum
- constants and the number of union data members are not equal.
-
New check aliases
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
index 7eac93c6c9602..cd7de571041e4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst
@@ -15,29 +15,13 @@ Example
.. code-block:: c++
- enum tags2 {
+ enum tags {
tag1,
tag2,
};
- struct taggedunion { // warning: Tagged union has more data members than tags! Data members: 3 Tags: 2 [bugprone-tagged-union-member-count]
- enum tags2 kind;
- union {
- int i;
- float f;
- char *str;
- } data;
- };
-
- enum tags4 {
- tag1,
- tag2,
- tag3,
- tag4,
- };
-
- struct taggedunion { // warning: Tagged union has more fewer members than tags! Data members: 3 Tags: 4 [bugprone-tagged-union-member-count]
- enum tags4 kind;
+ struct taggedUnion { // warning: Tagged union has more data members (3) than tags (2)! [bugprone-tagged-union-member-count]
+ enum tags kind;
union {
int i;
float f;
@@ -60,7 +44,173 @@ constant is used to keep track of how many enum constants have been declared.
tags_count, // is 3
};
-The checker detects this usage pattern heuristically and does not include
+The check detects this usage pattern heuristically and does not include
the counter enum constant in the final tag count, since the counter is not
meant to indicate the valid variant member.
+Options
+-------
+
+.. option:: EnumCounterHeuristicIsEnabled
+
+ This option enables or disables the counting enum heuristic.
+ To find possible counting enum constants this option uses the value of
+ the string :option:`EnumCounterSuffix`.
+
+ This option is enabled by default.
+
+Example of :option:`EnumCounterHeuristicIsEnabled`:
+
+When :option:`EnumCounterHeuristicIsEnabled` is false:
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ struct taggedUnion {
+ tags_with_counter tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+When :option:`EnumCounterHeuristicIsEnabled` is true:
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ struct taggedUnion { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ tags_with_counter tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+.. option:: EnumCounterSuffix
+
+ When defined, the check will use the given string to search for counting
+ enum constants. This option does not alter the check's behavior when
+ :option:`EnumCounterHeuristicIsEnabled` is disabled.
+
+ The default value is "count".
+
+Example of :option:`EnumCounterSuffix`:
+
+When :option:`EnumCounterHeuristicIsEnabled` is true and
+:option:`EnumCounterSuffix` is "size":
+
+.. code-block:: c++
+
+ enum tags_with_counter_count {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ enum tags_with_counter_size {
+ tag4,
+ tag5,
+ tag6,
+ tags_size,
+ };
+
+ struct taggedUnion1 {
+ tags_with_counter_count tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+ struct taggedUnion2 { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ tags_with_counter_size tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+When :option:`EnumCounterSuffix` is true:
+
+.. code-block:: c++
+
+ enum tags_with_counter {
+ tag1,
+ tag2,
+ tag3,
+ tags_count,
+ };
+
+ struct taggedUnion { // warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ tags_with_counter tag;
+ union data {
+ int a;
+ int b;
+ char *str;
+ float f;
+ };
+ };
+
+.. option:: StrictMode
+
+ When enabled, the check will also give a warning, when the number of tags
+ is greater than the number of union data members.
+
+ This option is not enabled by default.
+
+Example of :option:`StrictMode`:
+
+When :option:`StrictMode` is false:
+
+.. code-block:: c++
+
+ struct taggedUnion {
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ } tags;
+ union {
+ int i;
+ float f;
+ };
+ };
+
+When :option:`StrictMode` is true:
+
+.. code-block:: c++
+
+ struct taggedunion1 { // warning: Tagged union has fewer data members (2) than tags (3)! [bugprone-tagged-union-member-count]
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ } tags;
+ union {
+ int i;
+ float f;
+ };
+ };
+
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 3058bee1357fc..5b2fb2bfbfc30 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -142,7 +142,7 @@ Clang-Tidy Checks
:doc:`bugprone-suspicious-stringview-data-usage <bugprone/suspicious-stringview-data-usage>`,
:doc:`bugprone-swapped-arguments <bugprone/swapped-arguments>`, "Yes"
:doc:`bugprone-switch-missing-default-case <bugprone/switch-missing-default-case>`,
- :doc:`bugprone-tagged-union-member-count <bugprone/tagged-union-member-count>`, "Yes"
+ :doc:`bugprone-tagged-union-member-count <bugprone/tagged-union-member-count>`,
:doc:`bugprone-terminating-continue <bugprone/terminating-continue>`, "Yes"
:doc:`bugprone-throw-keyword-missing <bugprone/throw-keyword-missing>`,
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
new file mode 100644
index 0000000000000..69b44c10b7c31
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcounterheuristicisenabled.cpp
@@ -0,0 +1,38 @@
+// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \
+// RUN: -config='{CheckOptions: { \
+// RUN: bugprone-tagged-union-member-count.StrictMode: 1, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "count", \
+// RUN: }}' --
+
+// Without the heuristic the tags and the data members match
+struct taggedUnion1 { // No warnings expected
+ enum {
+ tags1,
+ tags2,
+ tags3,
+ tags_count,
+ } tags;
+ union {
+ int a;
+ int b;
+ int c;
+ int d;
+ } data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct taggedUnion2 {
+ enum {
+ tags11,
+ tags22,
+ tags33,
+ tags_count,
+ } tags;
+ union {
+ int a;
+ int b;
+ int c;
+ } data;
+};
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
new file mode 100644
index 0000000000000..cddbb003a01f9
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-enumcountersuffix.cpp
@@ -0,0 +1,47 @@
+// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \
+// RUN: -config='{CheckOptions: { \
+// RUN: bugprone-tagged-union-member-count.StrictMode: 0, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 1, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "size", \
+// RUN: }}' --
+
+typedef union union3 {
+ short *shorts;
+ int *ints;
+ float *floats;
+} union3;
+
+typedef union union4 {
+ short *shorts;
+ double *doubles;
+ int *ints;
+ float *floats;
+} union4;
+
+enum tag_with_counter_count {
+ twc1,
+ twc2,
+ twc3,
+ twc_count,
+};
+
+enum tag_with_counter_size {
+ twc11,
+ twc22,
+ twc33,
+ twc_size,
+};
+
+// The heuristic is configured with the "size" suffix so
+// twc_count will not be considered a counting enum constant
+struct taggedUnion1 { // No warning expected
+ enum tag_with_counter_count tag;
+ union union4 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedUnion2 {
+ enum tag_with_counter_size tag;
+ union union4 data;
+};
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
new file mode 100644
index 0000000000000..7d6c5f385c8e1
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count-strictmode.cpp
@@ -0,0 +1,150 @@
+// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t \
+// RUN: -config='{CheckOptions: { \
+// RUN: bugprone-tagged-union-member-count.StrictMode: 1, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterHeuristicIsEnabled: 0, \
+// RUN: bugprone-tagged-union-member-count.EnumCounterSuffix: "count", \
+// RUN: }}' --
+
+typedef enum tags3 {
+ tags3_1,
+ tags3_2,
+ tags3_3,
+} tags3;
+
+typedef enum tags4 {
+ tags4_1,
+ tags4_2,
+ tags4_3,
+ tags4_4,
+} tags4;
+
+typedef enum tags5 {
+ tags5_1,
+ tags5_2,
+ tags5_3,
+ tags5_4,
+ tags5_5,
+} tags5;
+
+enum class classtags3 {
+ classtags3_1,
+ classtags3_2,
+ classtags3_3,
+};
+
+enum class classtags4 {
+ classtags4_1,
+ classtags4_2,
+ classtags4_3,
+ classtags4_4,
+};
+
+enum class classtags5 {
+ classtags5_1,
+ classtags5_2,
+ classtags5_3,
+ classtags5_4,
+ classtags5_5,
+};
+
+enum class typedtags3 : unsigned int {
+ typedtags3_1,
+ typedtags3_2,
+ typedtags3_3,
+};
+
+enum class typedtags4 : long {
+ typedtags4_1,
+ typedtags4_2,
+ typedtags4_3,
+ typedtags4_4,
+};
+
+enum class typedtags5 {
+ typedtags5_1,
+ typedtags5_2,
+ typedtags5_3,
+ typedtags5_4,
+ typedtags5_5,
+};
+
+typedef union union3 {
+ short *shorts;
+ int *ints;
+ float *floats;
+} union3;
+
+typedef union union4 {
+ short *shorts;
+ double *doubles;
+ int *ints;
+ float *floats;
+} union4;
+
+// Technically this means that every enum value is defined from 0-256 and therefore a warning is given.
+enum mycolor {
+ mycolor_black = 0x00,
+ mycolor_gray = 0xcc,
+ mycolor_white = 0xff,
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (256)! [bugprone-tagged-union-member-count]
+struct taggedunion9 {
+ enum mycolor tag;
+ union {
+ int a;
+ float b;
+ struct {
+ double re;
+ double im;
+ } complex;
+ } data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withanonymous {
+ enum tags4 tag;
+ union {
+ int a;
+ float b;
+ char *c;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withTypedef1 {
+ tags4 tag;
+ union3 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withEnumClass1 {
+ enum classtags4 tag;
+ union3 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (3) than tags (4)! [bugprone-tagged-union-member-count]
+struct withTypedEnum2 {
+ typedtags4 tag;
+ union3 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has fewer data members (2) than tags (3)! [bugprone-tagged-union-member-count]
+struct anonymous2 {
+ tags3 tag;
+ union {
+ int a;
+ float f;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has fewer data members (3) than tags (5)! [bugprone-tagged-union-member-count]
+template <typename Union, typename Tag>
+struct templated {
+ Tag tag;
+ Union data;
+};
+
+templated<union3, tags3> t1; // No warning expected
+templated<union3, tags5> t2;
+
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
index 91165f1bd64e0..05ae457f57410 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp
@@ -1,38 +1,81 @@
-// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
+// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t
+// Test cases for the default configuration
-enum tags3 {
+typedef enum tags3 {
tags3_1,
tags3_2,
tags3_3,
-};
+} tags3;
-enum tags4 {
+typedef enum tags4 {
tags4_1,
tags4_2,
tags4_3,
tags4_4,
-};
+} tags4;
-enum tags5 {
+typedef enum tags5 {
tags5_1,
tags5_2,
tags5_3,
tags5_4,
tags5_5,
+} tags5;
+
+enum class classtags3 {
+ classtags3_1,
+ classtags3_2,
+ classtags3_3,
+};
+
+enum class classtags4 {
+ classtags4_1,
+ classtags4_2,
+ classtags4_3,
+ classtags4_4,
+};
+
+enum class classtags5 {
+ classtags5_1,
+ classtags5_2,
+ classtags5_3,
+ classtags5_4,
+ classtags5_5,
+};
+
+enum class typedtags3 : unsigned int {
+ typedtags3_1,
+ typedtags3_2,
+ typedtags3_3,
+};
+
+enum class typedtags4 : long {
+ typedtags4_1,
+ typedtags4_2,
+ typedtags4_3,
+ typedtags4_4,
+};
+
+enum class typedtags5 {
+ typedtags5_1,
+ typedtags5_2,
+ typedtags5_3,
+ typedtags5_4,
+ typedtags5_5,
};
-union union3 {
+typedef union union3 {
short *shorts;
int *ints;
float *floats;
-};
+} union3;
-union union4 {
+typedef union union4 {
short *shorts;
double *doubles;
int *ints;
float *floats;
-};
+} union4;
// It is not obvious which enum is the tag for the union.
struct taggedunion2 { // No warnings expected.
@@ -48,12 +91,29 @@ struct taggedunion4 { // No warnings expected.
union union3 dataA;
};
-struct taggedunion1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// It is not obvious which union does the tag belong to.
+struct taggedunion6 { // No warnings expected.
+ enum tags3 tag;
+ union {
+ int i1;
+ int i2;
+ int i3;
+ };
+ union {
+ float f1;
+ float f2;
+ float f3;
+ };
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion1 {
enum tags3 tag;
union union4 data;
};
-struct taggedunion5 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion5 {
enum tags3 tag;
union {
int *ints;
@@ -66,7 +126,8 @@ struct taggedunion5 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has
} data;
};
-struct taggedunion7 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion7 {
enum {
tag1,
tag2,
@@ -75,7 +136,8 @@ struct taggedunion7 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has
union union4 data;
};
-struct taggedunion8 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion8 {
enum {
tag1,
tag2,
@@ -92,40 +154,18 @@ struct taggedunion8 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has
} data;
};
-struct nested1 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
- enum tags3 tag;
- union {
- char c;
- short s;
- int i;
- struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has fewer data members than tags! Data members: 4 Tags: 5 [bugprone-tagged-union-member-count]
- enum tags5 tag;
- union union4 data;
- } inner;
- } data;
-};
-
-struct nested2 {
- enum tags3 tag;
- union {
- float f;
- int i;
- struct { // CHECK-MESSAGES: :[[@LINE]]:3: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
- enum tags3 tag;
- union union4 data;
- } inner;
- } data;
-};
-
-struct nested3 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 2 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct nested4 {
enum tags3 tag;
union {
float f;
int i;
- struct innerdecl { // CHECK-MESSAGES: :[[@LINE]]:10: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+ long l;
+ // CHECK-MESSAGES: :[[@LINE+1]]:10: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+ struct innerdecl {
enum tags3 tag;
union union4 data;
- };
+ } inner;
} data;
};
@@ -143,12 +183,14 @@ enum tag_with_counter_uppercase {
NODE_TYPE_COUNT,
};
-struct taggedunion10 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion10 {
enum tag_with_counter_lowercase tag;
union union4 data;
};
-struct taggedunion11 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has more data members than tags! Data members: 4 Tags: 3 [bugprone-tagged-union-member-count]
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct taggedunion11 {
enum tag_with_counter_uppercase tag;
union union4 data;
};
@@ -160,22 +202,42 @@ struct taggedunion12 { // No warnings expected.
union union3 data;
};
-// Technically this means that every enum value is defined from 0-256 and therefore a warning is given.
-enum mycolor {
- mycolor_black = 0x00,
- mycolor_gray = 0xcc,
- mycolor_white = 0xff,
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct withTypedef2 {
+ tags3 tag;
+ union4 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct withEnumClass2 {
+ enum classtags3 tag;
+ union4 data;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct withTypedEnum1 {
+ typedtags3 tag;
+ union4 data;
};
-struct taggedunion9 { // CHECK-MESSAGES: :[[@LINE]]:8: warning: Tagged union has fewer data members than tags! Data members: 3 Tags: 256 [bugprone-tagged-union-member-count]
- enum mycolor tag;
+// CHECK-MESSAGES: :[[@LINE+1]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+struct anonymous1 {
+ tags3 tag;
union {
int a;
- float b;
- struct {
- double re;
- double im;
- } complex;
- } data;
+ int b;
+ int c;
+ int d;
+ };
};
+// CHECK-MESSAGES: :[[@LINE+2]]:8: warning: Tagged union has more data members (4) than tags (3)! [bugprone-tagged-union-member-count]
+template <typename Union, typename Tag>
+struct templated {
+ Tag tag;
+ Union data;
+};
+
+templated<union3, tags3> t1; // No warning expected
+templated<union4, tags3> t3;
+
More information about the cfe-commits
mailing list