|
| 1 | +/** |
| 2 | + * @id cpp/misra/appropriate-structure-of-switch-statement |
| 3 | + * @name RULE-9-4-2: The structure of a switch statement shall be appropriate |
| 4 | + * @description A switch statement should have an appropriate structure with proper cases, default |
| 5 | + * labels, and break statements to ensure clear control flow and prevent unintended |
| 6 | + * fall-through behavior. |
| 7 | + * @kind problem |
| 8 | + * @precision very-high |
| 9 | + * @problem.severity error |
| 10 | + * @tags external/misra/id/rule-9-4-2 |
| 11 | + * correctness |
| 12 | + * maintainability |
| 13 | + * readability |
| 14 | + * external/misra/allocated-target/single-translation-unit |
| 15 | + * external/misra/enforcement/decidable |
| 16 | + * external/misra/obligation/required |
| 17 | + */ |
| 18 | + |
| 19 | +import cpp |
| 20 | +import codingstandards.cpp.misra |
| 21 | +import codingstandards.cpp.SwitchStatement |
| 22 | +import codingstandards.cpp.Noreturn |
| 23 | + |
| 24 | +from SwitchStmt switch, string message |
| 25 | +where |
| 26 | + not isExcluded(switch, StatementsPackage::appropriateStructureOfSwitchStatementQuery()) and |
| 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 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`. |
| 61 | + ) |
| 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" |
| 92 | +select switch, "Switch statement " + message + "." |
0 commit comments