diff --git a/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.md b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.md new file mode 100644 index 0000000000..52e2c70fce --- /dev/null +++ b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.md @@ -0,0 +1,106 @@ +# FLP30-C: Do not use floating-point variables as loop counters + +This query implements the CERT-C rule FLP30-C: + +> Do not use floating-point variables as loop counters + + +## Description + +Because floating-point numbers represent real numbers, it is often mistakenly assumed that they can represent any simple fraction exactly. Floating-point numbers are subject to representational limitations just as integers are, and binary floating-point numbers cannot represent all real numbers exactly, even if they can be represented in a small number of decimal digits. + +In addition, because floating-point numbers can represent large values, it is often mistakenly assumed that they can represent all significant digits of those values. To gain a large dynamic range, floating-point numbers maintain a fixed number of precision bits (also called the significand) and an exponent, which limit the number of significant digits they can represent. + +Different implementations have different precision limitations, and to keep code portable, floating-point variables must not be used as the loop induction variable. See Goldberg's work for an introduction to this topic \[[Goldberg 1991](https://www.securecoding.cert.org/confluence/display/java/Rule+AA.+References#RuleAA.References-Goldberg91)\]. + +For the purpose of this rule, a *loop counter* is an induction variable that is used as an operand of a comparison expression that is used as the controlling expression of a `do`, `while`, or `for` loop. An *induction variable* is a variable that gets increased or decreased by a fixed amount on every iteration of a loop \[[Aho 1986](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Aho1986)\]. Furthermore, the change to the variable must occur directly in the loop body (rather than inside a function executed within the loop). + +## Noncompliant Code Example + +In this noncompliant code example, a floating-point variable is used as a loop counter. The decimal number `0.1` is a repeating fraction in binary and cannot be exactly represented as a binary floating-point number. Depending on the implementation, the loop may iterate 9 or 10 times. + +```cpp +void func(void) { + for (float x = 0.1f; x <= 1.0f; x += 0.1f) { + /* Loop may iterate 9 or 10 times */ + } +} +``` +For example, when compiled with GCC or Microsoft Visual Studio 2013 and executed on an x86 processor, the loop is evaluated only nine times. + +## Compliant Solution + +In this compliant solution, the loop counter is an integer from which the floating-point value is derived: + +```cpp +#include + +void func(void) { + for (size_t count = 1; count <= 10; ++count) { + float x = count / 10.0f; + /* Loop iterates exactly 10 times */ + } +} +``` + +## Noncompliant Code Example + +In this noncompliant code example, a floating-point loop counter is incremented by an amount that is too small to change its value given its precision: + +```cpp +void func(void) { + for (float x = 100000001.0f; x <= 100000010.0f; x += 1.0f) { + /* Loop may not terminate */ + } +} +``` +On many implementations, this produces an infinite loop. + +## Compliant Solution + +In this compliant solution, the loop counter is an integer from which the floating-point value is derived. The variable `x` is assigned a computed value to reduce compounded rounding errors that are present in the noncompliant code example. + +```cpp +void func(void) { + for (size_t count = 1; count <= 10; ++count) { + float x = 100000000.0f + (count * 1.0f); + /* Loop iterates exactly 10 times */ + } +} +``` + +## Risk Assessment + +The use of floating-point variables as loop counters can result in [unexpected behavior ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
FLP30-C Low Probable Low P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 for-loop-float Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-FLP30 Fully implemented
Clang 3.9 cert-flp30-c Checked by clang-tidy
CodeSonar 7.2p0 LANG.STRUCT.LOOP.FPC Float-typed loop counter
Compass/ROSE
Coverity 2017.07 MISRA C 2004 Rule 13.4 MISRA C 2012 Rule 14.1 Implemented
ECLAIR 1.2 CC2.FLP30 Fully implemented
Helix QAC 2022.4 C3339, C3340, C3342 C++4234
Klocwork 2022.4 MISRA.FOR.COUNTER.FLT
LDRA tool suite 9.7.1 39 S Fully implemented
Parasoft C/C++test 2022.2 CERT_C-FLP30-a Do not use floating point variables as loop counters
PC-lint Plus 1.4 9009 Fully supported
Polyspace Bug Finder R2022b CERT C: Rule FLP30-C Checks for use of float variable as loop counter (rule fully covered)
PRQA QA-C 9.7 3339, 3340, 3342 Partially implemented
PRQA QA-C++ 4.4 4234
PVS-Studio 7.23 V1034
RuleChecker 22.04 for-loop-float Fully checked
SonarQube C/C++ Plugin 3.11 S2193 Fully implemented
TrustInSoft Analyzer 1.38 non-terminating Exhaustively detects non-terminating statements (see one compliant and one non-compliant example ).
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FLP30-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C FLP30-CPP. Do not use floating-point variables as loop counters Prior to 2018-01-12: CERT: Unspecified Relationship
CERT Oracle Secure Coding Standard for Java NUM09-J. Do not use floating-point variables as loop counters Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Floating-Point Arithmetic \[PLF\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Directive 1.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 14.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ Aho 1986 \]
\[ Goldberg 1991 \]
\[ Lockheed Martin 05 \] AV Rule 197
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FLP30-C: Do not use floating-point variables as loop counters](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.ql b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.ql new file mode 100644 index 0000000000..a26736707c --- /dev/null +++ b/c/cert/src/rules/FLP30-C/FloatingPointLoopCounters.ql @@ -0,0 +1,51 @@ +/** + * @id c/cert/floating-point-loop-counters + * @name FLP30-C: Do not use floating-point variables as loop counters + * @description Loop counters should not use floating-point variables to keep code portable. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/cert/id/flp30-c + * maintainability + * readability + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import codingstandards.cpp.Loops + +/* + * A variable that is increased or decreased by a fixed amount on each iteration. + */ + +class InductionVariable extends Variable { + Loop loop; + Expr update; + + InductionVariable() { + update.getParent+() = loop and + ( + update.(AssignArithmeticOperation).getRValue().isConstant() and + update.(AssignArithmeticOperation).getLValue() = this.getAnAccess() + or + exists(BinaryArithmeticOperation binop | + update.(Assignment).getLValue() = this.getAnAccess() and + update.(Assignment).getRValue() = binop and + binop.getAnOperand() = this.getAnAccess() and + binop.getAnOperand().isConstant() + ) + or + update.(CrementOperation).getOperand() = this.getAnAccess() + ) + } +} + +from Loop loop, InductionVariable loopCounter, ComparisonOperation comparison +where + not isExcluded(loop, Statements4Package::floatingPointLoopCountersQuery()) and + loop.getControllingExpr() = comparison and + comparison.getAnOperand() = loopCounter.getAnAccess() and + loopCounter.getType() instanceof FloatingPointType +select loop, "Loop using a $@ of type floating-point.", loopCounter, "loop counter" diff --git a/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.expected b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.expected new file mode 100644 index 0000000000..43f8a04a66 --- /dev/null +++ b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.expected @@ -0,0 +1,3 @@ +| test.c:3:3:4:3 | for(...;...;...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter | +| test.c:5:3:7:3 | while (...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter | +| test.c:9:3:11:22 | do (...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter | diff --git a/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.qlref b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.qlref new file mode 100644 index 0000000000..1ada999730 --- /dev/null +++ b/c/cert/test/rules/FLP30-C/FloatingPointLoopCounters.qlref @@ -0,0 +1 @@ +rules/FLP30-C/FloatingPointLoopCounters.ql \ No newline at end of file diff --git a/c/cert/test/rules/FLP30-C/test.c b/c/cert/test/rules/FLP30-C/test.c new file mode 100644 index 0000000000..e63dc5d1ed --- /dev/null +++ b/c/cert/test/rules/FLP30-C/test.c @@ -0,0 +1,26 @@ +void f1() { + float f = 0.0F; + for (f = 0.0F; f < 10.0F; f += 0.2F) { // NON_COMPLIANT + } + while (f < 10.0F) { // NON_COMPLIANT + f = f * 2.0F; + } + + do { // NON_COMPLIANT + f *= 2.0F; + } while (f < 10.0F); +} + +void f2() { + + for (int i = 0; i < 10; i++) { // COMPLIANT + } + int j = 0; + while (j < 10) { // COMPLIANT + j = j * 2; + } + + do { + j++; + } while (j < 10); // COMPLIANT +} diff --git a/c/common/test/rules/gotostatementcondition/GotoStatementCondition.expected b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.expected new file mode 100644 index 0000000000..e522289c7b --- /dev/null +++ b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.expected @@ -0,0 +1,3 @@ +| test.c:5:3:5:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:5:3:5:10 | goto ... | L1 | test.c:2:1:2:3 | label ...: | label ...: | +| test.c:14:3:14:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:14:3:14:10 | goto ... | L2 | test.c:12:1:12:3 | label ...: | label ...: | +| test.c:16:3:16:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:16:3:16:10 | goto ... | L1 | test.c:11:1:11:3 | label ...: | label ...: | diff --git a/c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql new file mode 100644 index 0000000000..826a161cc6 --- /dev/null +++ b/c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition diff --git a/c/common/test/rules/gotostatementcondition/test.c b/c/common/test/rules/gotostatementcondition/test.c new file mode 100644 index 0000000000..2c189cd433 --- /dev/null +++ b/c/common/test/rules/gotostatementcondition/test.c @@ -0,0 +1,17 @@ +void f1() { +L1:; + goto L2; // COMPLIANT + ; + goto L1; // NON_COMPLIANT + +L2:; +} + +void f2() { +L1:; +L2: + goto L3; // COMPLIANT + goto L2; // NON_COMPLIANT +L3: + goto L1; // NON_COMPLIANT +} diff --git a/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected new file mode 100644 index 0000000000..910ea55bab --- /dev/null +++ b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected @@ -0,0 +1,3 @@ +| test.c:16:3:20:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:16:3:20:3 | if (...) ... | `if...else` | +| test.c:33:5:37:5 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:33:5:37:5 | if (...) ... | `if...else` | +| test.c:45:3:55:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:45:3:55:3 | if (...) ... | `if...else` | diff --git a/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql new file mode 100644 index 0000000000..d96cb456ce --- /dev/null +++ b/c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct diff --git a/c/common/test/rules/ifelseterminationconstruct/test.c b/c/common/test/rules/ifelseterminationconstruct/test.c new file mode 100644 index 0000000000..c3fb95df15 --- /dev/null +++ b/c/common/test/rules/ifelseterminationconstruct/test.c @@ -0,0 +1,56 @@ +void f1(int p1) { + + if (p1) { // COMPLIANT + ; + } else if (p1) { + ; + } else { + ; + } +} + +void f2(int p1) { + if (p1) { // COMPLIANT + ; + } + if (p1) { // NON_COMPLIANT + ; + } else if (p1) { + ; + } +} + +void f3(int p1) { + + if (p1) { // COMPLIANT + ; + } else { + ; + } + if (p1) { // COMPLIANT + ; + } else if (p1) { + if (p1) { // NON_COMPLIANT + ; + } else if (p1) { + ; + } + } else { + ; + } +} + +void f4(int p1) { + + if (p1) { // NON_COMPLIANT + ; + } else if (p1) { + if (p1) { // COMPLIANT + ; + } else if (p1) { + ; + } else { + ; + } + } +} \ No newline at end of file diff --git a/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected new file mode 100644 index 0000000000..3adeecc903 --- /dev/null +++ b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected @@ -0,0 +1,3 @@ +| test.c:9:5:9:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:9:5:9:11 | case ...: | case ...: | test.c:6:3:17:3 | switch (...) ... | switch (...) ... | +| test.c:36:5:36:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:36:5:36:11 | case ...: | case ...: | test.c:23:3:43:3 | switch (...) ... | switch (...) ... | +| test.c:76:5:76:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:76:5:76:11 | case ...: | case ...: | test.c:73:3:79:3 | switch (...) ... | switch (...) ... | diff --git a/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql new file mode 100644 index 0000000000..a23fe0b2f9 --- /dev/null +++ b/c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch diff --git a/c/common/test/rules/nestedlabelinswitch/test.c b/c/common/test/rules/nestedlabelinswitch/test.c new file mode 100644 index 0000000000..5c04578f0b --- /dev/null +++ b/c/common/test/rules/nestedlabelinswitch/test.c @@ -0,0 +1,80 @@ +void f(); + +void f1(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + case 2: // NON_COMPLIANT + j; + break; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f2(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + case 3: // COMPLIANT + if (i) { + j; + case 4: // NON_COMPLIANT + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f3(int p1) { + + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + break; + case 3: // COMPLIANT + if (i) { + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f4(int p1) { + switch (p1) { + int i; + if (i) { + case 1: // NON_COMPLIANT + f(); + } + } +} diff --git a/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected new file mode 100644 index 0000000000..14cc8431da --- /dev/null +++ b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected @@ -0,0 +1 @@ +| test.c:5:3:24:3 | switch (...) ... | $@ statement not well formed because the first statement in a well formed switch statement must be a case clause. | test.c:5:3:24:3 | switch (...) ... | Switch | diff --git a/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql new file mode 100644 index 0000000000..65188d04f7 --- /dev/null +++ b/c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition diff --git a/c/common/test/rules/switchcasepositioncondition/test.c b/c/common/test/rules/switchcasepositioncondition/test.c new file mode 100644 index 0000000000..bbdbddfb80 --- /dev/null +++ b/c/common/test/rules/switchcasepositioncondition/test.c @@ -0,0 +1,42 @@ +void f1(int p1); + +void f2(int p1) { + + switch (p1) { + start:; // NON_COMPLIANT + case 1: + if (p1) { + ; + }; + break; + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} +void f3(int p1) { + + switch (p1) { // COMPLIANT + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} diff --git a/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected new file mode 100644 index 0000000000..6843b78cd9 --- /dev/null +++ b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected @@ -0,0 +1,3 @@ +| test.c:4:3:11:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.c:4:3:11:3 | switch (...) ... | Switch | test.c:5:3:5:9 | case ...: | case | +| test.c:14:3:21:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.c:14:3:21:3 | switch (...) ... | Switch | test.c:15:3:15:10 | case ...: | case | +| test.c:26:3:31:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.c:26:3:31:3 | switch (...) ... | Switch | test.c:27:3:27:9 | case ...: | case | diff --git a/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql new file mode 100644 index 0000000000..0a398a99a9 --- /dev/null +++ b/c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed diff --git a/c/common/test/rules/switchnotwellformed/test.c b/c/common/test/rules/switchnotwellformed/test.c new file mode 100644 index 0000000000..1082ee405c --- /dev/null +++ b/c/common/test/rules/switchnotwellformed/test.c @@ -0,0 +1,41 @@ + +void f1(); +void f2(int p1) { + switch (p1) { + case 1: + f1(); + int y = p1; // NON_COMPLIANT - `DeclStmt` whose parent + // statement is the switch body + f1(); + break; + } +} +void f3(int p1) { + switch (p1) { + case 10: + f1(); + goto L1; // NON_COMPLIANT - `JumpStmt` whose parent statement is the// + // switch// body + case 2: + break; + } +L1:; +} + +void f4(int p1) { + switch (p1) { + case 1: + L1:; // NON_COMPLIANT - `LabelStmt` whose parent statement is the + // switch body + break; + } +} + +void f5(int p1) { + switch (p1) { + case 1: // COMPLIANT + default: + p1 = 0; + break; + } +} diff --git a/c/misra/src/rules/RULE-14-2/ForLoopNotWellFormed.ql b/c/misra/src/rules/RULE-14-2/ForLoopNotWellFormed.ql new file mode 100644 index 0000000000..106bd9b5c6 --- /dev/null +++ b/c/misra/src/rules/RULE-14-2/ForLoopNotWellFormed.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/for-loop-not-well-formed + * @name RULE-14-2: A for loop shall be well-formed + * @description A well-formed for loop makes code easier to review. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-14-2 + * readability + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.Loops + +from ForStmt for, Element reasonLocation, string reason, string reasonLabel +where + not isExcluded(for, Statements4Package::forLoopNotWellFormedQuery()) and + isInvalidLoop(for, reason, reasonLocation, reasonLabel) +select for, "For loop is not well formed, " + reason + ".", reasonLocation, reasonLabel diff --git a/c/misra/src/rules/RULE-14-3/ControllingExprInvariant.ql b/c/misra/src/rules/RULE-14-3/ControllingExprInvariant.ql new file mode 100644 index 0000000000..eb8e9ede82 --- /dev/null +++ b/c/misra/src/rules/RULE-14-3/ControllingExprInvariant.ql @@ -0,0 +1,79 @@ +/** + * @id c/misra/controlling-expr-invariant + * @name RULE-14-3: Controlling expressions shall not be invariant + * @description If a controlling expression has an invariant value then it is possible that there is + * a programming error. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-14-3 + * correctness + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from Expr expr, string message +where + not isExcluded(expr, Statements5Package::controllingExprInvariantQuery()) and + ( + exists(IfStmt ifStmt | + ( + ifStmt.getControllingExpr() = expr and + ( + conditionAlwaysFalse(expr) + or + conditionAlwaysTrue(expr) + ) + ) + ) and + message = "Controlling expression in if statement has an invariant value." + or + exists(Loop loop | + loop.getControllingExpr() = expr and + ( + conditionAlwaysFalse(expr) and + not ( + getEssentialTypeCategory(getEssentialType(expr)) instanceof EssentiallyBooleanType and + expr.getValue() = "0" + ) + or + conditionAlwaysTrue(expr) and + // Exception allows for infinite loops, but we only permit that for literals like `true` + not expr instanceof Literal + ) + ) and + message = "Controlling expression in loop statement has an invariant value." + or + exists(SwitchStmt switch | + switch.getControllingExpr() = expr and + ( + conditionAlwaysFalse(expr) or + conditionAlwaysTrue(expr) + ) + ) and + message = "Controlling expression in switch statement has an invariant value." + or + exists(ConditionalExpr conditional | + conditional.getCondition() = expr and + ( + conditionAlwaysFalse(expr) or + conditionAlwaysTrue(expr) + ) + ) and + message = "Controlling expression in conditional statement has an invariant value." + ) and + // Exclude cases where the controlling expressions is affected by a macro, because they can appear + // invariant in a particular invocation, but be variant between invocations. + not ( + expr.isAffectedByMacro() and + // Permit boolean literal macros + not expr instanceof BooleanLiteral + ) and + // Exclude template variables, because they can be instantiated with different values. + not expr = any(TemplateVariable tv).getAnInstantiation().getAnAccess() +select expr, message diff --git a/c/misra/src/rules/RULE-14-4/NonBooleanIfCondition.ql b/c/misra/src/rules/RULE-14-4/NonBooleanIfCondition.ql new file mode 100644 index 0000000000..87d9d31512 --- /dev/null +++ b/c/misra/src/rules/RULE-14-4/NonBooleanIfCondition.ql @@ -0,0 +1,27 @@ +/** + * @id c/misra/non-boolean-if-condition + * @name RULE-14-4: The condition of an if-statement shall have type bool + * @description Non boolean conditions can be confusing for developers. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-14-4 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from Expr condition, Type essentialType +where + not isExcluded(condition, Statements4Package::nonBooleanIfConditionQuery()) and + exists(IfStmt ifStmt | + not ifStmt.isFromUninstantiatedTemplate(_) and + condition = ifStmt.getCondition() and + essentialType = getEssentialType(ifStmt.getCondition()) and + not getEssentialTypeCategory(essentialType) = EssentiallyBooleanType() + ) +select condition, "If condition has non boolean essential type " + essentialType + "." diff --git a/c/misra/src/rules/RULE-14-4/NonBooleanIterationCondition.ql b/c/misra/src/rules/RULE-14-4/NonBooleanIterationCondition.ql new file mode 100644 index 0000000000..b2644a7a92 --- /dev/null +++ b/c/misra/src/rules/RULE-14-4/NonBooleanIterationCondition.ql @@ -0,0 +1,36 @@ +/** + * @id c/misra/non-boolean-iteration-condition + * @name RULE-14-4: The condition of an iteration statement shall have type bool + * @description Non boolean conditions can be confusing for developers. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-14-4 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +/** A macro within the source location of this project. */ +class UserProvidedMacro extends Macro { + UserProvidedMacro() { exists(this.getFile().getRelativePath()) } +} + +/** A macro defined within a library used by this project. */ +class LibraryMacro extends Macro { + LibraryMacro() { not this instanceof UserProvidedMacro } +} + +from Expr condition, Loop l, Type essentialType +where + not isExcluded(condition, Statements4Package::nonBooleanIterationConditionQuery()) and + // Exclude loops generated from library macros + not l = any(LibraryMacro lm).getAnInvocation().getAGeneratedElement() and + condition = l.getCondition() and + essentialType = getEssentialType(condition) and + not getEssentialTypeCategory(essentialType) = EssentiallyBooleanType() +select condition, "Iteration condition has non boolean type " + essentialType + "." diff --git a/c/misra/src/rules/RULE-15-1/GotoStatementUsed.ql b/c/misra/src/rules/RULE-15-1/GotoStatementUsed.ql new file mode 100644 index 0000000000..ddc85c305c --- /dev/null +++ b/c/misra/src/rules/RULE-15-1/GotoStatementUsed.ql @@ -0,0 +1,21 @@ +/** + * @id c/misra/goto-statement-used + * @name RULE-15-1: The goto statement should not be used + * @description The goto statement shall not be used. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-15-1 + * correctness + * security + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +from Stmt s +where + not isExcluded(s, Statements6Package::gotoStatementUsedQuery()) and + (s instanceof GotoStmt or s instanceof ComputedGotoStmt) +select s, "Use of goto." diff --git a/c/misra/src/rules/RULE-15-2/GotoLabelLocationCondition.ql b/c/misra/src/rules/RULE-15-2/GotoLabelLocationCondition.ql new file mode 100644 index 0000000000..7ad9963d14 --- /dev/null +++ b/c/misra/src/rules/RULE-15-2/GotoLabelLocationCondition.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/goto-label-location-condition + * @name RULE-15-2: The goto statement shall jump to a label declared later in the same function + * @description Unconstrained use of goto can lead to unstructured code. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-2 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition + +class GotoLabelLocationConditionQuery extends GotoStatementConditionSharedQuery { + GotoLabelLocationConditionQuery() { + this = Statements2Package::gotoLabelLocationConditionQuery() + } +} diff --git a/c/misra/src/rules/RULE-15-3/GotoLabelBlockCondition.ql b/c/misra/src/rules/RULE-15-3/GotoLabelBlockCondition.ql new file mode 100644 index 0000000000..aeb356b501 --- /dev/null +++ b/c/misra/src/rules/RULE-15-3/GotoLabelBlockCondition.ql @@ -0,0 +1,62 @@ +/** + * @id c/misra/goto-label-block-condition + * @name RULE-15-3: The goto statement and any of its label shall be declared or enclosed in the same block + * @description Any label referenced by a goto statement shall be declared in the same block, or in + * any block enclosing the goto statement. + * @kind problem + * @precision high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-3 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +predicate isPartOfSwitch(Stmt goto) { + exists(SwitchStmt switch | switch.getStmt() = goto.getParent()) +} + +SwitchCase getSwitchCase(Stmt stmt) { + exists(int index, SwitchStmt switch | + getStmtInSwitch(switch, stmt, index) and getStmtInSwitch(switch, result, index - 1) + ) + or + exists(int index, SwitchStmt switch, Stmt other | + getStmtInSwitch(switch, stmt, index) and + getStmtInSwitch(switch, other, index - 1) and + not other instanceof SwitchCase and + result = getSwitchCase(other) + ) +} + +predicate getStmtInSwitch(SwitchStmt switch, Stmt s, int index) { + switch.getStmt().(BlockStmt).getStmt(index) = s +} + +int statementDepth(Stmt statement) { + statement.getParent() = statement.getEnclosingFunction().getBlock() and result = 1 + or + statementDepth(statement.getParent()) + 1 = result +} + +from GotoStmt goto, Stmt target, int gotoDepth, int targetDepth +where + not isExcluded(goto, Statements2Package::gotoLabelBlockConditionQuery()) and + goto.getTarget() = target and + gotoDepth = statementDepth(goto) and + targetDepth = statementDepth(target) and + targetDepth >= gotoDepth and + ( + targetDepth = gotoDepth + implies + ( + not isPartOfSwitch(goto) and not goto.getParent() = target.getParent() + or + isPartOfSwitch(goto) and not getSwitchCase(goto) = getSwitchCase(target) + ) + ) +select goto, "The $@ statement and its $@ are not declared or enclosed in the same block.", goto, + "goto", target, "label" diff --git a/c/misra/src/rules/RULE-15-4/LoopIterationCondition.ql b/c/misra/src/rules/RULE-15-4/LoopIterationCondition.ql new file mode 100644 index 0000000000..ed541a68d0 --- /dev/null +++ b/c/misra/src/rules/RULE-15-4/LoopIterationCondition.ql @@ -0,0 +1,55 @@ +/** + * @id c/misra/loop-iteration-condition + * @name RULE-15-4: There should be no more than one break or goto statement used to terminate any iteration statement + * @description More than one break or goto statement in iteration conditions may lead to + * readability and maintainability issues. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-15-4 + * maintainability + * readability + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +/** + * A breaking statement. + */ +class BreakOrGotoStmt extends JumpStmt { + BreakOrGotoStmt() { + this instanceof BreakStmt or + this instanceof GotoStmt + } + + /** + * Gets a loop this breaks out of, if any. + * + * - This can produce no results if this is a `break` and the enclosing breakable is a switch statement. + * - This can produce no result if this is a `goto`, and the target is within the same nearest enclosing loop. + * - This can produce multiple results if this is a `goto`, and the target is outside multiple enclosing loops. + */ + Loop getABrokenLoop() { + result = this.(BreakStmt).getBreakable() + or + exists(GotoStmt goto | + goto = this and + // Find any loop that encloses this goto + result.getChildStmt*() = goto and + // But does not enclose the target of the goto i.e. the goto breaks out of it + not result.getChildStmt*() = goto.getTarget() + ) + } +} + +from Loop loop, BreakOrGotoStmt breakOrGoto +where + not isExcluded(loop, Statements2Package::loopIterationConditionQuery()) and + // More than one break or goto statement in the loop + count(BreakOrGotoStmt terminationStmt | terminationStmt.getABrokenLoop() = loop) > 1 and + // Report a break or goto statement + breakOrGoto.getABrokenLoop() = loop +select loop, "Iteration statement contains more than one $@.", breakOrGoto, + "break or goto statement" diff --git a/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql b/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql new file mode 100644 index 0000000000..2fb5ad9d65 --- /dev/null +++ b/c/misra/src/rules/RULE-15-5/FunctionReturnCondition.ql @@ -0,0 +1,43 @@ +/** + * @id c/misra/function-return-condition + * @name RULE-15-5: A function should have a single point of exit at the end + * @description Not having a single point of exit in a function can lead to unintentional behaviour. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-5 + * maintainability + * readability + * correctness + * external/misra/obligation/advisory + */ + +import cpp +import codingstandards.c.misra + +class UserWrittenReturnStmt extends ReturnStmt { + UserWrittenReturnStmt() { not this.isCompilerGenerated() } +} + +from Function func, string message, UserWrittenReturnStmt returnStmt +where + not isExcluded(func, Statements5Package::functionReturnConditionQuery()) and + func.hasDefinition() and + // Ignore functions which have multiple bodies + count(func.getBlock()) = 1 and + // Ignore functions which are compiler generated + not func.isCompilerGenerated() and + // Report all the return statements in the function + returnStmt.getEnclosingFunction() = func and + ( + // There is more than one return statement + count(UserWrittenReturnStmt return | return.getEnclosingFunction() = func) > 1 and + message = "Function has more than one $@." + or + // There is exactly one return statement + count(UserWrittenReturnStmt return | return.getEnclosingFunction() = func) = 1 and + // But it is not the last statement in the function + not func.getBlock().getLastStmt() instanceof UserWrittenReturnStmt and + message = "The $@ is not the last statement of the function." + ) +select func, message, returnStmt, "return statement" diff --git a/c/misra/src/rules/RULE-15-6/LoopCompoundCondition.ql b/c/misra/src/rules/RULE-15-6/LoopCompoundCondition.ql new file mode 100644 index 0000000000..c596cb2970 --- /dev/null +++ b/c/misra/src/rules/RULE-15-6/LoopCompoundCondition.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/loop-compound-condition + * @name RULE-15-6: the statement forming the body of a loop shall be a compound statement + * @description if the body of a loop is not enclosed in braces, then this can lead to incorrect + * execution, and is hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-6 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from Loop loop +where + not isExcluded(loop, Statements3Package::loopCompoundConditionQuery()) and + not loop.getStmt() instanceof BlockStmt +select loop, "Loop body not enclosed within braces." diff --git a/c/misra/src/rules/RULE-15-6/SelectionCompoundCondition.ql b/c/misra/src/rules/RULE-15-6/SelectionCompoundCondition.ql new file mode 100644 index 0000000000..0c97b3ea5a --- /dev/null +++ b/c/misra/src/rules/RULE-15-6/SelectionCompoundCondition.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/selection-compound-condition + * @name RULE-15-6: the statement forming the body of a loop shall be a compound statement + * @description if the body of a selection statement is not enclosed in braces, then this can lead + * to incorrect execution, and is hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-6 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from IfStmt ifStmt +where + not isExcluded(ifStmt, Statements3Package::selectionCompoundConditionQuery()) and + not ifStmt.getChildStmt() instanceof BlockStmt +select ifStmt, "If statement not enclosed within braces." diff --git a/c/misra/src/rules/RULE-15-6/SwitchCompoundCondition.ql b/c/misra/src/rules/RULE-15-6/SwitchCompoundCondition.ql new file mode 100644 index 0000000000..837bfb12c1 --- /dev/null +++ b/c/misra/src/rules/RULE-15-6/SwitchCompoundCondition.ql @@ -0,0 +1,26 @@ +/** + * @id c/misra/switch-compound-condition + * @name RULE-15-6: The statement forming the body of a switch shall be a compound statement + * @description If the body of a switch is not enclosed in braces, then this can lead to incorrect + * execution, and is hard for developers to maintain. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-6 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.SwitchStatement + +from SwitchStmt switch +where + not isExcluded(switch, Statements3Package::switchCompoundConditionQuery()) and + ( + switch.getStmt() instanceof ArtificialBlock or + not switch.getStmt() instanceof BlockStmt + ) +select switch, "Switch body not enclosed within braces." diff --git a/c/misra/src/rules/RULE-15-7/IfElseEndCondition.ql b/c/misra/src/rules/RULE-15-7/IfElseEndCondition.ql new file mode 100644 index 0000000000..96132d3deb --- /dev/null +++ b/c/misra/src/rules/RULE-15-7/IfElseEndCondition.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/if-else-end-condition + * @name RULE-15-7: All if / else if constructs shall be terminated with an else statement + * @description Terminating an `if...else` construct is a defensive programming technique. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-15-7 + * readability + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct + +class IfElseEndConditionQuery extends IfElseTerminationConstructSharedQuery { + IfElseEndConditionQuery() { + this = Statements3Package::ifElseEndConditionQuery() + } +} diff --git a/c/misra/src/rules/RULE-16-1/6-4-3_grammar_1.png b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_1.png new file mode 100644 index 0000000000..d6adfd06c1 Binary files /dev/null and b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_1.png differ diff --git a/c/misra/src/rules/RULE-16-1/6-4-3_grammar_2.png b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_2.png new file mode 100644 index 0000000000..4a7ae673e8 Binary files /dev/null and b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_2.png differ diff --git a/c/misra/src/rules/RULE-16-1/6-4-3_grammar_3.png b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_3.png new file mode 100644 index 0000000000..7b58e6f41e Binary files /dev/null and b/c/misra/src/rules/RULE-16-1/6-4-3_grammar_3.png differ diff --git a/c/misra/src/rules/RULE-16-1/SwitchCaseStartCondition.ql b/c/misra/src/rules/RULE-16-1/SwitchCaseStartCondition.ql new file mode 100644 index 0000000000..32d390e33e --- /dev/null +++ b/c/misra/src/rules/RULE-16-1/SwitchCaseStartCondition.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/switch-case-start-condition + * @name RULE-16-1: A well formed switch statement must start with a case clause + * @description The switch statement syntax is weak and may lead to unspecified behaviour. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-1 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition + +class SwitchCaseStartConditionQuery extends SwitchCasePositionConditionSharedQuery { + SwitchCaseStartConditionQuery() { + this = Statements3Package::switchCaseStartConditionQuery() + } +} diff --git a/c/misra/src/rules/RULE-16-1/SwitchStmtNotWellFormed.ql b/c/misra/src/rules/RULE-16-1/SwitchStmtNotWellFormed.ql new file mode 100644 index 0000000000..30293e41dd --- /dev/null +++ b/c/misra/src/rules/RULE-16-1/SwitchStmtNotWellFormed.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/switch-stmt-not-well-formed + * @name RULE-16-1: A well formed switch statement should only have expression, compound, selection, iteration or try statements within its body + * @description The switch statement syntax is weak and may lead to unspecified behaviour. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-1 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed + +class SwitchStmtNotWellFormedQuery extends SwitchNotWellFormedSharedQuery { + SwitchStmtNotWellFormedQuery() { + this = Statements3Package::switchStmtNotWellFormedQuery() + } +} diff --git a/c/misra/src/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.ql b/c/misra/src/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.ql new file mode 100644 index 0000000000..df4b6fc93a --- /dev/null +++ b/c/misra/src/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.ql @@ -0,0 +1,22 @@ +/** + * @id c/misra/nest-switch-label-in-switch-statement + * @name RULE-16-2: Nested switch labels shall not be used + * @description Nested switch labels can lead to unstructured code. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-2 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch + +class NestSwitchLabelInSwitchStatementQuery extends NestedLabelInSwitchSharedQuery { + NestSwitchLabelInSwitchStatementQuery() { + this = Statements1Package::nestSwitchLabelInSwitchStatementQuery() + } +} diff --git a/c/misra/src/rules/RULE-16-3/BreakShallTerminateSwitchClause.ql b/c/misra/src/rules/RULE-16-3/BreakShallTerminateSwitchClause.ql new file mode 100644 index 0000000000..e62fe8c8d4 --- /dev/null +++ b/c/misra/src/rules/RULE-16-3/BreakShallTerminateSwitchClause.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/break-shall-terminate-switch-clause + * @name RULE-16-3: An unconditional break statement shall terminate every switch-clause + * @description An unterminated switch-clause occurring at the end of a switch statement may fall + * into switch clauses which are added later. + * @kind problem + * @precision high + * @problem.severity warning + * @tags external/misra/id/rule-16-3 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from SwitchCase case +where + not isExcluded(case, Statements1Package::breakShallTerminateSwitchClauseQuery()) and + not case.terminatesInBreakStmt() and + not case.getFollowingStmt() instanceof SwitchCase +select case, "The switch $@ does not terminate with a break statement.", case, "clause" diff --git a/c/misra/src/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql b/c/misra/src/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql new file mode 100644 index 0000000000..a5d7c3cf2c --- /dev/null +++ b/c/misra/src/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql @@ -0,0 +1,46 @@ +/** + * @id c/misra/every-switch-shall-have-default-label + * @name RULE-16-4: Every switch statement shall have a default label + * @description A default label that has no statements or a comment explaining why this is correct + * indicates a missing implementation that may result in unexpected behavior when the + * default case is executed. + * @kind problem + * @precision very-high + * @problem.severity warning + * @tags external/misra/id/rule-16-4 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +Stmt getFirstNonBlockStatement(BlockStmt bs) { + exists(Stmt nextStmt | nextStmt = bs.getStmt(0) | + if nextStmt instanceof BlockStmt + then result = getFirstNonBlockStatement(nextStmt) + else result = nextStmt + ) +} + +Stmt getFirstStatement(DefaultCase case) { + exists(Stmt next | next = case.getFollowingStmt() | + if next instanceof BlockStmt then result = getFirstNonBlockStatement(next) else result = next + ) +} + +from SwitchStmt switch, string message +where + not isExcluded(switch, Statements1Package::everySwitchShallHaveDefaultLabelQuery()) and + not switch.hasDefaultCase() and + message = "has missing default clause." + or + exists(SwitchCase case, BreakStmt break | + switch.getDefaultCase() = case and + getFirstStatement(case) = break and + not exists(Comment comment | comment.getCommentedElement() = break) and + message = + "has default label that does not terminate in a statement or comment before break statement" + ) +select switch, "$@ statement " + message, switch, "Switch" diff --git a/c/misra/src/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql b/c/misra/src/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql new file mode 100644 index 0000000000..f86e242ee3 --- /dev/null +++ b/c/misra/src/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/default-not-first-or-last-of-switch + * @name RULE-16-5: A default label shall appear as either the first or the last switch label or a switch statement + * @description Locating the default label is easier when it is the first or last label. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-5 + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.SwitchStatement + +from SwitchStmt switch, SwitchCase defaultCase +where + not isExcluded(switch, Statements1Package::defaultNotFirstOrLastOfSwitchQuery()) and + switch.getDefaultCase() = defaultCase and + exists(defaultCase.getPreviousSwitchCase()) and + finalClauseInSwitchNotDefault(switch) +select defaultCase, "$@ statement does not have $@ case as first or last switch label.", switch, + "Switch", defaultCase, "default" diff --git a/c/misra/src/rules/RULE-16-6/SwitchClauseNumberCondition.ql b/c/misra/src/rules/RULE-16-6/SwitchClauseNumberCondition.ql new file mode 100644 index 0000000000..8ddb2e49b2 --- /dev/null +++ b/c/misra/src/rules/RULE-16-6/SwitchClauseNumberCondition.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/switch-clause-number-condition + * @name RULE-16-6: Every switch statement shall have at least two switch-clauses + * @description Switch Statements with a single path are redundant and may cause programming errors. + * @kind problem + * @precision very-high + * @problem.severity recommendation + * @tags external/misra/id/rule-16-6 + * maintainability + * readability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from SwitchStmt switch +where + not isExcluded(switch, Statements2Package::switchClauseNumberConditionQuery()) and + count(SwitchCase case | + switch.getASwitchCase() = case and + case.getNextSwitchCase() != case.getFollowingStmt() + ) + 1 < 2 +select switch, "$@ statement has a single path.", switch, "Switch" diff --git a/c/misra/src/rules/RULE-16-7/SwitchExpressionBoolCondition.ql b/c/misra/src/rules/RULE-16-7/SwitchExpressionBoolCondition.ql new file mode 100644 index 0000000000..9aeb50d26e --- /dev/null +++ b/c/misra/src/rules/RULE-16-7/SwitchExpressionBoolCondition.ql @@ -0,0 +1,23 @@ +/** + * @id c/misra/switch-expression-bool-condition + * @name RULE-16-7: A switch-expression shall not have essentially Boolean type + * @description An `if-else` construct is more appropriate for boolean controlled expression. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-16-7 + * readability + * maintainability + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra +import codingstandards.c.misra.EssentialTypes + +from SwitchStmt switch, Expr controllingExpr +where + not isExcluded(switch, Statements2Package::switchExpressionBoolConditionQuery()) and + controllingExpr = switch.getControllingExpr() and + getEssentialTypeCategory(getEssentialType(controllingExpr)) = EssentiallyBooleanType() +select controllingExpr, "The condition of this $@ statement has boolean type", switch, "switch" diff --git a/c/misra/src/rules/RULE-17-2/RecursiveFunctionCondition.ql b/c/misra/src/rules/RULE-17-2/RecursiveFunctionCondition.ql new file mode 100644 index 0000000000..b6f13c4d1f --- /dev/null +++ b/c/misra/src/rules/RULE-17-2/RecursiveFunctionCondition.ql @@ -0,0 +1,25 @@ +/** + * @id c/misra/recursive-function-condition + * @name RULE-17-2: Functions shall not call themselves, either directly or indirectly + * @description Recursive function may cause memory and system failure issues. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-2 + * maintainability + * correctness + * external/misra/obligation/required + */ + +import cpp +import codingstandards.c.misra + +from FunctionCall fc, Function f, string msg +where + not isExcluded(fc, Statements3Package::recursiveFunctionConditionQuery()) and + fc.getEnclosingFunction() = f and + fc.getTarget().calls*(f) and + if fc.getTarget() = f + then msg = f + " calls itself directly." + else msg = f + " is indirectly recursive via this call to $@." +select fc, msg, fc.getTarget(), fc.getTarget().getName() diff --git a/c/misra/src/rules/RULE-17-4/NonVoidFunctionReturnCondition.ql b/c/misra/src/rules/RULE-17-4/NonVoidFunctionReturnCondition.ql new file mode 100644 index 0000000000..24329e5ab5 --- /dev/null +++ b/c/misra/src/rules/RULE-17-4/NonVoidFunctionReturnCondition.ql @@ -0,0 +1,24 @@ +/** + * @id c/misra/non-void-function-return-condition + * @name RULE-17-4: All exit paths from a function with non-void return type shall have an explicit return statement + * @description Not returning with an expression from a non-void function can lead to undefined + * behaviour. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-17-4 + * correctness + * maintainability + * readability + * external/misra/obligation/mandatory + */ + +import cpp +import codingstandards.c.misra +import codingstandards.cpp.rules.nonvoidfunctiondoesnotreturn.NonVoidFunctionDoesNotReturn + +class NonVoidFunctionReturnConditionQuery extends NonVoidFunctionDoesNotReturnSharedQuery { + NonVoidFunctionReturnConditionQuery() { + this = Statements5Package::nonVoidFunctionReturnConditionQuery() + } +} diff --git a/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.expected b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.expected new file mode 100644 index 0000000000..fc7fbc7c5f --- /dev/null +++ b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.expected @@ -0,0 +1,7 @@ +| test.c:7:3:8:3 | for(...;...;...) ... | For loop is not well formed, it uses a loop counter '$@' of type floating-point. | test.c:7:14:7:14 | f | f | +| test.c:14:3:15:3 | for(...;...;...) ... | For loop is not well formed, it uses multiple loop counters$@. | file://:0:0:0:0 | | | +| test.c:20:3:21:3 | for(...;...;...) ... | For loop is not well formed, it uses multiple loop counters$@. | file://:0:0:0:0 | | | +| test.c:25:3:26:3 | for(...;...;...) ... | For loop is not well formed, it $@ its loop counter 'i' with an operation that is not an increment or decrement. | test.c:25:28:25:28 | i | updates | +| test.c:38:3:39:3 | for(...;...;...) ... | For loop is not well formed, it $@ the loop counter 'x' irregularly. | test.c:38:26:38:26 | x | updates | +| test.c:52:3:53:3 | for(...;...;...) ... | For loop is not well formed, it updates $@, a loop control variable other than the loop counter, in the update expression of the loop. | test.c:52:28:52:29 | p1 | p1 | +| test.c:64:3:67:3 | for(...;...;...) ... | For loop is not well formed, it $@ the loop counter 'x' in the body of the loop. | test.c:65:5:65:5 | x | updates | diff --git a/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.qlref b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.qlref new file mode 100644 index 0000000000..f65068dfb2 --- /dev/null +++ b/c/misra/test/rules/RULE-14-2/ForLoopNotWellFormed.qlref @@ -0,0 +1 @@ +rules/RULE-14-2/ForLoopNotWellFormed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-2/test.c b/c/misra/test/rules/RULE-14-2/test.c new file mode 100644 index 0000000000..fbeb4be21f --- /dev/null +++ b/c/misra/test/rules/RULE-14-2/test.c @@ -0,0 +1,73 @@ + +#include "stdbool.h" +int g1 = 10; +int f1() { return g1++; } + +void f2() { + for (float f = 0.0F; f < 10.0F; f += 0.2F) { // NON_COMPLIANT + } + for (int i = 0; i < 10; i++) { // COMPLIANT + } +} + +void f3() { + for (int i = 0, j = 0; i < j; i++, j++) { // NON_COMPLIANT + } +} + +void f4() { + int i, j; + for (i = 0, j = 0; i < j; i++, j++) { // NON_COMPLIANT + } +} + +void f5() { + for (int i = 0; i != 10; i += 3) { // NON_COMPLIANT + } + + for (int i = 0; i != 10; i++) { // COMPLIANT + } +} + +void f7() { + for (int i = 0; i < 100; i += g1) { // COMPLIANT + } +} + +void f8() { + for (int x = 0; x < 5; x += f1()) { // NON_COMPLIANT + } +} + +void f9() { + bool l1 = true; + for (int x = 0; (x < 5) && l1; ++x) { // COMPLIANT + l1 = false; + } +} + +bool f10(int p1) { return false; } +void f11() { + bool p1 = true; + for (int x = 0; (x < 5); p1 = f10(++x)) { // NON_COMPLIANT + } +} + +void f12() { + bool l1 = true; + for (int x = 0; (x < 5) && l1; ++x) { // COMPLIANT + } +} + +void f13() { + int l1 = 1; + for (int x = 0; x < 5 && l1 == 9; ++x) { // NON_COMPLIANT + x = x + 2; + g1--; + } +} + +void f14() { + for (int i = 0; i < 10; i += 3) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.expected b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.expected new file mode 100644 index 0000000000..c03c04d6cc --- /dev/null +++ b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.expected @@ -0,0 +1,7 @@ +| test.c:4:7:4:11 | ... > ... | Controlling expression in if statement has an invariant value. | +| test.c:15:10:15:16 | ... > ... | Controlling expression in loop statement has an invariant value. | +| test.c:16:9:16:13 | ... > ... | Controlling expression in if statement has an invariant value. | +| test.c:20:20:20:24 | ... < ... | Controlling expression in loop statement has an invariant value. | +| test.c:27:10:27:14 | ... < ... | Controlling expression in loop statement has an invariant value. | +| test.c:37:3:37:6 | 1 | Controlling expression in conditional statement has an invariant value. | +| test.c:38:3:38:3 | 1 | Controlling expression in conditional statement has an invariant value. | diff --git a/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.qlref b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.qlref new file mode 100644 index 0000000000..dcee0a35ac --- /dev/null +++ b/c/misra/test/rules/RULE-14-3/ControllingExprInvariant.qlref @@ -0,0 +1 @@ +rules/RULE-14-3/ControllingExprInvariant.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-3/test.c b/c/misra/test/rules/RULE-14-3/test.c new file mode 100644 index 0000000000..38db3e1286 --- /dev/null +++ b/c/misra/test/rules/RULE-14-3/test.c @@ -0,0 +1,40 @@ +#include + +void f1(int p1) { + if (2 > 3) { // NON_COMPLIANT + } + + if (p1 > 0) { // COMPLIANT + } + + if (p1 < 10 && p1 > 20) { // NON_COMPLIANT[FALSE_NEGATIVE] + } +} + +void f2() { + while (20 > 10) { // NON_COMPLIANT + if (1 > 2) { + } // NON_COMPLIANT + } + + for (int i = 10; i < 5; i++) { // NON_COMPLIANT + } +} + +void f3() { + while (true) { // COMPLIANT - permitted by exception 1 + } + while (1 < 2) { // NON_COMPLIANT - likely an indication of a bug + } +} + +void f4() { + do { + } while (0u == 1u); // COMPLIANT - by exception 2 +} + +void f5(bool b1) { + true ? 1 : 2; // NON_COMPLIANT + 1 ? 1 : 2; // NON_COMPLIANT + b1 ? 1 : 2; // COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.expected b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.expected new file mode 100644 index 0000000000..c8a7508f2b --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.expected @@ -0,0 +1,3 @@ +| test.c:7:7:7:8 | l1 | If condition has non boolean essential type int. | +| test.c:9:7:9:8 | call to f1 | If condition has non boolean essential type int. | +| test.c:12:7:12:8 | l2 | If condition has non boolean essential type void *. | diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.qlref b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.qlref new file mode 100644 index 0000000000..cdfd3b5ea3 --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIfCondition.qlref @@ -0,0 +1 @@ +rules/RULE-14-4/NonBooleanIfCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.expected b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.expected new file mode 100644 index 0000000000..daf7a4be85 --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.expected @@ -0,0 +1,2 @@ +| test_iteration.c:5:20:5:20 | i | Iteration condition has non boolean type int. | +| test_iteration.c:7:10:7:11 | l1 | Iteration condition has non boolean type int. | diff --git a/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.qlref b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.qlref new file mode 100644 index 0000000000..b7483581b4 --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/NonBooleanIterationCondition.qlref @@ -0,0 +1 @@ +rules/RULE-14-4/NonBooleanIterationCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/test.c b/c/misra/test/rules/RULE-14-4/test.c new file mode 100644 index 0000000000..faf7efd83b --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/test.c @@ -0,0 +1,30 @@ +#include +int f1(); +void *f2(); + +void f3() { + int l1 = 1; + if (l1) { // NON_COMPLIANT + } + if (f1()) { // NON_COMPLIANT + } + void *l2 = f2(); + if (l2) { // NON_COMPLIANT + } +} + +void f4() { + int l1 = 1; + if ((bool)l1) { // COMPLIANT + } + + int l2 = 1; + if ((const bool)l2) { // COMPLIANT + } + + if (l2 < 3) { // COMPLIANT + } + + if (true) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-14-4/test_iteration.c b/c/misra/test/rules/RULE-14-4/test_iteration.c new file mode 100644 index 0000000000..8ecbb1c1fd --- /dev/null +++ b/c/misra/test/rules/RULE-14-4/test_iteration.c @@ -0,0 +1,15 @@ + + +void f1() { + int l1; + for (int i = 10; i; i++) { // NON_COMPLIANT + } + while (l1) { // NON_COMPLIANT + } +} + +void f2() { + int j = 0; + for (int i = 0; i < 10; i++) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-1/GotoStatementUsed.expected b/c/misra/test/rules/RULE-15-1/GotoStatementUsed.expected new file mode 100644 index 0000000000..7e06759159 --- /dev/null +++ b/c/misra/test/rules/RULE-15-1/GotoStatementUsed.expected @@ -0,0 +1 @@ +| test.c:4:3:4:14 | goto ... | Use of goto. | diff --git a/c/misra/test/rules/RULE-15-1/GotoStatementUsed.qlref b/c/misra/test/rules/RULE-15-1/GotoStatementUsed.qlref new file mode 100644 index 0000000000..338455d28f --- /dev/null +++ b/c/misra/test/rules/RULE-15-1/GotoStatementUsed.qlref @@ -0,0 +1 @@ +rules/RULE-15-1/GotoStatementUsed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-1/test.c b/c/misra/test/rules/RULE-15-1/test.c new file mode 100644 index 0000000000..d13f01961c --- /dev/null +++ b/c/misra/test/rules/RULE-15-1/test.c @@ -0,0 +1,9 @@ +void test_goto() { + int x = 1; + + goto label1; // NON_COMPLIANT + +label1: + + x = 2; +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.expected b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.expected new file mode 100644 index 0000000000..e69de29bb2 diff --git a/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.qlref b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.qlref new file mode 100644 index 0000000000..6a35e05154 --- /dev/null +++ b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-2/GotoLabelLocationCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.testref b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.testref new file mode 100644 index 0000000000..0573c85129 --- /dev/null +++ b/c/misra/test/rules/RULE-15-2/GotoLabelLocationCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/gotostatementcondition/GotoStatementCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.expected b/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.expected new file mode 100644 index 0000000000..730403cbd7 --- /dev/null +++ b/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.expected @@ -0,0 +1,3 @@ +| test.c:2:3:2:10 | goto ... | The $@ statement and its $@ are not declared or enclosed in the same block. | test.c:2:3:2:10 | goto ... | goto | test.c:4:3:4:5 | label ...: | label | +| test.c:40:3:40:10 | goto ... | The $@ statement and its $@ are not declared or enclosed in the same block. | test.c:40:3:40:10 | goto ... | goto | test.c:44:3:44:5 | label ...: | label | +| test.c:55:5:55:12 | goto ... | The $@ statement and its $@ are not declared or enclosed in the same block. | test.c:55:5:55:12 | goto ... | goto | test.c:58:3:58:5 | label ...: | label | diff --git a/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.qlref b/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.qlref new file mode 100644 index 0000000000..5f430f0790 --- /dev/null +++ b/c/misra/test/rules/RULE-15-3/GotoLabelBlockCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-3/GotoLabelBlockCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-3/test.c b/c/misra/test/rules/RULE-15-3/test.c new file mode 100644 index 0000000000..739affcfc1 --- /dev/null +++ b/c/misra/test/rules/RULE-15-3/test.c @@ -0,0 +1,85 @@ +void f1() { + goto L1; + for (int i = 0; i < 100; i++) { + L1: // NON_COMPLIANT + break; + } +} + +void f2() { + int i = 0; + if (i >= 0) { + for (int j = 0; j < 10; j++) { + goto L2; + } + } +L2: // COMPLIANT + return; +} + +void f3() { + int i = 0; + if (i >= 0) { + for (int j = 0; j < 10; j++) { + goto L3; + L3: // COMPLIANT + break; + } + } +} + +void f4() { + int i = 0; +L4: // COMPLIANT + if (i >= 0) { + goto L4; + } +} + +void f5(int p) { + goto L1; + + switch (p) { + case 0: + L1:; // NON_COMPLIANT + break; + default: + break; + } +} + +void f6(int p) { + + switch (p) { + case 0: + goto L1; + break; + default: + L1: // NON_COMPLIANT + break; + } +} + +void f7(int p) { +L1: // COMPLIANT + switch (p) { + case 0: + goto L1; + break; + default: + break; + } +} + +void f8(int p) { + + switch (p) { + case 0: + goto L1; + ; + L1:; // COMPLIANT + break; + default: + break; + } +} diff --git a/c/misra/test/rules/RULE-15-4/LoopIterationCondition.expected b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.expected new file mode 100644 index 0000000000..a8dae0f411 --- /dev/null +++ b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.expected @@ -0,0 +1,7 @@ +| test.c:24:3:32:3 | for(...;...;...) ... | Iteration statement contains more than one $@. | test.c:26:7:26:12 | break; | break or goto statement | +| test.c:24:3:32:3 | for(...;...;...) ... | Iteration statement contains more than one $@. | test.c:29:7:29:12 | break; | break or goto statement | +| test.c:24:3:32:3 | for(...;...;...) ... | Iteration statement contains more than one $@. | test.c:31:5:31:12 | goto ... | break or goto statement | +| test.c:38:3:45:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:40:7:40:12 | break; | break or goto statement | +| test.c:38:3:45:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:43:7:43:14 | goto ... | break or goto statement | +| test.c:61:3:72:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:64:7:64:12 | break; | break or goto statement | +| test.c:61:3:72:3 | while (...) ... | Iteration statement contains more than one $@. | test.c:68:7:68:14 | goto ... | break or goto statement | diff --git a/c/misra/test/rules/RULE-15-4/LoopIterationCondition.qlref b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.qlref new file mode 100644 index 0000000000..33ff4561da --- /dev/null +++ b/c/misra/test/rules/RULE-15-4/LoopIterationCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-4/LoopIterationCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-4/test.c b/c/misra/test/rules/RULE-15-4/test.c new file mode 100644 index 0000000000..2d4a0677a7 --- /dev/null +++ b/c/misra/test/rules/RULE-15-4/test.c @@ -0,0 +1,91 @@ +void f1() { +L1:; + + for (int k = 0; k < 10; k++) { // COMPLIANT + ; + } + + for (int i = 0; i < 10; i++) { // COMPLIANT + if (i > 5) { + break; + } + } + + for (int j = 0; j < 10; j++) { // COMPLIANT + goto L1; + } +} + +void f2() { +L1:; + + int k = 0; + + for (int i = 0; i < 10; i++) { // NON_COMPLIANT + if (i > 5) { + break; + } + if (i < 10) { + break; + } + goto L1; + } + + while (k < 10) { // COMPLIANT + ; + } + + while (k < 10) { // NON_COMPLIANT + if (k > 5) { + break; + } + while (k < 3) { // COMPLIANT + goto L1; + } + } + + while (k < 10) { // COMPLIANT - the nested goto + // only applies to the nested loop + if (k > 5) { + break; + } + while (k < 3) { // COMPLIANT + break; + } + } +} + +void f3(int k) { +L3: + k++; + while (k < 10) { // NON_COMPLIANT - the nested goto + // is an additional exit point for the while loop + if (k > 5) { + break; + } + switch (k) { + case 1: + goto L3; + case 2: + break; + } + } +} + +void f4(int k) { + k++; + while (k < 10) { // COMPLIANT + if (k > 5) { + break; + } + switch (k) { + case 1: + goto L4; + case 2: + k += 1; + L4: + k += 2; + break; + } + } +} diff --git a/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.expected b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.expected new file mode 100644 index 0000000000..dde5d709dd --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.expected @@ -0,0 +1,5 @@ +| test.c:1:6:1:7 | f1 | Function has more than one $@. | test.c:3:5:3:11 | return ... | return statement | +| test.c:1:6:1:7 | f1 | Function has more than one $@. | test.c:5:3:5:9 | return ... | return statement | +| test.c:14:6:14:7 | f3 | The $@ is not the last statement of the function. | test.c:17:3:17:9 | return ... | return statement | +| test.c:21:6:21:7 | f4 | Function has more than one $@. | test.c:23:5:23:11 | return ... | return statement | +| test.c:21:6:21:7 | f4 | Function has more than one $@. | test.c:25:3:25:9 | return ... | return statement | diff --git a/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref new file mode 100644 index 0000000000..fef14a8d42 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/FunctionReturnCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-5/FunctionReturnCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-5/test.c b/c/misra/test/rules/RULE-15-5/test.c new file mode 100644 index 0000000000..cbe36668f5 --- /dev/null +++ b/c/misra/test/rules/RULE-15-5/test.c @@ -0,0 +1,29 @@ +void f1(int p1) { // NON_COMPLIANT + if (p1) { + return; + } + return; +} + +void f2(int p1) { // COMPLIANT + if (p1) { + } + return; +} + +void f3(int p1) { // NON_COMPLIANT + if (p1) { + } + return; + p1++; +} + +void f4(int p1) { // NON_COMPLIANT + if (p1) { + return; + } + return; + p1++; +} + +void f5(); // Ignored - no body \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.expected b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.expected new file mode 100644 index 0000000000..263fee14de --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.expected @@ -0,0 +1,4 @@ +| test.c:4:3:5:9 | while (...) ... | Loop body not enclosed within braces. | +| test.c:7:3:8:5 | while (...) ... | Loop body not enclosed within braces. | +| test.c:11:3:12:9 | for(...;...;...) ... | Loop body not enclosed within braces. | +| test.c:14:3:15:5 | while (...) ... | Loop body not enclosed within braces. | diff --git a/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.qlref b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.qlref new file mode 100644 index 0000000000..8cd3c36d27 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/LoopCompoundCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-6/LoopCompoundCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.expected b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.expected new file mode 100644 index 0000000000..661d118a69 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.expected @@ -0,0 +1,4 @@ +| test.c:29:3:32:5 | if (...) ... | If statement not enclosed within braces. | +| test.c:34:3:41:7 | if (...) ... | If statement not enclosed within braces. | +| test.c:36:8:41:7 | if (...) ... | If statement not enclosed within braces. | +| test.c:37:5:41:7 | if (...) ... | If statement not enclosed within braces. | diff --git a/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.qlref b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.qlref new file mode 100644 index 0000000000..b62fe0b2c8 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SelectionCompoundCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-6/SelectionCompoundCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.expected b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.expected new file mode 100644 index 0000000000..eedc122cd6 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.expected @@ -0,0 +1 @@ +| test.c:75:3:79:5 | switch (...) ... | Switch body not enclosed within braces. | diff --git a/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.qlref b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.qlref new file mode 100644 index 0000000000..c34e33fcbd --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/SwitchCompoundCondition.qlref @@ -0,0 +1 @@ +rules/RULE-15-6/SwitchCompoundCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-6/test.c b/c/misra/test/rules/RULE-15-6/test.c new file mode 100644 index 0000000000..e8ed064b32 --- /dev/null +++ b/c/misra/test/rules/RULE-15-6/test.c @@ -0,0 +1,80 @@ +void f1(); + +void f2(int p1) { + while (p1) // NON_COMPLIANT + f1(); + + while (p1) // NON_COMPLIANT + ; + f1(); + + for (int i = 0; i < p1; i++) // NON_COMPLIANT + f1(); + + while (p1) + ; + { // NON_COMPLIANT + ; + } + + while (p1) { // COMPLIANT + ; + } + for (int i = 0; i < p1; i++) { // COMPLIANT + ; + } +} + +void f3(int p1) { + if (p1) // NON_COMPLIANT + ; + else + ; + + if (p1) // NON_COMPLIANT + ; + else if (p1) // NON_COMPLIANT + if (p1) // NON_COMPLIANT + + if (p1) { // COMPLIANT + ; + } + + if (p1) { // COMPLIANT + ; + } else { // COMPLIANT + ; + } + + if (p1) { // COMPLIANT + ; + } else if (p1) { // COMPLIANT + ; + } else { // COMPLIANT + ; + } +} + +void f4(int p1) { + + switch (p1) { // COMPLIANT + case 0: + while (p1) { + ; + } + break; + case 1: + if (p1) { + ; + } + break; + default: + break; + } + + switch (p1) // NON_COMPLIANT + case 0: + while (p1) { + ; + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-15-7/IfElseEndCondition.testref b/c/misra/test/rules/RULE-15-7/IfElseEndCondition.testref new file mode 100644 index 0000000000..89caf8f257 --- /dev/null +++ b/c/misra/test/rules/RULE-15-7/IfElseEndCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-1/SwitchCaseStartCondition.testref b/c/misra/test/rules/RULE-16-1/SwitchCaseStartCondition.testref new file mode 100644 index 0000000000..5d2b2ff0d1 --- /dev/null +++ b/c/misra/test/rules/RULE-16-1/SwitchCaseStartCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-1/SwitchStmtNotWellFormed.testref b/c/misra/test/rules/RULE-16-1/SwitchStmtNotWellFormed.testref new file mode 100644 index 0000000000..e37234ee4b --- /dev/null +++ b/c/misra/test/rules/RULE-16-1/SwitchStmtNotWellFormed.testref @@ -0,0 +1 @@ +c/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.testref b/c/misra/test/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.testref new file mode 100644 index 0000000000..329212287e --- /dev/null +++ b/c/misra/test/rules/RULE-16-2/NestSwitchLabelInSwitchStatement.testref @@ -0,0 +1 @@ +c/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.expected b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.expected new file mode 100644 index 0000000000..cac08cc449 --- /dev/null +++ b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.expected @@ -0,0 +1,4 @@ +| test.c:11:3:11:9 | case ...: | The switch $@ does not terminate with a break statement. | test.c:11:3:11:9 | case ...: | clause | +| test.c:14:3:14:9 | case ...: | The switch $@ does not terminate with a break statement. | test.c:14:3:14:9 | case ...: | clause | +| test.c:26:3:26:10 | default: | The switch $@ does not terminate with a break statement. | test.c:26:3:26:10 | default: | clause | +| test.c:45:3:45:10 | default: | The switch $@ does not terminate with a break statement. | test.c:45:3:45:10 | default: | clause | diff --git a/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.qlref b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.qlref new file mode 100644 index 0000000000..9764f620d0 --- /dev/null +++ b/c/misra/test/rules/RULE-16-3/BreakShallTerminateSwitchClause.qlref @@ -0,0 +1 @@ +rules/RULE-16-3/BreakShallTerminateSwitchClause.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-3/test.c b/c/misra/test/rules/RULE-16-3/test.c new file mode 100644 index 0000000000..ade65474f2 --- /dev/null +++ b/c/misra/test/rules/RULE-16-3/test.c @@ -0,0 +1,50 @@ +void f1(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + break; + case 2: // COMPLIANT + case 3: // COMPLIANT + case 4: // COMPLIANT + break; + case 5: // NON_COMPLIANT + i = j; + j++; + case 6: // NON_COMPLIANT + if (i > j) { + j++; + i++; + break; + } + case 7: // COMPLIANT + if (i > j) { + j++; + i++; + } + break; + default: // NON_COMPLIANT + i++; + } +} + +void f2(int p1) { + switch (p1) { + case 1: // COMPLIANT + break; + case 2: // COMPLIANT + case 3: // COMPLIANT + case 4: // COMPLIANT + default: // COMPLIANT + break; + } +} + +void f3(int p1) { + switch (p1) { + default: // NON_COMPLIANT + p1++; + case 1: // COMPLIANT + break; + } +} diff --git a/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.expected b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.expected new file mode 100644 index 0000000000..6ecfe62c3e --- /dev/null +++ b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.expected @@ -0,0 +1,3 @@ +| test.c:4:3:12:3 | switch (...) ... | $@ statement has missing default clause. | test.c:4:3:12:3 | switch (...) ... | Switch | +| test.c:13:3:22:3 | switch (...) ... | $@ statement has default label that does not terminate in a statement or comment before break statement | test.c:13:3:22:3 | switch (...) ... | Switch | +| test.c:53:3:60:3 | switch (...) ... | $@ statement has default label that does not terminate in a statement or comment before break statement | test.c:53:3:60:3 | switch (...) ... | Switch | diff --git a/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.qlref b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.qlref new file mode 100644 index 0000000000..394a5e941a --- /dev/null +++ b/c/misra/test/rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.qlref @@ -0,0 +1 @@ +rules/RULE-16-4/EverySwitchShallHaveDefaultLabel.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-4/test.c b/c/misra/test/rules/RULE-16-4/test.c new file mode 100644 index 0000000000..45fa298fc6 --- /dev/null +++ b/c/misra/test/rules/RULE-16-4/test.c @@ -0,0 +1,61 @@ +void f1(int p1) { + int i; + int j; + switch (p1) { // NON COMPLIANT + case 1: + i++; + j++; + break; + case 2: + case 3: + break; + } + switch (p1) { // NON_COMPLIANT + case 1: + i++; + break; + case 2: + j++; + break; + default: + break; + } + switch (p1) { // COMPLIANT + case 1: + i++; + break; + case 2: + j++; + break; + default: + // codeql + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + break; + default: + j++; + break; + } + switch (p1) { // COMPLIANT + case 1: + i++; + break; + default: + j++; + i++; + break; + } + + switch (p1) { // NON_COMPLIANT + case 1: + i++; + break; + default: { + break; + } + } +} diff --git a/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.expected b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.expected new file mode 100644 index 0000000000..a17969c296 --- /dev/null +++ b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.expected @@ -0,0 +1 @@ +| test.c:16:3:16:10 | default: | $@ statement does not have $@ case as first or last switch label. | test.c:12:3:22:3 | switch (...) ... | Switch | test.c:16:3:16:10 | default: | default | diff --git a/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.qlref b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.qlref new file mode 100644 index 0000000000..00e2e8aedf --- /dev/null +++ b/c/misra/test/rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.qlref @@ -0,0 +1 @@ +rules/RULE-16-5/DefaultNotFirstOrLastOfSwitch.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-5/test.c b/c/misra/test/rules/RULE-16-5/test.c new file mode 100644 index 0000000000..37d96bb0af --- /dev/null +++ b/c/misra/test/rules/RULE-16-5/test.c @@ -0,0 +1,34 @@ +void f1(int p1) { + int i; + int j; + switch (p1) { + default: // COMPLIANT + i++; + break; + case 1: + j++; + break; + } + switch (p1) { + case 1: + i++; + break; + default: // NON_COMPLIANT + j++; + break; + case 2: + i++; + break; + } + switch (p1) { + case 1: + i++; + break; + case 2: + j++; + break; + default: // COMPLIANT + i++; + break; + } +} diff --git a/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.expected b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.expected new file mode 100644 index 0000000000..112d0bdd96 --- /dev/null +++ b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.expected @@ -0,0 +1,3 @@ +| test.c:3:3:6:3 | switch (...) ... | $@ statement has a single path. | test.c:3:3:6:3 | switch (...) ... | Switch | +| test.c:8:3:12:3 | switch (...) ... | $@ statement has a single path. | test.c:8:3:12:3 | switch (...) ... | Switch | +| test.c:14:3:19:3 | switch (...) ... | $@ statement has a single path. | test.c:14:3:19:3 | switch (...) ... | Switch | diff --git a/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.qlref b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.qlref new file mode 100644 index 0000000000..6502b855f9 --- /dev/null +++ b/c/misra/test/rules/RULE-16-6/SwitchClauseNumberCondition.qlref @@ -0,0 +1 @@ +rules/RULE-16-6/SwitchClauseNumberCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-6/test.c b/c/misra/test/rules/RULE-16-6/test.c new file mode 100644 index 0000000000..38a1457a61 --- /dev/null +++ b/c/misra/test/rules/RULE-16-6/test.c @@ -0,0 +1,49 @@ +void f1(int p1) { + int i = 0; + switch (p1) { // NON_COMPLIANT + default: + break; + } + + switch (p1) { // NON_COMPLIANT + case 1: + default: + break; + } + + switch (p1) { // NON_COMPLIANT + case 1: + case 2: + default: + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + default: + i = 1; + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + case 2: + i = 2; + default: + i = 1; + break; + } + + switch (p1) { // COMPLIANT + case 1: + i++; + case 2: + i = 2; + case 3: + default: + i = 1; + break; + } +} diff --git a/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.expected b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.expected new file mode 100644 index 0000000000..ac74217dc3 --- /dev/null +++ b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.expected @@ -0,0 +1,2 @@ +| test.c:16:11:16:17 | ... == ... | The condition of this $@ statement has boolean type | test.c:16:3:24:3 | switch (...) ... | switch | +| test.c:28:11:28:24 | ... == ... | The condition of this $@ statement has boolean type | test.c:28:3:36:3 | switch (...) ... | switch | diff --git a/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.qlref b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.qlref new file mode 100644 index 0000000000..dc86fab7e9 --- /dev/null +++ b/c/misra/test/rules/RULE-16-7/SwitchExpressionBoolCondition.qlref @@ -0,0 +1 @@ +rules/RULE-16-7/SwitchExpressionBoolCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-16-7/test.c b/c/misra/test/rules/RULE-16-7/test.c new file mode 100644 index 0000000000..74b394bc6d --- /dev/null +++ b/c/misra/test/rules/RULE-16-7/test.c @@ -0,0 +1,37 @@ + +void f1(int p1) { + + switch (p1) // COMPLIANT + { + case 1: + break; + case 2: + break; + default: + break; + } +} + +void f2(int p1) { + switch (p1 == 1) // NON_COMPLIANT + { + case 0: + break; + case 1: + break; + default: + break; + } +} + +void f3(char *p1) { + switch (p1 == "CODEQL") // NON_COMPLIANT + { + case 0: + break; + case 1: + break; + default: + break; + } +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.expected b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.expected new file mode 100644 index 0000000000..06b8b5b762 --- /dev/null +++ b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.expected @@ -0,0 +1,3 @@ +| test.c:8:3:8:4 | call to f3 | f3 calls itself directly. | test.c:7:6:7:7 | f3 | f3 | +| test.c:15:3:15:4 | call to f2 | f5 is indirectly recursive via this call to $@. | test.c:17:6:17:7 | f2 | f2 | +| test.c:18:3:18:4 | call to f5 | f2 is indirectly recursive via this call to $@. | test.c:14:6:14:7 | f5 | f5 | diff --git a/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.qlref b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.qlref new file mode 100644 index 0000000000..da361b35f4 --- /dev/null +++ b/c/misra/test/rules/RULE-17-2/RecursiveFunctionCondition.qlref @@ -0,0 +1 @@ +rules/RULE-17-2/RecursiveFunctionCondition.ql \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-2/test.c b/c/misra/test/rules/RULE-17-2/test.c new file mode 100644 index 0000000000..800921c1e2 --- /dev/null +++ b/c/misra/test/rules/RULE-17-2/test.c @@ -0,0 +1,19 @@ +void f1(); +void f2(); +void f4(int p1) { // COMPLIANT + f1(); +} + +void f3() { + f3(); // NON_COMPLIANT +} +void f6() { + f3(); // NON_COMPLIANT +} + +void f5() { + f2(); // NON_COMPLIANT +} +void f2() { + f5(); // NON_COMPLIANT +} \ No newline at end of file diff --git a/c/misra/test/rules/RULE-17-4/NonVoidFunctionReturnCondition.testref b/c/misra/test/rules/RULE-17-4/NonVoidFunctionReturnCondition.testref new file mode 100644 index 0000000000..6ddd134ce3 --- /dev/null +++ b/c/misra/test/rules/RULE-17-4/NonVoidFunctionReturnCondition.testref @@ -0,0 +1 @@ +c/common/test/rules/nonvoidfunctiondoesnotreturn/NonVoidFunctionDoesNotReturn.ql \ No newline at end of file diff --git a/change_notes/2023-03-09-changed-alert-messages.md b/change_notes/2023-03-09-changed-alert-messages.md new file mode 100644 index 0000000000..4756d7822f --- /dev/null +++ b/change_notes/2023-03-09-changed-alert-messages.md @@ -0,0 +1,2 @@ + - `M6-6-2`: Changed formatting of the alert message. + - `M6-4-2`: Changed formatting of alert message. diff --git a/change_notes/2023-03-26-nested-switch-case.md b/change_notes/2023-03-26-nested-switch-case.md new file mode 100644 index 0000000000..0265f46954 --- /dev/null +++ b/change_notes/2023-03-26-nested-switch-case.md @@ -0,0 +1 @@ + * `M6-4-4` - alert message updated for clarity. \ No newline at end of file diff --git a/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql b/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql index 45b130e184..134ded1651 100644 --- a/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql +++ b/cpp/autosar/src/rules/A5-0-2/NonBooleanIfCondition.ql @@ -15,12 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.nonbooleanifstmt.NonBooleanIfStmt -from IfStmt ifStmt, Expr condition, Type explicitConversionType -where - not isExcluded(ifStmt, ConditionalsPackage::nonBooleanIfConditionQuery()) and - condition = ifStmt.getCondition() and - not ifStmt.isFromUninstantiatedTemplate(_) and - explicitConversionType = condition.getExplicitlyConverted().getType().getUnspecifiedType() and - not explicitConversionType instanceof BoolType -select condition, "If condition has non boolean type " + explicitConversionType + "." +class NonBooleanIfConditionQuery extends NonBooleanIfStmtSharedQuery { + NonBooleanIfConditionQuery() { + this = ConditionalsPackage::nonBooleanIfConditionQuery() + } +} diff --git a/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql b/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql index 07f8f4de3c..c52c100df8 100644 --- a/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql +++ b/cpp/autosar/src/rules/A5-0-2/NonBooleanIterationCondition.ql @@ -15,11 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.nonbooleaniterationstmt.NonBooleanIterationStmt -from Loop loopStmt, Expr condition, Type explicitConversionType -where - not isExcluded(loopStmt, ConditionalsPackage::nonBooleanIterationConditionQuery()) and - condition = loopStmt.getCondition() and - explicitConversionType = condition.getExplicitlyConverted().getType().getUnspecifiedType() and - not explicitConversionType instanceof BoolType -select condition, "Iteration condition has non boolean type " + explicitConversionType + "." +class NonBooleanIterationConditionQuery extends NonBooleanIterationStmtSharedQuery { + NonBooleanIterationConditionQuery() { + this = ConditionalsPackage::nonBooleanIterationConditionQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql b/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql index 1435ed2281..e75d365461 100644 --- a/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql +++ b/cpp/autosar/src/rules/M6-4-2/IfElseTerminationCondition.ql @@ -15,10 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct -from IfStmt ifStmt, IfStmt ifElse -where - not isExcluded(ifStmt, ConditionalsPackage::ifElseTerminationConditionQuery()) and - ifStmt.getElse() = ifElse and - not ifElse.hasElse() -select ifStmt, "The $@ if statement does not terminate with an else construct.", ifElse, "if...else" +class IfElseTerminationConditionQuery extends IfElseTerminationConstructSharedQuery { + IfElseTerminationConditionQuery() { + this = ConditionalsPackage::ifElseTerminationConditionQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql b/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql index d56bf1da0f..07953dd9f1 100644 --- a/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql +++ b/cpp/autosar/src/rules/M6-4-3/SwitchDoesNotStartWithCase.ql @@ -16,13 +16,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.SwitchStatement +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition -from SwitchStmt switch, SwitchCase case -where - not isExcluded(switch, ConditionalsPackage::switchDoesNotStartWithCaseQuery()) and - case = switch.getASwitchCase() and - switchWithCaseNotFirst(switch) -select switch, - "$@ statement not well formed because the first statement in a well formed switch statement must be a case clause.", - switch, "Switch" +class SwitchDoesNotStartWithCaseQuery extends SwitchCasePositionConditionSharedQuery { + SwitchDoesNotStartWithCaseQuery() { + this = ConditionalsPackage::switchDoesNotStartWithCaseQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql b/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql index 83d4c2017f..24ac2298b5 100644 --- a/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql +++ b/cpp/autosar/src/rules/M6-4-3/SwitchStatementNotWellFormed.ql @@ -16,13 +16,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.SwitchStatement +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed -from SwitchStmt switch, SwitchCase case -where - not isExcluded(switch, ConditionalsPackage::switchStatementNotWellFormedQuery()) and - case = switch.getASwitchCase() and - switchCaseNotWellFormed(case) -select switch, - "$@ statement not well formed because this $@ block uses a statement that is not allowed.", - switch, "Switch", case, "case" +class SwitchStatementNotWellFormedQuery extends SwitchNotWellFormedSharedQuery { + SwitchStatementNotWellFormedQuery() { + this = ConditionalsPackage::switchStatementNotWellFormedQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql b/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql index 18c07b8eea..c984053464 100644 --- a/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql +++ b/cpp/autosar/src/rules/M6-4-4/NestedCaseInSwitch.ql @@ -1,8 +1,7 @@ /** * @id cpp/autosar/nested-case-in-switch * @name M6-4-4: A switch-label shall only be used when the most closely-enclosing compound statement is the body of a switch statement - * @description By default in C++, the switch structure is weak, which may lead to switch labels - * being placed anywhere in the switch block. This can cause unspecified behaviour. + * @description Nested switch labels cause undefined behaviour. * @kind problem * @precision very-high * @problem.severity recommendation @@ -16,13 +15,10 @@ import cpp import codingstandards.cpp.autosar -import codingstandards.cpp.SwitchStatement +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch -from SwitchCase nestedCase, SwitchStmt switch -where - not isExcluded(nestedCase, ConditionalsPackage::nestedCaseInSwitchQuery()) and - switch.getASwitchCase() = nestedCase and - not nestedCase.getParentStmt() = switch.getChildStmt() -select nestedCase, - "Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement.", - switch, "switch", nestedCase, "case" +class NestedCaseInSwitchQuery extends NestedLabelInSwitchSharedQuery { + NestedCaseInSwitchQuery() { + this = ConditionalsPackage::nestedCaseInSwitchQuery() + } +} diff --git a/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql b/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql index 8729c948dd..8d20712021 100644 --- a/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql +++ b/cpp/autosar/src/rules/M6-5-2/NotEqualsInLoopCondition.ql @@ -19,7 +19,7 @@ import codingstandards.cpp.Loops from ForStmt fs, LoopControlVariable v where not isExcluded(fs, LoopsPackage::notEqualsInLoopConditionQuery()) and - isInvalidForLoopIncrementation(fs, v) + isInvalidForLoopIncrementation(fs, v, _) select fs, "For-loop counter $@ is updated by an increment larger than 1 and tested in the condition using == or !=.", v, v.getName() diff --git a/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql b/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql index 89e35990d5..bde6e8ddee 100644 --- a/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql +++ b/cpp/autosar/src/rules/M6-6-2/GotoStatementJumpCondition.ql @@ -15,22 +15,10 @@ import cpp import codingstandards.cpp.autosar +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition -from GotoStmt goto, Stmt target -where - not isExcluded(goto, ConditionalsPackage::gotoStatementJumpConditionQuery()) and - target = goto.getTarget() and - exists(Location targetLoc, Location gotoLoc | - targetLoc = target.getLocation() and - gotoLoc = goto.getLocation() and - targetLoc.getFile() = gotoLoc.getFile() - | - // Starts on a previous line - targetLoc.getStartLine() < gotoLoc.getEndLine() - or - // Starts on the same line, but an earlier column - targetLoc.getStartLine() = gotoLoc.getEndLine() and - targetLoc.getEndColumn() < gotoLoc.getStartColumn() - ) -select goto, "The goto jumps to the label $@ that is not declared later in the same function.", - target, goto.getName() +class GotoStatementJumpConditionQuery extends GotoStatementConditionSharedQuery { + GotoStatementJumpConditionQuery() { + this = ConditionalsPackage::gotoStatementJumpConditionQuery() + } +} diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.expected b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.expected deleted file mode 100644 index 655e5571e1..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:8:7:8:7 | i | If condition has non boolean type int. | -| test.cpp:10:7:10:7 | call to f | If condition has non boolean type int. | -| test.cpp:13:7:13:7 | a | If condition has non boolean type void *. | diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.qlref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.qlref deleted file mode 100644 index a2280d92c6..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-0-2/NonBooleanIfCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.testref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.testref new file mode 100644 index 0000000000..5f106ce750 --- /dev/null +++ b/cpp/autosar/test/rules/A5-0-2/NonBooleanIfCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.expected b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.expected deleted file mode 100644 index 091087b3a1..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.expected +++ /dev/null @@ -1,2 +0,0 @@ -| test.cpp:51:20:51:20 | i | Iteration condition has non boolean type int. | -| test.cpp:55:10:55:10 | j | Iteration condition has non boolean type int. | diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.qlref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.qlref deleted file mode 100644 index 535235d198..0000000000 --- a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/A5-0-2/NonBooleanIterationCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.testref b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.testref new file mode 100644 index 0000000000..36a500fcf8 --- /dev/null +++ b/cpp/autosar/test/rules/A5-0-2/NonBooleanIterationCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.expected b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.expected deleted file mode 100644 index 7716b684af..0000000000 --- a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:22:3:26:3 | if (...) ... | The $@ if statement does not terminate with an else construct. | test.cpp:24:10:26:3 | if (...) ... | if...else | -| test.cpp:42:5:46:5 | if (...) ... | The $@ if statement does not terminate with an else construct. | test.cpp:44:12:46:5 | if (...) ... | if...else | -| test.cpp:56:3:66:3 | if (...) ... | The $@ if statement does not terminate with an else construct. | test.cpp:58:10:66:3 | if (...) ... | if...else | diff --git a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.qlref b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.qlref deleted file mode 100644 index 28420bff1a..0000000000 --- a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-2/IfElseTerminationCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.testref b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.testref new file mode 100644 index 0000000000..d7ca04a26e --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-2/IfElseTerminationCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-2/test.cpp b/cpp/autosar/test/rules/M6-4-2/test.cpp deleted file mode 100644 index 8689771d8e..0000000000 --- a/cpp/autosar/test/rules/M6-4-2/test.cpp +++ /dev/null @@ -1,67 +0,0 @@ -void test_ifelse_valid(int expression) { - int i = 3; - int j = 4; - int k; - if (expression > 0) { // GOOD - k = i + j; - } else if (expression < 100) { - k = i - j; - } else { - k = j * j; - } -} - -void test_ifelse_mix_validity(int expression) { - int i = 4; - int j = 7; - int k; - - if (expression > 0) { // GOOD - k = i * i; - } - if (expression > 10) { // BAD - k = i + j; - } else if (expression < 0) { - k = i * 2; - } -} - -void test_ifelse_nested_invalid(int expression) { - int i = 5; - int j = 7; - int k; - - if (expression > 0) { // GOOD - k = i * i * i; - } else { - k = i * j; - } - if (expression > 10) { // GOOD - k = i; - } else if (expression < 0) { - if (expression < -10) { // BAD - k = 5 + j; - } else if (expression < -20) { - k = i * 3; - } - } else { - k = 3; - } -} - -void test_ifelse_nested_valid(int expression) { - int i = 3; - int j = 1; - int k; - if (expression > 10) { // BAD - k = i + j; - } else if (expression < 0) { - if (i > 3) { // GOOD - k = j; - } else if (i < 10) { - k = i % 3; - } else { - i = i % 2; - } - } -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.expected b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.expected deleted file mode 100644 index 58fffdcdbb..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.expected +++ /dev/null @@ -1 +0,0 @@ -| test.cpp:6:3:28:3 | switch (...) ... | $@ statement not well formed because the first statement in a well formed switch statement must be a case clause. | test.cpp:6:3:28:3 | switch (...) ... | Switch | diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.qlref b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.qlref deleted file mode 100644 index 6cd5c459fb..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-3/SwitchDoesNotStartWithCase.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.testref b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.testref new file mode 100644 index 0000000000..4dd98ccfb9 --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-3/SwitchDoesNotStartWithCase.testref @@ -0,0 +1 @@ +cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.expected b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.expected deleted file mode 100644 index 6985297c15..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:51:3:57:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:51:3:57:3 | switch (...) ... | Switch | test.cpp:52:3:52:9 | case ...: | case | -| test.cpp:61:3:70:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:61:3:70:3 | switch (...) ... | Switch | test.cpp:62:3:62:10 | case ...: | case | -| test.cpp:76:3:82:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:76:3:82:3 | switch (...) ... | Switch | test.cpp:77:3:77:9 | case ...: | case | diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.qlref b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.qlref deleted file mode 100644 index d71f5220bc..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-3/SwitchStatementNotWellFormed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.testref b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.testref new file mode 100644 index 0000000000..7695dc2772 --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-3/SwitchStatementNotWellFormed.testref @@ -0,0 +1 @@ +cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-3/test.cpp b/cpp/autosar/test/rules/M6-4-3/test.cpp deleted file mode 100644 index 4156053457..0000000000 --- a/cpp/autosar/test/rules/M6-4-3/test.cpp +++ /dev/null @@ -1,92 +0,0 @@ -void f(int y); - -void test_caseclausenotfirst_invalid(int expression) { - int i = 5; - int j; - switch (expression) { - start: - expression = 4; // NON_COMPLIANT - first statement must be case clause - case 1: - if (i > 4) { - j = 3; - } - i = 3; - break; - case 2: - if (i % 2 == 0) { - j = 1; - } - break; - case 3: - if (i % 2 == 1) { - j = 8; - } - break; - default: - j = 5; - break; - } -} -void test_switch_caseclausefirst_valid(int expression) { - int i = 5; - int j; - switch (expression) { - case 2: - if (i % 2 == 0) { - j = 1; - } - break; - case 3: - if (i % 2 == 1) { - j = 8; - } - break; - default: - j = 5; - break; - } -} - -void test_notwellformedswitch_expr(int expression) { - switch (expression) { - case 1: - int y = expression + 1; // NON_COMPLIANT - `DeclStmt` whose parent - // statementis the switch body - f(y); - break; - } -} -void test_notwellformedswitch_jmp(int expression) { - int y = 2; - switch (expression) { - case 10: - f(y); - goto end; // NON_COMPLIANT - `JumpStmt` whose parent statement is the - // switch - // body - - case 2: - break; - } -end: - expression = 3; -} - -void test_notwellformedswitch_labelStmt(int expression) { - switch (expression) { - case 1: - start: - expression = 4; // NON_COMPLIANT - `LabelStmt` whose parent statement is the - // switch body - break; - } -} - -void test_emptyfallthrough(int expression) { - switch (expression) { - case 1: // COMPLIANT - default: - expression = 0; - break; - } -} diff --git a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.expected b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.expected deleted file mode 100644 index c9fbbdcb35..0000000000 --- a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.expected +++ /dev/null @@ -1,3 +0,0 @@ -| test.cpp:9:5:9:11 | case ...: | Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement. | test.cpp:6:3:17:3 | switch (...) ... | switch | test.cpp:9:5:9:11 | case ...: | case | -| test.cpp:36:5:36:11 | case ...: | Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement. | test.cpp:23:3:43:3 | switch (...) ... | switch | test.cpp:36:5:36:11 | case ...: | case | -| test.cpp:75:5:75:11 | case ...: | Weak switch structure - the parent statement of this $@ clause does not belong to its $@ statement. | test.cpp:73:3:78:3 | switch (...) ... | switch | test.cpp:75:5:75:11 | case ...: | case | diff --git a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.qlref b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.qlref deleted file mode 100644 index ad5727e1dc..0000000000 --- a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-4-4/NestedCaseInSwitch.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.testref b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.testref new file mode 100644 index 0000000000..6c5434b0e2 --- /dev/null +++ b/cpp/autosar/test/rules/M6-4-4/NestedCaseInSwitch.testref @@ -0,0 +1 @@ +cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-4-4/test.cpp b/cpp/autosar/test/rules/M6-4-4/test.cpp deleted file mode 100644 index 6ccd440de3..0000000000 --- a/cpp/autosar/test/rules/M6-4-4/test.cpp +++ /dev/null @@ -1,79 +0,0 @@ -void f(); - -void test_switch_nested_case_invalid(int expression) { - int i = 5; - int j; - switch (expression) { - case 1: // BAD - if (i > 4) { - case 2: - j = 3; - break; - } - break; - default: - j = 5; - break; - } -} - -void test_switch_nested_case_invalid_2(int expression) { - int i = 5; - int j; - switch (expression) { - case 1: - if (i > 4) { - j = 3; - } - break; - case 2: - if (i % 2 == 0) { - j = 1; - } - case 3: - if (i % 2 == 1) { - j = 8; - case 4: // BAD - j++; - } - break; - default: - j = 5; - break; - } -} - -void test_switch_valid(int expression) { - - int i = 5; - int j; - switch (expression) { - case 1: - if (i > 4) { - j = 3; - } - break; - case 2: - if (i % 2 == 0) { - j = 1; - } - break; - case 3: - if (i % 2 == 1) { - j = 8; - } - break; - default: - j = 5; - break; - } -} - -void test_singlecase_invalid(int expression) { - switch (expression) { - { - case 1: - f(); - } - } -} \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.expected b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.expected deleted file mode 100644 index 58df2b5ec1..0000000000 --- a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.expected +++ /dev/null @@ -1,4 +0,0 @@ -| test.cpp:7:3:7:11 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:3:1:3:4 | label ...: | bad | -| test.cpp:21:3:21:11 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:17:1:17:4 | label ...: | bad | -| test.cpp:24:3:24:13 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:15:1:15:6 | label ...: | sobad | -| test.cpp:31:3:31:11 | goto ... | The goto jumps to the label $@ that is not declared later in the same function. | test.cpp:29:1:29:4 | label ...: | bad | diff --git a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.qlref b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.qlref deleted file mode 100644 index d66c789012..0000000000 --- a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/M6-6-2/GotoStatementJumpCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.testref b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.testref new file mode 100644 index 0000000000..b4f807e8e2 --- /dev/null +++ b/cpp/autosar/test/rules/M6-6-2/GotoStatementJumpCondition.testref @@ -0,0 +1 @@ +cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql \ No newline at end of file diff --git a/cpp/autosar/test/rules/M6-6-2/test.cpp b/cpp/autosar/test/rules/M6-6-2/test.cpp deleted file mode 100644 index e942077efd..0000000000 --- a/cpp/autosar/test/rules/M6-6-2/test.cpp +++ /dev/null @@ -1,32 +0,0 @@ -void test_goto_jump_forward_back() { - int i = 5; -bad: - if (i < 10) { - goto good; // GOOD - } - goto bad; // BAD - -good: - i++; -} - -void test_goto_mix_validity() { - int i = 5; -sobad: - i = i * i; -bad: - if (i < 10) { - goto good; // GOOD - } - goto bad; // BAD -good: - i++; - goto sobad; // BAD -} - -void test_goto_jumpsameline_invalid() { - int i = 3; -bad: - i = 4; - goto bad; // BAD -} \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/Loops.qll b/cpp/common/src/codingstandards/cpp/Loops.qll index 599b229771..82746b036c 100644 --- a/cpp/common/src/codingstandards/cpp/Loops.qll +++ b/cpp/common/src/codingstandards/cpp/Loops.qll @@ -5,6 +5,117 @@ import cpp import Operator +// ******* COPIED FROM semmle.code.cpp.Iteration ******* // +/** + * Holds if `child` is in the condition `forCondition` of a 'for' + * statement. + * + * For example, if a program includes + * ``` + * for (i = 0; i < 10; i++) { j++; } + * ``` + * then this predicate will hold with `forCondition` as `i < 10`, + * and `child` as any of `i`, `10` and `i < 10`. + */ +pragma[noopt] +private predicate inForCondition(Expr forCondition, Expr child) { + exists(ForStmt for | + forCondition = for.getCondition() and + child = forCondition and + for instanceof ForStmt + ) + or + exists(Expr mid | + inForCondition(forCondition, mid) and + child.getParent() = mid + ) +} + +// ******* COPIED FROM semmle.code.cpp.Iteration ******* // +/** + * Holds if `child` is in the update `forUpdate` of a 'for' statement. + * + * For example, if a program includes + * ``` + * for (i = 0; i < 10; i += 1) { j++; } + * ``` + * then this predicate will hold with `forUpdate` as `i += 1`, + * and `child` as any of `i`, `1` and `i += 1`. + */ +pragma[noopt] +private predicate inForUpdate(Expr forUpdate, Expr child) { + exists(ForStmt for | forUpdate = for.getUpdate() and child = forUpdate) + or + exists(Expr mid | inForUpdate(forUpdate, mid) and child.getParent() = mid) +} + +class MemberCrementOperation extends FunctionCall { + MemberCrementOperation() { this.getTarget() instanceof UserCrementOperator } + + Expr getOperand() { result = this.getQualifier() } +} + +class MemberAssignmentOperation extends FunctionCall { + MemberAssignmentOperation() { this.getTarget() instanceof AssignmentOperator } + + Expr getLValue() { result = this.getQualifier() } + + string getOperator() { result = this.getTarget().getName().regexpCapture("operator(.+)", 1) } +} + +/** + * Gets a LoopCounter for the given `ForStmt`. + * + * Equivalent to ForStmt.getAnIterationVariable(), but handles += and -= as well. + */ +pragma[noopt] +Variable getALoopCounter(ForStmt fs) { + // check that it is assigned to, incremented or decremented in the update + exists(Expr updateOpRoot, Expr updateOp | + updateOpRoot = fs.getUpdate() and + inForUpdate(updateOpRoot, updateOp) + | + exists(CrementOperation op, VariableAccess va | + op = updateOp and + op instanceof CrementOperation and + op.getOperand() = va and + va = result.getAnAccess() + ) + or + exists(MemberCrementOperation op, VariableAccess va | + op = updateOp and + op instanceof MemberCrementOperation and + op.getOperand() = va and + va = result.getAnAccess() + ) + or + exists(MemberAssignmentOperation op, VariableAccess va | + op = updateOp and + op instanceof MemberAssignmentOperation and + op.getOperator() = ["+=", "-="] and + op.getLValue() = va and + va = result.getAnAccess() + ) + or + exists(AssignArithmeticOperation op, VariableAccess va | + op = updateOp and + op instanceof AssignArithmeticOperation and + op.getOperator() = ["+=", "-="] and + op.getLValue() = va and + va = result.getAnAccess() + ) + or + updateOp = result.getAnAssignedValue() + ) and + result instanceof Variable and + // checked or used in the condition + exists(Expr e, VariableAccess va | + va = result.getAnAccess() and + inForCondition(e, va) and + e = fs.getCondition() + ) +} + /** * Gets an iteration variable as identified by the initialization statement for the loop. */ @@ -37,13 +148,26 @@ predicate isForLoopWithFloatingPointCounters(ForStmt forLoop, Variable v) { * Holds if for loop `forLoop` contains an invalid for loop incrementation. * M6-5-2 */ -predicate isInvalidForLoopIncrementation(ForStmt forLoop, LoopControlVariable v) { - v.getAnAccess() = forLoop.getCondition().getAChild*() and - exists(VariableAccess va | - va = v.getAnAccess() and - va = forLoop.getUpdate().getAChild*() and - not exists(CrementOperation cop | cop.getOperand() = va) and - not exists(Call c | c.getQualifier() = va and c.getTarget() instanceof UserCrementOperator) +predicate isInvalidForLoopIncrementation(ForStmt forLoop, Variable v, VariableAccess modification) { + v = getAnIterationVariable(forLoop) and + modification = v.getAnAccess() and + modification = forLoop.getUpdate().getAChild*() and + // Is modified + ( + // Variable directly modified + modification.isModified() + or + // Has a call to a member function on the variable, where the target is non-const, + // i.e. can modify the state of the object + exists(Call c | + c.getQualifier() = modification and + not c.getTarget() instanceof ConstMemberFunction + ) + ) and + // And not by a call to a crement operator + not exists(CrementOperation cop | cop.getOperand() = modification) and + not exists(Call c | + c.getQualifier() = modification and c.getTarget() instanceof UserCrementOperator ) and exists(VariableAccess va | va = forLoop.getCondition().getAChild*() and va = v.getAnAccess() | exists(EqualityOperation eop | eop.getAnOperand() = va) @@ -147,7 +271,8 @@ predicate isLoopControlVarModifiedInLoopExpr( ForStmt forLoop, LoopControlVariable loopControlVariable, VariableAccess loopControlVariableAccess ) { loopControlVariableAccess = loopControlVariable.getVariableAccessInLoop(forLoop) and - not loopControlVariable = getAnIterationVariable(forLoop) and + // Not a standard loop counter for this loop + not loopControlVariable = getALoopCounter(forLoop) and loopControlVariableAccess = forLoop.getUpdate().getAChild() and ( loopControlVariableAccess.isModified() or @@ -163,26 +288,72 @@ predicate isLoopControlVarModifiedInLoopExpr( predicate isNonBoolLoopControlVar( ForStmt forLoop, LoopControlVariable loopControlVariable, VariableAccess loopControlVariableAccess ) { - // get a loop control variable that is not a loop counter - loopControlVariableAccess = loopControlVariable.getVariableAccessInLoop(forLoop) and - not loopControlVariable = getAnIterationVariable(forLoop) and - loopControlVariableAccess.getEnclosingStmt() = forLoop.getStmt().getAChild*() and - // filter only loop control variables that are modified - ( - loopControlVariableAccess.isModified() or - loopControlVariableAccess.isAddressOfAccess() - ) and - // check if the variable type is anything but bool - not loopControlVariable.getType() instanceof BoolType -} - -predicate isInvalidLoop(ForStmt forLoop) { - isInvalidForLoopIncrementation(forLoop, _) or - isForLoopWithMulipleCounters(forLoop) or - isForLoopWithFloatingPointCounters(forLoop, _) or - isLoopCounterModifiedInCondition(forLoop, _) or - isLoopCounterModifiedInStatement(forLoop, _, _) or - isIrregularLoopCounterModification(forLoop, _, _) or - isLoopControlVarModifiedInLoopExpr(forLoop, _, _) or - isNonBoolLoopControlVar(forLoop, _, _) + exists(Variable loopCounter, ComparisonOperation terminationCheck | + loopCounter = getAnIterationVariable(forLoop) and + forLoop.getCondition() = terminationCheck.getParent*() + | + // get a loop control variable that is not a loop counter + loopControlVariableAccess = loopControlVariable.getVariableAccessInLoop(forLoop) and + not loopControlVariable = getAnIterationVariable(forLoop) and + // filter only loop control variables that are modified + ( + loopControlVariableAccess.isModified() or + loopControlVariableAccess.isAddressOfAccess() + ) and + // check if the variable type is anything but bool + not loopControlVariable.getType() instanceof BoolType and + // check if the control variable is part of the termination check, but is not compared to the loop counter + terminationCheck.getAnOperand() = loopControlVariable.getAnAccess().getParent*() and + not terminationCheck.getAnOperand() = loopCounter.getAnAccess().getParent*() + ) +} + +predicate isInvalidLoop(ForStmt forLoop) { isInvalidLoop(forLoop, _, _, _) } + +predicate isInvalidLoop(ForStmt forLoop, string reason, Locatable reasonLocation, string reasonLabel) { + exists(Variable loopCounter | + isInvalidForLoopIncrementation(forLoop, loopCounter, reasonLocation) and + reason = + "it $@ its loop counter '" + loopCounter.getName() + + "' with an operation that is not an increment or decrement" and + reasonLabel = "updates" + ) + or + isForLoopWithMulipleCounters(forLoop) and + reason = "it uses multiple loop counters$@" and + reasonLabel = "" and + reasonLocation.getLocation() instanceof UnknownExprLocation + or + isForLoopWithFloatingPointCounters(forLoop, reasonLocation) and + reason = "it uses a loop counter '$@' of type floating-point" and + reasonLabel = reasonLocation.(Variable).getName() + or + isLoopCounterModifiedInCondition(forLoop, reasonLocation) and + reason = + "it $@ the loop counter '" + reasonLocation.(VariableAccess).getTarget().getName() + + "' in the condition" and + reasonLabel = "updates" + or + exists(Variable loopCounter | + isLoopCounterModifiedInStatement(forLoop, loopCounter, reasonLocation) and + reason = "it $@ the loop counter '" + loopCounter.getName() + "' in the body of the loop" and + reasonLabel = "updates" + ) + or + exists(Variable loopCounter | + isIrregularLoopCounterModification(forLoop, loopCounter, reasonLocation) and + reason = "it $@ the loop counter '" + loopCounter.getName() + "' irregularly" and + reasonLabel = "updates" + ) + or + exists(Variable loopControlVariable | + isLoopControlVarModifiedInLoopExpr(forLoop, loopControlVariable, reasonLocation) and + reason = + "it updates $@, a loop control variable other than the loop counter, in the update expression of the loop" and + reasonLabel = loopControlVariable.getName() + ) + or + isNonBoolLoopControlVar(forLoop, reasonLocation, _) and + reason = "its $@ is not a boolean" and + reasonLabel = "loop control variable" } diff --git a/cpp/common/src/codingstandards/cpp/SwitchStatement.qll b/cpp/common/src/codingstandards/cpp/SwitchStatement.qll index 1f055be570..7e6686b41a 100644 --- a/cpp/common/src/codingstandards/cpp/SwitchStatement.qll +++ b/cpp/common/src/codingstandards/cpp/SwitchStatement.qll @@ -4,6 +4,30 @@ import cpp +/** + * Class to differentiate between extractor generated blockstmt and actual blockstmt. The extractor + * will generate an artificial blockstmt when there is a single case and statement, e.g. + * ``` + * switch(x) + * case 1: + * f(); + * ``` + * This is because our AST model considers the `case` to be a statement in its own right, so the + * extractor needs an aritifical block to hold both the case and the statement. + */ +class ArtificialBlock extends BlockStmt { + ArtificialBlock() { + exists(Location block, Location firstStatement | + block = getLocation() and firstStatement = getStmt(0).getLocation() + | + // We can identify artificial blocks as those where the start of the statement is at the same + // location as the start of the first statement in the block i.e. there was no opening brace. + block.getStartLine() = firstStatement.getStartLine() and + block.getStartColumn() = firstStatement.getStartColumn() + ) + } +} + /* A `SwitchCase` that contains a 'SwitchCase' inside its body */ class NestedSwitchCase extends SwitchCase { NestedSwitchCase() { diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index 977123ae5d..d3c2f271a4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -50,6 +50,12 @@ import SideEffects1 import SideEffects2 import SignalHandlers import StandardLibraryFunctionTypes +import Statements1 +import Statements2 +import Statements3 +import Statements4 +import Statements5 +import Statements6 import Strings1 import Strings2 import Strings3 @@ -105,6 +111,12 @@ newtype TCQuery = TSideEffects2PackageQuery(SideEffects2Query q) or TSignalHandlersPackageQuery(SignalHandlersQuery q) or TStandardLibraryFunctionTypesPackageQuery(StandardLibraryFunctionTypesQuery q) or + TStatements1PackageQuery(Statements1Query q) or + TStatements2PackageQuery(Statements2Query q) or + TStatements3PackageQuery(Statements3Query q) or + TStatements4PackageQuery(Statements4Query q) or + TStatements5PackageQuery(Statements5Query q) or + TStatements6PackageQuery(Statements6Query q) or TStrings1PackageQuery(Strings1Query q) or TStrings2PackageQuery(Strings2Query q) or TStrings3PackageQuery(Strings3Query q) or @@ -160,6 +172,12 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isSideEffects2QueryMetadata(query, queryId, ruleId, category) or isSignalHandlersQueryMetadata(query, queryId, ruleId, category) or isStandardLibraryFunctionTypesQueryMetadata(query, queryId, ruleId, category) or + isStatements1QueryMetadata(query, queryId, ruleId, category) or + isStatements2QueryMetadata(query, queryId, ruleId, category) or + isStatements3QueryMetadata(query, queryId, ruleId, category) or + isStatements4QueryMetadata(query, queryId, ruleId, category) or + isStatements5QueryMetadata(query, queryId, ruleId, category) or + isStatements6QueryMetadata(query, queryId, ruleId, category) or isStrings1QueryMetadata(query, queryId, ruleId, category) or isStrings2QueryMetadata(query, queryId, ruleId, category) or isStrings3QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements1.qll new file mode 100644 index 0000000000..88ea77c7d4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements1.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements1Query = + TNestSwitchLabelInSwitchStatementQuery() or + TBreakShallTerminateSwitchClauseQuery() or + TEverySwitchShallHaveDefaultLabelQuery() or + TDefaultNotFirstOrLastOfSwitchQuery() + +predicate isStatements1QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `nestSwitchLabelInSwitchStatement` query + Statements1Package::nestSwitchLabelInSwitchStatementQuery() and + queryId = + // `@id` for the `nestSwitchLabelInSwitchStatement` query + "c/misra/nest-switch-label-in-switch-statement" and + ruleId = "RULE-16-2" and + category = "required" + or + query = + // `Query` instance for the `breakShallTerminateSwitchClause` query + Statements1Package::breakShallTerminateSwitchClauseQuery() and + queryId = + // `@id` for the `breakShallTerminateSwitchClause` query + "c/misra/break-shall-terminate-switch-clause" and + ruleId = "RULE-16-3" and + category = "required" + or + query = + // `Query` instance for the `everySwitchShallHaveDefaultLabel` query + Statements1Package::everySwitchShallHaveDefaultLabelQuery() and + queryId = + // `@id` for the `everySwitchShallHaveDefaultLabel` query + "c/misra/every-switch-shall-have-default-label" and + ruleId = "RULE-16-4" and + category = "required" + or + query = + // `Query` instance for the `defaultNotFirstOrLastOfSwitch` query + Statements1Package::defaultNotFirstOrLastOfSwitchQuery() and + queryId = + // `@id` for the `defaultNotFirstOrLastOfSwitch` query + "c/misra/default-not-first-or-last-of-switch" and + ruleId = "RULE-16-5" and + category = "required" +} + +module Statements1Package { + Query nestSwitchLabelInSwitchStatementQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nestSwitchLabelInSwitchStatement` query + TQueryC(TStatements1PackageQuery(TNestSwitchLabelInSwitchStatementQuery())) + } + + Query breakShallTerminateSwitchClauseQuery() { + //autogenerate `Query` type + result = + // `Query` type for `breakShallTerminateSwitchClause` query + TQueryC(TStatements1PackageQuery(TBreakShallTerminateSwitchClauseQuery())) + } + + Query everySwitchShallHaveDefaultLabelQuery() { + //autogenerate `Query` type + result = + // `Query` type for `everySwitchShallHaveDefaultLabel` query + TQueryC(TStatements1PackageQuery(TEverySwitchShallHaveDefaultLabelQuery())) + } + + Query defaultNotFirstOrLastOfSwitchQuery() { + //autogenerate `Query` type + result = + // `Query` type for `defaultNotFirstOrLastOfSwitch` query + TQueryC(TStatements1PackageQuery(TDefaultNotFirstOrLastOfSwitchQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements2.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements2.qll new file mode 100644 index 0000000000..49dd38c316 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements2.qll @@ -0,0 +1,95 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements2Query = + TGotoLabelLocationConditionQuery() or + TGotoLabelBlockConditionQuery() or + TLoopIterationConditionQuery() or + TSwitchClauseNumberConditionQuery() or + TSwitchExpressionBoolConditionQuery() + +predicate isStatements2QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `gotoLabelLocationCondition` query + Statements2Package::gotoLabelLocationConditionQuery() and + queryId = + // `@id` for the `gotoLabelLocationCondition` query + "c/misra/goto-label-location-condition" and + ruleId = "RULE-15-2" and + category = "required" + or + query = + // `Query` instance for the `gotoLabelBlockCondition` query + Statements2Package::gotoLabelBlockConditionQuery() and + queryId = + // `@id` for the `gotoLabelBlockCondition` query + "c/misra/goto-label-block-condition" and + ruleId = "RULE-15-3" and + category = "required" + or + query = + // `Query` instance for the `loopIterationCondition` query + Statements2Package::loopIterationConditionQuery() and + queryId = + // `@id` for the `loopIterationCondition` query + "c/misra/loop-iteration-condition" and + ruleId = "RULE-15-4" and + category = "advisory" + or + query = + // `Query` instance for the `switchClauseNumberCondition` query + Statements2Package::switchClauseNumberConditionQuery() and + queryId = + // `@id` for the `switchClauseNumberCondition` query + "c/misra/switch-clause-number-condition" and + ruleId = "RULE-16-6" and + category = "required" + or + query = + // `Query` instance for the `switchExpressionBoolCondition` query + Statements2Package::switchExpressionBoolConditionQuery() and + queryId = + // `@id` for the `switchExpressionBoolCondition` query + "c/misra/switch-expression-bool-condition" and + ruleId = "RULE-16-7" and + category = "required" +} + +module Statements2Package { + Query gotoLabelLocationConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoLabelLocationCondition` query + TQueryC(TStatements2PackageQuery(TGotoLabelLocationConditionQuery())) + } + + Query gotoLabelBlockConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoLabelBlockCondition` query + TQueryC(TStatements2PackageQuery(TGotoLabelBlockConditionQuery())) + } + + Query loopIterationConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `loopIterationCondition` query + TQueryC(TStatements2PackageQuery(TLoopIterationConditionQuery())) + } + + Query switchClauseNumberConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchClauseNumberCondition` query + TQueryC(TStatements2PackageQuery(TSwitchClauseNumberConditionQuery())) + } + + Query switchExpressionBoolConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchExpressionBoolCondition` query + TQueryC(TStatements2PackageQuery(TSwitchExpressionBoolConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements3.qll new file mode 100644 index 0000000000..25c1a82ea2 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements3.qll @@ -0,0 +1,129 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements3Query = + TSwitchCompoundConditionQuery() or + TLoopCompoundConditionQuery() or + TSelectionCompoundConditionQuery() or + TIfElseEndConditionQuery() or + TSwitchCaseStartConditionQuery() or + TSwitchStmtNotWellFormedQuery() or + TRecursiveFunctionConditionQuery() + +predicate isStatements3QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `switchCompoundCondition` query + Statements3Package::switchCompoundConditionQuery() and + queryId = + // `@id` for the `switchCompoundCondition` query + "c/misra/switch-compound-condition" and + ruleId = "RULE-15-6" and + category = "required" + or + query = + // `Query` instance for the `loopCompoundCondition` query + Statements3Package::loopCompoundConditionQuery() and + queryId = + // `@id` for the `loopCompoundCondition` query + "c/misra/loop-compound-condition" and + ruleId = "RULE-15-6" and + category = "required" + or + query = + // `Query` instance for the `selectionCompoundCondition` query + Statements3Package::selectionCompoundConditionQuery() and + queryId = + // `@id` for the `selectionCompoundCondition` query + "c/misra/selection-compound-condition" and + ruleId = "RULE-15-6" and + category = "required" + or + query = + // `Query` instance for the `ifElseEndCondition` query + Statements3Package::ifElseEndConditionQuery() and + queryId = + // `@id` for the `ifElseEndCondition` query + "c/misra/if-else-end-condition" and + ruleId = "RULE-15-7" and + category = "required" + or + query = + // `Query` instance for the `switchCaseStartCondition` query + Statements3Package::switchCaseStartConditionQuery() and + queryId = + // `@id` for the `switchCaseStartCondition` query + "c/misra/switch-case-start-condition" and + ruleId = "RULE-16-1" and + category = "required" + or + query = + // `Query` instance for the `switchStmtNotWellFormed` query + Statements3Package::switchStmtNotWellFormedQuery() and + queryId = + // `@id` for the `switchStmtNotWellFormed` query + "c/misra/switch-stmt-not-well-formed" and + ruleId = "RULE-16-1" and + category = "required" + or + query = + // `Query` instance for the `recursiveFunctionCondition` query + Statements3Package::recursiveFunctionConditionQuery() and + queryId = + // `@id` for the `recursiveFunctionCondition` query + "c/misra/recursive-function-condition" and + ruleId = "RULE-17-2" and + category = "required" +} + +module Statements3Package { + Query switchCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchCompoundCondition` query + TQueryC(TStatements3PackageQuery(TSwitchCompoundConditionQuery())) + } + + Query loopCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `loopCompoundCondition` query + TQueryC(TStatements3PackageQuery(TLoopCompoundConditionQuery())) + } + + Query selectionCompoundConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `selectionCompoundCondition` query + TQueryC(TStatements3PackageQuery(TSelectionCompoundConditionQuery())) + } + + Query ifElseEndConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `ifElseEndCondition` query + TQueryC(TStatements3PackageQuery(TIfElseEndConditionQuery())) + } + + Query switchCaseStartConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchCaseStartCondition` query + TQueryC(TStatements3PackageQuery(TSwitchCaseStartConditionQuery())) + } + + Query switchStmtNotWellFormedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `switchStmtNotWellFormed` query + TQueryC(TStatements3PackageQuery(TSwitchStmtNotWellFormedQuery())) + } + + Query recursiveFunctionConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `recursiveFunctionCondition` query + TQueryC(TStatements3PackageQuery(TRecursiveFunctionConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements4.qll new file mode 100644 index 0000000000..b46cd2207b --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements4.qll @@ -0,0 +1,78 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements4Query = + TFloatingPointLoopCountersQuery() or + TForLoopNotWellFormedQuery() or + TNonBooleanIfConditionQuery() or + TNonBooleanIterationConditionQuery() + +predicate isStatements4QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `floatingPointLoopCounters` query + Statements4Package::floatingPointLoopCountersQuery() and + queryId = + // `@id` for the `floatingPointLoopCounters` query + "c/cert/floating-point-loop-counters" and + ruleId = "FLP30-C" and + category = "rule" + or + query = + // `Query` instance for the `forLoopNotWellFormed` query + Statements4Package::forLoopNotWellFormedQuery() and + queryId = + // `@id` for the `forLoopNotWellFormed` query + "c/misra/for-loop-not-well-formed" and + ruleId = "RULE-14-2" and + category = "required" + or + query = + // `Query` instance for the `nonBooleanIfCondition` query + Statements4Package::nonBooleanIfConditionQuery() and + queryId = + // `@id` for the `nonBooleanIfCondition` query + "c/misra/non-boolean-if-condition" and + ruleId = "RULE-14-4" and + category = "required" + or + query = + // `Query` instance for the `nonBooleanIterationCondition` query + Statements4Package::nonBooleanIterationConditionQuery() and + queryId = + // `@id` for the `nonBooleanIterationCondition` query + "c/misra/non-boolean-iteration-condition" and + ruleId = "RULE-14-4" and + category = "required" +} + +module Statements4Package { + Query floatingPointLoopCountersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `floatingPointLoopCounters` query + TQueryC(TStatements4PackageQuery(TFloatingPointLoopCountersQuery())) + } + + Query forLoopNotWellFormedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `forLoopNotWellFormed` query + TQueryC(TStatements4PackageQuery(TForLoopNotWellFormedQuery())) + } + + Query nonBooleanIfConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonBooleanIfCondition` query + TQueryC(TStatements4PackageQuery(TNonBooleanIfConditionQuery())) + } + + Query nonBooleanIterationConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonBooleanIterationCondition` query + TQueryC(TStatements4PackageQuery(TNonBooleanIterationConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll new file mode 100644 index 0000000000..d8312d11d7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements5.qll @@ -0,0 +1,61 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements5Query = + TControllingExprInvariantQuery() or + TFunctionReturnConditionQuery() or + TNonVoidFunctionReturnConditionQuery() + +predicate isStatements5QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `controllingExprInvariant` query + Statements5Package::controllingExprInvariantQuery() and + queryId = + // `@id` for the `controllingExprInvariant` query + "c/misra/controlling-expr-invariant" and + ruleId = "RULE-14-3" and + category = "required" + or + query = + // `Query` instance for the `functionReturnCondition` query + Statements5Package::functionReturnConditionQuery() and + queryId = + // `@id` for the `functionReturnCondition` query + "c/misra/function-return-condition" and + ruleId = "RULE-15-5" and + category = "advisory" + or + query = + // `Query` instance for the `nonVoidFunctionReturnCondition` query + Statements5Package::nonVoidFunctionReturnConditionQuery() and + queryId = + // `@id` for the `nonVoidFunctionReturnCondition` query + "c/misra/non-void-function-return-condition" and + ruleId = "RULE-17-4" and + category = "mandatory" +} + +module Statements5Package { + Query controllingExprInvariantQuery() { + //autogenerate `Query` type + result = + // `Query` type for `controllingExprInvariant` query + TQueryC(TStatements5PackageQuery(TControllingExprInvariantQuery())) + } + + Query functionReturnConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `functionReturnCondition` query + TQueryC(TStatements5PackageQuery(TFunctionReturnConditionQuery())) + } + + Query nonVoidFunctionReturnConditionQuery() { + //autogenerate `Query` type + result = + // `Query` type for `nonVoidFunctionReturnCondition` query + TQueryC(TStatements5PackageQuery(TNonVoidFunctionReturnConditionQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Statements6.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements6.qll new file mode 100644 index 0000000000..7261d0980a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Statements6.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Statements6Query = TGotoStatementUsedQuery() + +predicate isStatements6QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `gotoStatementUsed` query + Statements6Package::gotoStatementUsedQuery() and + queryId = + // `@id` for the `gotoStatementUsed` query + "c/misra/goto-statement-used" and + ruleId = "RULE-15-1" and + category = "advisory" +} + +module Statements6Package { + Query gotoStatementUsedQuery() { + //autogenerate `Query` type + result = + // `Query` type for `gotoStatementUsed` query + TQueryC(TStatements6PackageQuery(TGotoStatementUsedQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/rules/gotostatementcondition/GotoStatementCondition.qll b/cpp/common/src/codingstandards/cpp/rules/gotostatementcondition/GotoStatementCondition.qll new file mode 100644 index 0000000000..74c6abbade --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/gotostatementcondition/GotoStatementCondition.qll @@ -0,0 +1,36 @@ +/** + * Provides a library which includes a `problems` predicate for reporting goto statements that jump to labels + * declared later in the same funciton. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class GotoStatementConditionSharedQuery extends Query { } + +Query getQuery() { result instanceof GotoStatementConditionSharedQuery } + +query predicate problems( + GotoStmt goto, string message, GotoStmt gotoLocation, string gotoLabel, Stmt target, + string targetLabel +) { + not isExcluded(goto, getQuery()) and + target = goto.getTarget() and + exists(Location targetLoc, Location gotoLoc | + targetLoc = target.getLocation() and + gotoLoc = goto.getLocation() and + targetLoc.getFile() = gotoLoc.getFile() + | + // Starts on a previous line + targetLoc.getStartLine() < gotoLoc.getEndLine() + or + // Starts on the same line, but an earlier column + targetLoc.getStartLine() = gotoLoc.getEndLine() and + targetLoc.getEndColumn() < gotoLoc.getStartColumn() + ) and + goto = gotoLocation and + message = "The $@ statement jumps to a $@ that is not declared later in the same function." and + gotoLabel = goto.getName() and + targetLabel = target.toString() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/ifelseterminationconstruct/IfElseTerminationConstruct.qll b/cpp/common/src/codingstandards/cpp/rules/ifelseterminationconstruct/IfElseTerminationConstruct.qll new file mode 100644 index 0000000000..5755ed8f38 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/ifelseterminationconstruct/IfElseTerminationConstruct.qll @@ -0,0 +1,22 @@ +/** + * Provides a library which includes a `problems` predicate for reporting.... + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class IfElseTerminationConstructSharedQuery extends Query { } + +Query getQuery() { result instanceof IfElseTerminationConstructSharedQuery } + +query predicate problems(IfStmt ifStmt, string message, IfStmt ifLocation, string ifElseString) { + not isExcluded(ifStmt, getQuery()) and + exists(IfStmt ifElse | + ifStmt.getElse() = ifElse and + not ifElse.hasElse() + ) and + ifLocation = ifStmt and + message = "The $@ construct does not terminate with else statement." and + ifElseString = "`if...else`" +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nestedlabelinswitch/NestedLabelInSwitch.qll b/cpp/common/src/codingstandards/cpp/rules/nestedlabelinswitch/NestedLabelInSwitch.qll new file mode 100644 index 0000000000..d6e75d6faf --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nestedlabelinswitch/NestedLabelInSwitch.qll @@ -0,0 +1,25 @@ +/** + * Provides a library which includes a `problems` predicate for reporting nested labels in a switch statement. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NestedLabelInSwitchSharedQuery extends Query { } + +Query getQuery() { result instanceof NestedLabelInSwitchSharedQuery } + +query predicate problems( + SwitchCase case, string message, SwitchCase caseLocation, string caseLabel, SwitchStmt switch, + string switchLabel +) { + not isExcluded(case, getQuery()) and + switch.getASwitchCase() = caseLocation and + not case.getParentStmt() = switch.getChildStmt() and + case = caseLocation and + message = + "The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement." and + caseLabel = case.toString() and + switchLabel = switch.toString() +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nonbooleanifstmt/NonBooleanIfStmt.qll b/cpp/common/src/codingstandards/cpp/rules/nonbooleanifstmt/NonBooleanIfStmt.qll new file mode 100644 index 0000000000..18346a8159 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nonbooleanifstmt/NonBooleanIfStmt.qll @@ -0,0 +1,22 @@ +/** + * Provides a library which includes a `problems` predicate for reporting if statements which have non boolean conditions. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NonBooleanIfStmtSharedQuery extends Query { } + +Query getQuery() { result instanceof NonBooleanIfStmtSharedQuery } + +query predicate problems(Expr condition, string message) { + not isExcluded(condition, getQuery()) and + exists(IfStmt ifStmt, Type explicitConversionType | + condition = ifStmt.getCondition() and + not ifStmt.isFromUninstantiatedTemplate(_) and + explicitConversionType = condition.getExplicitlyConverted().getUnderlyingType() and + not explicitConversionType instanceof BoolType and + message = "If condition has non boolean type " + explicitConversionType + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.qll b/cpp/common/src/codingstandards/cpp/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.qll new file mode 100644 index 0000000000..f1ee555406 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.qll @@ -0,0 +1,21 @@ +/** + * Provides a library which includes a `problems` predicate for reporting.... + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions + +abstract class NonBooleanIterationStmtSharedQuery extends Query { } + +Query getQuery() { result instanceof NonBooleanIterationStmtSharedQuery } + +query predicate problems(Loop loopStmt, string message) { + not isExcluded(loopStmt, getQuery()) and + exists(Expr condition, Type explicitConversionType | + condition = loopStmt.getCondition() and + explicitConversionType = condition.getExplicitlyConverted().getType().getUnspecifiedType() and + not explicitConversionType instanceof BoolType and + message = "Iteration condition has non boolean type " + explicitConversionType + "." + ) +} diff --git a/cpp/common/src/codingstandards/cpp/rules/switchcasepositioncondition/SwitchCasePositionCondition.qll b/cpp/common/src/codingstandards/cpp/rules/switchcasepositioncondition/SwitchCasePositionCondition.qll new file mode 100644 index 0000000000..68ba9850af --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/switchcasepositioncondition/SwitchCasePositionCondition.qll @@ -0,0 +1,32 @@ +/** + * Provides a library which includes a `problems` predicate for reporting.... + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.SwitchStatement + +abstract class SwitchCasePositionConditionSharedQuery extends Query { } + +Query getQuery() { result instanceof SwitchCasePositionConditionSharedQuery } + +//from SwitchStmt switch, SwitchCase case +//where +//not isExcluded(switch, ConditionalsPackage::switchDoesNotStartWithCaseQuery()) and +//case = switch.getASwitchCase() and +//switchWithCaseNotFirst(switch) +//select switch, +//"$@ statement not well formed because the first statement in a well formed switch statement must be a case clause.", +//switch, "Switch" +query predicate problems( + SwitchStmt switch, string message, SwitchStmt switchLocation, string switchMessage +) { + not isExcluded(switch, getQuery()) and + exists(SwitchCase case | case = switch.getASwitchCase()) and + switchWithCaseNotFirst(switch) and + switchLocation = switch and + switchMessage = "Switch" and + message = + "$@ statement not well formed because the first statement in a well formed switch statement must be a case clause." +} diff --git a/cpp/common/src/codingstandards/cpp/rules/switchnotwellformed/SwitchNotWellFormed.qll b/cpp/common/src/codingstandards/cpp/rules/switchnotwellformed/SwitchNotWellFormed.qll new file mode 100644 index 0000000000..ee04228a95 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/switchnotwellformed/SwitchNotWellFormed.qll @@ -0,0 +1,26 @@ +/** + * Provides a library which includes a `problems` predicate for reporting.... + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.SwitchStatement + +abstract class SwitchNotWellFormedSharedQuery extends Query { } + +Query getQuery() { result instanceof SwitchNotWellFormedSharedQuery } + +query predicate problems( + SwitchStmt switch, string message, SwitchStmt switchLocation, string switchMessage, + SwitchCase case, string caseMessage +) { + not isExcluded(switch, getQuery()) and + case = switch.getASwitchCase() and + switchCaseNotWellFormed(case) and + switch = switchLocation and + message = + "$@ statement not well formed because this $@ block uses a statement that is not allowed." and + switchMessage = "Switch" and + caseMessage = "case" +} diff --git a/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.expected b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.expected new file mode 100644 index 0000000000..c1b2f35eda --- /dev/null +++ b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.expected @@ -0,0 +1,4 @@ +| test.cpp:7:3:7:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:7:3:7:10 | goto ... | l1 | test.cpp:3:1:3:3 | label ...: | label ...: | +| test.cpp:19:3:19:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:19:3:19:10 | goto ... | l2 | test.cpp:15:1:15:3 | label ...: | label ...: | +| test.cpp:21:3:21:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:21:3:21:10 | goto ... | l1 | test.cpp:14:1:14:3 | label ...: | label ...: | +| test.cpp:26:3:26:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.cpp:26:3:26:10 | goto ... | l1 | test.cpp:25:1:25:3 | label ...: | label ...: | diff --git a/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql new file mode 100644 index 0000000000..826a161cc6 --- /dev/null +++ b/cpp/common/test/rules/gotostatementcondition/GotoStatementCondition.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition diff --git a/cpp/common/test/rules/gotostatementcondition/test.cpp b/cpp/common/test/rules/gotostatementcondition/test.cpp new file mode 100644 index 0000000000..225c1b32f6 --- /dev/null +++ b/cpp/common/test/rules/gotostatementcondition/test.cpp @@ -0,0 +1,27 @@ +void f1(int p1) { + +l1: + if (p1) { + goto l2; // COMPLIANT + } + goto l1; // NON_COMPLIANT + +l2:; +} + +void f2(int p1) { + +l1:; +l2: + if (p1) { + goto l3; // COMPLIANT + } + goto l2; // NON_COMPLIANT +l3: + goto l1; // NON_COMPLIANT +} + +void f3() { +l1: + goto l1; // NON_COMPLIANT +} \ No newline at end of file diff --git a/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected new file mode 100644 index 0000000000..bcbc388ca6 --- /dev/null +++ b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.expected @@ -0,0 +1,3 @@ +| test.cpp:21:5:25:5 | if (...) ... | The $@ construct does not terminate with else statement. | test.cpp:21:5:25:5 | if (...) ... | `if...else` | +| test.cpp:41:7:45:7 | if (...) ... | The $@ construct does not terminate with else statement. | test.cpp:41:7:45:7 | if (...) ... | `if...else` | +| test.cpp:55:5:65:5 | if (...) ... | The $@ construct does not terminate with else statement. | test.cpp:55:5:65:5 | if (...) ... | `if...else` | diff --git a/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql new file mode 100644 index 0000000000..d96cb456ce --- /dev/null +++ b/cpp/common/test/rules/ifelseterminationconstruct/IfElseTerminationConstruct.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct diff --git a/cpp/common/test/rules/ifelseterminationconstruct/test.cpp b/cpp/common/test/rules/ifelseterminationconstruct/test.cpp new file mode 100644 index 0000000000..ccb59b6ca0 --- /dev/null +++ b/cpp/common/test/rules/ifelseterminationconstruct/test.cpp @@ -0,0 +1,67 @@ + +void test_ifelse_valid(int expression) { + int i = 3; + int j = 4; + int k; + if (expression > 0) { // GOOD + k = i + j; + } else if (expression < 100) { + k = i - j; + } else { + k = j * j; + } + void test_ifelse_mix_validity(int expression) { + int i = 4; + int j = 7; + int k; + + if (expression > 0) { // GOOD + k = i * i; + } + if (expression > 10) { // BAD + k = i + j; + } else if (expression < 0) { + k = i * 2; + } + } + + void test_ifelse_nested_invalid(int expression) { + int i = 5; + int j = 7; + int k; + + if (expression > 0) { // GOOD + k = i * i * i; + } else { + k = i * j; + } + if (expression > 10) { // GOOD + k = i; + } else if (expression < 0) { + if (expression < -10) { // BAD + k = 5 + j; + } else if (expression < -20) { + k = i * 3; + } + } else { + k = 3; + } + } + + void test_ifelse_nested_valid(int expression) { + int i = 3; + int j = 1; + int k; + if (expression > 10) { // BAD + k = i + j; + } else if (expression < 0) { + if (i > 3) { // GOOD + k = j; + } else if (i < 10) { + k = i % 3; + } else { + i = i % 2; + } + } + } +} diff --git a/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected new file mode 100644 index 0000000000..58a238dbc4 --- /dev/null +++ b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.expected @@ -0,0 +1,3 @@ +| test.cpp:9:5:9:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.cpp:9:5:9:11 | case ...: | case ...: | test.cpp:6:3:17:3 | switch (...) ... | switch (...) ... | +| test.cpp:36:5:36:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.cpp:36:5:36:11 | case ...: | case ...: | test.cpp:23:3:43:3 | switch (...) ... | switch (...) ... | +| test.cpp:76:5:76:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.cpp:76:5:76:11 | case ...: | case ...: | test.cpp:73:3:79:3 | switch (...) ... | switch (...) ... | diff --git a/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql new file mode 100644 index 0000000000..a23fe0b2f9 --- /dev/null +++ b/cpp/common/test/rules/nestedlabelinswitch/NestedLabelInSwitch.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch diff --git a/cpp/common/test/rules/nestedlabelinswitch/test.cpp b/cpp/common/test/rules/nestedlabelinswitch/test.cpp new file mode 100644 index 0000000000..5c04578f0b --- /dev/null +++ b/cpp/common/test/rules/nestedlabelinswitch/test.cpp @@ -0,0 +1,80 @@ +void f(); + +void f1(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + case 2: // NON_COMPLIANT + j; + break; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f2(int p1) { + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + case 3: // COMPLIANT + if (i) { + j; + case 4: // NON_COMPLIANT + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f3(int p1) { + + int i; + int j; + switch (p1) { + case 1: // COMPLIANT + if (i) { + j; + } + break; + case 2: // COMPLIANT + if (i) { + j; + } + break; + case 3: // COMPLIANT + if (i) { + j; + } + break; + default: // COMPLIANT + j; + break; + } +} + +void f4(int p1) { + switch (p1) { + int i; + if (i) { + case 1: // NON_COMPLIANT + f(); + } + } +} diff --git a/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.expected b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.expected new file mode 100644 index 0000000000..f3899bf81c --- /dev/null +++ b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.expected @@ -0,0 +1,3 @@ +| test.cpp:9:7:9:7 | i | If condition has non boolean type int. | +| test.cpp:11:7:11:7 | call to f | If condition has non boolean type int. | +| test.cpp:14:7:14:7 | a | If condition has non boolean type void *. | diff --git a/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql new file mode 100644 index 0000000000..da907fcf9e --- /dev/null +++ b/cpp/common/test/rules/nonbooleanifstmt/NonBooleanIfStmt.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonbooleanifstmt.NonBooleanIfStmt diff --git a/cpp/autosar/test/rules/A5-0-2/test.cpp b/cpp/common/test/rules/nonbooleanifstmt/test.cpp similarity index 52% rename from cpp/autosar/test/rules/A5-0-2/test.cpp rename to cpp/common/test/rules/nonbooleanifstmt/test.cpp index 00fc281605..b10cd7034e 100644 --- a/cpp/autosar/test/rules/A5-0-2/test.cpp +++ b/cpp/common/test/rules/nonbooleanifstmt/test.cpp @@ -1,3 +1,4 @@ + #include int f(); @@ -45,56 +46,3 @@ void test_boolean_conditions() { if (a) { // COMPLIANT - a has an explicit operator bool() } } - -void test_non_boolean_iterations() { - int j; - for (int i = 10; i; i++) { // NON_COMPLIANT - j = 3; - } - - while (j) { // NON_COMPLIANT - int k = 3; - } -} - -void test_boolean_iterations() { - int j = 0; - for (int i = 0; i < 10; i++) { // COMPLIANT - j = i + j; - } - - int boolean = 0; - while (bool(boolean)) { // COMPLIANT - j = 5; - } - - while (int i = 0) { // COMPLIANT - due to exception - } - - ClassA a; - while (a) { // COMPLIANT - a has an explicit operator bool() - } -} - -template class ClassB { -public: - std::deque d; - void f() { - if (d.empty()) { // COMPLIANT - } - } -}; - -void class_b_test() { - ClassB b; - - b.f(); -} - -class ClassC { - void run() { - std::deque d; - if (!d.empty()) { // COMPLIANT - } - } -}; \ No newline at end of file diff --git a/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.expected b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.expected new file mode 100644 index 0000000000..05dfadc1f7 --- /dev/null +++ b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.expected @@ -0,0 +1,2 @@ +| test.cpp:7:3:9:3 | for(...;...;...) ... | Iteration condition has non boolean type int. | +| test.cpp:11:3:13:3 | while (...) ... | Iteration condition has non boolean type int. | diff --git a/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql new file mode 100644 index 0000000000..ffe3f351c6 --- /dev/null +++ b/cpp/common/test/rules/nonbooleaniterationstmt/NonBooleanIterationStmt.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.nonbooleaniterationstmt.NonBooleanIterationStmt diff --git a/cpp/common/test/rules/nonbooleaniterationstmt/test.cpp b/cpp/common/test/rules/nonbooleaniterationstmt/test.cpp new file mode 100644 index 0000000000..ed25cad311 --- /dev/null +++ b/cpp/common/test/rules/nonbooleaniterationstmt/test.cpp @@ -0,0 +1,44 @@ +#include + +int f(); +void *g(); +void test_non_boolean_iterations() { + int j; + for (int i = 10; i; i++) { // NON_COMPLIANT + j = 3; + } + + while (j) { // NON_COMPLIANT + int k = 3; + } +} + +void test_boolean_iterations() { + int j = 0; + for (int i = 0; i < 10; i++) { // COMPLIANT + j = i + j; + } +} + +template class ClassB { +public: + std::deque d; + void f() { + if (d.empty()) { // COMPLIANT + } + } +}; + +void class_b_test() { + ClassB b; + + b.f(); +} + +class ClassC { + void run() { + std::deque d; + if (!d.empty()) { // COMPLIANT + } + } +}; \ No newline at end of file diff --git a/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected new file mode 100644 index 0000000000..9bad1dc42e --- /dev/null +++ b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.expected @@ -0,0 +1 @@ +| test.cpp:5:3:24:3 | switch (...) ... | $@ statement not well formed because the first statement in a well formed switch statement must be a case clause. | test.cpp:5:3:24:3 | switch (...) ... | Switch | diff --git a/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql new file mode 100644 index 0000000000..65188d04f7 --- /dev/null +++ b/cpp/common/test/rules/switchcasepositioncondition/SwitchCasePositionCondition.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition diff --git a/cpp/common/test/rules/switchcasepositioncondition/test.cpp b/cpp/common/test/rules/switchcasepositioncondition/test.cpp new file mode 100644 index 0000000000..bbdbddfb80 --- /dev/null +++ b/cpp/common/test/rules/switchcasepositioncondition/test.cpp @@ -0,0 +1,42 @@ +void f1(int p1); + +void f2(int p1) { + + switch (p1) { + start:; // NON_COMPLIANT + case 1: + if (p1) { + ; + }; + break; + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} +void f3(int p1) { + + switch (p1) { // COMPLIANT + case 2: + if (p1) { + ; + } + break; + case 3: + if (p1) { + ; + } + break; + default:; + break; + } +} diff --git a/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected new file mode 100644 index 0000000000..0353e68531 --- /dev/null +++ b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.expected @@ -0,0 +1,3 @@ +| test.cpp:4:3:10:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:4:3:10:3 | switch (...) ... | Switch | test.cpp:5:3:5:9 | case ...: | case | +| test.cpp:13:3:20:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:13:3:20:3 | switch (...) ... | Switch | test.cpp:14:3:14:10 | case ...: | case | +| test.cpp:25:3:30:3 | switch (...) ... | $@ statement not well formed because this $@ block uses a statement that is not allowed. | test.cpp:25:3:30:3 | switch (...) ... | Switch | test.cpp:26:3:26:9 | case ...: | case | diff --git a/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql new file mode 100644 index 0000000000..0a398a99a9 --- /dev/null +++ b/cpp/common/test/rules/switchnotwellformed/SwitchNotWellFormed.ql @@ -0,0 +1,2 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.switchnotwellformed.SwitchNotWellFormed diff --git a/cpp/common/test/rules/switchnotwellformed/test.cpp b/cpp/common/test/rules/switchnotwellformed/test.cpp new file mode 100644 index 0000000000..d1fe00d5af --- /dev/null +++ b/cpp/common/test/rules/switchnotwellformed/test.cpp @@ -0,0 +1,40 @@ + +void f1(); +void f2(int p1) { + switch (p1) { + case 1: + int y = p1; // NON_COMPLIANT - `DeclStmt` whose parent + // statement is the switch body + f1(); + break; + } +} +void f3(int p1) { + switch (p1) { + case 10: + f1(); + goto L1; // NON_COMPLIANT - `JumpStmt` whose parent statement is the// + // switch// body + case 2: + break; + } +L1:; +} + +void f4(int p1) { + switch (p1) { + case 1: + L1:; // NON_COMPLIANT - `LabelStmt` whose parent statement is the + // switch body + break; + } +} + +void f5(int p1) { + switch (p1) { + case 1: // COMPLIANT + default: + p1 = 0; + break; + } +} diff --git a/rule_packages/c/Statements1.json b/rule_packages/c/Statements1.json new file mode 100644 index 0000000000..a8dc1b55ea --- /dev/null +++ b/rule_packages/c/Statements1.json @@ -0,0 +1,82 @@ +{ + "MISRA-C-2012": { + "RULE-16-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Nested switch labels can lead to unstructured code.", + "kind": "problem", + "name": "Nested switch labels shall not be used", + "precision": "very-high", + "severity": "recommendation", + "short_name": "NestSwitchLabelInSwitchStatement", + "shared_implementation_short_name": "NestedLabelInSwitch", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement" + }, + "RULE-16-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "An unterminated switch-clause occurring at the end of a switch statement may fall into switch clauses which are added later.", + "kind": "problem", + "name": "An unconditional break statement shall terminate every switch-clause", + "precision": "high", + "severity": "warning", + "short_name": "BreakShallTerminateSwitchClause", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "An unconditional break statement shall terminate every switch-clause" + }, + "RULE-16-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A default label that has no statements or a comment explaining why this is correct indicates a missing implementation that may result in unexpected behavior when the default case is executed.", + "kind": "problem", + "name": "Every switch statement shall have a default label", + "precision": "very-high", + "severity": "warning", + "short_name": "EverySwitchShallHaveDefaultLabel", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "Every switch statement shall have a default label" + }, + "RULE-16-5": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Locating the default label is easier when it is the first or last label.", + "kind": "problem", + "name": "A default label shall appear as either the first or the last switch label or a switch statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "DefaultNotFirstOrLastOfSwitch", + "tags": [] + } + ], + "title": "A default label shall appear as either the first or the last switch label of a switch statement" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements2.json b/rule_packages/c/Statements2.json new file mode 100644 index 0000000000..8aa44c5091 --- /dev/null +++ b/rule_packages/c/Statements2.json @@ -0,0 +1,105 @@ +{ + "MISRA-C-2012": { + "RULE-15-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Unconstrained use of goto can lead to unstructured code.", + "kind": "problem", + "name": "The goto statement shall jump to a label declared later in the same function", + "precision": "very-high", + "severity": "recommendation", + "short_name": "GotoLabelLocationCondition", + "shared_implementation_short_name": "GotoStatementCondition", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "The goto statement shall jump to a label declared later in the same function" + }, + "RULE-15-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement.", + "kind": "problem", + "name": "The goto statement and any of its label shall be declared or enclosed in the same block", + "precision": "high", + "severity": "recommendation", + "short_name": "GotoLabelBlockCondition", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement" + }, + "RULE-15-4": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "More than one break or goto statement in iteration conditions may lead to readability and maintainability issues.", + "kind": "problem", + "name": "There should be no more than one break or goto statement used to terminate any iteration statement", + "precision": "very-high", + "severity": "error", + "short_name": "LoopIterationCondition", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "There should be no more than one break or goto statement used to terminate any iteration statement" + }, + "RULE-16-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Switch Statements with a single path are redundant and may cause programming errors.", + "kind": "problem", + "name": "Every switch statement shall have at least two switch-clauses", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SwitchClauseNumberCondition", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "Every switch statement shall have at least two switch-clauses" + }, + "RULE-16-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "An `if-else` construct is more appropriate for boolean controlled expression.", + "kind": "problem", + "name": "A switch-expression shall not have essentially Boolean type", + "precision": "very-high", + "severity": "error", + "short_name": "SwitchExpressionBoolCondition", + "tags": [ + "readability", + "maintainability" + ] + } + ], + "title": "A switch-expression shall not have essentially Boolean type" + } + } +} diff --git a/rule_packages/c/Statements3.json b/rule_packages/c/Statements3.json new file mode 100644 index 0000000000..41463415a6 --- /dev/null +++ b/rule_packages/c/Statements3.json @@ -0,0 +1,123 @@ +{ + "MISRA-C-2012": { + "RULE-15-6": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "If the body of a switch is not enclosed in braces, then this can lead to incorrect execution, and is hard for developers to maintain.", + "kind": "problem", + "name": "The statement forming the body of a switch shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SwitchCompoundCondition", + "tags": [ + "maintainability", + "readability" + ] + }, + { + "description": "if the body of a loop is not enclosed in braces, then this can lead to incorrect execution, and is hard for developers to maintain.", + "kind": "problem", + "name": "the statement forming the body of a loop shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "LoopCompoundCondition", + "tags": [ + "maintainability", + "readability" + ] + }, + { + "description": "if the body of a selection statement is not enclosed in braces, then this can lead to incorrect execution, and is hard for developers to maintain.", + "kind": "problem", + "name": "the statement forming the body of a loop shall be a compound statement", + "precision": "very-high", + "severity": "recommendation", + "short_name": "SelectionCompoundCondition", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "The body of an iteration-statement or a selection-statement shall be a compund-statement" + }, + "RULE-15-7": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Terminating an `if...else` construct is a defensive programming technique.", + "kind": "problem", + "name": "All if / else if constructs shall be terminated with an else statement", + "precision": "very-high", + "severity": "recommendation", + "shared_implementation_short_name": "IfElseTerminationConstruct", + "short_name": "IfElseEndCondition", + "tags": [ + "readability", + "maintainability" + ] + } + ], + "title": "All if / else if constructs shall be terminated with an else statement" + }, + "RULE-16-1": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "The switch statement syntax is weak and may lead to unspecified behaviour.", + "kind": "problem", + "name": "A well formed switch statement must start with a case clause", + "precision": "very-high", + "severity": "recommendation", + "shared_implementation_short_name": "SwitchCasePositionCondition", + "short_name": "SwitchCaseStartCondition", + "tags": [ + "maintainability", + "readability" + ] + }, + { + "description": "The switch statement syntax is weak and may lead to unspecified behaviour.", + "kind": "problem", + "name": "A well formed switch statement should only have expression, compound, selection, iteration or try statements within its body", + "precision": "very-high", + "severity": "recommendation", + "shared_implementation_short_name": "SwitchNotWellFormed", + "short_name": "SwitchStmtNotWellFormed", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "All switch statements shall be well-formed" + }, + "RULE-17-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Recursive function may cause memory and system failure issues.", + "kind": "problem", + "name": "Functions shall not call themselves, either directly or indirectly", + "precision": "very-high", + "severity": "error", + "short_name": "RecursiveFunctionCondition", + "tags": [ + "maintainability", + "correctness" + ] + } + ], + "title": "Functions shall not call themselves, either directly or indirectly" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements4.json b/rule_packages/c/Statements4.json new file mode 100644 index 0000000000..56e13c9de6 --- /dev/null +++ b/rule_packages/c/Statements4.json @@ -0,0 +1,79 @@ +{ + "CERT-C": { + "FLP30-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Loop counters should not use floating-point variables to keep code portable.", + "kind": "problem", + "name": "Do not use floating-point variables as loop counters", + "precision": "very-high", + "severity": "recommendation", + "short_name": "FloatingPointLoopCounters", + "tags": [ + "maintainability", + "readability", + "correctness" + ] + } + ], + "title": "Do not use floating-point variables as loop counters" + } + }, + "MISRA-C-2012": { + "RULE-14-2": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "A well-formed for loop makes code easier to review.", + "kind": "problem", + "name": "A for loop shall be well-formed", + "precision": "very-high", + "severity": "recommendation", + "short_name": "ForLoopNotWellFormed", + "tags": [ + "readability", + "maintainability" + ] + } + ], + "title": "A for loop shall be well-formed" + }, + "RULE-14-4": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "Non boolean conditions can be confusing for developers.", + "kind": "problem", + "name": "The condition of an if-statement shall have type bool", + "precision": "very-high", + "severity": "recommendation", + "short_name": "NonBooleanIfCondition", + "tags": [ + "maintainability", + "readability" + ] + }, + { + "description": "Non boolean conditions can be confusing for developers.", + "kind": "problem", + "name": "The condition of an iteration statement shall have type bool", + "precision": "very-high", + "severity": "recommendation", + "short_name": "NonBooleanIterationCondition", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/Statements5.json b/rule_packages/c/Statements5.json new file mode 100644 index 0000000000..93a533939b --- /dev/null +++ b/rule_packages/c/Statements5.json @@ -0,0 +1,68 @@ +{ + "MISRA-C-2012": { + "RULE-14-3": { + "properties": { + "obligation": "required" + }, + "queries": [ + { + "description": "If a controlling expression has an invariant value then it is possible that there is a programming error.", + "kind": "problem", + "name": "Controlling expressions shall not be invariant", + "precision": "very-high", + "severity": "error", + "short_name": "ControllingExprInvariant", + "tags": [ + "correctness", + "maintainability", + "readability" + ] + } + ], + "title": "Controlling expressions shall not be invariant" + }, + "RULE-15-5": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "Not having a single point of exit in a function can lead to unintentional behaviour.", + "kind": "problem", + "name": "A function should have a single point of exit at the end", + "precision": "very-high", + "severity": "recommendation", + "short_name": "FunctionReturnCondition", + "tags": [ + "maintainability", + "readability", + "correctness" + ] + } + ], + "title": "A function should have a single point of exit at the end" + }, + "RULE-17-4": { + "properties": { + "obligation": "mandatory" + }, + "queries": [ + { + "description": "Not returning with an expression from a non-void function can lead to undefined behaviour.", + "kind": "problem", + "name": "All exit paths from a function with non-void return type shall have an explicit return statement", + "precision": "very-high", + "severity": "error", + "short_name": "NonVoidFunctionReturnCondition", + "shared_implementation_short_name": "NonVoidFunctionDoesNotReturn", + "tags": [ + "correctness", + "maintainability", + "readability" + ] + } + ], + "title": "All exit paths from a function with non-void return type shall have an explicit return statement with an expression" + } + } +} diff --git a/rule_packages/c/Statements6.json b/rule_packages/c/Statements6.json new file mode 100644 index 0000000000..101987f9c3 --- /dev/null +++ b/rule_packages/c/Statements6.json @@ -0,0 +1,24 @@ +{ + "MISRA-C-2012": { + "RULE-15-1": { + "properties": { + "obligation": "advisory" + }, + "queries": [ + { + "description": "The goto statement shall not be used.", + "kind": "problem", + "name": "The goto statement should not be used", + "precision": "very-high", + "severity": "error", + "short_name": "GotoStatementUsed", + "tags": [ + "correctness", + "security" + ] + } + ], + "title": "The goto statement should not be used" + } + } +} \ No newline at end of file diff --git a/rule_packages/cpp/Conditionals.json b/rule_packages/cpp/Conditionals.json index 4c382a06e9..c2afb626e4 100644 --- a/rule_packages/cpp/Conditionals.json +++ b/rule_packages/cpp/Conditionals.json @@ -16,6 +16,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "NonBooleanIfCondition", + "shared_implementation_short_name": "NonBooleanIfStmt", "tags": [ "maintainability", "readability" @@ -28,6 +29,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "NonBooleanIterationCondition", + "shared_implementation_short_name": "NonBooleanIterationStmt", "tags": [ "maintainability", "readability" @@ -136,6 +138,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "IfElseTerminationCondition", + "shared_implementation_short_name": "IfElseTerminationConstruct", "tags": [ "maintainability", "readability" @@ -160,6 +163,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "SwitchDoesNotStartWithCase", + "shared_implementation_short_name": "SwitchCasePositionCondition", "tags": [ "maintainability", "readability" @@ -172,6 +176,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "SwitchStatementNotWellFormed", + "shared_implementation_short_name": "SwitchNotWellFormed", "tags": [ "maintainability", "readability" @@ -190,12 +195,13 @@ }, "queries": [ { - "description": "By default in C++, the switch structure is weak, which may lead to switch labels being placed anywhere in the switch block. This can cause unspecified behaviour.", + "description": "Nested switch labels cause undefined behaviour.", "kind": "problem", "name": "A switch-label shall only be used when the most closely-enclosing compound statement is the body of a switch statement", "precision": "very-high", "severity": "recommendation", "short_name": "NestedCaseInSwitch", + "shared_implementation_short_name": "NestedLabelInSwitch", "tags": [ "maintainability", "readability" @@ -328,6 +334,7 @@ "precision": "very-high", "severity": "recommendation", "short_name": "GotoStatementJumpCondition", + "shared_implementation_short_name": "GotoStatementCondition", "tags": [ "maintainability", "readability" diff --git a/rules.csv b/rules.csv index 59d4bc3fda..ab005cbe5b 100644 --- a/rules.csv +++ b/rules.csv @@ -543,7 +543,7 @@ c,CERT-C,FIO44-C,Yes,Rule,,,Only use values for fsetpos() that are returned from c,CERT-C,FIO45-C,Yes,Rule,,,Avoid TOCTOU race conditions while accessing files,,IO4,Medium, c,CERT-C,FIO46-C,Yes,Rule,,,Do not access a closed file,FIO51-CPP,IO1,Hard, c,CERT-C,FIO47-C,Yes,Rule,,,Use valid format strings,,IO4,Hard, -c,CERT-C,FLP30-C,Yes,Rule,,,Do not use floating-point variables as loop counters,,Statements,Easy, +c,CERT-C,FLP30-C,Yes,Rule,,,Do not use floating-point variables as loop counters,,Statements4,Easy, c,CERT-C,FLP32-C,Yes,Rule,,,Prevent or detect domain and range errors in math functions,A0-4-4,FloatingTypes,Medium, c,CERT-C,FLP34-C,Yes,Rule,,,Ensure that floating-point conversions are within range of the new type,,FloatingTypes,Medium, c,CERT-C,FLP36-C,Yes,Rule,,,Preserve precision when converting integral values to floating-point type,,FloatingTypes,Medium, @@ -692,29 +692,29 @@ c,MISRA-C-2012,RULE-13-2,Yes,Required,,,The value of an expression and its persi c,MISRA-C-2012,RULE-13-3,Yes,Advisory,,,A full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operator,,SideEffects2,Medium, c,MISRA-C-2012,RULE-13-4,Yes,Advisory,,,The result of an assignment operator should not be used,M6-2-1,SideEffects1,Easy, c,MISRA-C-2012,RULE-13-5,Yes,Required,,,The right hand operand of a logical && or || operator shall not contain persistent side effects,M5-14-1,SideEffects1,Import, -c,MISRA-C-2012,RULE-13-6,Yes,Mandatory,,,The operand of the sizeof operator shall not contain any expression which has potential side effects,M5-3-4,SideEffects1,Import, +c,MISRA-C-2012,RULE-13-6,Yes,Mandatory,,,The operand of the sizeof operator shall not contain any expressiosn which has potential side effects,M5-3-4,SideEffects1,Import, c,MISRA-C-2012,RULE-14-1,Yes,Required,,,A loop counter shall not have essentially floating type,FLP30-C A6-5-2,EssentialTypes,Hard, -c,MISRA-C-2012,RULE-14-2,Yes,Required,,,A for loop shall be well-formed,M6-5-1...M6-5-6,Statements,Medium, -c,MISRA-C-2012,RULE-14-3,Yes,Required,,,Controlling expressions shall not be invariant,,Statements,Medium, -c,MISRA-C-2012,RULE-14-4,Yes,Required,,,The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type,A5-0-2,Statements,Medium, -c,MISRA-C-2012,RULE-15-1,No,Advisory,,,The goto statement should not be used,A6-6-1,,Import, -c,MISRA-C-2012,RULE-15-2,Yes,Required,,,The goto statement shall jump to a label declared later in the same function,M6-6-2,Statements,Import, -c,MISRA-C-2012,RULE-15-3,Yes,Required,,,"Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement",M6-6-1,Statements,Import, -c,MISRA-C-2012,RULE-15-4,Yes,Advisory,,,There should be no more than one break or goto statement used to terminate any iteration statement,,Statements,Medium, -c,MISRA-C-2012,RULE-15-5,Yes,Advisory,,,A function should have a single point of exit at the end,,Statements,Medium, -c,MISRA-C-2012,RULE-15-6,Yes,Required,,,The body of an iteration-statement or a selection-statement shall be a compund-statement,M6-3-1,Statements,Import, -c,MISRA-C-2012,RULE-15-7,Yes,Required,,,All if / else if constructs shall be terminated with an else statement,M6-4-2,Statements,Import, -c,MISRA-C-2012,RULE-16-1,Yes,Required,,,All switch statements shall be well-formed,M6-4-3,Statements,Import, -c,MISRA-C-2012,RULE-16-2,Yes,Required,,,A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement,M6-4-4,Statements,Import, -c,MISRA-C-2012,RULE-16-3,Yes,Required,,,An unconditional break statement shall terminate every switch-clause,M6-4-5,Statements,Import, -c,MISRA-C-2012,RULE-16-4,Yes,Required,,,Every switch statement shall have a default label,M6-4-6,Statements,Easy, -c,MISRA-C-2012,RULE-16-5,Yes,Required,,,A default label shall appear as either the first or the last switch label of a switch statement,M6-4-6,Statements,Easy, -c,MISRA-C-2012,RULE-16-6,Yes,Required,,,Every switch statement shall have at least two switch-clauses,A6-4-1,Statements,Medium, -c,MISRA-C-2012,RULE-16-7,Yes,Required,,,A switch-expression shall not have essentially Boolean type,M6-4-7,Statements,Medium, +c,MISRA-C-2012,RULE-14-2,Yes,Required,,,A for loop shall be well-formed,M6-5-1...M6-5-6,Statements4,Medium, +c,MISRA-C-2012,RULE-14-3,Yes,Required,,,Controlling expressions shall not be invariant,,Statements5,Medium, +c,MISRA-C-2012,RULE-14-4,Yes,Required,,,The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type,A5-0-2,Statements4,Medium, +c,MISRA-C-2012,RULE-15-1,Yes,Advisory,,,The goto statement should not be used,A6-6-1,Statements6,Import, +c,MISRA-C-2012,RULE-15-2,Yes,Required,,,The goto statement shall jump to a label declared later in the same function,M6-6-2,Statements2,Import, +c,MISRA-C-2012,RULE-15-3,Yes,Required,,,"Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement",M6-6-1,Statements2,Import, +c,MISRA-C-2012,RULE-15-4,Yes,Advisory,,,There should be no more than one break or goto statement used to terminate any iteration statement,,Statements2,Medium, +c,MISRA-C-2012,RULE-15-5,Yes,Advisory,,,A function should have a single point of exit at the end,,Statements5,Medium, +c,MISRA-C-2012,RULE-15-6,Yes,Required,,,The body of an iteration-statement or a selection-statement shall be a compund-statement,M6-3-1,Statements3,Import, +c,MISRA-C-2012,RULE-15-7,Yes,Required,,,All if / else if constructs shall be terminated with an else statement,M6-4-2,Statements3,Import, +c,MISRA-C-2012,RULE-16-1,Yes,Required,,,All switch statements shall be well-formed,M6-4-3,Statements3,Import, +c,MISRA-C-2012,RULE-16-2,Yes,Required,,,A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement,M6-4-4,Statements1,Import, +c,MISRA-C-2012,RULE-16-3,Yes,Required,,,An unconditional break statement shall terminate every switch-clause,M6-4-5,Statements1,Import, +c,MISRA-C-2012,RULE-16-4,Yes,Required,,,Every switch statement shall have a default label,M6-4-6,Statements1,Easy, +c,MISRA-C-2012,RULE-16-5,Yes,Required,,,A default label shall appear as either the first or the last switch label of a switch statement,M6-4-6,Statements1,Easy, +c,MISRA-C-2012,RULE-16-6,Yes,Required,,,Every switch statement shall have at least two switch-clauses,A6-4-1,Statements2,Medium, +c,MISRA-C-2012,RULE-16-7,Yes,Required,,,A switch-expression shall not have essentially Boolean type,M6-4-7,Statements2,Medium, c,MISRA-C-2012,RULE-17-1,Yes,Required,,,The features of shall not be used,,Banned,Easy, -c,MISRA-C-2012,RULE-17-2,Yes,Required,,,"Functions shall not call themselves, either directly or indirectly",A7-5-2,Statements,Import, +c,MISRA-C-2012,RULE-17-2,Yes,Required,,,"Functions shall not call themselves, either directly or indirectly",A7-5-2,Statements3,Import, c,MISRA-C-2012,RULE-17-3,Yes,Mandatory,,,A function shall not be declared implicitly,,Declarations6,Medium, -c,MISRA-C-2012,RULE-17-4,Yes,Mandatory,,,All exit paths from a function with non-void return type shall have an explicit return statement with an expression,MSC52-CPP,Statements,Medium, +c,MISRA-C-2012,RULE-17-4,Yes,Mandatory,,,All exit paths from a function with non-void return type shall have an explicit return statement with an expression,MSC52-CPP,Statements5,Medium, c,MISRA-C-2012,RULE-17-5,Yes,Advisory,,,The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements,,Contracts6,Hard, c,MISRA-C-2012,RULE-17-6,No,Mandatory,,,The declaration of an array parameter shall not contain the static keyword between the [ ],,,, c,MISRA-C-2012,RULE-17-7,Yes,Required,,,The value returned by a function having non-void return type shall be used,A0-1-2,Contracts6,Easy, @@ -762,7 +762,7 @@ c,MISRA-C-2012,RULE-21-16,Yes,Required,,,"The pointer arguments to the Standard c,MISRA-C-2012,RULE-21-17,Yes,Mandatory,,,Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters,,Memory2,Hard, c,MISRA-C-2012,RULE-21-18,Yes,Mandatory,,,The size_t argument passed to any function in shall have an appropriate value,,OutOfBounds,Hard, c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts2,Medium, -c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV34-C",Contracts2,Import, +c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function",ENV34-C,Contracts2,Import, c,MISRA-C-2012,RULE-21-21,Yes,Required,,,The Standard Library function system of shall not be used,ENV33-C,Banned,Import, c,MISRA-C-2012,RULE-22-1,Yes,Required,,,All resources obtained dynamically by means of Standard Library functions shall be explicitly released,,Memory2,Hard, c,MISRA-C-2012,RULE-22-2,Yes,Mandatory,,,A block of memory shall only be freed if it was allocated by means of a Standard Library function,,Memory2,Hard, @@ -774,3 +774,4 @@ c,MISRA-C-2012,RULE-22-7,Yes,Required,,,The macro EOF shall only be compared wit c,MISRA-C-2012,RULE-22-8,Yes,Required,,,The value of errno shall be set to zero prior to a call to an errno-setting-function,ERR30-C,Contracts3,Medium, c,MISRA-C-2012,RULE-22-9,Yes,Required,,,The value of errno shall be tested against zero after calling an errno-setting-function,,Contracts3,Medium, c,MISRA-C-2012,RULE-22-10,Yes,Required,,,The value of errno shall only be tested when the last function to be called was an errno-setting-function,,Contracts3,Medium, +,,,,,,,,,,0,