C++ Force compile-time error/warning on implicit fall-through in switch
Well clang has -Wimplicit-fallthrough
which I did not know about but found by using -Weverything
. So for this code it gives me the following warning (see it live):
warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough]
case 2:
^
note: insert '[[clang::fallthrough]];' to silence this warning
case 2:
^
[[clang::fallthrough]];
note: insert 'break;' to avoid fall-through
case 2:
^
break;
The only documentation I can find for this flag is in the Attribute Reference which says:
The clang::fallthrough attribute is used along with the -Wimplicit-fallthrough argument to annotate intentional fall-through between switch labels. It can only be applied to a null statement placed at a point of execution between any statement and the next switch label. It is common to mark these places with a specific comment, but this attribute is meant to replace comments with a more strict annotation, which can be checked by the compiler.
and provides an example of how to mark explicit fall-through:
case 44: // warning: unannotated fall-through
g();
[[clang::fallthrough]];
case 55: // no warning
This use of an attribute to mark explicit fall-through has the disadvantage of not being portable. Visual Studio
generate an error and gcc
generates the following warning:
warning: attributes at the beginning of statement are ignored [-Wattributes]
which is a problem if you want to use -Werror
.
I tried this with gcc 4.9
and it looks like gcc
does not support this warning:
error: unrecognized command line option '-Wimplicit-fallthrough'
As of GCC 7, -Wimplicit-fallthrough
is supported and __attribute__((fallthrough))
can be used to suppress the warnings when fallthrough is intentional. GCC does recognize "fallthrough" comments in certain scenarios, but it can be confused fairly easily.
I do not see a way of generating such a warning for Visual Studio
.
Note, Chandler Carruth explains that -Weverything
is not for production use:
This is an insane group that literally enables every warning in Clang. Don't use this on your code. It is intended strictly for Clang developers or for exploring what warnings exist.
but it is useful for figuring out what warnings exist.
C++17 changes
In C++17 we get the attribute [[fallthrough]] covered in [dcl.attr.fallthrough]p1:
The attribute-token fallthrough may be applied to a null statement (9.2); such a statement is a fallthrough statement. The attribute-token fallthrough shall appear at most once in each attribute-list and no attributeargument- clause shall be present. A fallthrough statement may only appear within an enclosing switch statement (9.4.2). The next statement that would be executed after a fallthrough statement shall be a labeled statement whose label is a case label or default label for the same switch statement. The program is ill-formed if there is no such statement.
...
[ Example: void f(int n) { void g(), h(), i(); switch (n) { case 1: case 2: g(); [[fallthrough]]; case 3: // warning on fallthrough discouraged h(); case 4: // implementation may warn on fallthrough i(); [[fallthrough]]; // ill-formed } } —end example ]
See live example using attribute.
I always write a break;
before each case
, as follows:
switch(val) {
break; case 0:
foo();
break; case 1:
bar();
break; case 2:
baz();
break; default:
roomba();
}
This way, it is much more obvious to the eye if a break;
is missing. The initial break;
is redundant I suppose, but it helps to be consistent.
This is a conventional switch
statement, I've simply used whitespace in a different way, removing the newline that is normally after a break;
and before the next case
.
Advice: if you consistently put a blank line in between case clauses, the absence of a 'break' becomes more visible to a human skimming the code:
switch (val) {
case 0:
foo();
break;
case 1:
bar();
case 2:
baz();
break;
default:
roomba();
}
This isn't as effective when there's a lot of code inside individual case clauses, but that tends to be a bad code smell in itself.