Skip to content

[clang-tidy] Fix bugprone-tagged-union-member-count false-positive #135831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

tigbr
Copy link
Contributor

@tigbr tigbr commented Apr 15, 2025

This patch implements a fix for the false-positive case described in issue #134840 for the bugprone-tagged-union-member-count clang-tidy check.

The example given in the linked issue was the following:

#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 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:

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.

@llvmbot
Copy link
Member

llvmbot commented Apr 15, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: None (tigbr)

Changes

This patch implements a fix for the false-positive case described in issue #134840 for the bugprone-tagged-union-member-count clang-tidy check.

The example given in the linked issue was the following:

#include &lt;pthread.h&gt;

typedef enum {
  MYENUM_ONE,
  MYENUM_TWO,
} myEnumT;

typedef struct {
  pthread_mutex_t mtx;       
  myEnumT         my_enum;
} myTypeT;

The bugprone-tagged-union-member-count check emits the following a warning for this struct:

&lt;source&gt;: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:

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.


Full diff: https://github.com/llvm/llvm-project/pull/135831.diff

5 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp (+8-4)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c (+13)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp (+13)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m (+13)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm (+13)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index db99ef3786e5f..b91da7db39463 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -106,11 +106,15 @@ void TaggedUnionMemberCountCheck::storeOptions(
 
 void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
 
-  auto UnionField = fieldDecl(hasType(qualType(
-      hasCanonicalType(recordType(hasDeclaration(recordDecl(isUnion())))))));
+  auto NotFromSystemHeaderOrStdNamespace =
+      unless(anyOf(isExpansionInSystemHeader(), isInStdNamespace()));
 
-  auto EnumField = fieldDecl(hasType(
-      qualType(hasCanonicalType(enumType(hasDeclaration(enumDecl()))))));
+  auto UnionField =
+      fieldDecl(hasType(qualType(hasCanonicalType(recordType(hasDeclaration(
+          recordDecl(isUnion(), NotFromSystemHeaderOrStdNamespace)))))));
+
+  auto EnumField = fieldDecl(hasType(qualType(hasCanonicalType(
+      enumType(hasDeclaration(enumDecl(NotFromSystemHeaderOrStdNamespace)))))));
 
   auto hasOneUnionField = fieldCountOfKindIsOne(UnionField, UnionMatchBindName);
   auto hasOneEnumField = fieldCountOfKindIsOne(EnumField, TagMatchBindName);
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 60c93c553baca..96255c7fdd4fe 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
@@ -147,3 +147,16 @@ struct Name {\
 
 // CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
 DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};
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 25827e8c8de0c..f21c23b87ae44 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
@@ -308,3 +308,16 @@ void DoNotMatchLambdas() {
 	} u;
     auto L = [e, u] () {};
 }
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};
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 60c93c553baca..96255c7fdd4fe 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
@@ -147,3 +147,16 @@
 
 // CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
 DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};
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 8b308555281c5..b169b5cd480b5 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
@@ -307,3 +307,16 @@ void DoNotMatchLambdas() {
 	} u;
     auto L = [e, u] () {};
 }
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};

@llvmbot
Copy link
Member

llvmbot commented Apr 15, 2025

@llvm/pr-subscribers-clang-tidy

Author: None (tigbr)

Changes

This patch implements a fix for the false-positive case described in issue #134840 for the bugprone-tagged-union-member-count clang-tidy check.

The example given in the linked issue was the following:

#include &lt;pthread.h&gt;

typedef enum {
  MYENUM_ONE,
  MYENUM_TWO,
} myEnumT;

typedef struct {
  pthread_mutex_t mtx;       
  myEnumT         my_enum;
} myTypeT;

The bugprone-tagged-union-member-count check emits the following a warning for this struct:

&lt;source&gt;: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:

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.


Full diff: https://github.com/llvm/llvm-project/pull/135831.diff

5 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp (+8-4)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.c (+13)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.cpp (+13)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.m (+13)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/tagged-union-member-count.mm (+13)
diff --git a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
index db99ef3786e5f..b91da7db39463 100644
--- a/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/TaggedUnionMemberCountCheck.cpp
@@ -106,11 +106,15 @@ void TaggedUnionMemberCountCheck::storeOptions(
 
 void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {
 
-  auto UnionField = fieldDecl(hasType(qualType(
-      hasCanonicalType(recordType(hasDeclaration(recordDecl(isUnion())))))));
+  auto NotFromSystemHeaderOrStdNamespace =
+      unless(anyOf(isExpansionInSystemHeader(), isInStdNamespace()));
 
-  auto EnumField = fieldDecl(hasType(
-      qualType(hasCanonicalType(enumType(hasDeclaration(enumDecl()))))));
+  auto UnionField =
+      fieldDecl(hasType(qualType(hasCanonicalType(recordType(hasDeclaration(
+          recordDecl(isUnion(), NotFromSystemHeaderOrStdNamespace)))))));
+
+  auto EnumField = fieldDecl(hasType(qualType(hasCanonicalType(
+      enumType(hasDeclaration(enumDecl(NotFromSystemHeaderOrStdNamespace)))))));
 
   auto hasOneUnionField = fieldCountOfKindIsOne(UnionField, UnionMatchBindName);
   auto hasOneEnumField = fieldCountOfKindIsOne(EnumField, TagMatchBindName);
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 60c93c553baca..96255c7fdd4fe 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
@@ -147,3 +147,16 @@ struct Name {\
 
 // CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
 DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};
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 25827e8c8de0c..f21c23b87ae44 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
@@ -308,3 +308,16 @@ void DoNotMatchLambdas() {
 	} u;
     auto L = [e, u] () {};
 }
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};
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 60c93c553baca..96255c7fdd4fe 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
@@ -147,3 +147,16 @@
 
 // CHECK-MESSAGES: :[[@LINE+1]]:44: warning: tagged union has more data members (4) than tags (3)
 DECLARE_TAGGED_UNION_STRUCT(Tags3, Union4, TaggedUnionStructFromMacro);
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};
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 8b308555281c5..b169b5cd480b5 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
@@ -307,3 +307,16 @@ void DoNotMatchLambdas() {
 	} u;
     auto L = [e, u] () {};
 }
+
+// Typedefed unions from system header files should be ignored when
+// we are trying to pinpoint the union part in a user-defined tagged union.
+#include "pthread.h"
+
+// This should not be analyzed as a user-defined tagged union,
+// even though pthread_mutex_t may be declared as a typedefed union.
+struct SystemTypedefedUnionDataMemberShouldBeIgnored {
+  pthread_mutex_t Mutex;
+  enum {
+    MyEnum
+  } EnumField;
+};

Types from system headers and the std namespace are no longer considered as
the enum part or the union part of a user-defined tagged union.

Fixes llvm#134840
@tigbr tigbr force-pushed the tagged-union-member-count-fix branch from d69cd61 to 525459a Compare April 15, 2025 19:56
Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add entry in ReleaseNotes.rst file.

@EugeneZelenko
Copy link
Contributor

Please mention changes in Release Notes.

@tigbr tigbr requested a review from vbvictor April 17, 2025 11:39
@vbvictor
Copy link
Contributor

vbvictor commented Apr 17, 2025

I'm not sure if we need to check explicitly if some struct/enum comes from std since all std namespace is already placed in system headers unless some niche cases like std::hash<>. I'm okay leaving it as is, but please then add test with std namespace because now only system headers are tested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants