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