Skip to content

Commit c46e44e

Browse files
jubnzvdanmar
authored andcommitted
misra.py: R14.2: Verify for loop counter modification (#2409)
* misra.py: R14.2: Verify for counter modification Add additional check to detect modification of loop counter in loop body. Related issue: https://trac.cppcheck.net/ticket/9490 Add small fix that treat all assignment operators defined in N1750 6.5.16 as has side affects. This will affects rules 13.1, 13.3, 13.5 and allow to catch some false negatives. * Add tests for fixed FPs for R13.{1,5,6} * fix * use isAssignmentOp from cppcheck data * remove unused set * handle case with empty body or syntax error * add test with outer variable * Fix FP in nested loops, add tests * Fix FP on outer variables * Fixup false positives for not loop counters
1 parent f87cd98 commit c46e44e

2 files changed

Lines changed: 158 additions & 20 deletions

File tree

addons/misra.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,38 @@ def getForLoopExpressions(forToken):
256256
lpar.astOperand2.astOperand2.astOperand2]
257257

258258

259+
def getForLoopCounterVariables(forToken):
260+
""" Return a set of Variable objects defined in ``for`` statement and
261+
satisfy requirements to loop counter term from section 8.14 of MISRA
262+
document.
263+
"""
264+
if not forToken or forToken.str != 'for':
265+
return None
266+
tn = forToken.next
267+
if not tn or tn.str != '(':
268+
return None
269+
vars_defined = set()
270+
vars_exit = set()
271+
vars_modified = set()
272+
cur_clause = 1
273+
te = tn.link
274+
while tn and tn != te:
275+
if tn.variable:
276+
if cur_clause == 1 and tn.variable.nameToken == tn:
277+
vars_defined.add(tn.variable)
278+
elif cur_clause == 2:
279+
vars_exit.add(tn.variable)
280+
elif cur_clause == 3:
281+
if tn.next and hasSideEffectsRecursive(tn.next):
282+
vars_modified.add(tn.variable)
283+
elif tn.previous and tn.previous.str in ('++', '--'):
284+
vars_modified.add(tn.variable)
285+
if tn.str == ';':
286+
cur_clause += 1
287+
tn = tn.next
288+
return vars_defined & vars_exit & vars_modified
289+
290+
259291
def findCounterTokens(cond):
260292
if not cond:
261293
return []
@@ -304,7 +336,7 @@ def isFloatCounterInWhileLoop(whileToken):
304336

305337

306338
def hasSideEffectsRecursive(expr):
307-
if not expr:
339+
if not expr or expr.str == ';':
308340
return False
309341
if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '[':
310342
prev = expr.astOperand1.previous
@@ -316,7 +348,7 @@ def hasSideEffectsRecursive(expr):
316348
e = e.astOperand1
317349
if e and e.str == '.':
318350
return False
319-
if expr.str in ('++', '--', '='):
351+
if expr.isAssignmentOp or expr.str in {'++', '--'}:
320352
return True
321353
# Todo: Check function calls
322354
return hasSideEffectsRecursive(expr.astOperand1) or hasSideEffectsRecursive(expr.astOperand2)
@@ -1425,6 +1457,28 @@ def misra_14_2(self, data):
14251457
elif hasSideEffectsRecursive(expressions[1]):
14261458
self.reportError(token, 14, 2)
14271459

1460+
# Inspect modification of loop counter in loop body
1461+
counter_vars = getForLoopCounterVariables(token)
1462+
outer_scope = token.scope
1463+
body_scope = None
1464+
tn = token.next
1465+
while tn and tn.next != outer_scope.bodyEnd:
1466+
if tn.scope and tn.scope.nestedIn == outer_scope:
1467+
body_scope = tn.scope
1468+
break
1469+
tn = tn.next
1470+
if not body_scope:
1471+
continue
1472+
tn = body_scope.bodyStart
1473+
while tn and tn != body_scope.bodyEnd:
1474+
if tn.variable and tn.variable in counter_vars:
1475+
if tn.next:
1476+
# TODO: Check modifications in function calls
1477+
if hasSideEffectsRecursive(tn.next):
1478+
self.reportError(tn, 14, 2)
1479+
tn = tn.next
1480+
1481+
14281482
def misra_14_4(self, data):
14291483
for token in data.tokenlist:
14301484
if token.str != '(':

