Deprecated: Function get_magic_quotes_gpc() is deprecated in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 99

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 619

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1169

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176
8000 Implement "Statements" package by jeongsoolee09 · Pull Request #938 · github/codeql-coding-standards · GitHub
Nothing Special   »   [go: up one dir, main page]

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
c8f559c
Generate query files for "Statements" package
jeongsoolee09 Jul 29, 2025
ffcb432
Add agent-generated first draft
jeongsoolee09 Jul 30, 2025
221b9b2
Add some test cases
jeongsoolee09 Jul 30, 2025
fabb7e5
Finish first draft
jeongsoolee09 Jul 31, 2025
6909528
Add test case for Rule 9.5.2
jeongsoolee09 Aug 1, 2025
0b5250f
Minor
jeongsoolee09 Aug 1, 2025
b239862
Update test case for Rule 9.5.2
jeongsoolee09 Aug 1, 2025
a013388
Add first draft of `ForRangeInitializerAtMostOneFunctionCall`
jeongsoolee09 Aug 1, 2025
1c623f4
Add test cases
jeongsoolee09 Aug 8, 2025
b792897
Merge branch 'main' into jeongsoolee09/MISRA-C++-2023-Statements
jeongsoolee09 Aug 8, 2025
0d38cd7
Merge branch 'main' into jeongsoolee09/MISRA-C++-2023-Statements
jeongsoolee09 Aug 8, 2025
c2cbed3
Update unit test
jeongsoolee09 Aug 18, 2025
b7ae38e
Add more cases and more vocabularies to reason about them
jeongsoolee09 Aug 27, 2025
86aaa0e
Fix labeling of two cases
jeongsoolee09 Sep 8, 2025
49bdc07
Add cases of addresses taken as part of non-const declaration or expr…
jeongsoolee09 Sep 8, 2025
01e3276
Finish first draft
jeongsoolee09 Sep 9, 2025
9cab1f5
Merge branch 'main' into jeongsoolee09/MISRA-C++-2023-Statements
jeongsoolee09 Sep 9, 2025
2f6fc3d
Finish first draft
jeongsoolee09 Sep 10, 2025
999e870
Tidy up, refine a bit more, add a series of test cases
jeongsoolee09 Sep 17, 2025
562c7be
Add two more cases
jeongsoolee09 Sep 17, 2025
6855e6a
Add QLDocs to two helper predicates
jeongsoolee09 Sep 17, 2025
5e24d1b
Introduce `from` variables and fix logical operator association
jeongsoolee09 Sep 22, 2025
18daff7
Introduce newtype
jeongsoolee09 Sep 22, 2025
55b8476
Split cases `5-1` and `5-2`
jeongsoolee09 Sep 22, 2025
662f51f
Debug 5-1-2 and 5-2-2 not being reported
jeongsoolee09 Sep 22, 2025
a653b66
Add LegacyForLoopUpdateExpression and test cases
jeongsoolee09 Sep 23, 2025
c8c0770
Separate out helper classes into libraries
jeongsoolee09 Sep 24, 2025
2227255
Finish draft of `LegacyForStatementsShouldBeSimple`
jeongsoolee09 Sep 24, 2025
38b5fbc
Decouple ForStmt from `Increment.qll` and rewrite `getLoopStepOfForStmt`
jeongsoolee09 Sep 24, 2025
23aa711
Update expected result of `RULE-9-5-1`
jeongsoolee09 Sep 29, 2025
f0b53e2
Update expected results of `RULE-9-5-2`
jeongsoolee09 Sep 29, 2025
7d5f08b
Count in typedefs and cv-qualifiers
jeongsoolee09 Oct 7, 2025
e135fe6
Fix cross-join issue in `TLoopCounterUpdatedNotByCrementOrAddSubAssig…
jeongsoolee09 Oct 7, 2025
b13ffd2
Slightly change wording in the message
jeongsoolee09 Oct 7, 2025
82f9adc
Make the loop counter detection more relaxed
jeongsoolee09 Oct 7, 2025
2116400
Refine `LegacyForLoopCondition`
jeongsoolee09 Oct 8, 2025
1f8083a
Change phrasing of message from `TNoRelationalOperatorInLoopCondition`
jeongsoolee09 Oct 8, 2025
1355eff
Use `upperbound/0` and `getFullyConverted/0` to more precisely infer …
jeongsoolee09 Oct 8, 2025
3ad5ef2
Apply suggestion from @Copilot
jeongsoolee09 Oct 9, 2025
fb20065
Apply suggestion from @Copilot
jeongsoolee09 Oct 9, 2025
bce38a0
Reformat test cases of 9-4-2 and 9-5-2
jeongsoolee09 Oct 9, 2025
32c36fe
Merge branch 'jeongsoolee09/MISRA-C++-2023-Statements' of github.com:…
jeongsoolee09 Oct 9, 2025
c0a8253
Merge branch 'main' into jeongsoolee09/MISRA-C++-2023-Statements
jeongsoolee09 Oct 9, 2025
59a4096
Update expected results coming from change in message
jeongsoolee09 Oct 9, 2025
d37bc70
Use different range predicate and update tests
jeongsoolee09 Oct 9, 2025
25e29e4
Refine `TLoopCounterMutatedInLoopBody`
jeongsoolee09 Oct 10, 2025
01216fa
Add more candidate exprs and remove duplicate reportings on compound …
jeongsoolee09 Oct 13, 2025
0f998ea
Add more test cases
jeongsoolee09 Oct 13, 2025
87a5158
Fix loop counter -> loop bound
jeongsoolee09 Oct 13, 2025
2aed0f1
Add const-but-mutable pointer examples
jeongsoolee09 Oct 13, 2025
33222ae
Debug loopVariableAssignedToNonConstPointOrReferenceType
jeongsoolee09 Oct 14, 2025
3dca053
Update expected results of 9-5-1
jeongsoolee09 Oct 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions cpp/common/src/codingstandards/cpp/Loops.qll
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,43 @@ predicate isInvalidLoop(ForStmt forLoop, string reason, Locatable reasonLocation
reason = "its $@ is not a boolean" and
reasonLabel = "loop control variable"
}

/**
* A comparison expression that has the minimum qualification as being a valid termination
* condition of a legacy for-loop. It is characterized by a value read from a variable being
* compared to a value, which is supposed to be the loop bound.
*/
class LegacyForLoopCondition extends RelationalOperation {
/**
* The legacy for-loop this relational operation is a condition of.
*/
ForStmt forLoop;
VariableAccess loopCounter;
Expr loopBound;

LegacyForLoopCondition() {
this = forLoop.getCondition() and
exists(Expr loopCounterExpr |
loopCounterExpr = this.getAnOperand() and
loopBound = this.getAnOperand() and
loopCounter = loopCounterExpr.getAChild*() and
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAChild*() is the right tool here, but it must be coupled with an allow-list or we'll have FNs, because it's still casting a very very wide net.

We should ensure (either here, or reported as an error in the query) that loopCounterExpr is not any arbitrary type of expression.

The following should be non-compliant:

for (int i = 0; f(i) < 10; ++i) {}
for (int i = 0; i * i < 10; ++i) {}
for (int i = 0; i + f() < 10; ++i) {}
for (int i = 0; (i > other_var) < 1; ++i) {}
// etc

Basically, we probably just want an allow-list where every expr from loopCounterExpr to loopCounter is either loopCounter itself or an addition/subtraction with only constant values on one side and an allow-listed expression on the other.

for (int i = 0; i + 10 < 20; ++i) {} // OK, `i` is allowed and `ALLOWED + 10` is allowed
for (int i = 0; 10 - i < 20; ++i) {} // OK, `i` is allowed and  `10 - ALLOWED` is allowed
for (int i = 0; -i < 20; ++i) {} // OK, `i` is allowed and `-ALLOWED` is allowed
for (int i = 0; -i + 10 - < 20; ++i) {} // OK, `i` is allowed and  -ALLOWED is allowed
for (int i = 0; (i + 5) + 3 < 20; ++i) {} // OK, `i` is allowed, `ALLOWED + 5`, and `ALLOWED + 3` is allowed
for (int i = 0; i + (5 + 3) < 20; ++i) {} // OK, `i` is allowed, `ALLOWED + (5 + 3)` is allowed

for (int i = 0; i + i < 20; ++i) {} // BAD, `i` is allowed but `ALLOWED + ALLOWED` is not allowed
for (int i = 0; i + j < 20; ++j) {} // BAD, 'j' is not allowed
for (int i = 0; (i + 10) + (i + 10) < 20; ++i) {} // BAD, `i` and `ALLOWED + 10` is allowed, but `ALLOWED + ALLOWED` is not allowed

Hopefully that mostly makes sense.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand what you mean, I guess what you suggest is to define a allowed class of ASTs where:

  • The base case is a VariableAccess or a unary operator application on it; and
  • The recursive case is an application of addition or subtraction to the allowed class.

However, I think even these are non-compliant since the rules dictate that (paraphrased by me):

The comparison between the loop-counter and the loop-step is done in the condition by only using a relational operator

which is preceded by (also paraphrased and highlighted by me):

The only thing the init-statement does is declaring and initializing a loop-counter of integer type

From these two, we can infer that the rule implicitly wants (1) the loop counter to be a variable, and (2) the loop condition to be a direct comparison between the loop condition variable and some loop-bound expression. This reflected in the alert message we emit in case of a violation of this sub-rule: "The [loop condition] does not determine termination based only on a comparison against the value of the counter variable".

Then, one might ask, why do we distinguish loopCounterExpr from loopCounter in the characteristic predicate? That's for internal purposes only; we somehow need to distinguish which of the operands has loopCounter in it, so we dig into one of the expressions to find a variable whose target is the initialized counter variable. I'd say it's unfortunate but necessary.

Then, a follow-up objection might be: Why don't we demand that one of the operands to the relational operator is a variable access in the first place? That's because we don't want to tailor this code specifically to this rule, because it's library code. 9-5-1 wants one of the things compared is an access to the initialized variable, but other rules might not, and in those cases this class (LegacyForLoopCondition) can be used in other ways.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I thought that this code was intended to handle the following case, based on real code examples in open pilot:

for (int i = 0; i + 10 < j; ++i) {}

I believe we have leeway to be able to interpret 9-5-1 to accept or reject this. It's trivially clear that i + 10 < j is equivalent to i < j + 10. It's fair to say that the MISRA team doesn't think of every edge case and that we have some discretion here. It's also fair to say that this violates the plain text of the rule.

I'm happy with us rejecting the above case if you think so. However, I don't think the query currently rejects it because of the .getAChild*(). Quickly looking for calls to getLoopCounter, I don't see anything that later checks that getLoopCounter() is a direct operand of the LegacyForLoopCondition.

I believe that 9-5-1 will also currently not flag

for (int i = 0; f(i) < j; ++i) {}

which is clearly intended to be non-compliant.

So we should add tests for these cases. We should test f(i) < j case is flagged as non compliant, and we should add a test for i + 10 < j with a comment about how we came to decide that it should or shouldn't be considered compliant.

My preference would be to remove getAChild*(), and either replace it with getAnOperand() or a predicate that finds a restricted set of operations. Currently LegacyForLoopCondition is only used here, and we can document its restrictions. If you would rather leave it as is and add checks in 9-5-1 that getLoopCounter() = loopCondition.getAnOperand(), that is reasonable too. But getAChild*() casts a very wide net that might not be expected in future queries, so I wouldn't say we need to have that behavior now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I believe it is reasonable to do so. Thank you for your second opinion!

loopCounter.getTarget() = getAnIterationVariable(forLoop) and
loopBound != loopCounterExpr
)
}

/**
* Gets the for-loop this expression is a termination condition of.
*/
ForStmt getForLoop() { result = forLoop }

/**
* Gets the variable access to the loop counter variable, appearing in this loop condition.
*/
VariableAccess getLoopCounter() { result = loopCounter }

/**
* Gets the variable access to the loop bound variable, appearing in this loop condition.
*/
Expr getLoopBound() { result = loopBound }
}
76 changes: 76 additions & 0 deletions cpp/common/src/codingstandards/cpp/ast/Increment.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Provides a library for working with expressions that update the value
* of a numeric variable by incrementing or decrementing it by a certain
* amount.
*/

import cpp

private class AssignAddOrSubExpr extends AssignArithmeticOperation {
Ass 809 ignAddOrSubExpr() {
this instanceof AssignAddExpr or
this instanceof AssignSubExpr
}
}

private class AddOrSubExpr extends BinaryArithmeticOperation {
AddOrSubExpr() {
this instanceof AddExpr or
this instanceof SubExpr
}
}

/**
* An expression that updates a numeric variable by adding to or subtracting
* from it a certain amount.
*/
abstract class StepCrementUpdateExpr extends Expr {
/**
* The expression in the abstract syntax tree that represents the amount of
* value by which the variable is updated.
*/
abstract Expr getAmountExpr();
}

/**
* An increment or decrement operator application, either postfix or prefix.
*/
class PostfixOrPrefixCrementExpr extends CrementOperation, StepCrementUpdateExpr {
override Expr getAmountExpr() { none() }
}

/**
* An add-then-assign or subtract-then-assign expression in a shortened form,
* i.e. `+=` or `-=`.
*/
class AssignAddOrSubUpdateExpr extends AssignAddOrSubExpr, StepCrementUpdateExpr {
override Expr getAmountExpr() { result = this.getRValue() }
}

/**
* An add-then-assign expression or a subtract-then-assign expression, i.e.
* `x = x + E` or `x = x - E`, where `x` is some variable and `E` an
* arbitrary expression.
*/
class AddOrSubThenAssignExpr extends AssignExpr, StepCrementUpdateExpr {
/** The `x` as in the left-hand side of `x = x + E`. */
VariableAccess lvalueVariable;
/** The `x + E` as in `x = x + E`. */
AddOrSubExpr addOrSubExpr;
/** The `E` as in `x = x + E`. */
Expr amountExpr;

AddOrSubThenAssignExpr() {
this.getLValue() = lvalueVariable and
this.getRValue() = addOrSubExpr and
exists(VariableAccess lvalueVariableAsRvalue |
lvalueVariableAsRvalue = addOrSubExpr.getAnOperand() and
amountExpr = addOrSubExpr.getAnOperand() and
lvalueVariableAsRvalue != amountExpr
|
lvalueVariable.getTarget() = lvalueVariableAsRvalue.(VariableAccess).getTarget()
)
}

override Expr getAmountExpr() { result = amountExpr }
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import SideEffects1
import SideEffects2
import SmartPointers1
import SmartPointers2
import Statements
import Strings
import Templates
import Toolchain
Expand Down Expand Up @@ -108,6 +109,7 @@ newtype TCPPQuery =
TSideEffects2PackageQuery(SideEffects2Query q) or
TSmartPointers1PackageQuery(SmartPointers1Query q) or
TSmartPointers2PackageQuery(SmartPointers2Query q) or
TStatementsPackageQuery(StatementsQuery q) or
TStringsPackageQuery(StringsQuery q) or
TTemplatesPackageQuery(TemplatesQuery q) or
TToolchainPackageQuery(ToolchainQuery q) or
Expand Down Expand Up 801 @@ -166,6 +168,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isSideEffects2QueryMetadata(query, queryId, ruleId, category) or
isSmartPointers1QueryMetadata(query, queryId, ruleId, category) or
isSmartPointers2QueryMetadata(query, queryId, ruleId, category) or
isStatementsQueryMetadata(query, queryId, ruleId, category) or
isStringsQueryMetadata(query, queryId, ruleId, category) or
isTemplatesQueryMetadata(query, queryId, ruleId, category) or
isToolchainQueryMetadata(query, queryId, ruleId, category) or
Expand Down
61 changes: 61 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/Statements.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype StatementsQuery =
TAppropriateStructureOfSwitchStatementQuery() or
TLegacyForStatementsShouldBeSimpleQuery() or
TForRangeInitializerAtMostOneFunctionCallQuery()

predicate isStatementsQueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `appropriateStructureOfSwitchStatement` query
StatementsPackage::appropriateStructureOfSwitchStatementQuery() and
queryId =
// `@id` for the `appropriateStructureOfSwitchStatement` query
"cpp/misra/appropriate-structure-of-switch-statement" and
ruleId = "RULE-9-4-2" and
category = "required"
or
query =
// `Query` instance for the `legacyForStatementsShouldBeSimple` query
StatementsPackage::legacyForStatementsShouldBeSimpleQuery() and
queryId =
// `@id` for the `legacyForStatementsShouldBeSimple` query
"cpp/misra/legacy-for-statements-should-be-simple" and
ruleId = "RULE-9-5-1" and
category = "advisory"
or
query =
// `Query` instance for the `forRangeInitializerAtMostOneFunctionCall` query
StatementsPackage::forRangeInitializerAtMostOneFunctionCallQuery() and
queryId =
// `@id` for the `forRangeInitializerAtMostOneFunctionCall` query
"cpp/misra/for-range-initializer-at-most-one-function-call" and
ruleId = "RULE-9-5-2" and
category = "required"
}

module StatementsPackage {
Query appropriateStructureOfSwitchStatementQuery() {
//autogenerate `Query` type
result =
// `Query` type for `appropriateStructureOfSwitchStatement` query
TQueryCPP(TStatementsPackageQuery(TAppropriateStructureOfSwitchStatementQuery()))
}

Query legacyForStatementsShouldBeSimpleQuery() {
//autogenerate `Query` type
result =
// `Query` type for `legacyForStatementsShouldBeSimple` query
TQueryCPP(TStatementsPackageQuery(TLegacyForStatementsShouldBeSimpleQuery()))
}

Query forRangeInitializerAtMostOneFunctionCallQuery() {
//autogenerate `Query` type
result =
// `Query` type for `forRangeInitializerAtMostOneFunctionCall` query
TQueryCPP(TStatementsPackageQuery(TForRangeInitializerAtMostOneFunctionCallQuery()))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @id cpp/misra/appropriate-structure-of-switch-statement
* @name RULE-9-4-2: The structure of a switch statement shall be appropriate
* @description A switch statement should have an appropriate structure with proper cases, default
* labels, and break statements to ensure clear control flow and prevent unintended
* fall-through behavior.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/misra/id/rule-9-4-2
* correctness
* maintainability
* readability
* external/misra/allocated-target/single-translation-unit
* external/misra/enforcement/decidable
* external/misra/obligation/required
*/

import cpp
import codingstandards.cpp.misra
import codingstandards.cpp.SwitchStatement
import codingstandards.cpp.Noreturn

from SwitchStmt switch, string message
where
not isExcluded(switch, StatementsPackage::appropriateStructureOfSwitchStatementQuery()) and
/* 1. There is a statement that appears as an initializer and is not a declaration statement. */
exists(Stmt initializer | initializer = switch.getInitialization() |
not initializer instanceof DeclStmt
) and
message = "contains a statement that is not a simple declaration"
or
/* 2. There is a switch case label that does not lead a branch (i.e. a switch case label is nested). */
exists(SwitchCase case | case = switch.getASwitchCase() | case instanceof NestedSwitchCase) and
message = "contains a switch label that is not directly within the switch body"
or
/* 3. There is a non-case label in a label group. */
exists(SwitchCase case | case = switch.getASwitchCase() |
case.getAStmt().getChildStmt*() instanceof LabelStmt
) and
message = "contains a statement label that is not a case label"
or
/* 4. There is a statement before the first case label. */
exists(Stmt switchBody | switchBody = switch.getStmt() |
not switchBody.getChild(0) instanceof SwitchCase
) and
message = "has a statement that is not a case label as its first element"
or
/* 5. There is a switch case whose terminator is not one of the allowed kinds. */
exists(SwitchCase case, Stmt lastStmt |
case = switch.getASwitchCase() and lastStmt = case.getLastStmt()
|
not (
lastStmt instanceof BreakStmt or
lastStmt instanceof ReturnStmt or
lastStmt instanceof GotoStmt or
lastStmt instanceof ContinueStmt or
lastStmt.(ExprStmt).getExpr() instanceof ThrowExpr or
lastStmt.(ExprStmt).getExpr().(Call).getTarget() instanceof NoreturnFunction or
lastStmt.getAnAttribute().getName().matches("%fallthrough") // We'd like to consider compiler variants such as `clang::fallthrough`.
)
) and
message = "is missing a terminator that moves the control out of its body"
or
/* 6. The switch statement does not have more than two unique branches. */
count(SwitchCase case |
case = switch.getASwitchCase() and
/*
* If the next switch case is the following statement of this switch case, then the two
* switch cases are consecutive and should be considered as constituting one branch
* together.
*/

not case.getNextSwitchCase() = case.getFollowingStmt()
|
case
) < 2 and
message = "contains less than two branches"
or
/* 7-1. The switch statement is not an enum switch statement and is missing a default case. */
not switch instanceof EnumSwitch and
not switch.hasDefaultCase() and
message = "lacks a default case"
or
/*
* 7-2. The switch statement is an enum switch statement and is missing a branch for a
* variant.
*/

exists(switch.(EnumSwitch).getAMissingCase()) and
message = "lacks a case for one of its variants"
select switch, "Switch statement " + message + "."
Loading
Loading
0