Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"stderr;" as a statement throws no warning

Suppose I have a source code like this:

#include <stdio.h>

FILE *p;

int main(void) {
    p;
}

... which will compile (using gcc) without any errors or warnings – unless I turn on -Wall, in which case a statement with no effect warning will be output.

However if the code went like this:

#include <stdio.h>

int main(void) {
    stderr;
}

... no warnings would be shown regardless of -Wall.

Am I missing something here?

gcc version 9.3.0

like image 801
mj420 Avatar asked May 31 '20 20:05

mj420


People also ask

Do warnings go to stderr?

Warning messages are normally written to sys. stderr , but their disposition can be changed flexibly, from ignoring all warnings to turning them into exceptions. The disposition of warnings can vary based on the warning category, the text of the warning message, and the source location where it is issued.

Should warnings be printed to stderr?

Warnings (and other human readable diagnostics) should be printed to stderr while program output (the updated configuration) should be printed to stderr, so the updated yaml can be e.g. redirected to a file or captured by a program.

Should warnings go to stderr or stdout?

If you're expecting the user to take some action as a result of the warning, it should go to STDERR. If some downstream script is likely to be tripped up by the warning, it should go to STDERR.

What is the point of stderr?

Standard error (stderr) Standard error is another output stream typically used by programs to output error messages or diagnostics. It is a stream independent of standard output and can be redirected separately.


1 Answers

This program:

$ cat main.c
#include <stdio.h>

int main(void)
{
    FILE *p;
    42;         // statement with no effect
    p;          // statement with no effect
    stderr;     // statement with no effect
    return 0;
}

might be expected to elicit 3 statement with no effect diagnostics whenever it elicits any. But as you've discovered, in the hands of gcc, that is not true.

$ gcc --version
gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0
...

$ gcc -Wall -c main.c
main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;
      |     ^

stderr - which denotes a FILE *, like p - has a free pass for being implicitly evaluated with no effect.

Notoriously, -Wall does not really enable all warnings. But this free pass survives to the customary limit of diagnostic rigour:

$ gcc -Wall -Wextra -pedantic -c main.c
main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;
      |     ^

We should be clear that this free pass is carried by the identfier stderr, as distinct from the value it names:-

It is not transferable to another FILE * by making that other one equal to stderr:

$ cat main.c; gcc -Wall -c main.c
#include <stdio.h>

int main(void)
{
    FILE *p = stderr;
    42;
    p;
    return 0;
}

main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;
      |     ^

It is not enjoyed by the FILE * that actually is stderr, if we do not refer to it as stderr.

$ cat main.c; gcc -Wall -c main.c
#include <stdio.h>

int main(void)
{
    FILE **p = &stderr;
    42;
    *p;     // a.k.a `stderr`
    return 0;
}

main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     *p;     // a.k.a `stderr`
      |     ^~

But on the other hand, even when stderr is referred to as stderr, the free pass is forfeit if that identifier is anything less than the entire context that is evaluated with no effect:

$ cat main.c; gcc -Wall -c main.c
#include <stdio.h>

int main(void)
{
    stdout;             // Undiagnosed
    stderr;             // Undiagnosed
    stderr, stdout;     // Diagnosed once
    42, stderr;         // Diagnosed twice
    stderr - stdout;    // Diagnosed once
    (stderr);           // Diagnosed once
    return 0;
}

main.c: In function ‘main’:
main.c:7:11: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    7 |     stderr, stdout;     // Diagnosed once
      |           ^
main.c:8:7: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    8 |     42, stderr;         // Diagnosed twice
      |       ^
main.c:8:5: warning: statement with no effect [-Wunused-value]
    8 |     42, stderr;         // Diagnosed twice
      |     ^~
main.c:9:12: warning: statement with no effect [-Wunused-value]
    9 |     stderr - stdout;    // Diagnosed once
      |            ^
main.c:10:5: warning: statement with no effect [-Wunused-value]
   10 |     (stderr);           // Diagnosed once
      |     ^