addons/test/misra/misra-test.c

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void misra_2_7_used_params (int *param1, int param2, int param3)
4040
void misra_3_2(int enable)
4141
{
4242
// This won't generate a violation because of subsequent blank line \
43-
43+
4444
int y = 0;
4545
int x = 0; // 3.2 non-compliant comment ends with backslash \
4646
if (enable != 0)
@@ -67,16 +67,16 @@ static int misra_5_2_var_hides_var______31x;
6767
static int misra_5_2_var_hides_var______31y;//5.2
6868
static int misra_5_2_function_hides_var_31x;
6969
void misra_5_2_function_hides_var_31y(void) {}//5.2
70-
void foo(void)
70+
void foo(void)
7171
{
7272
int i;
7373
switch(misra_5_2_func1()) //16.4 16.6
7474
{
75-
case 1:
75+
case 1:
7676
{
7777
do
7878
{
79-
for(i = 0; i < 10; i++)
79+
for(i = 0; i < 10; i++)
8080
{
8181
if(misra_5_2_func3()) //14.4
8282
{
@@ -85,7 +85,7 @@ void foo(void)
8585
}
8686
}
8787
} while(misra_5_2_func2()); //14.4
88-
}
88+
}
8989
}
9090
}
9191

@@ -161,13 +161,13 @@ void misra_5_5_functionhides_macro31y(int misra_5_5_param_hides_macro__31y){(voi
161161
struct misra_5_5_tag_hides_macro____31y { //5.5
162162
int x;
163163
};
164-
void misra_5_5_func1()
164+
void misra_5_5_func1()
165165
{
166166
switch(misra_5_5_func2()) //16.4 16.6
167167
{
168168
case 1:
169169
{
170-
do
170+
do
171171
{
172172
if(misra_5_5_func3()) //14.4
173173
{
@@ -184,11 +184,11 @@ void misra_7_1() {
184184
}
185185

186186
void misra_7_3() {
187-
long misra_7_3_a = 0l; //7.3
188-
long misra_7_3_b = 0lU; //7.3
189-
long long misra_7_3_c = 0Ull; //7.3
190-
long long misra_7_3_d = 0ll; //7.3
191-
long double misra_7_3_e = 7.3l; //7.3
187+
long misra_7_3_a = 0l; //7.3
188+
long misra_7_3_b = 0lU; //7.3
189+
long long misra_7_3_c = 0Ull; //7.3
190+
long long misra_7_3_d = 0ll; //7.3
191+
long double misra_7_3_e = 7.3l; //7.3
192192
}
193193

194194

@@ -241,7 +241,7 @@ void misra_10_4(u32 x, s32 y) {
241241
enum misra_10_4_enumb { misra_10_4_B1, misra_10_4_B2, misra_10_4_B3 };
242242
if ( misra_10_4_B1 > misra_10_4_A1 ) //10.4
243243
{
244-
;
244+
;
245245
}
246246
z = x + y; //10.4
247247
z = (a == misra_10_4_A3) ? x : y; //10.4
@@ -351,6 +351,12 @@ void misra_12_4() {
351351

352352
struct misra_13_1_t { int a; int b; };
353353
void misra_13_1(int *p) {
354+
volatile int v;
355+
int a1[3] = {0, (*p)++, 2}; // 13.1
356+
int a2[3] = {0, ((*p) += 1), 2}; // 13.1
357+
int a3[3] = {0, ((*p) = 19), 2}; // 13.1
358+
int b[2] = {v,1};
359+
struct misra_13_1_t c = { .a=4, .b=5 }; // no fp
354360
volatile int vv;
355361
int v = 42;
356362

@@ -403,10 +409,15 @@ void misra_13_4() {
403409

404410
void misra_13_5() {
405411
if (x && (y++ < 123)){} // 13.5
412+
if (x || ((y += 19) > 33)){} // 13.5
413+
if (x || ((y = 25) > 33)){} // 13.5 13.4
414+
if (x || ((--y) > 33)){} // 13.5
406415
else {}
407416
}
408417

409418
void misra_13_6() {
419+
int a = sizeof(x|=42); // 13.6
420+
a = sizeof(--x); // 13.6 13.3
410421
return sizeof(x++); // 13.6
411422
}
412423

@@ -428,24 +439,97 @@ void misra_14_1() {
428439
void misra_14_2_init_value(int32_t *var) {
429440
*var = 0;
430441
}
431-
void misra_14_2(bool b) {
432-
for (dostuff();a<10;a++) {} // 14.2
442+
void misra_14_2_fn1(bool b) {
433443
for (;i++<10;) {} // 14.2
434444
for (;i<10;dostuff()) {} // TODO
435445
int32_t g = 0;
446+
int g_arr[42];
447+
g += 2; // no-warning
436448
for (int32_t i2 = 0; i2 < 8; ++i2) {
437-
i2 += 2; // FIXME False negative for "14.2". Trac #9490
438-
g += 2; // no-warning
449+
i2 += 2; // 14.2
450+
i2 |= 2; // 14.2
451+
g += 2;
452+
i2 ^= 2; // 14.2
453+
if (i2 == 2) {
454+
g += g_arr[i2];
455+
}
456+
misra_14_2_init_value(&i2); // TODO: Fix false negative in function call
439457
}
458+
440459
for (misra_14_2_init_value(&i); i < 10; ++i) {} // no-warning FIXME: False positive for 14.2 Trac #9491
460+
441461
bool abort = false;
442462
for (i = 0; (i < 10) && !abort; ++i) { // no-warning
443463
if (b) {
444464
abort = true;
445465
}
446466
}
447467
for (;;) {} // no-warning
448-
// TODO check more variants
468+
469+
int x = 10;
470+
for (int i = x; i < 42; i++) {
471+
x++; // no warning
472+
}
473+
for (int i = (x - 3); i < 42; i++) {
474+
x ^= 3; // no warning
475+
}
476+
477+
for (int i = 0, j = 19; i < 42; i++) { // 12.3 14.2
478+
i += 12; // 14.2
479+
j /= 3; // TODO: 14.2
480+
}
481+
482+
for (int i = 0; i < 19; i++) {
483+
for (int j = 0; j < 42; j++) {
484+
i--; // 14.2
485+
for (int k = j; k > 5; k--) {
486+
i++; // 14.2
487+
for (int h = 35; h > 5; k++) // 14.2
488+
{}
489+
}
490+
}
491+
}
492+
}
493+
static void misra_14_2_fn2()
494+
{
495+
int y = 0;
496+
497+
// Handle cases when i is not treated as loop counter according MISRA
498+
// definition.
499+
for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3
500+
i++; // no warning
501+
}
502+
for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3
503+
i++; // no warning
504+
}
505+
for (int i = 0; y < 10; y++) { // TODO: 14.2
506+
i++; // no warning
507+
}
508+
for (int i = 0; i < 10; y++) { // TODO: 14.2
509+
i++; // no warning
510+
}
511+
for (int i = 0; y < 10; i++) { // TODO: 14.2
512+
i++; // no warning
513+
}
514+
for (int i = 0; i < 10; (y+=i)) {
515+
i++; // no warning
516+
}
517+
518+
// i is a loop counter according MISRA definition
519+
for (int i = 0; i < 10; i++) {
520+
i++; // 14.2
521+
if (++i > 5) { // 14.2
522+
break;
523+
}
524+
}
525+
for (int i = 0; i < 10; (i+=42)) {
526+
i++; // 14.2
527+
}
528+
for (int i = 0; i < 10; (i|=y)) {
529+
i++; // 14.2
530+
}
531+
532+
return 0;
449533
}
450534

451535
struct {

0 commit comments

Comments
 (0)