refactor: Enable -Wswitch in exhaustive switch'es, Enable -Wcovered-switch-default#34684
refactor: Enable -Wswitch in exhaustive switch'es, Enable -Wcovered-switch-default#34684maflcko wants to merge 2 commits intobitcoin:masterfrom
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please copy-paste ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
l0rinc
left a comment
There was a problem hiding this comment.
ACK 6666ff4
I had a similar attempt last week, had a few more cases covered (pun intended), but haven't finished, so it's likely not all correct - see if there's anything you can include here: https://github.com/l0rinc/bitcoin/pull/119/changes
6666ff4 to
fa6aa9e
Compare
thx, taken. Except for the cases where the switch was not exhaustive. I think it is fine to have |
|
ACK fa6aa9e |
|
Excuse my almost-certainly-smooth-brained question, but, the PR title says "enable -Wswitch", though that flag is not in the changeset. Am I correct in inferring that, |
|
Sorry, I forgot to write the motivation in the description. Fixed now |
willcl-ark
left a comment
There was a problem hiding this comment.
ACK fa6aa9e
This looks correct to me. I found other switch cases with default remaining, but none that qualify for a refactor-only PR, as they are non-exhaustive, non-enum, or othery types.
This appears to fix all exhaustive switches where removal of the default case is only a refactor.
The addition of the CHECK_NONFATAL in wallet/rpc/spend.cpp is needed to avoid -Wreturn-type error on gcc, as per https://gcc.gnu.org/wiki/VerboseDiagnostics#enum_switch (reading that, I wonder why clang doesn't warn here too).
fa6aa9e to
fa0476f
Compare
fa0476f to
fa84cd0
Compare
|
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
willcl-ark
left a comment
There was a problem hiding this comment.
reACK fa84cd0
In addition to the second commit, I also checked this time that my version of gcc includes -Wswitch in -Wall, as I didn't do that previously:
[nix-shell:~/src/core/worktrees/pr-34684]$ gcc --version
gcc (GCC) 15.2.0
[nix-shell:~/src/core/worktrees/pr-34684]$ gcc -Wall -Q --help=warnings | grep enabled | grep switch
-Wswitch [enabled]
-Wswitch-bool [enabled]
-Wswitch-outside-range [enabled]
-Wswitch-unreachable [enabled]
The new second commit provides warnings if anyone tries to add a redundant default case to a covered switch, preventing regressions the other way. Notably this is a clang-only warning, not supported by gcc:
[nix-shell:~/src/core/worktrees/pr-34684]$ gcc -Wcovered-switch-default -x c -c /dev/null -o /dev/null
gcc: error: unrecognized command-line option ‘-Wcovered-switch-default’; did you mean ‘-Wno-switch-default’?
...but try_append_cxx_flags handles this correctly.
Wrapping RPCHelpMan in a lambda and returning makes more sense ergonomically.
|
NACK I consider "no default case, so the compiler can warn about missing cases" a compiler bug. Why can't the compiler warn about missing cases when there is a default? In my view, there should be two guidelines:
With GCC, those two guidelines can be enforced with In this code: enum color: int { red, blue };
|
Hmmm, these are fair comments, and I think I find myself agreeing with them... Two things stand out to me: We're pretty much a gcc project (currently, for our release builds at least), and as noted gcc supports The other concern is that without a This PR's clang warnings enforce a convention (no Changing our developer-notes.md and all switches is going to be a much larger change though, and perhaps not worth it? |
I don't think this is about compilers, it is more about existing code using the Enabling My preference would be to discuss and enable that in a separate pull request, if there is need for it. To clarify, there are two different patterns used in this codebase: bool IsApple(Fruit f) {
switch (f) {
case apple: return 1;
default: return 0; // all other fruits (or invalid values)
}
}and int Calories(Fruit f) {
switch (f) {
case apple: return 95;
case banana: return 105;
} // no default case, so the compiler can warn about missing cases
assert(false); // Any other value of Fruit would be a logic error or data corruption -> terminate
}Enabling Personally, I think it is perfectly fine (and desirable) to have both patterns at the disposal, and have a way to document which pattern is used, so that compilers and code readers can understand and enforce each pattern. Edit: about |
stickies-v
left a comment
There was a problem hiding this comment.
Concept ACK.
I find reserving the default case strictly for switches that are not exhaustive (on the named enumerators) to be natural and idiomatic. It is unfortunate that we have to rely on a convention-enforced safety net (assert/NONFATAL_UNREACHABLE) to handle non-enumerator values, but requiring exhaustive case listing in every enum switch seems too costly a measure given the current state of compiler tooling.
There was a problem hiding this comment.
ACK fa3ec4a
I can understand the objection and kind of lean toward adding all values of every enum everywhere (we can just add 100 to a single line if the noise is the concern, but I agree with the author that it should be decided in a follow-up).
I'm personally fine with this change and will let others decide.
|
Maybe #ifndef them so the default is omitted only on debug builds? |
Not sure what the benefit would be. Also, the net benefit is already possible today (and even enforced for RPC code): Example from above: int Calories(Fruit f) {
switch (f) {
case apple: return 95;
case banana: return 105;
} // no default case, so the compiler can warn about missing cases
assert(false); // Any other value of Fruit would be a logic error or data corruption -> terminate
}Example for a release-only fallback: int Calories(Fruit f) {
switch (f) {
case apple: return 95;
case banana: return 105;
} // no default case, so the compiler can warn about missing cases
Assume(false); // Any other value of Fruit would be a logic error or data corruption
return DEFAULT_CALORIES;
}Example for an RPC-style non-fatal check: int Calories(Fruit f) {
switch (f) {
case apple: return 95;
case banana: return 105;
} // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false); // Any other value of Fruit would be a logic error or data corruption -> throw
} |
Also, apply the comment according to the dev notes. Also, modify the dev notes to give a lambda-wrapped example. Can be reviewed via --ignore-all-space Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
The leveldb exclusion is required to avoid this warning in the subtree:
```
src/leveldb/util/status.cc:63:7: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]
63 | default:
| ^
1 warning generated.
```
The compiler flag
-Wswitchis enabled. However, it can not fire when adefault:case exists. Fix that by removing the default case where a switch is already handling all cases exhaustively.Also, enable
-Wcovered-switch-defaultto catch those cases at compile time in the future.Also, apply the comment according to the dev notes.
Can be reviewed via
--ignore-all-space