Skip to content

Commit a612c2c

Browse files
Merge pull request #938 from github/jeongsoolee09/MISRA-C++-2023-Statements
Implement "Statements" package
2 parents b43fff2 + 2163c54 commit a612c2c

17 files changed

+1790
-0
lines changed

cpp/common/src/codingstandards/cpp/Loops.qll

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,40 @@ predicate isInvalidLoop(ForStmt forLoop, string reason, Locatable reasonLocation
374374
reason = "its $@ is not a boolean" and
375375
reasonLabel = "loop control variable"
376376
}
377+
378+
/**
379+
* A comparison expression that has the minimum qualification as being a valid termination
380+
* condition of a legacy for-loop. It is characterized by a value read from a variable being
381+
* compared to a value, which is supposed to be the loop bound.
382+
*/
383+
class LegacyForLoopCondition extends RelationalOperation {
384+
/**
385+
* The legacy for-loop this relational operation is a condition of.
386+
*/
387+
ForStmt forLoop;
388+
VariableAccess loopCounter;
389+
Expr loopBound;
390+
391+
LegacyForLoopCondition() {
392+
this = forLoop.getCondition() and
393+
loopCounter = this.getAnOperand() and
394+
loopBound = this.getAnOperand() and
395+
loopCounter.getTarget() = getAnIterationVariable(forLoop) and
396+
loopBound != loopCounter
397+
}
398+
399+
/**
400+
* Gets the for-loop this expression is a termination condition of.
401+
*/
402+
ForStmt getForLoop() { result = forLoop }
403+
404+
/**
405+
* Gets the variable access to the loop counter variable, appearing in this loop condition.
406+
*/
407+
VariableAccess getLoopCounter() { result = loopCounter }
408+
409+
/**
410+
* Gets the variable access to the loop bound variable, appearing in this loop condition.
411+
*/
412+
Expr getLoopBound() { result = loopBound }
413+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Provides a library for working with expressions that update the value
3+
* of a numeric variable by incrementing or decrementing it by a certain
4+
* amount.
5+
*/
6+
7+
import cpp
8+
9+
private class AssignAddOrSubExpr extends AssignArithmeticOperation {
10+
AssignAddOrSubExpr() {
11+
this instanceof AssignAddExpr or
12+
this instanceof AssignSubExpr
13+
}
14+
}
15+
16+
private class AddOrSubExpr extends BinaryArithmeticOperation {
17+
AddOrSubExpr() {
18+
this instanceof AddExpr or
19+
this instanceof SubExpr
20+
}
21+
}
22+
23+
/**
24+
* An expression that updates a numeric variable by adding to or subtracting
25+
* from it a certain amount.
26+
*/
27+
abstract class StepCrementUpdateExpr extends Expr {
28+
/**
29+
* The expression in the abstract syntax tree that represents the amount of
30+
* value by which the variable is updated.
31+
*/
32+
abstract Expr getAmountExpr();
33+
}
34+
35+
/**
36+
* An increment or decrement operator application, either postfix or prefix.
37+
*/
38+
class PostfixOrPrefixCrementExpr extends CrementOperation, StepCrementUpdateExpr {
39+
override Expr getAmountExpr() { none() }
40+
}
41+
42+
/**
43+
* An add-then-assign or subtract-then-assign expression in a shortened form,
44+
* i.e. `+=` or `-=`.
45+
*/
46+
class AssignAddOrSubUpdateExpr extends AssignAddOrSubExpr, StepCrementUpdateExpr {
47+
override Expr getAmountExpr() { result = this.getRValue() }
48+
}
49+
50+
/**
51+
* An add-then-assign expression or a subtract-then-assign expression, i.e.
52+
* `x = x + E` or `x = x - E`, where `x` is some variable and `E` an
53+
* arbitrary expression.
54+
*/
55+
class AddOrSubThenAssignExpr extends AssignExpr, StepCrementUpdateExpr {
56+
/** The `x` as in the left-hand side of `x = x + E`. */
57+
VariableAccess lvalueVariable;
58+
/** The `x + E` as in `x = x + E`. */
59+
AddOrSubExpr addOrSubExpr;
60+
/** The `E` as in `x = x + E`. */
61+
Expr amountExpr;
62+
63+
AddOrSubThenAssignExpr() {
64+
this.getLValue() = lvalueVariable and
65+
this.getRValue() = addOrSubExpr and
66+
exists(VariableAccess lvalueVariableAsRvalue |
67+
lvalueVariableAsRvalue = addOrSubExpr.getAnOperand() and
68+
amountExpr = addOrSubExpr.getAnOperand() and
69+
lvalueVariableAsRvalue != amountExpr
70+
|
71+
lvalueVariable.getTarget() = lvalueVariableAsRvalue.(VariableAccess).getTarget()
72+
)
73+
}
74+
75+
override Expr getAmountExpr() { result = amountExpr }
76+
}

cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import SideEffects1
5050
import SideEffects2
5151
import SmartPointers1
5252
import SmartPointers2
53+
import Statements
5354
import Strings
5455
import Templates
5556
import Toolchain
@@ -108,6 +109,7 @@ newtype TCPPQuery =
108109
TSideEffects2PackageQuery(SideEffects2Query q) or
109110
TSmartPointers1PackageQuery(SmartPointers1Query q) or
110111
TSmartPointers2PackageQuery(SmartPointers2Query q) or
112+
TStatementsPackageQuery(StatementsQuery q) or
111113
TStringsPackageQuery(StringsQuery q) or
112114
TTemplatesPackageQuery(TemplatesQuery q) or
113115
TToolchainPackageQuery(ToolchainQuery q) or
@@ -166,6 +168,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
166168
isSideEffects2QueryMetadata(query, queryId, ruleId, category) or
167169
isSmartPointers1QueryMetadata(query, queryId, ruleId, category) or
168170
isSmartPointers2QueryMetadata(query, queryId, ruleId, category) or
171+
isStatementsQueryMetadata(query, queryId, ruleId, category) or
169172
isStringsQueryMetadata(query, queryId, ruleId, category) or
170173
isTemplatesQueryMetadata(query, queryId, ruleId, category) or
171174
isToolchainQueryMetadata(query, queryId, ruleId, category) or
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype StatementsQuery =
7+
TAppropriateStructureOfSwitchStatementQuery() or
8+
TLegacyForStatementsShouldBeSimpleQuery() or
9+
TForRangeInitializerAtMostOneFunctionCallQuery()
10+
11+
predicate isStatementsQueryMetadata(Query query, string queryId, string ruleId, string category) {
12+
query =
13+
// `Query` instance for the `appropriateStructureOfSwitchStatement` query
14+
StatementsPackage::appropriateStructureOfSwitchStatementQuery() and
15+
queryId =
16+
// `@id` for the `appropriateStructureOfSwitchStatement` query
17+
"cpp/misra/appropriate-structure-of-switch-statement" and
18+
ruleId = "RULE-9-4-2" and
19+
category = "required"
20+
or
21+
query =
22+
// `Query` instance for the `legacyForStatementsShouldBeSimple` query
23+
StatementsPackage::legacyForStatementsShouldBeSimpleQuery() and
24+
queryId =
25+
// `@id` for the `legacyForStatementsShouldBeSimple` query
26+
"cpp/misra/legacy-for-statements-should-be-simple" and
27+
ruleId = "RULE-9-5-1" and
28+
category = "advisory"
29+
or
30+
query =
31+
// `Query` instance for the `forRangeInitializerAtMostOneFunctionCall` query
32+
StatementsPackage::forRangeInitializerAtMostOneFunctionCallQuery() and
33+
queryId =
34+
// `@id` for the `forRangeInitializerAtMostOneFunctionCall` query
35+
"cpp/misra/for-range-initializer-at-most-one-function-call" and
36+
ruleId = "RULE-9-5-2" and
37+
category = "required"
38+
}
39+
40+
module StatementsPackage {
41+
Query appropriateStructureOfSwitchStatementQuery() {
42+
//autogenerate `Query` type
43+
result =
44+
// `Query` type for `appropriateStructureOfSwitchStatement` query
45+
TQueryCPP(TStatementsPackageQuery(TAppropriateStructureOfSwitchStatementQuery()))
46+
}
47+
48+
Query legacyForStatementsShouldBeSimpleQuery() {
49+
//autogenerate `Query` type
50+
result =
51+
// `Query` type for `legacyForStatementsShouldBeSimple` query
52+
TQueryCPP(TStatementsPackageQuery(TLegacyForStatementsShouldBeSimpleQuery()))
53+
}
54+
55+
Query forRangeInitializerAtMostOneFunctionCallQuery() {
56+
//autogenerate `Query` type
57+
result =
58+
// `Query` type for `forRangeInitializerAtMostOneFunctionCall` query
59+
TQueryCPP(TStatementsPackageQuery(TForRangeInitializerAtMostOneFunctionCallQuery()))
60+
}
61+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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

Comments
 (0)