From c8f559c1af50db1419c068dfbcac3cf587639449 Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Tue, 29 Jul 2025 19:33:14 -0400 Subject: [PATCH 1/8] Generate query files for "Statements" package --- .../cpp/exclusions/cpp/RuleMetadata.qll | 3 + .../cpp/exclusions/cpp/Statements.qll | 61 +++++++++++++++ .../AppropriateStructureOfSwitchStatement.ql | 25 ++++++ .../LegacyForStatementsShouldBeSimple.ql | 24 ++++++ ...orRangeInitializerAtMostOneFunctionCall.ql | 25 ++++++ ...opriateStructureOfSwitchStatement.expected | 1 + ...ppropriateStructureOfSwitchStatement.qlref | 1 + ...LegacyForStatementsShouldBeSimple.expected | 1 + .../LegacyForStatementsShouldBeSimple.qlref | 1 + ...eInitializerAtMostOneFunctionCall.expected | 1 + ...angeInitializerAtMostOneFunctionCall.qlref | 1 + rule_packages/cpp/Statements.json | 78 +++++++++++++++++++ 12 files changed, 222 insertions(+) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Statements.qll create mode 100644 cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql create mode 100644 cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql create mode 100644 cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql create mode 100644 cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected create mode 100644 cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.qlref create mode 100644 cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.expected create mode 100644 cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.qlref create mode 100644 cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected create mode 100644 cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.qlref create mode 100644 rule_packages/cpp/Statements.json diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index abd6aeff96..0db51d4224 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -47,6 +47,7 @@ import SideEffects1 import SideEffects2 import SmartPointers1 import SmartPointers2 +import Statements import Strings import Templates import Toolchain @@ -102,6 +103,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 @@ -157,6 +159,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 diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Statements.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Statements.qll new file mode 100644 index 0000000000..fe202ce31f --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Statements.qll @@ -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())) + } +} diff --git a/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql b/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql new file mode 100644 index 0000000000..8bfa68f5f2 --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql @@ -0,0 +1,25 @@ +/** + * @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 + +from +where + not isExcluded(x, StatementsPackage::appropriateStructureOfSwitchStatementQuery()) and +select diff --git a/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql b/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql new file mode 100644 index 0000000000..1a29e90a40 --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql @@ -0,0 +1,24 @@ +/** + * @id cpp/misra/legacy-for-statements-should-be-simple + * @name RULE-9-5-1: Legacy for statements should be simple + * @description Legacy for statements with complex initialization, condition, and increment + * expressions can be difficult to understand and maintain. Simple for loops are more + * readable. + * @kind problem + * @precision high + * @problem.severity recommendation + * @tags external/misra/id/rule-9-5-1 + * maintainability + * readability + * external/misra/allocated-target/single-translation-unit + * external/misra/enforcement/decidable + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.cpp.misra + +from +where + not isExcluded(x, StatementsPackage::legacyForStatementsShouldBeSimpleQuery()) and +select diff --git a/cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql b/cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql new file mode 100644 index 0000000000..47d27287bc --- /dev/null +++ b/cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql @@ -0,0 +1,25 @@ +/** + * @id cpp/misra/for-range-initializer-at-most-one-function-call + * @name RULE-9-5-2: A for-range-initializer shall contain at most one function call + * @description Multiple function calls in a for-range-initializer can lead to unclear iteration + * behavior and potential side effects that make the code harder to understand and + * debug. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-9-5-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 + +from +where + not isExcluded(x, StatementsPackage::forRangeInitializerAtMostOneFunctionCallQuery()) and +select diff --git a/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.qlref b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.qlref new file mode 100644 index 0000000000..9f475afbe1 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.qlref @@ -0,0 +1 @@ +rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.expected b/cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.qlref b/cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.qlref new file mode 100644 index 0000000000..bf443d6d68 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.qlref @@ -0,0 +1 @@ +rules/RULE-9-5-1/LegacyForStatementsShouldBeSimple.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected b/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.qlref b/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.qlref new file mode 100644 index 0000000000..be7fc0ef10 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.qlref @@ -0,0 +1 @@ +rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql \ No newline at end of file diff --git a/rule_packages/cpp/Statements.json b/rule_packages/cpp/Statements.json new file mode 100644 index 0000000000..91a643253d --- /dev/null +++ b/rule_packages/cpp/Statements.json @@ -0,0 +1,78 @@ +{ + "MISRA-C++-2023": { + "RULE-9-4-2": { + "properties": { + "allocated-target": [ + "Single Translation Unit" + ], + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "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", + "name": "The structure of a switch statement shall be appropriate", + "precision": "very-high", + "severity": "error", + "short_name": "AppropriateStructureOfSwitchStatement", + "tags": [ + "correctness", + "maintainability", + "readability" + ] + } + ], + "title": "The structure of a switch statement shall be appropriate" + }, + "RULE-9-5-1": { + "properties": { + "allocated-target": [ + "Single Translation Unit" + ], + "enforcement": "decidable", + "obligation": "advisory" + }, + "queries": [ + { + "description": "Legacy for statements with complex initialization, condition, and increment expressions can be difficult to understand and maintain. Simple for loops are more readable.", + "kind": "problem", + "name": "Legacy for statements should be simple", + "precision": "high", + "severity": "recommendation", + "short_name": "LegacyForStatementsShouldBeSimple", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "Legacy for statements should be simple" + }, + "RULE-9-5-2": { + "properties": { + "allocated-target": [ + "Single Translation Unit" + ], + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "Multiple function calls in a for-range-initializer can lead to unclear iteration behavior and potential side effects that make the code harder to understand and debug.", + "kind": "problem", + "name": "A for-range-initializer shall contain at most one function call", + "precision": "very-high", + "severity": "error", + "short_name": "ForRangeInitializerAtMostOneFunctionCall", + "tags": [ + "correctness", + "maintainability", + "readability" + ] + } + ], + "title": "A for-range-initializer shall contain at most one function call" + } + } +} From ffcb4323eb4ad9d95f1ed6332a06457afb92ed54 Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Wed, 30 Jul 2025 17:13:22 -0400 Subject: [PATCH 2/8] Add agent-generated first draft --- .../AppropriateStructureOfSwitchStatement.ql | 52 ++- ...opriateStructureOfSwitchStatement.expected | 16 +- cpp/misra/test/rules/RULE-9-4-2/test.cpp | 304 ++++++++++++++++++ 3 files changed, 368 insertions(+), 4 deletions(-) create mode 100644 cpp/misra/test/rules/RULE-9-4-2/test.cpp diff --git a/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql b/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql index 8bfa68f5f2..3d68e31abb 100644 --- a/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql +++ b/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql @@ -18,8 +18,54 @@ import cpp import codingstandards.cpp.misra +import codingstandards.cpp.SwitchStatement -from +from SwitchStmt switch, string message where - not isExcluded(x, StatementsPackage::appropriateStructureOfSwitchStatementQuery()) and -select + not isExcluded(switch, StatementsPackage::appropriateStructureOfSwitchStatementQuery()) and + ( + // RULE-16-1: Switch not well-formed (has inappropriate statements) + exists(SwitchCase case | + case = switch.getASwitchCase() and + switchCaseNotWellFormed(case) and + message = "has a case that contains inappropriate statements (only expression, compound, selection, iteration or try statements are allowed)" + ) + or + // RULE-16-2: Nested switch labels + exists(SwitchCase case | + case = switch.getASwitchCase() and + case instanceof NestedSwitchCase and + message = "contains a switch label that is not directly within the switch body" + ) + or + // RULE-16-3: Non-empty case doesn't terminate with break + exists(SwitchCase case | + case = switch.getASwitchCase() and + case instanceof CaseDoesNotTerminate and + message = "has a non-empty case that does not terminate with an unconditional break or throw statement" + ) + or + // RULE-16-4: Missing default clause + not switch.hasDefaultCase() and + message = "is missing a default clause" + or + // RULE-16-5: Default clause not first or last + exists(SwitchCase defaultCase | + switch.getDefaultCase() = defaultCase and + exists(defaultCase.getPreviousSwitchCase()) and + finalClauseInSwitchNotDefault(switch) and + message = "has a default clause that is not the first or last switch label" + ) + or + // RULE-16-6: Less than two case clauses + count(SwitchCase case | + switch.getASwitchCase() = case and + case.getNextSwitchCase() != case.getFollowingStmt() + ) + 1 < 2 and + message = "has fewer than two switch-clauses" + or + // RULE-16-7: Boolean switch expression + switch instanceof BooleanSwitchStmt and + message = "has a controlling expression of essentially Boolean type" + ) +select switch, "Switch statement " + message + "." diff --git a/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected index 2ec1a0ac6c..8900facc63 100644 --- a/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected +++ b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected @@ -1 +1,15 @@ -No expected results have yet been specified \ No newline at end of file +| test.cpp:40:3:40:8 | Switch statement has a case that contains inappropriate statements (only expression, compound, selection, iteration or try statements are allowed). | +| test.cpp:86:3:86:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | +| test.cpp:86:3:86:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | +| test.cpp:98:3:98:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | +| test.cpp:123:3:123:8 | Switch statement is missing a default clause. | +| test.cpp:166:3:166:8 | Switch statement has a default clause that is not the first or last switch label. | +| test.cpp:203:3:203:8 | Switch statement has fewer than two switch-clauses. | +| test.cpp:210:3:210:8 | Switch statement has fewer than two switch-clauses. | +| test.cpp:235:3:235:8 | Switch statement has a controlling expression of essentially Boolean type. | +| test.cpp:245:3:245:8 | Switch statement has a controlling expression of essentially Boolean type. | +| test.cpp:266:3:266:8 | Switch statement has a controlling expression of essentially Boolean type. | +| test.cpp:266:3:266:8 | Switch statement is missing a default clause. | +| test.cpp:266:3:266:8 | Switch statement has fewer than two switch-clauses. | +| test.cpp:275:3:275:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | +| test.cpp:275:3:275:8 | Switch statement has a default clause that is not the first or last switch label. | \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-9-4-2/test.cpp b/cpp/misra/test/rules/RULE-9-4-2/test.cpp new file mode 100644 index 0000000000..fc948480b2 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-4-2/test.cpp @@ -0,0 +1,304 @@ +// Test cases for RULE-9-4-2: The structure of a switch statement shall be +// appropriate This rule combines RULE-16-1 through RULE-16-7 + +void test_rule_16_1_well_formed(int expr) { + int i = 0; + + // COMPLIANT - well-formed switch with proper statements + switch (expr) { + case 1: + i++; // expression statement + break; + case 2: { // compound statement + i++; + } break; + case 3: + if (i > 0) { // selection statement + i++; + } + break; + case 4: + while (i < 10) { // iteration statement + i++; + } + break; + default: + break; + } + + // NON_COMPLIANT - switch with inappropriate statement (declaration) + switch (expr) { + case 1: + int j = 5; // declaration statement - not allowed + break; + default: + break; + } +} + +void test_rule_16_2_nested_labels(int expr) { + // COMPLIANT - labels directly within switch body + switch (expr) { + case 1: + break; + case 2: + break; + default: + break; + } + + // NON_COMPLIANT - nested switch labels (this would be a compiler error in + // practice) switch (expr) { case 1: + // { + // case 2: // nested label - not allowed + // break; + // } + // break; + // default: + // break; + // } +} + +void test_rule_16_3_termination(int expr) { + int i = 0; + + // COMPLIANT - all cases properly terminated + switch (expr) { + case 1: + i++; + break; + case 2: + case 3: // empty cases are fine + i++; + break; + case 4: + throw "error"; // throw is also valid termination + default: + break; + } + + // NON_COMPLIANT - case 1 falls through without break + switch (expr) { + case 1: // NON_COMPLIANT - missing break + i++; + case 2: // COMPLIANT - properly terminated + i++; + break; + default: + break; + } + + // NON_COMPLIANT - default case falls through + switch (expr) { + case 1: + i++; + break; + default: // NON_COMPLIANT - missing break + i++; + } +} + +void test_rule_16_4_default_label(int expr) { + int i = 0; + + // COMPLIANT - has default label + switch (expr) { + case 1: + i++; + break; + case 2: + i++; + break; + default: + break; + } + + // NON_COMPLIANT - missing default label + switch (expr) { + case 1: + i++; + break; + case 2: + i++; + break; + } +} + +void test_rule_16_5_default_position(int expr) { + int i = 0; + + // COMPLIANT - default is first + switch (expr) { + default: + i++; + break; + case 1: + i++; + break; + case 2: + i++; + break; + } + + // COMPLIANT - default is last + switch (expr) { + case 1: + i++; + break; + case 2: + i++; + break; + default: + i++; + break; + } + + // NON_COMPLIANT - default is in the middle + switch (expr) { + case 1: + i++; + break; + default: // NON_COMPLIANT - not first or last + i++; + break; + case 2: + i++; + break; + } +} + +void test_rule_16_6_two_clauses(int expr) { + int i = 0; + + // COMPLIANT - has multiple clauses + switch (expr) { + case 1: + i++; + break; + case 2: + i++; + break; + default: + break; + } + + // NON_COMPLIANT - only has default (single clause) + switch (expr) { + default: + i++; + break; + } + + // NON_COMPLIANT - only has one case plus default (still only one effective + // clause) + switch (expr) { + case 1: + default: + i++; + break; + } +} + +void test_rule_16_7_boolean_expression() { + int i = 0; + bool flag = true; + + // COMPLIANT - non-boolean expression + switch (i) { + case 0: + break; + case 1: + break; + default: + break; + } + + // NON_COMPLIANT - boolean expression + switch (flag) { + case true: + break; + case false: + break; + default: + break; + } + + // NON_COMPLIANT - boolean comparison expression + switch (i == 0) { + case true: + break; + case false: + break; + default: + break; + } +} + +int f() { return 1; } + +void test_complex_violations(int expr) { + int i = 0; + bool flag = true; + + // NON_COMPLIANT - multiple violations: + // - Boolean expression (16-7) + // - Missing default (16-4) + // - Single clause (16-6) + switch (flag) { + case true: + i++; + } + + // NON_COMPLIANT - multiple violations: + // - Fall-through case (16-3) + // - Default not first/last (16-5) + switch (expr) { + case 1: // NON_COMPLIANT - falls through + i++; + default: // NON_COMPLIANT - not first/last + i++; + break; + case 2: + i++; + break; + } + + switch (expr) { + int i = 0; + case 1: + i++; + } + + switch (int x = f(); x) { + case 1: + i++; + } + + switch (expr = f(); expr) { + case 1: + i++; + } + + switch (expr) { + case 1: + { + case 2: + i++; + } + } + + switch (expr) { + case 1: { + i++; + goto someLabel; + someLabel: + i++; + } + } + + switch (expr) { + int x = 1; + case 1: + i++; + } +} \ No newline at end of file From 221b9b2c4b1be5074aacb763549e8fa1c78bfa52 Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Wed, 30 Jul 2025 18:41:25 -0400 Subject: [PATCH 3/8] Add some test cases --- cpp/misra/test/rules/RULE-9-4-2/test.cpp | 310 ++++++++++------------- 1 file changed, 128 insertions(+), 182 deletions(-) diff --git a/cpp/misra/test/rules/RULE-9-4-2/test.cpp b/cpp/misra/test/rules/RULE-9-4-2/test.cpp index fc948480b2..e99a2fb1f4 100644 --- a/cpp/misra/test/rules/RULE-9-4-2/test.cpp +++ b/cpp/misra/test/rules/RULE-9-4-2/test.cpp @@ -1,304 +1,250 @@ -// Test cases for RULE-9-4-2: The structure of a switch statement shall be -// appropriate This rule combines RULE-16-1 through RULE-16-7 +int i = 0; -void test_rule_16_1_well_formed(int expr) { - int i = 0; - - // COMPLIANT - well-formed switch with proper statements - switch (expr) { +/** + * Test the initializer of a switch statement. + */ +void testInitializer(int expr) { + switch (expr) { // COMPLIANT: No initializer case 1: - i++; // expression statement - break; - case 2: { // compound statement i++; - } break; - case 3: - if (i > 0) { // selection statement - i++; - } - break; - case 4: - while (i < 10) { // iteration statement - i++; - } break; default: + i--; break; } - // NON_COMPLIANT - switch with inappropriate statement (declaration) - switch (expr) { + switch (int j = 0; + expr) { // COMPLIANT: Only declaration statement can be an initializer case 1: - int j = 5; // declaration statement - not allowed + j++; break; default: + j--; break; } -} -void test_rule_16_2_nested_labels(int expr) { - // COMPLIANT - labels directly within switch body - switch (expr) { + switch ( + i = 1; + expr) { // NON_COMPLIANT: Only declaration statement can be an initializer case 1: - break; - case 2: + i++; break; default: + i--; break; } - - // NON_COMPLIANT - nested switch labels (this would be a compiler error in - // practice) switch (expr) { case 1: - // { - // case 2: // nested label - not allowed - // break; - // } - // break; - // default: - // break; - // } } -void test_rule_16_3_termination(int expr) { - int i = 0; - - // COMPLIANT - all cases properly terminated - switch (expr) { +void testNestedCaseLabels(int expr) { + switch (expr) { // COMPLIANT: Consecutive case labels are allowed case 1: - i++; - break; case 2: - case 3: // empty cases are fine i++; break; - case 4: - throw "error"; // throw is also valid termination default: + i--; break; } - // NON_COMPLIANT - case 1 falls through without break - switch (expr) { - case 1: // NON_COMPLIANT - missing break - i++; - case 2: // COMPLIANT - properly terminated + switch (expr) { // NON_COMPLIANT: Statements with case labels should all be at + // the same level + case 1: { + case 2: i++; break; + } default: break; } - // NON_COMPLIANT - default case falls through - switch (expr) { + switch (expr) { // NON_COMPLIANT: Statements with case labels should all be at + // the same level case 1: i++; break; - default: // NON_COMPLIANT - missing break - i++; + case 2: { + default: + break; + } } } -void test_rule_16_4_default_label(int expr) { - int i = 0; - - // COMPLIANT - has default label - switch (expr) { - case 1: +void testOtherLabelsInBranch(int expr) { + switch (expr) { // NON_COMPLIANT: Non-case labels appearing in a switch branch + case 1: { i++; - break; - case 2: + goto someLabel; + someLabel: i++; break; default: break; } + } +} + +void testLeadingNonCaseStatement(int expr) { + switch (expr) { // NON_COMPLIANT: Non-case statement is the first statement in + // the switch body - // NON_COMPLIANT - missing default label - switch (expr) { case 1: i++; break; - case 2: - i++; + default: break; } } -void test_rule_16_5_default_position(int expr) { - int i = 0; +[[noreturn]] void f() {} +void g() {} - // COMPLIANT - default is first - switch (expr) { - default: - i++; - break; +void testSwitchBranchTerminator(int expr) { + switch (expr) { // COMPLIANT: Break is allowed as a branch terminator case 1: i++; break; - case 2: - i++; + default: break; } - // COMPLIANT - default is last - switch (expr) { + for (int j = 0; j++; j < 10) { + switch (expr) { // COMPLIANT: Continue is allowed as a branch terminator + case 1: + i++; + continue; + default: + continue; + } + } + + switch (expr) { // COMPLIANT: Goto is allowed as a branch terminator case 1: i++; - break; - case 2: - i++; - break; + goto error; default: - i++; - break; + goto error; } - // NON_COMPLIANT - default is in the middle - switch (expr) { + switch (expr) { // COMPLIANT: Throw is allowed as a branch terminator case 1: i++; - break; - default: // NON_COMPLIANT - not first or last - i++; - break; - case 2: - i++; - break; + throw; + default: + throw; } -} - -void test_rule_16_6_two_clauses(int expr) { - int i = 0; - // COMPLIANT - has multiple clauses - switch (expr) { + switch (expr) { // COMPLIANT: Call to a `[[noreturn]]` function is allowed as + // a branch terminator case 1: i++; - break; - case 2: - i++; - break; + f(); default: - break; + f(); } - // NON_COMPLIANT - only has default (single clause) - switch (expr) { + switch (expr) { // NON_COMPLIANT: Branch ends with a call to a function that + // is not `[[noreturn]]` + case 1: + i++; + g(); default: + g(); + } + + switch (expr) { // COMPLIANT: Return is allowed as a branch terminator + case 1: i++; - break; + return; + default: + return; } - // NON_COMPLIANT - only has one case plus default (still only one effective - // clause) - switch (expr) { + switch (expr) { // COMPLIANT: Empty statement with `[[fallthrough]]` is + // allowed as a branch terminator case 1: + i++; + [[fallthrough]]; default: i++; - break; } -} -void test_rule_16_7_boolean_expression() { - int i = 0; - bool flag = true; +error: + return; +} - // COMPLIANT - non-boolean expression - switch (i) { - case 0: - break; +void testSwitchBranchCount(int expr) { + switch (expr) { // COMPLIANT: Branch count is 2 case 1: + i++; break; default: + i++; break; } - // NON_COMPLIANT - boolean expression - switch (flag) { - case true: - break; - case false: - break; + switch (expr) { // NON_COMPLIANT: Branch count is 1 default: + i++; break; } - // NON_COMPLIANT - boolean comparison expression - switch (i == 0) { - case true: - break; - case false: - break; + switch (expr) { // NON_COMPLIANT: Branch count is 1 + case 1: + case 2: default: + i++; break; } } -int f() { return 1; } - -void test_complex_violations(int expr) { - int i = 0; - bool flag = true; - - // NON_COMPLIANT - multiple violations: - // - Boolean expression (16-7) - // - Missing default (16-4) - // - Single clause (16-6) - switch (flag) { - case true: - i++; - } +enum E { V1, V2, V3 }; - // NON_COMPLIANT - multiple violations: - // - Fall-through case (16-3) - // - Default not first/last (16-5) - switch (expr) { - case 1: // NON_COMPLIANT - falls through - i++; - default: // NON_COMPLIANT - not first/last +void testDefaultLabelPresence(int expr) { + switch (expr) { // COMPLIANT: There is a default branch + case 1: i++; break; - case 2: + default: i++; break; } - switch (expr) { - int i = 0; - case 1: - i++; - } - - switch (int x = f(); x) { + switch (expr) { // NON_COMPLIANT: Default branch is missing case 1: i++; + break; } - switch (expr = f(); expr) { - case 1: - i++; - } + E e; - switch (expr) { - case 1: - { - case 2: - i++; - } + switch (e) { // COMPLIANT: There is a default branch + case V1: + i++; + break; + default: + break; } - switch (expr) { - case 1: { + switch (e) { // NON_COMPLIANT: Default branch is missing on a non-exhaustive + // enum switch + case V1: i++; - goto someLabel; - someLabel: + break; + case V2: i++; - } + break; } - switch (expr) { - int x = 1; - case 1: + switch (e) { // COMPLIANT: Default branch can be omitted on an exhaustive enum + // switch + case V1: + i++; + break; + case V2: i++; + break; + case V3: + i++; + break; } } \ No newline at end of file From fabb7e5db8af43e4b245dee1fb61dc971674b1b2 Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Thu, 31 Jul 2025 18:39:06 -0400 Subject: [PATCH 4/8] Finish first draft --- .../AppropriateStructureOfSwitchStatement.ql | 109 +++++++++++------- ...opriateStructureOfSwitchStatement.expected | 27 ++--- cpp/misra/test/rules/RULE-9-4-2/test.cpp | 9 +- 3 files changed, 83 insertions(+), 62 deletions(-) diff --git a/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql b/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql index 3d68e31abb..f419834325 100644 --- a/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql +++ b/cpp/misra/src/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.ql @@ -19,53 +19,74 @@ 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 - ( - // RULE-16-1: Switch not well-formed (has inappropriate statements) - exists(SwitchCase case | - case = switch.getASwitchCase() and - switchCaseNotWellFormed(case) and - message = "has a case that contains inappropriate statements (only expression, compound, selection, iteration or try statements are allowed)" + /* 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 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`. ) - or - // RULE-16-2: Nested switch labels - exists(SwitchCase case | - case = switch.getASwitchCase() and - case instanceof NestedSwitchCase and - message = "contains a switch label that is not directly within the switch body" - ) - or - // RULE-16-3: Non-empty case doesn't terminate with break - exists(SwitchCase case | - case = switch.getASwitchCase() and - case instanceof CaseDoesNotTerminate and - message = "has a non-empty case that does not terminate with an unconditional break or throw statement" - ) - or - // RULE-16-4: Missing default clause - not switch.hasDefaultCase() and - message = "is missing a default clause" - or - // RULE-16-5: Default clause not first or last - exists(SwitchCase defaultCase | - switch.getDefaultCase() = defaultCase and - exists(defaultCase.getPreviousSwitchCase()) and - finalClauseInSwitchNotDefault(switch) and - message = "has a default clause that is not the first or last switch label" - ) - or - // RULE-16-6: Less than two case clauses - count(SwitchCase case | - switch.getASwitchCase() = case and - case.getNextSwitchCase() != case.getFollowingStmt() - ) + 1 < 2 and - message = "has fewer than two switch-clauses" - or - // RULE-16-7: Boolean switch expression - switch instanceof BooleanSwitchStmt and - message = "has a controlling expression of essentially Boolean type" - ) + ) 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 + "." diff --git a/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected index 8900facc63..ab8320ef8f 100644 --- a/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected +++ b/cpp/misra/test/rules/RULE-9-4-2/AppropriateStructureOfSwitchStatement.expected @@ -1,15 +1,12 @@ -| test.cpp:40:3:40:8 | Switch statement has a case that contains inappropriate statements (only expression, compound, selection, iteration or try statements are allowed). | -| test.cpp:86:3:86:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | -| test.cpp:86:3:86:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | -| test.cpp:98:3:98:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | -| test.cpp:123:3:123:8 | Switch statement is missing a default clause. | -| test.cpp:166:3:166:8 | Switch statement has a default clause that is not the first or last switch label. | -| test.cpp:203:3:203:8 | Switch statement has fewer than two switch-clauses. | -| test.cpp:210:3:210:8 | Switch statement has fewer than two switch-clauses. | -| test.cpp:235:3:235:8 | Switch statement has a controlling expression of essentially Boolean type. | -| test.cpp:245:3:245:8 | Switch statement has a controlling expression of essentially Boolean type. | -| test.cpp:266:3:266:8 | Switch statement has a controlling expression of essentially Boolean type. | -| test.cpp:266:3:266:8 | Switch statement is missing a default clause. | -| test.cpp:266:3:266:8 | Switch statement has fewer than two switch-clauses. | -| test.cpp:275:3:275:8 | Switch statement has a non-empty case that does not terminate with an unconditional break or throw statement. | -| test.cpp:275:3:275:8 | Switch statement has a default clause that is not the first or last switch label. | \ No newline at end of file +| test.cpp:28:3:37:3 | switch (...) ... | Switch statement contains a statement that that is not a simple declaration. | +| test.cpp:51:3:60:3 | switch (...) ... | Switch statement contains a switch label that is not directly within the switch body. | +| test.cpp:62:3:71:3 | switch (...) ... | Switch statement contains a switch label that is not directly within the switch body. | +| test.cpp:75:3:85:3 | switch (...) ... | Switch statement contains a statement label that is not a case label. | +| test.cpp:89:3:97:3 | switch (...) ... | Switch statement has a statement that is not a case label as its first element. | +| test.cpp:147:3:154:3 | switch (...) ... | Switch statement is missing a terminator that moves the control out of its body. | +| test.cpp:188:3:192:3 | switch (...) ... | Switch statement contains less than two branches. | +| test.cpp:194:3:200:3 | switch (...) ... | Switch statement contains less than two branches. | +| test.cpp:215:3:219:3 | switch (...) ... | Switch statement contains less than two branches. | +| test.cpp:215:3:219:3 | switch (...) ... | Switch statement lacks a default case. | +| test.cpp:223:3:229:3 | switch (...) ... | Switch statement lacks a case for one of its variants. | +| test.cpp:231:3:239:3 | switch (...) ... | Switch statement lacks a case for one of its variants. | diff --git a/cpp/misra/test/rules/RULE-9-4-2/test.cpp b/cpp/misra/test/rules/RULE-9-4-2/test.cpp index e99a2fb1f4..6bbb97e81b 100644 --- a/cpp/misra/test/rules/RULE-9-4-2/test.cpp +++ b/cpp/misra/test/rules/RULE-9-4-2/test.cpp @@ -1,3 +1,5 @@ +#include + int i = 0; /** @@ -77,16 +79,16 @@ void testOtherLabelsInBranch(int expr) { someLabel: i++; break; + } default: break; } - } } void testLeadingNonCaseStatement(int expr) { switch (expr) { // NON_COMPLIANT: Non-case statement is the first statement in // the switch body - + int x = 1; case 1: i++; break; @@ -95,7 +97,7 @@ void testLeadingNonCaseStatement(int expr) { } } -[[noreturn]] void f() {} +[[noreturn]] void f() { exit(0); } void g() {} void testSwitchBranchTerminator(int expr) { @@ -166,6 +168,7 @@ void testSwitchBranchTerminator(int expr) { [[fallthrough]]; default: i++; + break; } error: From 6909528361de035834ce6cdd60e7fa30311560cc Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Fri, 1 Aug 2025 14:56:20 -0400 Subject: [PATCH 5/8] Add test case for Rule 9.5.2 --- cpp/misra/test/rules/RULE-9-5-2/test.cpp | 129 +++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 cpp/misra/test/rules/RULE-9-5-2/test.cpp diff --git a/cpp/misra/test/rules/RULE-9-5-2/test.cpp b/cpp/misra/test/rules/RULE-9-5-2/test.cpp new file mode 100644 index 0000000000..9d5255f0c0 --- /dev/null +++ b/cpp/misra/test/rules/RULE-9-5-2/test.cpp @@ -0,0 +1,129 @@ +#include + +/* Helper functions */ +std::vector getData() { return {1, 2, 3}; } +std::vector processData(const std::vector &input) { return input; } +std::vector getContainer() { return {4, 5, 6}; } + +class MyContainer { +public: + MyContainer() = default; + MyContainer(std::vector data) : data_(data) {} + std::vector::iterator begin() { return data_.begin(); } + std::vector::iterator end() { return data_.end(); } + +private: + std::vector data_{7, 8, 9}; +}; + +class ConvertibleToVector { +public: + operator std::vector() const { return {}; } + std::array::iterator begin() { return data_.begin(); } + std::array::iterator end() { return data_.end(); } + +private: + std::array data_{7, 8, 9}; +}; + +std::vector operator+(const std::vector &a, + const std::vector &b) { + std::vector result = a; + result.insert(result.end(), b.begin(), b.end()); + return result; +} + +std::vector convertToIntVector(std::vector vector) { return vector; } + +int main() { + std::vector localVec = {1, 2, 3}; + std::vector *vecPtr = &localVec; + ConvertibleToVector convertible; + + /* ========== 1. EXPLICIT FUNCTION CALLS ========== */ + + for (auto x : getContainer()) { // COMPLIANT: 1 function call only + } + + for (auto x : processData(getData())) { // NON_COMPLIANT: 2 function calls + } + + /* ========== 2. OBJECT CREATION (CONSTRUCTOR CALLS) ========== */ + + for (auto x : + std::vector{1, 2, 3}) { // COMPLIANT: 1 constructor call only + } + + for (auto x : MyContainer()) { // COMPLIANT: 1 constructor call only + } + + for (auto x : std::string("hello")) { // COMPLIANT: 1 constructor call only + } + + for (auto x : std::vector( + getData())) { // NON-COMPLIANT: 1 constructor + 1 function call + } + + for (auto x : MyContainer(processData( + localVec))) { // NON-COMPLIANT: 1 constructor + 1 function call + } + + auto data = std::vector(getData()); + for (auto x : data) { // NON-COMPLIANT: 1 constructor + 1 function call + } + + MyContainer myContainer = MyContainer(processData(localVec)); + for (auto x : myContainer) { // NON-COMPLIANT: 1 constructor + 1 function call + } + + /* ========== 3. OPERATOR OVERLOADING ========== */ + + std::vector anotherVec = {4, 5, 6}; + for (auto x : localVec + anotherVec) { // COMPLIANT: 1 operator+ call only + } + + std::vector vec1 = {1}, vec2 = {2}, vec3 = {3}; + for (auto x : (vec1 + vec2) + vec3) { // NON-COMPLIANT: 2 operator+ calls + } + + for (auto x : + getData() + + processData( + localVec)) { // NON-COMPLIANT: 2 function calls + 1 operator call + } + + std::vector vec1 = {1}, vec2 = {2}, vec3 = {3}; + std::vector appendedVector = (vec1 + vec2) + vec3; + for (auto x : appendedVector) { // COMPLIANT: 0 calls + } + + std::vector appendedVector2 = getData() + processData(localVec); + for (auto x : appendedVector2) { // COMPLIANT: 0 calls + } + + /* ========== 4. IMPLICIT CONVERSIONS ========== */ + + ConvertibleToVector convertible; + for (auto x : convertible) { // COMPLIANT: 1 conversion operator call only + } + + for (auto x : + convertToIntVector(convertible)) { // NON_COMPLIANT: 1 function call + 1 + // conversion operator call + } + + for (auto x : + convertToIntVector(convertible)) { // NON_COMPLIANT: 1 function call + 1 + // conversion operator call + } + + std::vector intVector1 = convertToIntVector(convertible); + for (auto x : intVector1) { // NON_COMPLIANT: 1 function call + 1 + // conversion operator call + } + + std::vector intVector2 = convertToIntVector(convertible); + for (auto x : intVector2) { // NON_COMPLIANT: 1 function call + 1 + // conversion operator call + } +} \ No newline at end of file From 0b5250f892849cee3c9959005534c1196513a30f Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Fri, 1 Aug 2025 15:07:53 -0400 Subject: [PATCH 6/8] Minor --- cpp/misra/test/rules/RULE-9-5-2/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/misra/test/rules/RULE-9-5-2/test.cpp b/cpp/misra/test/rules/RULE-9-5-2/test.cpp index 9d5255f0c0..ba3bfa191b 100644 --- a/cpp/misra/test/rules/RULE-9-5-2/test.cpp +++ b/cpp/misra/test/rules/RULE-9-5-2/test.cpp @@ -18,7 +18,7 @@ class MyContainer { class ConvertibleToVector { public: - operator std::vector() const { return {}; } + operator std::vector() const { return {7, 8, 9}; } std::array::iterator begin() { return data_.begin(); } std::array::iterator end() { return data_.end(); } From b23986210658da7dab5751520793f26aad706c2a Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Fri, 1 Aug 2025 15:52:58 -0400 Subject: [PATCH 7/8] Update test case for Rule 9.5.2 --- cpp/misra/test/rules/RULE-9-5-2/test.cpp | 26 +++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/cpp/misra/test/rules/RULE-9-5-2/test.cpp b/cpp/misra/test/rules/RULE-9-5-2/test.cpp index ba3bfa191b..025a247ad7 100644 --- a/cpp/misra/test/rules/RULE-9-5-2/test.cpp +++ b/cpp/misra/test/rules/RULE-9-5-2/test.cpp @@ -1,4 +1,5 @@ #include +#include /* Helper functions */ std::vector getData() { return {1, 2, 3}; } @@ -9,8 +10,8 @@ class MyContainer { public: MyContainer() = default; MyContainer(std::vector data) : data_(data) {} - std::vector::iterator begin() { return data_.begin(); } - std::vector::iterator end() { return data_.end(); } + std::vector::const_iterator begin() const { return data_.begin(); } + std::vector::const_iterator end() const { return data_.end(); } private: std::vector data_{7, 8, 9}; @@ -19,15 +20,15 @@ class MyContainer { class ConvertibleToVector { public: operator std::vector() const { return {7, 8, 9}; } - std::array::iterator begin() { return data_.begin(); } - std::array::iterator end() { return data_.end(); } + std::array::const_iterator begin() const { return data_.begin(); } + std::array::const_iterator end() const { return data_.end(); } private: std::array data_{7, 8, 9}; }; -std::vector operator+(const std::vector &a, - const std::vector &b) { +std::vector operator+(std::vector a, + std::vector b) { std::vector result = a; result.insert(result.end(), b.begin(), b.end()); return result; @@ -37,8 +38,6 @@ std::vector convertToIntVector(std::vector vector) { return vector; } int main() { std::vector localVec = {1, 2, 3}; - std::vector *vecPtr = &localVec; - ConvertibleToVector convertible; /* ========== 1. EXPLICIT FUNCTION CALLS ========== */ @@ -92,7 +91,6 @@ int main() { localVec)) { // NON-COMPLIANT: 2 function calls + 1 operator call } - std::vector vec1 = {1}, vec2 = {2}, vec3 = {3}; std::vector appendedVector = (vec1 + vec2) + vec3; for (auto x : appendedVector) { // COMPLIANT: 0 calls } @@ -104,26 +102,26 @@ int main() { /* ========== 4. IMPLICIT CONVERSIONS ========== */ ConvertibleToVector convertible; - for (auto x : convertible) { // COMPLIANT: 1 conversion operator call only + for (int x : convertible) { // COMPLIANT: 1 conversion operator call only } - for (auto x : + for (int x : convertToIntVector(convertible)) { // NON_COMPLIANT: 1 function call + 1 // conversion operator call } - for (auto x : + for (int x : convertToIntVector(convertible)) { // NON_COMPLIANT: 1 function call + 1 // conversion operator call } std::vector intVector1 = convertToIntVector(convertible); - for (auto x : intVector1) { // NON_COMPLIANT: 1 function call + 1 + for (int x : intVector1) { // NON_COMPLIANT: 1 function call + 1 // conversion operator call } std::vector intVector2 = convertToIntVector(convertible); - for (auto x : intVector2) { // NON_COMPLIANT: 1 function call + 1 + for (int x : intVector2) { // NON_COMPLIANT: 1 function call + 1 // conversion operator call } } \ No newline at end of file From a0133887da526bb2ab7ed971556ecfe142d07b1a Mon Sep 17 00:00:00 2001 From: Jeongsoo Lee Date: Fri, 1 Aug 2025 18:01:15 -0400 Subject: [PATCH 8/8] Add first draft of `ForRangeInitializerAtMostOneFunctionCall` --- ...orRangeInitializerAtMostOneFunctionCall.ql | 8 +-- ...eInitializerAtMostOneFunctionCall.expected | 9 +++- cpp/misra/test/rules/RULE-9-5-2/test.cpp | 54 ++++++++++--------- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql b/cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql index 47d27287bc..b78da2ad99 100644 --- a/cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql +++ b/cpp/misra/src/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.ql @@ -19,7 +19,9 @@ import cpp import codingstandards.cpp.misra -from +from RangeBasedForStmt foreach, string message where - not isExcluded(x, StatementsPackage::forRangeInitializerAtMostOneFunctionCallQuery()) and -select + not isExcluded(foreach, StatementsPackage::forRangeInitializerAtMostOneFunctionCallQuery()) and + count(Call call | call = foreach.getRange().getAChild*() | call) >= 2 and + message = "has nested call expression in its initializer" +select foreach, "Range-based for loop " + message + "." diff --git a/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected b/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected index 2ec1a0ac6c..a8567ff48d 100644 --- a/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected +++ b/cpp/misra/test/rules/RULE-9-5-2/ForRangeInitializerAtMostOneFunctionCall.expected @@ -1 +1,8 @@ -No expected results have yet been specified \ No newline at end of file +| test.cpp:48:3:49:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | +| test.cpp:56:3:59:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | +| test.cpp:71:3:73:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | +| test.cpp:95:3:97:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | +| test.cpp:99:3:101:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | +| test.cpp:103:3:107:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | +| test.cpp:116:3:119:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | +| test.cpp:121:3:124:3 | for(...:...) ... | Range-based for loop has nested call expression in its initializer. | diff --git a/cpp/misra/test/rules/RULE-9-5-2/test.cpp b/cpp/misra/test/rules/RULE-9-5-2/test.cpp index 025a247ad7..b9ec5a0574 100644 --- a/cpp/misra/test/rules/RULE-9-5-2/test.cpp +++ b/cpp/misra/test/rules/RULE-9-5-2/test.cpp @@ -1,5 +1,5 @@ -#include #include +#include /* Helper functions */ std::vector getData() { return {1, 2, 3}; } @@ -10,8 +10,8 @@ class MyContainer { public: MyContainer() = default; MyContainer(std::vector data) : data_(data) {} - std::vector::const_iterator begin() const { return data_.begin(); } - std::vector::const_iterator end() const { return data_.end(); } + std::vector::iterator begin() { return data_.begin(); } + std::vector::iterator end() { return data_.end(); } private: std::vector data_{7, 8, 9}; @@ -20,15 +20,16 @@ class MyContainer { class ConvertibleToVector { public: operator std::vector() const { return {7, 8, 9}; } - std::array::const_iterator begin() const { return data_.begin(); } - std::array::const_iterator end() const { return data_.end(); } + std::array::iterator begin() { return data_.begin(); } + std::array::iterator end() { return data_.end(); } + std::array::const_iterator begin() const { return data_.cbegin(); } + std::array::const_iterator end() const { return data_.cend(); } private: std::array data_{7, 8, 9}; }; -std::vector operator+(std::vector a, - std::vector b) { +std::vector operator+(std::vector a, std::vector b) { std::vector result = a; result.insert(result.end(), b.begin(), b.end()); return result; @@ -49,8 +50,12 @@ int main() { /* ========== 2. OBJECT CREATION (CONSTRUCTOR CALLS) ========== */ + for (auto x : std::vector(3)) { // COMPLIANT: 1 constructor call only + } + for (auto x : - std::vector{1, 2, 3}) { // COMPLIANT: 1 constructor call only + std::vector{1, 2, 3}) { // NON_COMPLIANT: 2 constructor call to + // `vector` and `initializer_list`, respectively } for (auto x : MyContainer()) { // COMPLIANT: 1 constructor call only @@ -77,12 +82,22 @@ int main() { /* ========== 3. OPERATOR OVERLOADING ========== */ + std::vector vec1 = {1}, vec2 = {2}, vec3 = {3}; + std::vector appendedVector = (vec1 + vec2) + vec3; + for (auto x : appendedVector) { // COMPLIANT: 0 calls + } + + std::vector appendedVector2 = getData() + processData(localVec); + for (auto x : appendedVector2) { // COMPLIANT: 0 calls + } + std::vector anotherVec = {4, 5, 6}; - for (auto x : localVec + anotherVec) { // COMPLIANT: 1 operator+ call only + for (auto x : localVec + anotherVec) { // NON_COMPLIANT: 2 calls to vector's + // constructor, 1 operator+ call } - std::vector vec1 = {1}, vec2 = {2}, vec3 = {3}; - for (auto x : (vec1 + vec2) + vec3) { // NON-COMPLIANT: 2 operator+ calls + for (auto x : (vec1 + vec2) + vec3) { // NON-COMPLIANT: 3 calls to vector's + // constructor, 2 operator+ calls } for (auto x : @@ -91,18 +106,11 @@ int main() { localVec)) { // NON-COMPLIANT: 2 function calls + 1 operator call } - std::vector appendedVector = (vec1 + vec2) + vec3; - for (auto x : appendedVector) { // COMPLIANT: 0 calls - } - - std::vector appendedVector2 = getData() + processData(localVec); - for (auto x : appendedVector2) { // COMPLIANT: 0 calls - } - /* ========== 4. IMPLICIT CONVERSIONS ========== */ ConvertibleToVector convertible; - for (int x : convertible) { // COMPLIANT: 1 conversion operator call only + for (int x : + ConvertibleToVector()) { // COMPLIANT: 1 conversion operator call only } for (int x : @@ -116,12 +124,10 @@ int main() { } std::vector intVector1 = convertToIntVector(convertible); - for (int x : intVector1) { // NON_COMPLIANT: 1 function call + 1 - // conversion operator call + for (int x : intVector1) { // COMPLIANT: 0 function calls } std::vector intVector2 = convertToIntVector(convertible); - for (int x : intVector2) { // NON_COMPLIANT: 1 function call + 1 - // conversion operator call + for (int x : intVector2) { // COMPLIANT: 0 function calls } } \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy