CFamily: Rule S994 : Forcing const for all 'by-value' function parameters?

I’m using Version 8.7 (build 41497), CFamily plugin version: 6.17.0.27551

Rule S994: This is tied to MISRA C++2008 rule 7-1-1.
The example code in the MISRA standard does not cover the case of ‘by-value’ function parameters, so I guess it’s open for interpretation.

The way this rule is currently implemented results in a large amount of (what I consider) false-positives:

void myFunction(int parameter) {} // this should NOT trigger cpp-S994

In my opinion, forcing this on ‘by-value’ function parameters is pointless – the parameters are copies already. This is not about “contract” in the function signature.

It shadows the actually interesting/relevant cases: ‘pass by reference/pointer’ function parameters and stack variables which are only assigned to once and could be made const.

Hello @Sidelobe,

The MISRA standard actually explicitly covers this case, and mandates that parameter should be const. If you look for instance at the first example in the MISRA document, p1 is considered as non-compliant: Even though the pointed-to object is modified, the pointer itself is not, so the pointer should be const-qualified. Additionally, the rule says that:

A non-parametric variable will then require its initialization at the point of declaration.

Which means that for a parameter, constness does not require initialization. And also implies that parameters are indeed covered by this rule. I agree that it is not currently obvious, and next version of MISRA will make this case crystal clear.

This MISRA rule is not about specifying a clear contract for the function, it’s about making sure that the value cannot be accidentally modified in the body of the function under consideration. It’s exactly the same as for a local variable inside a function: Const or non const will not change the interface of the function, but this rule still requires const.

The behaviour that you are looking for (which I agree is more important) is covered by other rules: S995 (for parameters) and S5350 (for stack variables, which goes beyond what MISRA C++2008 specifies).

I don’t think anything is shadowed when it comes to reference/pointer. S995 & S5350 handle the most important cases and S994 doesn’t trigger on them.

I hope this answers your questions

2 Likes

Hi Loïc

Thank you very much for your thorough answer, I highly appreciate it!
It makes sense to me now – with S995 & S5350 I do have exactly the granularity I was looking for.

So if I understand correctly, it’s the fact that the variable is not being modified that triggers S994, not because all function parameters need to be const per se.

So for the record (I just verified this) the following code would not trigger S994:

bool functionThatHasAnOutputParameter(int* a)
{
     ...
     *a = result;
     return wasSuccessful;
}

void function(int a) 
{
      bool wasSuccessful = functionThatHasAnOutputParameter(&a);
      ...
      // use a
}

I actually consider it bad style to modify a function parameter, and would probably do a copy to a stack variable in this case – I’m just making a point.

Thanks again for you help!

Hello @Sidelobe,

In your code sample, both a and wasSuccessful (in function) should be const-qualified. An example where a would not need to be const-qualified would be:

bool functionThatHasAnOutputParameter(int* a, int * const b)
{
     ...
    while (a<b) {
     *(a++) = result;
    }
    return wasSuccessful;
}

But (in my example) if a is constant in function(), then it cannot be modified (assignment from result) by functionThatHasAnOutputParameter()

I’ve created a dumb, but fully working/compiling example:

The following code is also fully working:

#include <iostream>

bool functionThatHasAnOutputParameter(int* const a) // Totally different from: int const *a
{
     const int result = 4;
     *a = result;
     return true; 
}

// this does not trigger https://jira.sonarsource.com/browse/RSPEC-995, because a is modified in the function
bool function(int a) 
{
    bool wasSuccessful = functionThatHasAnOutputParameter(&a);

    if (wasSuccessful) {
        if (a > 5) {
            wasSuccessful = false;
        } else {
            wasSuccessful = true;  
        }
    }
    return wasSuccessful;
}

int main() 
{
    std::cout << function(20) << "\n";
}

Here:

  • In functionThatHasAnOutputParameter, a is a constant pointer to a non-constant integer. It will be used to modify this integer, but the pointer itself won’t be modified.
  • In function, a is a non-const integer. It cannot be const, because it’s address is taken, then passed to a function that expects a pointer to a non-const integer.

So the code that you wrote will trigger S994 in functionThatHasAnOutputParameter(but not in function). The code I wrote back will not trigger it at all.

Ok, fully understood now. I was on the right track – the rule basically ensures (together with S995 and S5350) that:
“everything that can be const, is const” (data and pointers)

I guess this forces the tightest-possible contracts between callers and functions.

Thanks for the further exemplification!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.