"stderr;" as a statement throws no warning
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.