From 2f50b03c378afcc01b92356148bf528fce904358 Mon Sep 17 00:00:00 2001 From: Jan Schlosser Date: Thu, 18 Jun 2026 16:57:39 +0200 Subject: [PATCH] RULE-8-2-10: Avoid callers of recursive functions being flagged The rule forbids only recursive functions. It can be fair to have a recursive function and justify this within one. With the behavior before this change, also every caller would need to be argued. This is additonal burden that is not required and intended by the MISRA standard. Fixes #935 --- c/misra/test/rules/RULE-17-2/test.c | 2 +- change_notes/2026-06-18-fix-fp-recursive-functions.md | 2 ++ .../FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll | 5 +++-- ...ctionsCallThemselvesEitherDirectlyOrIndirectly.expected | 7 +++---- .../test.cpp | 6 +++++- 5 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 change_notes/2026-06-18-fix-fp-recursive-functions.md diff --git a/c/misra/test/rules/RULE-17-2/test.c b/c/misra/test/rules/RULE-17-2/test.c index 800921c1e2..649fb34cfa 100644 --- a/c/misra/test/rules/RULE-17-2/test.c +++ b/c/misra/test/rules/RULE-17-2/test.c @@ -8,7 +8,7 @@ void f3() { f3(); // NON_COMPLIANT } void f6() { - f3(); // NON_COMPLIANT + f3(); // COMPLIANT - merely calls a recursive function } void f5() { diff --git a/change_notes/2026-06-18-fix-fp-recursive-functions.md b/change_notes/2026-06-18-fix-fp-recursive-functions.md new file mode 100644 index 0000000000..07a12f8602 --- /dev/null +++ b/change_notes/2026-06-18-fix-fp-recursive-functions.md @@ -0,0 +1,2 @@ +`A7-5-2`, `RULE-8-2-10`: `FunctionsCallThemselvesEitherDirectlyOrIndirectly.ql`: + - Fix false positives where callers of recursive functions were incorrectly reported as recursive. Only calls that participate in a recursive cycle are now reported. diff --git a/cpp/common/src/codingstandards/cpp/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll b/cpp/common/src/codingstandards/cpp/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll index e54e4378e9..bb5c5670e5 100644 --- a/cpp/common/src/codingstandards/cpp/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll +++ b/cpp/common/src/codingstandards/cpp/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.qll @@ -23,7 +23,7 @@ class RecursiveFunction extends Function { RecursiveFunction() { exists(RecursiveCall fc | fc.getEnclosingFunction() = this) } } -query predicate problems(FunctionCall fc, string message, RecursiveFunction f, string functionName) { +query predicate problems(RecursiveCall fc, string message, Function f, string functionName) { not isExcluded(fc, getQuery()) and f = fc.getTarget() and functionName = f.getName() and @@ -31,5 +31,6 @@ query predicate problems(FunctionCall fc, string message, RecursiveFunction f, s then message = "This call directly invokes its containing function $@." else message = - "The function " + fc.getEnclosingFunction() + " is indirectly recursive via this call to $@." + "The function " + fc.getEnclosingFunction() + + " is indirectly recursive via this call to $@." } diff --git a/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.expected b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.expected index 5dbc78c6f6..b0b7d06aa0 100644 --- a/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.expected +++ b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/FunctionsCallThemselvesEitherDirectlyOrIndirectly.expected @@ -1,5 +1,4 @@ | test.cpp:7:13:7:35 | call to test_recursive_function | This call directly invokes its containing function $@. | test.cpp:5:5:5:27 | test_recursive_function | test_recursive_function | -| test.cpp:21:9:21:31 | call to test_recursive_function | The function test_indirect_recursive_function is indirectly recursive via this call to $@. | test.cpp:5:5:5:27 | test_recursive_function | test_recursive_function | -| test.cpp:27:5:27:30 | call to test_indirect_recursive_f2 | The function test_indirect_recursive_f1 is indirectly recursive via this call to $@. | test.cpp:36:5:36:30 | test_indirect_recursive_f2 | test_indirect_recursive_f2 | -| test.cpp:38:9:38:34 | call to test_indirect_recursive_f1 | The function test_indirect_recursive_f2 is indirectly recursive via this call to $@. | test.cpp:25:5:25:30 | test_indirect_recursive_f1 | test_indirect_recursive_f1 | -| test.cpp:75:10:75:19 | call to operator== | This call directly invokes its containing function $@. | test.cpp:74:6:74:15 | operator== | operator== | +| test.cpp:31:5:31:30 | call to test_indirect_recursive_f2 | The function test_indirect_recursive_f1 is indirectly recursive via this call to $@. | test.cpp:40:5:40:30 | test_indirect_recursive_f2 | test_indirect_recursive_f2 | +| test.cpp:42:9:42:34 | call to test_indirect_recursive_f1 | The function test_indirect_recursive_f2 is indirectly recursive via this call to $@. | test.cpp:29:5:29:30 | test_indirect_recursive_f1 | test_indirect_recursive_f1 | +| test.cpp:79:10:79:19 | call to operator== | This call directly invokes its containing function $@. | test.cpp:78:6:78:15 | operator== | operator== | diff --git a/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/test.cpp b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/test.cpp index b51b8fa991..77fc51eb15 100644 --- a/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/test.cpp +++ b/cpp/common/test/rules/functionscallthemselveseitherdirectlyorindirectly/test.cpp @@ -18,10 +18,14 @@ int test_nonrecursive_function(int i) { // COMPLIANT int test_indirect_recursive_function(int i) { if (i > 10) { - i = test_recursive_function(i * i); // NON_COMPLIANT + i = test_recursive_function(i * i); // COMPLIANT - merely calls a recursive function } } +int test_calls_recursive_function(int i) { + return test_recursive_function(i); // COMPLIANT - not part of a recursive cycle +} + int test_indirect_recursive_f1(int i) { if (i > 10) { test_indirect_recursive_f2(i + i); // NON_COMPLIANT