clang-tidy - bugprone-tagged-union-member-count — Extra Clang Tools 22.1.8 documentation

# Extra Clang Tools 22.1.8 documentation

## 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. Furthermore, the types of the union and the enum members must not come from system header files nor the `std` namespace.

Example:

    enum Tags {
      Tag1,
      Tag2,
    };

    struct TaggedUnion { // warning: tagged union has more data members (3) than tags (2)
      enum Tags Kind;
      union {
        int I;
        float F;
        char *Str;
      } Data;
    };

The following example illustrates the exception for unions and enums from system header files and the `std` namespace.

    #include <pthread.h>

    struct NotTaggedUnion {
      enum MyEnum { MyEnumConstant1, MyEnumConstant2 } En;
      pthread_mutex_t Mutex;
    };

The `pthread_mutex_t` type may be defined as a union behind a `typedef`, in which case the check could mistake this type as a user-defined tagged union. After all, it has exactly one enum data member and exactly one union data member. To avoid false-positive cases originating from this, unions and enums from system headers and the `std` namespace are ignored when pinpointing the union part and the enum part of a potential user-defined tagged union.

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

    enum TagWithLast {
      Tag1 = 0,
      Tag2 = 1,
      Tag3 = 2,
      LastTag = 2
    };

    enum TagWithCounter {
      Tag1, // is 0
      Tag2, // is 1
      Tag3, // is 2
      TagCount, // is 3
    };

The check counts the number of distinct values among the enum constants and not the enum constants themselves. This way the enum constants that are essentially just aliases of other enum constants are not included in the final count.

Handling of counting enum constants (ones like `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 `CountingEnumPrefixes`, `CountingEnumSuffixes` 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 (`EnableCountingEnumHeuristic`) or configured to follow your naming convention (`CountingEnumPrefixes`, `CountingEnumSuffixes`). The strings specified in `CountingEnumPrefixes`, `CountingEnumSuffixes` are matched case insensitively.

Example counts:

    // 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

EnableCountingEnumHeuristic

This option enables or disables the counting enum heuristic. It uses the prefixes and suffixes specified in the options `CountingEnumPrefixes`, `CountingEnumSuffixes` to find counting enum constants by using them for prefix and suffix matching.

This option is enabled by default.

When `EnableCountingEnumHeuristic` is false:

    enum TagWithCounter {
      Tag1,
      Tag2,
      Tag3,
      TagCount,
    };

    struct TaggedUnion {
      TagWithCounter Kind;
      union {
        int A;
        long B;
        char *Str;
        float F;
      } Data;
    };

When `EnableCountingEnumHeuristic` is true:

    enum TagWithCounter {
      Tag1,
      Tag2,
      Tag3,
      TagCount,
    };

    struct TaggedUnion { // warning: tagged union has more data members (4) than tags (3)
      TagWithCounter Kind;
      union {
        int A;
        long B;
        char *Str;
        float F;
      } Data;
    };

CountingEnumPrefixes

See `CountingEnumSuffixes` below.

CountingEnumSuffixes

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 `EnableCountingEnumHeuristic` is false then these options do nothing.

The default value of `CountingEnumSuffixes` is count and of `CountingEnumPrefixes` is the empty string.

When `EnableCountingEnumHeuristic` is true and `CountingEnumSuffixes` is count;size:

    enum TagWithCounterCount {
      Tag1,
      Tag2,
      Tag3,
      TagCount,
    };

    struct TaggedUnionCount { // warning: tagged union has more data members (4) than tags (3)
      TagWithCounterCount Kind;
      union {
        int A;
        long B;
        char *Str;
        float F;
      } Data;
    };

    enum TagWithCounterSize {
      Tag11,
      Tag22,
      Tag33,
      TagSize,
    };

    struct TaggedUnionSize { // warning: tagged union has more data members (4) than tags (3)
      TagWithCounterSize Kind;
      union {
        int A;
        long B;
        char *Str;
        float F;
      } Data;
    };

When `EnableCountingEnumHeuristic` is true and `CountingEnumPrefixes` is maxsize;last_

    enum TagWithCounterLast {
      Tag1,
      Tag2,
      Tag3,
      last_tag,
    };

    struct TaggedUnionLast { // warning: tagged union has more data members (4) than tags (3)
      TagWithCounterLast tag;
      union {
        int I;
        short S;
        char *C;
        float F;
      } Data;
    };

    enum TagWithCounterMaxSize {
      Tag1,
      Tag2,
      Tag3,
      MaxSizeTag,
    };

    struct TaggedUnionMaxSize { // warning: tagged union has more data members (4) than tags (3)
      TagWithCounterMaxSize tag;
      union {
        int I;
        short S;
        char *C;
        float F;
      } Data;
    };

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 disabled by default.

When `StrictMode` is false:

    struct TaggedUnion {
      enum {
        Tag1,
        Tag2,
        Tag3,
      } Tags;
      union {
        int I;
        float F;
      } Data;
    };

When `StrictMode` is true:

    struct TaggedUnion { // warning: tagged union has fewer data members (2) than tags (3)
      enum {
        Tag1,
        Tag2,
        Tag3,
      } Tags;
      union {
        int I;
        float F;
      } Data;
    };