Reputation: 550
In some legacy code I have a lot of enums, and a huge switch cases. I would like to test that the switches have pure enum types. Nonsense example:
typedef enum EN
{
EN_0,
EN_1
} EN_T;
typedef enum DK
{
DK_0,
DK_1
} DK_T;
EN_T bar = ...
switch( bar )
{
case EN_0:
...
break;
case DK_1: //<-- mixed type
...
break;
}
I tried compiling this with gcc with -Wall -Wextra -pedantic
, and get no warnings. Any ideas of how to test for this? Either as compiler warnings or dedicated test code. As both the switches and enums have 100+ members it has to be generic to some level.
Edit: Please notice I am not concerned about if this is legal c, according to the C standard.
It is bad practice, and compiler can warn about bad practice or potential errors that do not break the standard, like if( a = 1)...
would always be true, perfectly legal but likely to be a mistake.
I can make the compiler warn if a switch on an enum does not contain all values of that enum a.s.o.
It is preferred if the compiler can to the work, but if a tool like lint or similar can do this I would be happy too.
Upvotes: 11
Views: 1736
Reputation: 550
Well I will answer it myself. After some more research i conclude that at least gcc will not complain about this, I need to use an extra program like pc-lint.
I made a slight rewrite to emphasis the problem.
#include <stdio.h>
typedef enum EN
{
ZERO,
ONE
} EN_T;
typedef enum DK
{
EN, /* Danish word for One */
TO /* Danish word for Two */
} DK_T;
char* E2str( EN_T en )
{
char* ret;
switch( en )
{
case ZERO:
ret = "0";
break;
case TO:
ret = "2";
break;
}
return ret;
}
int main( void )
{
printf( "0 = %s\n", E2str( ZERO ) );
printf( "1 = %s\n", E2str( ONE ) );
return 0;
}
This will compile fine, with no warnings even with:
gcc -o t.exe t.c -Wall -Wextra -pedantic
The output will be:
0 = 0
1 = 2
It is clear that this output was probably not the intention of the writer. And yes in this small example it is clear and obvious when just looking at the code. But imagine this being a switch with 200+ cases, and the switch contains other switches, and the naming of the enum is not as clear as in my example in the original question. It becomes close to impossible to spot errors like the one in this example.
Also note that by using -Wextra
I enable a check in gcc that will warn if I have a switch on an enum, and the cases does not contain all the values in that enum. But because the TO
enum has the sane numeric value as ONE
, gcc doesn't even complain about missing Enums in switch, apparently it does only look at the numeric value, and not the provided enum for this check.
My test with pc-lint, spotted both
--- Module: t.c (C) _ case TO: t.c 23 Warning 408: Type mismatch with switch expression _ } t.c 26 Info 787: enum constant 'EN::ONE' not used within switch
Unfortunately this was not the answer I was hoping for, it would be so much nicer to have this done by the compiler, and not by yet another tool.
Still open to give someone else the credit for an better answer.
Upvotes: 0
Reputation: 33506
case xxx
is simple keyword with not-that-hard typical syntax. When in pinch, it should be possible to catch most occurrences it by regular expressions. First candidate for expression that comes to me is something like
(^|\s)case\s+[^:]+:
\---/anything terminated by colon
\----/drop things like 'uppercase'
This would catch most, if not all, typical occurrences of a case keyword though the file. Then, detect switch keywords:
)\s*{\s*case\s
That should do it. Although it wouldn't look for switch
keyword, it detects first closing-parenthesis that is before first case
. IMHO, close enough and should work in most cases.
Being able to detect case
and switch
and their location, you can group cases by their preceding switch, and perform validation of case values.
That of course means you would have to write a small utility that would do that, but for me it sounds like 50-100 lines of non-minimified code.
That way of course will not handle things like:
// macros:
#define SAFE_SWITCH(x) switch(assert_non_negative(x)){
#define SWITCH_END }
SAFE_SWITCH(..) case BAR: .... SWITCH_END
// clever hacks from bored programmers:
switch(parser.nodetype)
{
default: throw ..;
#include "opcodes.inc"
#include "operands.inc"
#include "keywords.inc"
}
etc. so that's not a perfect solution, but if your switch/case are 'clean' (no such macros, and so on) then it's worth considering.
Upvotes: 0
Reputation: 7749
From the documentation on -Wswitch-enum
(assuming you are using GCC):
"case labels outside the enumeration range also provoke warnings when this option is used." AFAICK, this switch is not enabled by -Wall
or -Wextra
.
Upvotes: 0
Reputation: 30926
From standard there is only one constraint so far case labeled statement
The expression of each case label shall be an integer constant expression and no two of the case constant expressions in the same switch statement shall have the same value after conversion.
As long as it is an integer constant expression it doesn't matter whether they belong to different enums or not. So yes you can't do what you want in C
.
Upvotes: 3
Reputation: 234685
No, you can't restrict switch
case
labels to the explicit values of a particular enum
. (You can in C++ out of interest from C++11).
If you are able to change the enum
values so they don't intersect, that might help you a little, but only at runtime.
Upvotes: 7