Skip to content

Commit 8f67e38

Browse files
committed
RULE-9-2-1 - NoStandaloneTypeCastExpression
Detects explicit type conversions using functional notation as standalone expression statements that create immediately-destroyed temporary objects. [a]
1 parent 5687a7b commit 8f67e38

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @id cpp/misra/no-standalone-type-cast-expression
3+
* @name RULE-9-2-1: An explicit type conversion shall not be an expression statement
4+
* @description Using an explicit type conversion as an expression statement creates a temporary
5+
* object that is immediately discarded, which can lead to unintended premature
6+
* resource cleanup.
7+
* @kind problem
8+
* @precision very-high
9+
* @problem.severity error
10+
* @tags external/misra/id/rule-9-2-1
11+
* scope/single-translation-unit
12+
* external/misra/enforcement/decidable
13+
* external/misra/obligation/required
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
19+
from ExprStmt stmt, Expr expr
20+
where
21+
not isExcluded(stmt, Conversions2Package::noStandaloneTypeCastExpressionQuery()) and
22+
expr = stmt.getExpr() and
23+
(
24+
// Explicit conversions which call a constructor
25+
expr instanceof ConstructorCall
26+
or
27+
// Cast expressions using functional notation
28+
expr instanceof Cast
29+
) and
30+
// Exclude init-statements in if/for statements
31+
// This is because the extractor has a bug as of 2.20.7 which means it does not parse
32+
// these two cases separately. We choose to ignore if statements, which can cause false
33+
// negatives, but will prevent false positives
34+
not exists(IfStmt ifStmt | ifStmt.getInitialization() = stmt)
35+
select stmt,
36+
"Explicit type conversion used as expression statement creates temporary object that is immediately discarded."
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
| test.cpp:20:3:20:35 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. |
2+
| test.cpp:21:3:21:23 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. |
3+
| test.cpp:22:3:22:27 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. |
4+
| test.cpp:23:3:23:34 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. |
5+
| test.cpp:42:8:42:40 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. |
6+
| test.cpp:66:3:66:17 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. |
7+
| test.cpp:67:3:67:17 | ExprStmt | Explicit type conversion used as expression statement creates temporary object that is immediately discarded. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/RULE-9-2-1/NoStandaloneTypeCastExpression.ql
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <cstdint>
2+
#include <memory>
3+
#include <mutex>
4+
5+
std::mutex g1;
6+
std::mutex g2;
7+
8+
void f1(std::unique_lock<std::mutex> l1) {
9+
// Function parameter for testing compliant cases
10+
}
11+
12+
void test_explicit_type_conversion_expression_statement() {
13+
// Compliant cases - declarations
14+
std::unique_lock<std::mutex> l1(g1); // COMPLIANT
15+
std::unique_lock<std::mutex> l2{g1}; // COMPLIANT
16+
std::unique_lock<std::mutex>(l3); // COMPLIANT - declaration with redundant
17+
// parentheses around variable name
18+
19+
// Non-compliant cases - explicit type conversions as expression statements
20+
std::unique_lock<std::mutex>{g1}; // NON_COMPLIANT
21+
std::scoped_lock{g1}; // NON_COMPLIANT
22+
std::scoped_lock(g1, g2); // NON_COMPLIANT
23+
std::lock_guard<std::mutex>{g1}; // NON_COMPLIANT
24+
25+
// Compliant cases - type conversions not as expression statements
26+
f1(std::unique_lock<std::mutex>(g1)); // COMPLIANT
27+
f1(std::unique_lock<std::mutex>{g1}); // COMPLIANT
28+
auto l4 = std::unique_lock<std::mutex>(g1); // COMPLIANT
29+
auto l5 = std::unique_lock<std::mutex>{g1}; // COMPLIANT
30+
31+
// The extractor has a bug as of 2.20.7 which means it does not parse
32+
// these two cases separately. We choose to ignore if statements, which
33+
// can cause false negatives, but will prevent false positives
34+
if (std::unique_lock<std::mutex>(g1); true) { // COMPLIANT - init-statement
35+
}
36+
if (std::unique_lock<std::mutex>{g1}; true) { // NON_COMPLIANT[FALSE_NEGATIVE]
37+
}
38+
39+
for (std::unique_lock<std::mutex>(g1);;) { // COMPLIANT - init-statement
40+
break;
41+
}
42+
for (std::unique_lock<std::mutex>{g1};;) { // NON_COMPLIANT - init-statement
43+
break;
44+
}
45+
}
46+
47+
void test_primitive_type_conversions() {
48+
// Non-compliant cases with primitive types
49+
std::int32_t(42); // NON_COMPLIANT
50+
float(3.14); // NON_COMPLIANT
51+
double(2.71); // NON_COMPLIANT
52+
bool(true); // NON_COMPLIANT
53+
54+
// Compliant cases
55+
auto l1 = std::int32_t(42); // COMPLIANT
56+
auto l2 = float(3.14); // COMPLIANT
57+
std::int32_t l3(42); // COMPLIANT - declaration
58+
std::int32_t l4{42}; // COMPLIANT - declaration
59+
}
60+
61+
struct CustomType {
62+
CustomType(std::int32_t) {}
63+
};
64+
void test_custom_types() {
65+
// Non-compliant cases
66+
CustomType(42); // NON_COMPLIANT
67+
CustomType{42}; // NON_COMPLIANT
68+
69+
// Compliant cases
70+
CustomType l1(42); // COMPLIANT - declaration
71+
CustomType l2{42}; // COMPLIANT - declaration
72+
auto l3 = CustomType(42); // COMPLIANT
73+
auto l4 = CustomType{42}; // COMPLIANT
74+
}

0 commit comments

Comments
 (0)