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