Here I've slipped in the assumption that what goes for stderr goes likewise for stdout, which is vindicated. It's a notable detail that while 42, stderr; is diagnosed as a statement with no effect, stderr, stdout; is not.

It seems fair to say that gcc does not come across as self-assured about the nature and limits of the diagnostic immunity it wants to extend to stderr and similarly qualifying identifiers. This is perhaps understandable, when we probe the ramifications in the sort of code that nobody writes except to fence with the compiler.

Be that as it may, one would like to be clear about the motivation of this diagnostic immunity and to know whether gcc can be told to revoke it, so that, e.g. all of the ineffectual statements I write in a program will be diagnosed as such.

The answer on the second score is Yes:

$ cat main.c; gcc -Wall -Wsystem-headers -c main.c
#include <stdio.h>

int main(void)
{
    FILE *p;
    42;         // statement with no effect
    p;          // statement with no effect
    stderr;     // statement with no effect
    return 0;
}
main.c: In function ‘main’:
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     42;         // statement with no effect
      |     ^~
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     p;          // statement with no effect
      |     ^
In file included from main.c:1:
main.c:8:5: warning: statement with no effect [-Wunused-value]
    8 |     stderr;     // statement with no effect
      |     ^~~~~~

and:

$ cat main.c; gcc -Wall -Wsystem-headers -c main.c
#include <stdio.h>

int main(void)
{
    stdout;
    stderr;
    stderr, stdout;
    42, stderr;
    stderr - stdout;
    (stderr);
    return 0;
}
In file included from main.c:1:
main.c: In function ‘main’:
main.c:5:5: warning: statement with no effect [-Wunused-value]
    5 |     stdout;
      |     ^~~~~~
main.c:6:5: warning: statement with no effect [-Wunused-value]
    6 |     stderr;
      |     ^~~~~~
main.c:7:11: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    7 |     stderr, stdout;
      |           ^
In file included from main.c:1:
main.c:7:5: warning: statement with no effect [-Wunused-value]
    7 |     stderr, stdout;
      |     ^~~~~~
main.c:8:7: warning: left-hand operand of comma expression has no effect [-Wunused-value]
    8 |     42, stderr;
      |       ^
main.c:8:5: warning: statement with no effect [-Wunused-value]
    8 |     42, stderr;
      |     ^~
main.c:9:12: warning: statement with no effect [-Wunused-value]
    9 |     stderr - stdout;
      |            ^
main.c:10:5: warning: statement with no effect [-Wunused-value]
   10 |     (stderr);
      |     ^

And the documentation of -Wsystem-headers offers the motivating rationale:

-Wsystem-headers

Print warning messages for constructs found in system header files. Warnings from system headers are normally suppressed, on the assumption that they usually do not indicate real problems and would only make the compiler output harder to read. Using this command-line option tells GCC to emit warnings from system headers as if they occurred in user code. ...

So, stderr, stderr get their diagnostic immunity by virtue of being declared in a system header, <stdio.h>1. Warnings from system headers are by default assumed to be spurious.

Before we go about our business, however, its worth appreciating that the documented explanation of the effect of -Wsystem-headers, and of its absence, does not actually explain those effects as we observe them. The failure to diagnose

stderr;     // statement with no effect

in our first program in the absence of -Wsystem-headers is not the suppression of a warning from a system header. It is the suppression of a warning from main.c, in which that statement is exactly as ineffectual as:

p;          // statement with no effect

And the effect of -Wsystem-headers on the compilation of that program is not that GCC starts to emit any previously suppressed warning from a system header as if it occurred in user code. It causes GCC to emit a previously suppressed warning that occurred in user code all along.

Evidently the real effect of the default -Wno-system-headers includes, at least, the suppression of certain warnings, in user code or not, when the context

... identifier ...

that would otherwise provoke the warning contains an identifier that was declared in a system header. The manual tells us how to stop this, but only gestures at explaining it.


[1] It is not obvious what is meant by system header in the documentation, but experimentation shows that a file is only a system header in the appropriate sense if it is a header file installed by GCC.

like image 146
Mike Kinghan Avatar answered Oct 12 '22 03:10

Mike Kinghan