Format warnings ignored by -Wformat

Jonathan Wakely jwakely.gcc@gmail.com
Tue Feb 26 14:26:00 GMT 2019


On Mon, 25 Feb 2019 at 23:32, Segher Boessenkool
<segher@kernel.crashing.org> wrote:
>
> Hi!
>
> On Mon, Feb 25, 2019 at 11:18:31AM -0800, Jon Flatley wrote:
> > Hello,
> >
> > I am doing a some investigation into some of the format options used
> > in the upstream Linux Kbuilds in both GCC and Clang. This started when
> > I noticed -Wformat is used when building the kernel with GCC, but not
> > with Clang. As it turns out, this is important. Enabling format
> > warnings in Clang leads to hundreds of warnings when building the
> > Linux kernel, see here:
> >
> > https://gist.github.com/nathanchance/443db156e56cd3c0f6b21d9d77728d80
> >
> > After some more investigating I found a little code snippet that
> > demonstrates the discrepancy.
> > For example consider the following:
> >   int i = 5;
> >   printf("%hu\n", i);
> >   printf("%zu\n", i);
> >
> > GCC produces only one warning under -Wformat on the second print
> > statement, where Clang produces warnings for both.
> >
> > Compiling that example with GCC under -Wformat prints:
> >   format_test.c:7:13: warning: format ‘%zu’ expects argument of type
> > ‘size_t’, but argument 2 has type ‘int’ [-Wformat=]
> >      printf("%zu\n", i);
> >              ~~^
> >              %u
> >
> > But compiling with Clang under -Wformat prints:
> >   format_test.c:6:19: warning: format specifies type 'unsigned short' but the
> >         argument has type 'int' [-Wformat]
> >     printf("%hu\n", i);
> >             ~~~     ^
> >             %d
> >   format_test.c:7:19: warning: format specifies type 'size_t'
> >         (aka 'unsigned long') but the argument has type 'int' [-Wformat]
> >     printf("%zu\n", i);
> >             ~~~     ^
> >             %d
> >   2 warnings generated.
> >
> >
> > I was hoping I could reach out and gain some insight as to why GCC
> > ignores these scenarios where Clang produces warnings. This would be
> > very helpful as we move to enable -Wformat for both GCC and Clang in
> > the upstream kernel, which is an issue we're tracking here:
> > https://github.com/ClangBuiltLinux/linux/issues/378
>
> You cannot pass a short int to a varargs function like printf, it is
> always promoted to int.  As the C standard says for the h flag character:
>
> h
>     Specifies that a following d, i, o, u, x, or X conversion specifier
>     applies to a short int or unsigned short int argument (the argument
>     will have been promoted according to the integer promotions, but
>     its value shall be converted to short int or unsigned short int
>     before printing); or that a following n conversion specifier applies
>     to a pointer to a short int argument.

A bit later it says:

If any argument is
not the correct type for the corresponding conversion specification,
the behavior is
undefined.

The "conversion specification" is %hd so includes both the h length
modifier and the d conversion specifier, and it's not clear to me
whether "the correct type" means the type as modified by the length
modifier or not, i.e. whether %hd

> Assuming the integer started out as short int before passing it to the
> printf function is something GCC does not do;

But GCC doesn't need to assume anything when it can see exactly what's
being passed to printf. It can see that an int really is used there,
not just something promoted to int.

> it will warn on a lot of
> perfectly valid code.

But also misses warnings for code with unintended (maybe even
undefined) behaviour:

#include <stdio.h>
#include <limits.h>

int main()
{
  int i = SHRT_MAX + 1;
  printf("%hd %hd\n", i, SHRT_MAX + 1);
}

Inside printf the int values are converted to short, which is either
an overflow (so undefined) or, if the conversion is done via unsigned
types, at least a change in value that is probably unwanted.

The compiler should be able to warn that SHRT_MAX+1 will not be
printed correctly.

This seems worth a bug report.



More information about the Gcc-help mailing list