Skip to content

Commit 8dba813

Browse files
Next query first pass working
1 parent f381feb commit 8dba813

File tree

3 files changed

+88
-18
lines changed

3 files changed

+88
-18
lines changed

cpp/misra/src/rules/RULE-28-3-1/PredicateWithPersistentSideEffects.ql

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,26 @@
1818

1919
import cpp
2020
import codingstandards.cpp.misra
21+
import codingstandards.cpp.sideeffect.DefaultEffects
2122
import codingstandards.cpp.SideEffect
2223
import codingstandards.cpp.types.Predicate
2324

24-
predicate functionHasSideEffect(Function f) {
25-
hasExternalOrGlobalSideEffectInFunction(f)
26-
}
27-
28-
predicate isPredicateObject(PredicateFunctionObject obj, PredicateType pred, Locatable usageSite, Function callOperator) {
29-
obj.getSubstitution().getASubstitutionSite() = usageSite and
30-
pred = obj.getPredicateType() and
31-
callOperator = obj.getCallOperator()
32-
}
33-
34-
predicate isPredicateFunctionPointerUse(PredicateFunctionPointerUse predPtrUse, PredicateType pred, Function func) {
35-
pred = predPtrUse.getPredicateType() and
36-
func = predPtrUse.getTarget()
37-
}
38-
25+
from Locatable usageSite, Function f, SideEffect effect
3926
where
40-
not isExcluded(x, SideEffects6Package::predicateWithPersistentSideEffectsQuery()) and
41-
select
27+
not isExcluded([usageSite, effect], SideEffects6Package::predicateWithPersistentSideEffectsQuery()) and
28+
effect = getAnExternalOrGlobalSideEffectInFunction(f) and
29+
not effect instanceof ConstructorFieldInit and
30+
(
31+
// Case 1: a function pointer used directly as a predicate argument
32+
exists(PredicateFunctionPointerUse use |
33+
use = usageSite and
34+
f = use.getTarget()
35+
)
36+
or
37+
// Case 2: a function object whose call operator has side effects
38+
exists(PredicateFunctionObject obj |
39+
usageSite = obj.getSubstitution().getASubstitutionSite() and
40+
f = obj.getCallOperator()
41+
)
42+
)
43+
select usageSite, "Predicate $@ has a $@.", f, f.getName(), effect, "persistent side effect"
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
No expected results have yet been specified
1+
| test.cpp:60:35:60:49 | cmp_with_static | Predicate $@ has a $@. | test.cpp:52:6:52:20 | cmp_with_static | cmp_with_static | test.cpp:54:3:54:11 | ++ ... | persistent side effect |
2+
| test.cpp:73:35:73:49 | cmp_with_global | Predicate $@ has a $@. | test.cpp:66:6:66:20 | cmp_with_global | cmp_with_global | test.cpp:67:3:67:26 | ++ ... | persistent side effect |
3+
| test.cpp:86:3:86:11 | call to sort | Predicate $@ has a $@. | test.cpp:78:8:78:17 | operator() | operator() | test.cpp:79:5:79:28 | ++ ... | persistent side effect |

cpp/misra/test/rules/RULE-28-3-1/test.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,70 @@ void test_standard_library_predicates() {
4040
std::vector<std::int32_t> l1 = {1, 2, 3, 4, 5};
4141
// TODO: implement stubs for greater comparator.
4242
// std::sort(l1.begin(), l1.end(), std::greater<std::int32_t>()); // COMPLIANT
43+
}
44+
45+
// =============================================================================
46+
// Test cases for Rule 28.3.1#PredicateWithPersistentSideEffects
47+
// This query checks that predicates do not have persistent (global/static)
48+
// side effects.
49+
// =============================================================================
50+
51+
// Non-compliant: free function predicate modifying a static local variable
52+
bool cmp_with_static(std::int32_t l1, std::int32_t l2) {
53+
static std::int32_t g_count = 0;
54+
++g_count; // NON_COMPLIANT
55+
return l1 < l2;
56+
}
57+
58+
void test_predicate_fn_static_local() {
59+
std::vector<std::int32_t> l1 = {5, 2, 8, 1, 9};
60+
std::sort(l1.begin(), l1.end(), cmp_with_static); // NON_COMPLIANT
61+
}
62+
63+
// Non-compliant: free function predicate modifying a global variable
64+
std::int32_t g_predicate_call_count = 0;
65+
66+
bool cmp_with_global(std::int32_t l1, std::int32_t l2) {
67+
++g_predicate_call_count; // NON_COMPLIANT
68+
return l1 < l2;
69+
}
70+
71+
void test_predicate_fn_global() {
72+
std::vector<std::int32_t> l1 = {5, 2, 8, 1, 9};
73+
std::sort(l1.begin(), l1.end(), cmp_with_global); // NON_COMPLIANT
74+
}
75+
76+
// Non-compliant: function object whose operator() modifies a global variable
77+
struct F3_SideEffect {
78+
bool operator()(std::int32_t l1, std::int32_t l2) const {
79+
++g_predicate_call_count; // NON_COMPLIANT
80+
return l1 < l2;
81+
}
82+
};
83+
84+
void test_function_object_with_global_side_effect() {
85+
std::vector<std::int32_t> l1 = {5, 2, 8, 1, 9};
86+
std::sort(l1.begin(), l1.end(), F3_SideEffect{}); // NON_COMPLIANT
87+
}
88+
89+
// Compliant: free function predicate with no side effects
90+
bool cmp_pure(std::int32_t l1, std::int32_t l2) { // COMPLIANT
91+
return l1 < l2;
92+
}
93+
94+
void test_predicate_fn_pure() {
95+
std::vector<std::int32_t> l1 = {5, 2, 8, 1, 9};
96+
std::sort(l1.begin(), l1.end(), cmp_pure); // COMPLIANT
97+
}
98+
99+
// Compliant: function object with const operator() and no side effects
100+
struct F4_Pure {
101+
bool operator()(std::int32_t l1, std::int32_t l2) const { // COMPLIANT
102+
return l1 < l2;
103+
}
104+
};
105+
106+
void test_function_object_pure() {
107+
std::vector<std::int32_t> l1 = {5, 2, 8, 1, 9};
108+
std::sort(l1.begin(), l1.end(), F4_Pure{}); // COMPLIANT
43109
}

0 commit comments

Comments
 (0)