How to make gcc warn about passing wrong enum to a function

Rocketmagnet picture Rocketmagnet · Jan 12, 2011 · Viewed 9.4k times · Source

gcc doesn't seem to produce a warning with the following code. How can I get it to produce a warning?

typedef enum
{
    REG8_A,
    REG8_B,
    REG8_C
}REG8;

typedef enum
{
    REG16_A,
    REG16_B,
    REG16_C
}REG16;

void function(REG8 reg8)
{

}

int main(void)
{
    function(REG16_A);    // Should warn about wrong enum
}

Answer

Brandin picture Brandin · Jan 19, 2014

For a way to do this in C using GCC's -Wenum-compare (which is enabled by default if you enable -Wall), you must perform a comparison on the enumeration constant before you pass it to the function in order to get the desired diagnostic.

-Wenum-compare

Warn about a comparison between values of different enumerated types. In C++ enumeral mismatches in conditional expressions are also diagnosed and the warning is enabled by default. In C this warning is enabled by -Wall.

http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

For such a comparison happen to happen automatically when we call the function, we can wrap the function in a macro. For readability I also define a macro SAFE_ENUM that performs a harmless comparison on the enumeration constant (this is what ultimately triggers the warning when trying to pass the wrong enumeration constant to foo or bar).

/**
  SAFE_ENUM: evaluate an enumeration constant safely
  TYPE: the enumeration type
  VAL: the enumeration constant to evaluate
*/
#define SAFE_ENUM(TYPE, VAL) ((VAL) == (TYPE)0 ? (VAL) : (VAL))

typedef enum
{
    REG8_DEFAULT,
    REG8_A,
    REG8_B,
    REG8_C
} Reg8;

typedef enum
{
    REG16_DEFAULT,
    REG16_A,
    REG16_B,
    REG16_C
} Reg16;

void foo(Reg8 reg8)
#define foo(reg8) foo(SAFE_ENUM(Reg8, reg8))
{
    printf("%s called with value %d\n", __func__, reg8);
}

void bar(Reg16 reg16)
#define bar(reg16) bar(SAFE_ENUM(Reg16, reg16))
{
    printf("%s called with value %d\n", __func__, reg16);
}

int main(void)
{
    foo(REG8_A);  // ok
    bar(REG16_A); // ok
    foo(REG16_B); // warning
    bar(REG8_B);  // warning

    Reg16 a_reg16 = 42;
    foo(a_reg16); // warning: foo requires a Reg8 but you gave it a Reg16
}