Skip to content

Commit d9ce2ac

Browse files
tigbrkrishna2803
authored andcommitted
[clang-tidy] Fix bugprone-tagged-union-member-count false-positive (llvm#135831)
This patch implements a fix for the false-positive case described in [issue llvm#134840](llvm#134840) for the [bugprone-tagged-union-member-count](https://clang.llvm.org/extra/clang-tidy/checks/bugprone/tagged-union-member-count.html) clang-tidy check. The example given in the linked issue was the following: ```C #include <pthread.h> typedef enum { MYENUM_ONE, MYENUM_TWO, } myEnumT; typedef struct { pthread_mutex_t mtx; myEnumT my_enum; } myTypeT; ``` The [bugprone-tagged-union-member-count](https://clang.llvm.org/extra/clang-tidy/checks/bugprone/tagged-union-member-count.html) check emits the following a warning for this struct: ``` <source>:8:9: warning: tagged union has more data members (3) than tags (2)! [bugprone-tagged-union-member-count] 8 | typedef struct { | ^ 1 warning generated. ``` The issue is that `pthread_mutex_t` can be a union behind a typedef like the following: ```C typedef union { struct __pthread_mutex_s __data; char __size[__SIZEOF_PTHREAD_MUTEX_T]; long int __align; } pthread_mutex_t; ``` From the checker's point of view therefore `myTypeT` contains a data member with an enum type and another data member with a union type and starts analyzing it like a user-defined tagged union. The proposed solution is that the types from system headers and the std namespace are no longer candidates to be the enum part or the union part of a user-defined tagged union. This filtering for enums and the std namespace may not be strictly necessary in this example, however I added it preemptively out of (perhaps unnecessary) caution. Fixes llvm#134840.
1 parent 09d5155 commit d9ce2ac

File tree

9 files changed

+128
-8
lines changed

9 files changed

+128
-8
lines changed

clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,15 @@ void TaggedUnionMemberCountCheck::storeOptions(
105105

106106
void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
107107

108-
auto UnionField = fieldDecl(hasType(qualType(
109-
hasCanonicalType(recordType(hasDeclaration(recordDecl(isUnion())))))));
108+
auto NotFromSystemHeaderOrStdNamespace =
109+
unless(anyOf(isExpansionInSystemHeader(), isInStdNamespace()));
110110

111-
auto EnumField = fieldDecl(hasType(
112-
qualType(hasCanonicalType(enumType(hasDeclaration(enumDecl()))))));
111+
auto UnionField =
112+
fieldDecl(hasType(qualType(hasCanonicalType(recordType(hasDeclaration(
113+
recordDecl(isUnion(), NotFromSystemHeaderOrStdNamespace)))))));
114+
115+
auto EnumField = fieldDecl(hasType(qualType(hasCanonicalType(
116+
enumType(hasDeclaration(enumDecl(NotFromSystemHeaderOrStdNamespace)))))));
113117

114118
auto HasOneUnionField = fieldCountOfKindIsOne(UnionField, UnionMatchBindName);
115119
auto HasOneEnumField = fieldCountOfKindIsOne(EnumField, TagMatchBindName);

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ Changes in existing checks
142142
<clang-tidy/checks/bugprone/signed-char-misuse>` check by fixing
143143
false positives on C23 enums with the fixed underlying type of signed char.
144144

145+
- Improved :doc:`bugprone-tagged-union-member-count
146+
<clang-tidy/checks/bugprone/tagged-union-member-count>` by fixing a false
147+
positive when enums or unions from system header files or the ``std``
148+
namespace are treated as the tag or the data part of a user-defined
149+
tagged union respectively.
150+
145151
- Improved :doc:`bugprone-unhandled-self-assignment
146152
<clang-tidy/checks/bugprone/unhandled-self-assignment>` check by adding
147153
an additional matcher that generalizes the copy-and-swap idiom pattern

clang-tools-extra/docs/clang-tidy/checks/bugprone/tagged-union-member-count.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ different from the number of data members inside the union.
99
A struct or a class is considered to be a tagged union if it has
1010
exactly one union data member and exactly one enum data member and
1111
any number of other data members that are neither unions or enums.
12+
Furthermore, the types of the union and the enum members must
13+
not come from system header files nor the ``std`` namespace.
1214

1315
Example:
1416

@@ -28,6 +30,25 @@ Example:
2830
} Data;
2931
};
3032
33+
The following example illustrates the exception for unions and enums from
34+
system header files and the ``std`` namespace.
35+
36+
.. code-block:: c++
37+
38+
#include <pthread.h>
39+
40+
struct NotTaggedUnion {
41+
enum MyEnum { MyEnumConstant1, MyEnumConstant2 } En;
42+
pthread_mutex_t Mutex;
43+
};
44+
45+
The ``pthread_mutex_t`` type may be defined as a union behind a ``typedef``,
46+
in which case the check could mistake this type as a user-defined tagged union.
47+
After all, it has exactly one enum data member and exactly one union data member.
48+
To avoid false-positive cases originating from this, unions and enums from
49+
system headers and the ``std`` namespace are ignored when pinpointing the
50+
union part and the enum part of a potential user-defined tagged union.
51+
3152
How enum constants are counted
3253
------------------------------
3354

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#define __SIZEOF_PTHREAD_MUTEX_T 40
2+
3+
namespace std {
4+
typedef union {
5+
struct __pthread_mutex_s {
6+
int __lock;
7+
unsigned int __count;
8+
} __data;
9+
char __size[__SIZEOF_PTHREAD_MUTEX_T];
10+
long int __align;
11+
} pthread_mutex_t;
12+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#define __SIZEOF_PTHREAD_MUTEX_T 40
2+
3+
typedef union {
4+
struct __pthread_mutex_s {
5+
int __lock;
6+
unsigned int __count;
7+
} __data;
8+
char __size[__SIZEOF_PTHREAD_MUTEX_T];
9+
long int __align;
10+
} pthread_mutex_t;

clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
1+
// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t -- -- \
2+
// RUN: -isystem %S/Inputs/tagged-union-member-count/system
23

34
typedef enum Tags3 {
45
tags3_1,
@@ -147,3 +148,16 @@ struct Name {\
147148

148149
// CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
149150
DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
151+
152+
// Unions from system header files should be ignored when
153+
// we are trying to pinpoint the union part in a user-defined tagged union.
154+
#include <pthread.h>
155+
156+
// This should not be analyzed as a user-defined tagged union,
157+
// even though pthread_mutex_t could be a union.
158+
struct SystemTypedefedUnionDataMemberShouldBeIgnored {
159+
pthread_mutex_t Mutex;
160+
enum {
161+
MyEnum
162+
} EnumField;
163+
};

clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t
1+
// RUN: %check_clang_tidy -std=c++98-or-later %s bugprone-tagged-union-member-count %t -- -- \
2+
// RUN: -I%S/Inputs/tagged-union-member-count \
3+
// RUN: -isystem %S/Inputs/tagged-union-member-count/system
24
// Test check with C++ features
35

46
typedef enum Tags3 {
@@ -308,3 +310,26 @@ void DoNotMatchLambdas() {
308310
} u;
309311
auto L = [e, u] () {};
310312
}
313+
314+
// Typedefed unions from system header files should be ignored when
315+
// we are trying to pinpoint the union part in a user-defined tagged union.
316+
#include <pthread.h>
317+
318+
// This should not be analyzed as a user-defined tagged union,
319+
// even though pthread_mutex_t may be declared as a typedefed union.
320+
struct SystemTypedefedUnionDataMemberShouldBeIgnored {
321+
pthread_mutex_t Mutex;
322+
enum {
323+
MyEnum
324+
} EnumField;
325+
};
326+
327+
// Filter when union or enum comes from the std namespace but not a system header
328+
#include "stdnamespace.h"
329+
330+
struct StdNameSpaceUnionDataMemberShouldBeIgnored {
331+
std::pthread_mutex_t Mutex;
332+
enum {
333+
MyEnum
334+
} EnumField;
335+
};

clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
1+
// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t -- -- \
2+
// RUN: -isystem %S/Inputs/tagged-union-member-count/system
23

34
typedef enum Tags3 {
45
tags3_1,
@@ -147,3 +148,16 @@
147148

148149
// CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
149150
DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
151+
152+
// Typedefed unions from system header files should be ignored when
153+
// we are trying to pinpoint the union part in a user-defined tagged union.
154+
#include <pthread.h>
155+
156+
// This should not be analyzed as a user-defined tagged union,
157+
// even though pthread_mutex_t may be declared as a typedefed union.
158+
struct SystemTypedefedUnionDataMemberShouldBeIgnored {
159+
pthread_mutex_t Mutex;
160+
enum {
161+
MyEnum
162+
} EnumField;
163+
};

clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t
1+
// RUN: %check_clang_tidy %s bugprone-tagged-union-member-count %t -- -- \
2+
// RUN: -isystem %S/Inputs/tagged-union-member-count/system
23

34
typedef enum Tags3 {
45
tags3_1,
@@ -307,3 +308,16 @@ void DoNotMatchLambdas() {
307308
} u;
308309
auto L = [e, u] () {};
309310
}
311+
312+
// Typedefed unions from system header files should be ignored when
313+
// we are trying to pinpoint the union part in a user-defined tagged union.
314+
#include <pthread.h>
315+
316+
// This should not be analyzed as a user-defined tagged union,
317+
// even though pthread_mutex_t may be declared as a typedefed union.
318+
struct SystemTypedefedUnionDataMemberShouldBeIgnored {
319+
pthread_mutex_t Mutex;
320+
enum {
321+
MyEnum
322+
} EnumField;
323+
};

0 commit comments

Comments
 (0)