This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] Add check for possibly incomplete switch statements
ClosedPublic

Authored by xgupta on Aug 4 2014, 12:13 PM.

Details

Summary

While clang warns about a possibly incomplete switch statement when switching over an enum variable and failing to cover all enum values (either explicitly or with a default case), no such warning is emitted if a plain integer variable is used as switch variable.

Add a clang-tidy check to diagnose these scenarios.

No fixit hint is provided since there are multiple possible solutions.

Diff Detail

Event Timeline

bbannier updated this revision to Diff 12172.Aug 4 2014, 12:13 PM
bbannier retitled this revision from to [clang-tidy] Add check for possibly incomplete switch statements .
bbannier updated this object.
bbannier edited the test plan for this revision. (Show Details)
bbannier added a reviewer: bkramer.
bbannier added a subscriber: Unknown Object (MLST).
bkramer added inline comments.Aug 4 2014, 12:32 PM
clang-tidy/misc/IncompleteSwitchCheck.cpp
11 ↗(On Diff #12172)

This will also match casts that occur somewhere inside of the switch cases, you probably want to use a plain has instead of hasDescendent.

12 ↗(On Diff #12172)

Similar problem, what if one switch with a default case is contained within a switch without one?

19 ↗(On Diff #12172)

I think actually checking that you're casting from an enum would be a good idea, integral casts are common with integers too.

short foo;
switch (foo) {// Implicit int promotion.
  ...
}
test/clang-tidy/incomplete-switch.cpp
1 ↗(On Diff #12172)

Please add -implicit-check-not="{{warning|error}}:" here, otherwise it doesn't check if additional warnings are emitted.

xgupta commandeered this revision.Jul 11 2023, 10:13 PM
xgupta added a reviewer: bbannier.
Herald added a project: Restricted Project. · View Herald TranscriptJul 11 2023, 10:13 PM
xgupta updated this revision to Diff 539378.Jul 11 2023, 10:15 PM
xgupta retitled this revision from [clang-tidy] Add check for possibly incomplete switch statements to [clang-tidy] Add check for possibly incomplete switch statements.
xgupta added a reviewer: PiotrZSL.

Just updated the patch to trunk

Herald added a project: Restricted Project. · View Herald Transcript
xgupta updated this revision to Diff 539389.Jul 11 2023, 10:48 PM

Address bkramer's comments except one for "checking that you're casting from an enum".

xgupta updated this revision to Diff 539390.Jul 11 2023, 10:59 PM

Update ReleaseNotes.rst

PiotrZSL requested changes to this revision.Jul 12 2023, 1:35 AM

Missing documentation for check.
Once you ready with update, please switch review into ready for review.

clang-tools-extra/clang-tidy/misc/IncompleteSwitchCheck.cpp
20

no need to check casts, just check if its not enum... or check if its integer (validate type of expression).
and use IgnoreUnlessSpelledInSource to exclude implicit code (like other checks)

21

so switch in switch is allowed ? why

32

put some meaningful names of variables according to LLVM standard

clang-tools-extra/docs/ReleaseNotes.rst
222 ↗(On Diff #539390)

name is to generic. maybe misc-switch-missing-default-case or something similar, maybe it should be readability module or bugprone ? Still misc could be not bad trade-off.

225 ↗(On Diff #539390)

put info about non enum, for enums we got compiler warnings already

clang-tools-extra/test/clang-tidy/checkers/misc/incomplete-switch.cpp
2

Check currently use different run command. Base on new one.

This revision now requires changes to proceed.Jul 12 2023, 1:35 AM
xgupta updated this revision to Diff 539558.Jul 12 2023, 7:44 AM
xgupta edited the summary of this revision. (Show Details)

Address some comments

xgupta marked an inline comment as done.Jul 12 2023, 7:45 AM
xgupta updated this revision to Diff 539593.Jul 12 2023, 9:12 AM
xgupta marked 3 inline comments as done.

Address more comments

xgupta updated this revision to Diff 539602.Jul 12 2023, 9:28 AM

Add documentation

PiotrZSL added inline comments.Jul 12 2023, 11:53 AM
clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.cpp
16–29 ↗(On Diff #539602)

I do not like that implicitCastExpr.

this is AST for your example:

`-SwitchStmt <line:4:3, line:7:3>
   |-ImplicitCastExpr <line:4:11> 'int' <LValueToRValue>
   | `-DeclRefExpr <col:11> 'int' lvalue Var 0x555de93bc200 'i' 'int'
   `-CompoundStmt <col:14, line:7:3>
     `-CaseStmt <line:5:3, line:6:5>
       |-ConstantExpr <line:5:8> 'int'
       | |-value: Int 0
       | `-IntegerLiteral <col:8> 'int' 0
       `-BreakStmt <line:6:5>

look that case there is only because we got cast from rvalue to lvalue.
if you write example like this:

int getValue();

void Positive() {
  switch (getValue()) {
  case 0:
    break;
  }
}

there is not cast because AST will look like this:

-SwitchStmt <line:4:3, line:7:3>
      |-CallExpr <line:4:11, col:20> 'int'
      | `-ImplicitCastExpr <col:11> 'int (*)()' <FunctionToPointerDecay>
      |   `-DeclRefExpr <col:11> 'int ()' lvalue Function 0x5573a3b38100 'getValue' 'int ()'
      `-CompoundStmt <col:23, line:7:3>
        `-CaseStmt <line:5:3, line:6:5>
          |-ConstantExpr <line:5:8> 'int'
          | |-value: Int 0
          | `-IntegerLiteral <col:8> 'int' 0
          `-BreakStmt <line:6:5>

So add test with rvalue int... remove that implicit cast checks, and use IgnoreUnlessSpelledInSource (it will remove IntegralCast cast from enum to int, so hasCondition would match enum directly)

19 ↗(On Diff #539602)

this should be something like:

hasCondition(expr(hasType(qualType(hasCanonicalType(unless(hasDeclaration(enumDecl()))))))

Or you can verify just if type is integral type.
Note that with modern C++ you may have init statements in enums.

20 ↗(On Diff #539602)

this doesn't make sense, and i'm not sure why its even working.... for your case with default.
because proper solution would be something like:

AST_MATCHER(SwitchStmt, hasDefaultCase) {
 const SwitchCase *Case = Node.getSwitchCaseList();
 while(Case) {
    if (DefaultStmt::classof(Case))
        return true;

    Case = Case->getNextSwitchCase ();
 }
 return false;
}

and then just

unless(hasDefaultCase())
clang-tools-extra/docs/ReleaseNotes.rst
222 ↗(On Diff #539602)

put checks in alphabetical order

225 ↗(On Diff #539602)

actually that are complete switch statements, developer covered everything He/She wanted, so other wording should be used

226 ↗(On Diff #539602)

this suggest that enums are covered by check, and thats false

clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst
4 ↗(On Diff #539602)
6 ↗(On Diff #539602)

first sentence in documentation should be in pair to first sentence in release notes and class comment.

8 ↗(On Diff #539602)
13 ↗(On Diff #539602)

avoid such long lines, no need to duplicate check name, and you can put warning before switch

41–44 ↗(On Diff #539602)

this probably should be before example

46–49 ↗(On Diff #539602)

this isnt needed, no options, not point to mention them

52 ↗(On Diff #539602)

would be nice to list them

58 ↗(On Diff #539602)

C.89 is C.89: Make a hash noexcept
There is "ES.79: Use default to handle common cases (only)" but thats only about enums., still could be put here as reference, in such case you should put link

PiotrZSL added inline comments.Jul 12 2023, 11:57 AM
clang-tools-extra/test/clang-tidy/checkers/bugprone/switch-missing-default-case.cpp
27 ↗(On Diff #539602)

Add also tests with int/enum behind typedef and no default case.

xgupta updated this revision to Diff 539857.Jul 12 2023, 11:21 PM
xgupta marked 12 inline comments as done.

Address all comments excpet in SwitchMissingDefaultCaseCheck.cpp

xgupta added inline comments.Jul 12 2023, 11:24 PM
clang-tools-extra/docs/ReleaseNotes.rst
225 ↗(On Diff #539602)

Sure.

226 ↗(On Diff #539602)

Corrected.

clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst
46–49 ↗(On Diff #539602)

Sure, will remove.

52 ↗(On Diff #539602)

Didn't understand, what should I list, non-enum types?

58 ↗(On Diff #539602)

Accurate, thanks.

PiotrZSL added inline comments.Jul 13 2023, 10:26 AM
clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst
52 ↗(On Diff #539602)

No, not a types, compiler warnings (ones for enums). Simply so when user enable this check for integers, He/she could make sure that enum specific warnings are also enabled.
and remove this list that you added, as you cannot do switch on pointers or floating point types.

clang-tools-extra/test/clang-tidy/checkers/bugprone/switch-missing-default-case.cpp
4 ↗(On Diff #539857)
xgupta updated this revision to Diff 540102.Jul 13 2023, 10:34 AM

Updated SwitchMissingDefaultCaseCheck.cpp but still WIP
Will look again on saturday/sunday

xgupta updated this revision to Diff 540119.Jul 13 2023, 11:01 AM
xgupta marked an inline comment as done.

Address latest two comments

xgupta marked an inline comment as done.Jul 13 2023, 11:03 AM
xgupta added inline comments.
clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst
52 ↗(On Diff #539602)

I can see there is -Wswitch which I have mentioned.

xgupta updated this revision to Diff 540798.Jul 16 2023, 5:41 AM
xgupta marked an inline comment as done.

Address comment

xgupta marked 3 inline comments as done.Jul 16 2023, 5:48 AM
xgupta added inline comments.
clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.cpp
16–29 ↗(On Diff #539602)

Removed the implicit cast but couldn't get how to use IgnoreUnlessSpelledInSource here.

19 ↗(On Diff #539602)

For some reason, the check is giving warning for enum cases and I couldn't understand why, can you please help?

PiotrZSL added inline comments.Jul 16 2023, 6:34 AM
clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.cpp
16–29 ↗(On Diff #539602)

Just add

std::optional<TraversalKind> getCheckTraversalKind() const override {
   return TK_IgnoreUnlessSpelledInSource;
 }

to header, like it is in other checks.
this should also solve template handling, so add test like this:

template<typename T>
void testTemplate(T value)
{
   switch(value)  {
   ...
   }
}

testTemplate(5);
testTemplate(SomeEnum);
clang-tools-extra/docs/clang-tidy/checks/list.rst
95 ↗(On Diff #540798)

check does not provide fixes, so remove that "Yes"

PiotrZSL added inline comments.Jul 16 2023, 6:37 AM
clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.cpp
19 ↗(On Diff #539602)

Add getCheckTraversalKind and check again....

xgupta updated this revision to Diff 540818.Jul 16 2023, 10:19 AM
xgupta marked 5 inline comments as done.

Address comments

clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.cpp
19 ↗(On Diff #539602)

Wow, feel like a magic!

Tests need to be reverted, and template handling corrected (check comments).
After that it looks good to me.

clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.cpp
29 ↗(On Diff #540818)
clang-tools-extra/test/clang-tidy/checkers/bugprone/switch-missing-default-case.cpp
1 ↗(On Diff #540818)
19 ↗(On Diff #540818)

wrong, do not change everything to use only testTemplates, leave previous one, template should be additional test, this is why test fail. Previous tests were fine, just some additional should be added.

xgupta updated this revision to Diff 540845.Jul 16 2023, 7:13 PM
xgupta marked an inline comment as done.

Updated testcase [WIP]

xgupta marked 2 inline comments as done.Jul 16 2023, 7:13 PM
PiotrZSL accepted this revision.Jul 16 2023, 9:15 PM

Just few nits (column numbers in test, missing doxygen comment, ...).
Please fix those before committing.

Except that, looking good to me.

clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.cpp
16–25 ↗(On Diff #540845)

Put this into anonymous namespace to avoid ODR issues

40–41 ↗(On Diff #540845)

should never happend

clang-tools-extra/clang-tidy/bugprone/SwitchMissingDefaultCaseCheck.h
18 ↗(On Diff #540845)

Missing class doxygen comment.
Check other classes, should be something like:

/// Ensures that switch statements without default cases are flagged, focuses only
/// on covering cases with non-enums where the compiler may not issue warnings.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/switch-missing-default-case.html
clang-tools-extra/test/clang-tidy/checkers/bugprone/switch-missing-default-case.cpp
9 ↗(On Diff #540845)
16 ↗(On Diff #540845)
23 ↗(On Diff #540845)
This revision is now accepted and ready to land.Jul 16 2023, 9:15 PM
xgupta updated this revision to Diff 540857.Jul 16 2023, 10:09 PM
xgupta marked 6 inline comments as done.

Address comments

This revision was landed with ongoing or failed builds.Jul 16 2023, 10:11 PM
This revision was automatically updated to reflect the committed changes.

Just few nits (column numbers in test, missing doxygen comment, ...).
Please fix those before committing.

Except that, looking good to me.

Thanks for this nice code review.

bbannier added a comment.EditedJul 17 2023, 1:46 AM

Thanks for the reviews and @xgupta for pushing this over the finish line. This had been lingering a long time.