Inconsistent printf format warnings for macro in system headers

Martin Sebor msebor@gmail.com
Wed Oct 21 17:34:00 GMT 2015


On 10/16/2015 08:40 AM, Olivier Blin wrote:
> Hello,
>
> In gcc versions starting from 4.8, it seems that printf format warnings
> in macros are inconsistent, depending on whether the header defining the
> macro is included as a system header or not, and on the build mode (C or
> C++).
>
> With gcc < 4.8 (tried gcc 4.6.3 and 4.7.2), there are printf format
> warnings for both C and C++ languages, when the macro is in a system
> header or not.
>
> With gcc >= 4.8 (tried 4.8.2 and 5.2.1 snapshot), there are no printf
> format warnings when the build mode is C++ *and* when the macro is in a
> system header.
> If the build mode is C or if the macro is in a non-system header, the
> printf format warnings are emitted.
>
> Please see below for a test case and results in various configurations.
> Can this be considered as a bug in gcc ?
> Or is this behavior somehow expected?
> If so, what is the proper way to get printf format warnings in C++ mode
> for macros defined in system headers?

I'm inclined to say it's a bug. I recommend opening it in Bugzilla.
The only way I was able to work around it is by changing MY_PRINTF
from a function-like macro to an object macro like so:

   #define MY_PRINTF printf

The problem isn't specific to -Wformat but can be reproduced with
other warnings as well. For example, replacing the contents of
the header in the test case with this:

   #pragma GCC system_header

   static inline void __attribute__ ((deprecated)) foobar (void) { }

   #define foo foobar
   #define bar() foobar()

and the body of main with

   foo ();
   bar ();

emits a warning for each of foo() and bar() in C but just one,
for foo(), in C++.

While stepping through the code that checks to see whether or not
a diagnostic should be issued (diagnostic_issue_diagnostic) I see
that the difference in the bar() case is in the return value of
the linemap_macro_expansion_map_p() function which is documented
to

   Return TRUE if MAP encodes locations coming from a macro
   replacement-list at macro expansion point.

and implemented by testing the location MAP's REASON member for
equality to the LC_ENTER_MACRO constant. In C, the REASON member
is set to LC_LEAVE while in C++ to LC_ENTER_MACRO. In C, the
call to bar is then treated as an ordinary token whose location
(i.e., bar's invocation) determines whether or not to issue the
diagnostic. In C++ the reference to bar is recognized as a macro
expansion and the function proceeds to find the token it expands
to and use that to determine whether or not to diagnose it. And
since the token bar() expands to is defined in a system header,
C++ doesn't issue the warning.

In the case of foo(), the REASON is LC_LEAVE in both front-ends
and so they both diagnose it.

Martin

>
> Thanks in advance
>
>
> Here is a sample test case:
> $ mkdir -p syshdr_dir
> $ cat <<EOF > syshdr_dir/myprintf.h
> #include <stdio.h>
> #define MY_PRINTF(x, ...) printf(x, ## __VA_ARGS__);
> EOF
>
> $ cat <<EOF > test-format-printf.c
> #include <myprintf.h>
> int main(void) {
>      long long ll = 0;
>      MY_PRINTF("%ld", ll);
>      return 0;
> }
> EOF
>
> With gcc 4.6.3 and 4.7.2, I get printf format warnings for both C and
> C++ languages.
>
> $ gcc -isystem $PWD/syshdr_dir -Wall -Werror -x c++ -c
> test-format-printf.c -o /dev/null
> test-format-printf.c: In function ‘int main()’:
> test-format-printf.c:4:5: error: format ‘%ld’ expects argument of type
> ‘long int’, but argument 2 has type ‘long long int’ [-Werror=format]
> cc1plus: all warnings being treated as errors
>
> $ gcc -isystem $PWD/syshdr_dir -Wall -Werror -x c -c
> test-format-printf.c -o /dev/null
> test-format-printf.c: In function ‘main’:
> test-format-printf.c:4:5: error: format ‘%ld’ expects argument of type
> ‘long int’, but argument 2 has type ‘long long int’ [-Werror=format]
> cc1: all warnings being treated as errors
>
>
> With gcc 4.8.2 and a 5.2.1 snapshot, there is a warning for C, but no
> warning for C++ when the macro is in a system header.
>
> $ gcc -isystem $PWD/syshdr_dir -Wall -Werror -x c++ -c
> test-format-printf.c -o /dev/null
>
> $ gcc -isystem $PWD/syshdr_dir -Wall -Werror -x c -c
> test-format-printf.c -o /dev/null
> In file included from test-format-printf.c:1:0:
> test-format-printf.c: In function ‘main’:
> test-format-printf.c:4:15: error: format ‘%ld’ expects argument of type
> ‘long int’, but argument 2 has type ‘long long int’ [-Werror=format=]
>       MY_PRINTF("%ld", ll);
>                 ^
> cc1: all warnings being treated as errors
>
> $ gcc -I $PWD/syshdr_dir -Wall -Werror -x c++ -c test-format-printf.c -o
> /dev/null
> In file included from test-format-printf.c:1:0:
> test-format-printf.c: In function ‘int main()’:
> /home/sah0146/dev/workspaces/bcm7252/hgw/build/MAIN/syshdr_dir/myprintf.h:2:51:
> error: format ‘%ld’ expects argument of type ‘long int’, but argument 2
> has type ‘long long int’ [-Werror=format=]
>   #define MY_PRINTF(x, ...) printf(x, ## __VA_ARGS__);
>                                                     ^
> test-format-printf.c:4:5: note: in expansion of macro ‘MY_PRINTF’
>       MY_PRINTF("%ld", ll);
>       ^
> cc1plus: all warnings being treated as errors
>



More information about the Gcc-help mailing list