diff --git a/change_notes/2026-10-26-fix-fp-rule-7-11-3-template-callee.md b/change_notes/2026-10-26-fix-fp-rule-7-11-3-template-callee.md new file mode 100644 index 0000000000..58b2232a33 --- /dev/null +++ b/change_notes/2026-10-26-fix-fp-rule-7-11-3-template-callee.md @@ -0,0 +1,2 @@ +- `RULE-7-11-3` - `FunctionPointerConversionContext.ql`: + - Fixed false positives on unqualified calls to member and free functions inside class and function templates, where the callee is modelled as a function access that is the called expression of a call rather than a conversion to a function pointer. diff --git a/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql b/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql index f58629f536..c7cb1d18d6 100644 --- a/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql +++ b/cpp/misra/src/rules/RULE-7-11-3/FunctionPointerConversionContext.ql @@ -33,7 +33,10 @@ class FunctionAccessConversionToFunctionPointer extends FunctionAccess, FunctionToFunctionPointerConversion { FunctionAccessConversionToFunctionPointer() { - this.getType().getUnspecifiedType() instanceof FunctionPointerIshType + this.getType().getUnspecifiedType() instanceof FunctionPointerIshType and + // In templates, the callee of a dependent call is modelled as a function access that is the + // called expression of an ExprCall. This is a function call, not a conversion. + not any(ExprCall call).getExpr() = this } } diff --git a/cpp/misra/test/rules/RULE-7-11-3/test.cpp b/cpp/misra/test/rules/RULE-7-11-3/test.cpp index 0522a703fc..5fd6f61f23 100644 --- a/cpp/misra/test/rules/RULE-7-11-3/test.cpp +++ b/cpp/misra/test/rules/RULE-7-11-3/test.cpp @@ -95,4 +95,33 @@ void test_logical_expressions() { // Lambda used in logical OR expression if (lam || false) { // NON_COMPLIANT } -} \ No newline at end of file +} +template class TemplateWithMemberCalls { +public: + void callMembers() { + helper(); // COMPLIANT: callee of a call, not a conversion + this->helper(); // COMPLIANT + staticHelper(); // COMPLIANT + TemplateWithMemberCalls::staticHelper(); // COMPLIANT + } + + auto takeAddress() { + return &TemplateWithMemberCalls::helper; // COMPLIANT: member pointer, not a + // function pointer + } + +private: + void helper() {} + static void staticHelper() {} +}; + +template void templateCallsFree() { + f(); // COMPLIANT +} + +void instantiate() { + TemplateWithMemberCalls t; + t.callMembers(); + t.takeAddress(); + templateCallsFree(); +}