|
19 | 19 | import cpp
|
20 | 20 | import codingstandards.cpp.misra
|
21 | 21 | import codingstandards.cpp.SwitchStatement
|
| 22 | +import codingstandards.cpp.Noreturn |
22 | 23 |
|
23 | 24 | from SwitchStmt switch, string message
|
24 | 25 | where
|
25 | 26 | not isExcluded(switch, StatementsPackage::appropriateStructureOfSwitchStatementQuery()) and
|
26 |
| - ( |
27 |
| - // RULE-16-1: Switch not well-formed (has inappropriate statements) |
28 |
| - exists(SwitchCase case | |
29 |
| - case = switch.getASwitchCase() and |
30 |
| - switchCaseNotWellFormed(case) and |
31 |
| - message = "has a case that contains inappropriate statements (only expression, compound, selection, iteration or try statements are allowed)" |
| 27 | + /* 1. There is a statement that appears as an initializer and is not a declaration statement. */ |
| 28 | + exists(Stmt initializer | initializer = switch.getInitialization() | |
| 29 | + not initializer instanceof DeclStmt |
| 30 | + ) and |
| 31 | + message = "contains a statement that that is not a simple declaration" |
| 32 | + or |
| 33 | + /* 2. There is a switch case label that does not lead a branch (i.e. a switch case label is nested). */ |
| 34 | + exists(SwitchCase case | case = switch.getASwitchCase() | case instanceof NestedSwitchCase) and |
| 35 | + message = "contains a switch label that is not directly within the switch body" |
| 36 | + or |
| 37 | + /* 3. There is a non-case label in a label group. */ |
| 38 | + exists(SwitchCase case | case = switch.getASwitchCase() | |
| 39 | + case.getAStmt().getChildStmt*() instanceof LabelStmt |
| 40 | + ) and |
| 41 | + message = "contains a statement label that is not a case label" |
| 42 | + or |
| 43 | + /* 4. There is a statement before the first case label. */ |
| 44 | + exists(Stmt switchBody | switchBody = switch.getStmt() | |
| 45 | + not switchBody.getChild(0) instanceof SwitchCase |
| 46 | + ) and |
| 47 | + message = "has a statement that is not a case label as its first element" |
| 48 | + or |
| 49 | + /* 5. There is a switch case whose terminator is not one of the allowed kinds. */ |
| 50 | + exists(SwitchCase case, Stmt lastStmt | |
| 51 | + case = switch.getASwitchCase() and lastStmt = case.getLastStmt() |
| 52 | + | |
| 53 | + not ( |
| 54 | + lastStmt instanceof BreakStmt or |
| 55 | + lastStmt instanceof ReturnStmt or |
| 56 | + lastStmt instanceof GotoStmt or |
| 57 | + lastStmt instanceof ContinueStmt or |
| 58 | + lastStmt.(ExprStmt).getExpr() instanceof ThrowExpr or |
| 59 | + lastStmt.(ExprStmt).getExpr().(Call).getTarget() instanceof NoreturnFunction or |
| 60 | + lastStmt.getAnAttribute().getName().matches("%fallthrough") // We'd like to consider compiler variants such as `clang::fallthrough`. |
32 | 61 | )
|
33 |
| - or |
34 |
| - // RULE-16-2: Nested switch labels |
35 |
| - exists(SwitchCase case | |
36 |
| - case = switch.getASwitchCase() and |
37 |
| - case instanceof NestedSwitchCase and |
38 |
| - message = "contains a switch label that is not directly within the switch body" |
39 |
| - ) |
40 |
| - or |
41 |
| - // RULE-16-3: Non-empty case doesn't terminate with break |
42 |
| - exists(SwitchCase case | |
43 |
| - case = switch.getASwitchCase() and |
44 |
| - case instanceof CaseDoesNotTerminate and |
45 |
| - message = "has a non-empty case that does not terminate with an unconditional break or throw statement" |
46 |
| - ) |
47 |
| - or |
48 |
| - // RULE-16-4: Missing default clause |
49 |
| - not switch.hasDefaultCase() and |
50 |
| - message = "is missing a default clause" |
51 |
| - or |
52 |
| - // RULE-16-5: Default clause not first or last |
53 |
| - exists(SwitchCase defaultCase | |
54 |
| - switch.getDefaultCase() = defaultCase and |
55 |
| - exists(defaultCase.getPreviousSwitchCase()) and |
56 |
| - finalClauseInSwitchNotDefault(switch) and |
57 |
| - message = "has a default clause that is not the first or last switch label" |
58 |
| - ) |
59 |
| - or |
60 |
| - // RULE-16-6: Less than two case clauses |
61 |
| - count(SwitchCase case | |
62 |
| - switch.getASwitchCase() = case and |
63 |
| - case.getNextSwitchCase() != case.getFollowingStmt() |
64 |
| - ) + 1 < 2 and |
65 |
| - message = "has fewer than two switch-clauses" |
66 |
| - or |
67 |
| - // RULE-16-7: Boolean switch expression |
68 |
| - switch instanceof BooleanSwitchStmt and |
69 |
| - message = "has a controlling expression of essentially Boolean type" |
70 |
| - ) |
| 62 | + ) and |
| 63 | + message = "is missing a terminator that moves the control out of its body" |
| 64 | + or |
| 65 | + /* 6. The switch statement does not have more than two unique branches. */ |
| 66 | + count(SwitchCase case | |
| 67 | + case = switch.getASwitchCase() and |
| 68 | + /* |
| 69 | + * If the next switch case is the following statement of this switch case, then the two |
| 70 | + * switch cases are consecutive and should be considered as constituting one branch |
| 71 | + * together. |
| 72 | + */ |
| 73 | + |
| 74 | + not case.getNextSwitchCase() = case.getFollowingStmt() |
| 75 | + | |
| 76 | + case |
| 77 | + ) < 2 and |
| 78 | + message = "contains less than two branches" |
| 79 | + or |
| 80 | + /* 7-1. The switch statement is not an enum switch statement and is missing a default case. */ |
| 81 | + not switch instanceof EnumSwitch and |
| 82 | + not switch.hasDefaultCase() and |
| 83 | + message = "lacks a default case" |
| 84 | + or |
| 85 | + /* |
| 86 | + * 7-2. The switch statement is an enum switch statement and is missing a branch for a |
| 87 | + * variant. |
| 88 | + */ |
| 89 | + |
| 90 | + exists(switch.(EnumSwitch).getAMissingCase()) and |
| 91 | + message = "lacks a case for one of its variants" |
71 | 92 | select switch, "Switch statement " + message + "."
|
0 commit comments