|
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