4

I want the compiler to emit an error if I'm using a specific function without checking its return value.

I'm using GCC in eclipse.

For example :

int fun (){ return 3;}

void main ()
{
   printf("%d",fun ());
}

I call the function fun and print the return value but I do not check the return value. Instead, I would like to enforce something like this:

int ret=fun();
if(ret != some_value) { /* Do something */ }
printf("%d",fun ());

Is this possible?

klutt
  • 30,332
  • 17
  • 55
  • 95
joif doi
  • 181
  • 1
  • 2
  • 10
  • 2
    What do you mean by "If the return value wasn't check" ? You want the compiler to produce an error when a function doesn't return a value as it should ? – Guillaume Petitjean Oct 15 '19 at 07:35
  • 1
    I want to mark this as a duplicate: [How to enforce the usage of return values in C](https://stackoverflow.com/questions/32391824/how-to-enforce-the-usage-of-return-values-in-c). – Weather Vane Oct 15 '19 at 07:37
  • 1
    @WeatherVane I edit the post , that not duplicate , I didn't mean warning when unuse return value , I want warning when return value didn't check – joif doi Oct 15 '19 at 07:40
  • 2
    Well, how will the compiler know if the check itself is any good? Must an integer type have its range checked, i.e. be checked twice? And what about values set by a function that are not its return value, for example `scanf`? And, suppose you pass the unchecked value to another function (as in the example) and the job of that function *is* to check the value? Finally, suppose there is no *point* in checking the function return value, for example a function that adds two `int`s and returns their sum as `long long`? – Weather Vane Oct 15 '19 at 07:43
  • You can insist that the value is used — assigned, passed to a function, used in a condition — but you can’t insist that the value is used in a condition. – Jonathan Leffler Oct 15 '19 at 07:49
  • 2
    Can you also add an example of code that *does* "check" the return value ? Ie. what do you mean by "check" ? – Sander De Dycker Oct 15 '19 at 07:49
  • See GCC documentation on [Common Function Attributes](https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes) — and look for `warn_unused_result` near the end. I don't think it does what you're asking for; it only insists that the result is used somehow, not that it is 'checked'. – Jonathan Leffler Oct 15 '19 at 08:08
  • @joifdoi I changed the question to what I assume you're asking for to make it a bit clearer. Please review. – klutt Oct 15 '19 at 12:33

1 Answers1

8

You cannot force a proper check

I don't think there is any way to do that in the way you want it. After all, in the statement printf("%d",fun ()) you are actually checking the return value from fun() by sending it to another function. Of course, printf doesn't really "check" the return value, but you could have used it this way:

void exit_if_fun_returns_negative_value(int val) { if(val<0) exit(EXIT_FAILURE); }

int main(void)
{
    exit_if_fun_returns_negative_value(fun());
}

But there is no way to make the compiler understand the difference between this and your printf statement. I assume you want to force the programmer to save the return value in a variable, but I don't know how that should be done, and even if you could do that, it does not ensure a proper check. Just look at this example:

char *array;
void *ptr;
ptr = realloc(array, 20);
strcpy(array, "Hello, World!");

Note how we are saving the return value in ptr but since we're not doing something like if(ptr == NULL) exit(EXIT_FAILURE); it's pretty pointless.

But you can prevent completely ignoring the return value

However, there is a way to prevent statements where you're not using (which of course is not the same thing as actually checking) the return value at all.

As far as I know, there is no portable way to do this. You will have to rely on compiler extensions. Gcc has such an extension.

__attribute__ ((warn_unused_result)) int foo (void) 
{
    return 5;
}

int main(void)
{
    foo();
}

Compiling this will generate this warning:

$ gcc main.c
main.c: In function ‘main’:
main.c:9:5: warning: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Wunused-result]
    9 |     foo();
      |     ^~~~~

In order to treat it as an error, compile with -Werror

$ gcc main.c -Werror
main.c: In function ‘main’:
main.c:9:5: error: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Werror=unused-result]
    9 |     foo();
      |     ^~~~~
cc1: all warnings being treated as errors

If you want all other warnings to be just warnings, compile with -Werror=unused-result. Example:

$ cat main.c 
__attribute__ ((warn_unused_result))
int foo (void)
{
    return 5;
}

int main(void)
{
    int *x = 5;
    foo();
}

$ gcc main.c -Werror=unused-result
main.c: In function ‘main’:
main.c:9:14: warning: initialization of ‘int *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
    9 |     int *x = 5;
      |              ^
main.c:10:5: error: ignoring return value of ‘foo’, declared with attribute warn_unused_result [-Werror=unused-result]
   10 |     foo();
      |     ^~~~~
cc1: some warnings being treated as errors

Change the signature to include an output parameter

One option to accomplish something similar is to move the return value to a parameter. That will force saving the "return value" to the parameter, and you cannot call the function without sending an output variable to it. Change

int fun (){ return 3;} 

to

void fun(int *ret) { *ret=3; }

But as I mentioned above, I cannot see how you can force a proper check of the variable. This only forces an assignment.

Wrapper functions

Another option is to use wrapper functions. This will often greatly reduce the flexibility so think twice before using it, but it is an option that could be useful in some cases. Let's say that you have a header/source pair. Put the prototype of the wrapper in the header file and the implementation of both the wrapper and the function in the source file. That will hide the original function from the programmer. It can look like this:

.h

int fun_wrapper();

.c

int fun() { return 3; }

int fun_wrapper() 
{ 
    int ret = fun(); 
    if(ret<0) exit(EXIT_FAILURE);
    return ret;
}

How to use this technique for library functions

I created a related question that has an answer. It's about how to use this for fgets and other functions from a library: How to create wrappers to library functions with original name?

klutt
  • 30,332
  • 17
  • 55
  • 95