misra_14_2 various fixes (#4324)

This commit is contained in:
g-chauvel 2022-08-21 20:11:10 +02:00 committed by GitHub
parent 9b4973d711
commit be658e2392
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 45 deletions

View File

@ -711,6 +711,7 @@ def getForLoopCounterVariables(forToken):
if not tn or tn.str != '(': if not tn or tn.str != '(':
return None return None
vars_defined = set() vars_defined = set()
vars_initialized = set()
vars_exit = set() vars_exit = set()
vars_modified = set() vars_modified = set()
cur_clause = 1 cur_clause = 1
@ -722,14 +723,16 @@ def getForLoopCounterVariables(forToken):
elif cur_clause == 2: elif cur_clause == 2:
vars_exit.add(tn.variable) vars_exit.add(tn.variable)
elif cur_clause == 3: elif cur_clause == 3:
if tn.next and hasSideEffectsRecursive(tn.next): if tn.next and countSideEffectsRecursive(tn.next) > 0:
vars_modified.add(tn.variable) vars_modified.add(tn.variable)
elif tn.previous and tn.previous.str in ('++', '--'): elif tn.previous and tn.previous.str in ('++', '--'):
vars_modified.add(tn.variable) vars_modified.add(tn.variable)
if cur_clause == 1 and tn.isAssignmentOp and tn.astOperand1.variable:
vars_initialized.add(tn.astOperand1.variable)
if tn.str == ';': if tn.str == ';':
cur_clause += 1 cur_clause += 1
tn = tn.next tn = tn.next
return vars_defined & vars_exit & vars_modified return vars_defined | vars_initialized, vars_exit & vars_modified
def findCounterTokens(cond): def findCounterTokens(cond):
@ -779,23 +782,23 @@ def isFloatCounterInWhileLoop(whileToken):
return False return False
def hasSideEffectsRecursive(expr): def countSideEffectsRecursive(expr):
if not expr or expr.str == ';': if not expr or expr.str == ';':
return False return 0
if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '[': if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '[':
prev = expr.astOperand1.previous prev = expr.astOperand1.previous
if prev and (prev.str == '{' or prev.str == '{'): if prev and (prev.str == '{' or prev.str == '{'):
return hasSideEffectsRecursive(expr.astOperand2) return countSideEffectsRecursive(expr.astOperand2)
if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '.': if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '.':
e = expr.astOperand1 e = expr.astOperand1
while e and e.str == '.' and e.astOperand2: while e and e.str == '.' and e.astOperand2:
e = e.astOperand1 e = e.astOperand1
if e and e.str == '.': if e and e.str == '.':
return False return 0
if expr.isAssignmentOp or expr.str in {'++', '--'}: if expr.isAssignmentOp or expr.str in {'++', '--'}:
return True return 1
# Todo: Check function calls # Todo: Check function calls
return hasSideEffectsRecursive(expr.astOperand1) or hasSideEffectsRecursive(expr.astOperand2) return countSideEffectsRecursive(expr.astOperand1) + countSideEffectsRecursive(expr.astOperand2)
def isBoolExpression(expr): def isBoolExpression(expr):
@ -2694,12 +2697,12 @@ class MisraChecker:
def misra_13_5(self, data): def misra_13_5(self, data):
for token in data.tokenlist: for token in data.tokenlist:
if token.isLogicalOp and hasSideEffectsRecursive(token.astOperand2): if token.isLogicalOp and countSideEffectsRecursive(token.astOperand2) > 0:
self.reportError(token, 13, 5) self.reportError(token, 13, 5)
def misra_13_6(self, data): def misra_13_6(self, data):
for token in data.tokenlist: for token in data.tokenlist:
if token.str == 'sizeof' and hasSideEffectsRecursive(token.next): if token.str == 'sizeof' and countSideEffectsRecursive(token.next) > 0:
self.reportError(token, 13, 6) self.reportError(token, 13, 6)
def misra_14_1(self, data): def misra_14_1(self, data):
@ -2717,34 +2720,45 @@ class MisraChecker:
def misra_14_2(self, data): def misra_14_2(self, data):
for token in data.tokenlist: for token in data.tokenlist:
expressions = getForLoopExpressions(token) if token.str == 'for':
if not expressions: expressions = getForLoopExpressions(token)
continue if not expressions:
if expressions[0] and not expressions[0].isAssignmentOp: continue
self.reportError(token, 14, 2) if expressions[0] and not expressions[0].isAssignmentOp:
elif hasSideEffectsRecursive(expressions[1]): self.reportError(token, 14, 2)
self.reportError(token, 14, 2) if countSideEffectsRecursive(expressions[1]) > 0:
self.reportError(token, 14, 2)
if countSideEffectsRecursive(expressions[2]) > 1:
self.reportError(token, 14, 2)
# Inspect modification of loop counter in loop body counter_vars_first_clause, counter_vars_exit_modified = getForLoopCounterVariables(token)
counter_vars = getForLoopCounterVariables(token) if len(counter_vars_exit_modified) == 0:
outer_scope = token.scope # if it's not possible to identify a loop counter, all 3 clauses must be empty
body_scope = None for idx in range(len(expressions)):
tn = token.next if expressions[idx]:
while tn and tn.next != outer_scope.bodyEnd: self.reportError(token, 14, 2)
if tn.scope and tn.scope.nestedIn == outer_scope: break
body_scope = tn.scope elif len(counter_vars_exit_modified) > 1:
break # there shall be a single loop counter
tn = tn.next self.reportError(token, 14, 2)
if not body_scope: else: # len(counter_vars_exit_modified) == 1:
continue loop_counter = counter_vars_exit_modified.pop()
tn = body_scope.bodyStart # if the first clause is not empty, then it shall (declare and) initialize the loop counter
while tn and tn != body_scope.bodyEnd: if expressions[0] is not None and loop_counter not in counter_vars_first_clause:
if tn.variable and tn.variable in counter_vars: self.reportError(token, 14, 2)
if tn.next:
# TODO: Check modifications in function calls # Inspect modification of loop counter in loop body
if hasSideEffectsRecursive(tn.next): body_scope = token.next.link.next.scope
self.reportError(tn, 14, 2) if not body_scope:
tn = tn.next continue
tn = body_scope.bodyStart
while tn and tn != body_scope.bodyEnd:
if tn.variable == loop_counter:
if tn.next:
# TODO: Check modifications in function calls
if countSideEffectsRecursive(tn.next) > 0:
self.reportError(tn, 14, 2)
tn = tn.next
def misra_14_4(self, data): def misra_14_4(self, data):
for token in data.tokenlist: for token in data.tokenlist:

View File

@ -894,7 +894,8 @@ void misra_12_3(int a, int b, int c) {
f((1,2),3); // TODO f((1,2),3); // TODO
for (i=0; i<10; i++, j++){} // 12.3 // third clause: 2 persistent side effects instead of 1 (14.2)
for (i=0; i<10; i++, j++){} // 12.3 14.2
for (int i = 0, p = &a1; // 12.3 14.2 for (int i = 0, p = &a1; // 12.3 14.2
i < 42; i < 42;
++i, ++p ) // 12.3 ++i, ++p ) // 12.3
@ -1157,7 +1158,7 @@ static void misra_14_2_init_value(int32_t *var) {
} }
static void misra_14_2_fn1(bool b) { static void misra_14_2_fn1(bool b) {
for (;i++<10;) {} // 14.2 for (;i++<10;) {} // 14.2
for (;i<10;dostuff()) {} // TODO for (;i<10;dostuff()) {} // 14.2
int32_t g = 0; int32_t g = 0;
int g_arr[42]; int g_arr[42];
g += 2; // no-warning g += 2; // no-warning
@ -1175,7 +1176,12 @@ static void misra_14_2_fn1(bool b) {
for (misra_14_2_init_value(&i); i < 10; ++i) {} // no-warning FIXME: False positive for 14.2 Trac #9491 for (misra_14_2_init_value(&i); i < 10; ++i) {} // no-warning FIXME: False positive for 14.2 Trac #9491
bool abort = false; bool abort = false;
for (i = 0; (i < 10) && !abort; ++i) { // no-warning for (i = 0; (i < 10) && !abort; ++i) { // 14.2 as 'i' is not a variable
if (b) {
abort = true;
}
}
for (int i = 0; (i < 10) && !abort; ++i) { // no warning
if (b) { if (b) {
abort = true; abort = true;
} }
@ -1186,6 +1192,18 @@ static void misra_14_2_fn1(bool b) {
for (int i = x; i < 42; i++) { for (int i = x; i < 42; i++) {
x++; // no warning x++; // no warning
} }
// 1st clause item 2 + loop counter modification
for(x = 0; x < 10; x++) {
x++; // 14.2
}
// third clause: 2 persistent side effects instead of 1 (14.2)
for (int i = 0; i < 10; i++, x++) { // 12.3 14.2
}
// 2 loop counters, there shall be only 1
for(int i=0, j=0; (i<10) && (j<10); i++, j++) { // 12.3 14.2
}
for (int i = (x - 3); i < 42; i++) { for (int i = (x - 3); i < 42; i++) {
x ^= 3; // no warning x ^= 3; // no warning
} }
@ -1218,16 +1236,18 @@ static void misra_14_2_fn2(void)
for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3 for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3
i++; // no warning i++; // no warning
} }
for (int i = 0; y < 10; y++) { // TODO: 14.2 // 1st clause is not empty, but is not used in 2nd and 3rd clause
for (int i = 0; y < 10; y++) { // 14.2
i++; // no warning i++; // no warning
} }
for (int i = 0; i < 10; y++) { // TODO: 14.2 for (; y < 10; y++) {} // without 1st clause, no error
for (int i = 0; i < 10; y++) { // 14.2
i++; // no warning i++; // no warning
} }
for (int i = 0; y < 10; i++) { // TODO: 14.2 for (int i = 0; y < 10; i++) { // 14.2
i++; // no warning i++; // no warning
} }
for (int i = 0; i < 10; (y+=i)) { for (int i = 0; i < 10; (y+=i)) { // 14.2
i++; // no warning i++; // no warning
} }
@ -1323,7 +1343,7 @@ static void misra_15_4(void) {
if (y==2) { if (y==2) {
break; break;
} }
for (z = 0; y < 42; ++z) { for (z = 0; y < 42; ++z) { // 14.2
if (z==1) { if (z==1) {
break; break;
} }