22 March 2011

Suppressing Warnings in GCC


(This article was originally written by me as part of the Boost Developer Warning Guidelines.)

With GCC there are a couple of options when you want to supress warnings. First, beginning with version 3.1, since GCC won't report warnings for files that GCC considers system files, you can make GCC consider the problematic files system files. This is heavy handed, makes it easy for problems to creep in unnoticed, but effective. Second, beginning with version 4.2, you can suppress a particular class of warning either for an entire file, or (beginning at version 4.6), for a section of a file.

Supressing Warnings For A File By Making GCC See It As A System Header

Using a pragma to make GCC think a file or part of a file is a system header
Beginning with GCC 3.1, for a particular file, you can turn off all warnings including most warnings generated by the expansion of macros specified in a file by putting the following in a file
#pragma GCC system_header File considered a system header.
It can be specified anywhere in the file, and code that precedes the pragma in the file will be unaffected. The intent of declaring a file a system header, is for operating system specific code that can't be strictly conforming C or C++. This should not be seen as a handy way of turning off bothersome warnings. Many (most?) warnings point to real issues and should be dealt with appropriately.
Using -i to make GCC think all files in a directory are system headers
You can also turn off warnings for all files in a directory, by putting the directory into the include search path with -i instead of -I
The -idirectoryName command line option adds its argument to the list of directories to search for headers, just like -I. Any headers found in that directory will be considered system headers. It also has the side effect of changing the inclusion order, in that all files included from directories specified with -i are included after files included from directories specified via -I. If the directory is specified with both -I and -i it is still only searched after normal includes as part of the system include directories. This may be appropriate if you have to deal with other's spotty code that generates a lot of warnings that you can't fix.
If you turn off warnings for files that are shared with your users, you need to be able to see the warnings yourself so that as new problems arise you will see them. You can turn on warnings back on for system headers when you compile with:
-Wsystem-headers
This makes GCC print warning messages for constructs found in system header files that would normally not be seen. Using -Wall in conjunction with this option will not warn about unknown pragmas in system headers. For that, -Wunknown-pragmas must also be used.

Turning off warnings locally with gcc

So. Suppose you are getting a warning and have checked the code and are sure that it's a spurious warning. There's nothing wrong. If the warning is controllable via a command line -W option, then you can (if you have GCC version 4.2 or newer) turn it off temporarily. First you need to find out what the option might be, then if it exists turn it off via a pragma. How you do this varies a bit with GCC version.
Finding out what option controls the warning
-fdiagnostics-show-option
In GCC, for versions 4.2 and higher, this option instructs the diagnostic machinery to add text to each diagnostic emitted, which indicates which command line option directly controls that diagnostic, when such an option is known to the diagnostic machinery. The added text will look similar to [-Wsign-compare]. If you see this, that tells you that the -Wsign-compare command line option turns this warning on.
Turning the warnings off and on
I want to particularly thank Jonathan Wakely for his willingness to help and for the great help he offered on the gcc-help mailing list. It's people like him that make open source work. Without his help it would have taken me much longer to write this section. Any problems of course are my fault;) In addition to picking Jonathan's brain, I read the appropriate source in diagnostics.c and perused many releases of GCC documentation to get this information.
GCC provides the following pragmas to control warnings.
#pragma GCC diagnostic push
Available since GCC version 4.6, this pragma lets you remember diagnostic options in place at a particular time. This pragma can occur on any line of a file. The number of nested pushes is limited only by the size of memory and the size of an int index into the diagnostic information.
#pragma GCC diagnostic pop
Available since GCC version 4.6, this pragma lets you restore diagnostic options that were remembered by a diagnostic push. This pragma can occur on any line of a file. The number of pops is limited by memory and the size of an int index into the diagnostic information. At a pop information is moved from the table used by push into a history. An unbalanced pop, i.e. popping when nothing is on the stack is harmless and will simply reassert the user's command line diagnostic choices.
#pragma GCC diagnostic [warning|error|ignored] OPTION
From GCC version 4.2.4 and before GCC version 4.6 this could be specified at file scope outside of any functions, classes, unions, structs, or methods, to change the behavior when a particular class of error was seen. For GCC version 4.6 and later, it can be put in any line of a file, and affects from that position forward. For any supported version, it only works with warnings that have explicit -W arguments, use -fdiagnostic-show-option to find out which one to use. An example: #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
version 4.2. You can turn them off but then what?
So starting from 4.2 but before 4.6, just put near the top of the file something like:
#pragma GCC diagnostic ignored "-Wdeprecated-declarations
to turn off warnings from that point forward in the file of the use of deprecated declarations. Problematically, you have no way of knowing what the user had this option set to. They might have already had the warnings turned on, they might have had them set to ignore, or they might have had them set to cause an error. At the end of the file, if you do nothing else, the diagnostic for deprecated declarations stays ignored for anything that includes your file. You can set them to ignored, error, or warning at the end of the file before exiting, but you don't know which to use. This is sure to cause angst.
version 4.6 Now you can restore the user's flags
For version 4.6 or later, you can save the state of the user's diagnostic flags. You can insert this around the line that causes the spurious warning:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Next you would have any amount of code for which you'd like to suppress that warning
#pragma GCC diagnostic pop
Of course this could cover everything from a line up to the whole file, and in between the push and the pop you could make multiple changes to each of multiple options. Be careful, though, that you don't pop to soon. In this example:
foo()
{
int unused,i;
i=3;
}
We might want to suppress the unused variable like this:
foo()
{
#pragma GCC diagnostic push
#pragma GCC diagnostic "-Wunused-variable"
int unused,i;
#pragma GCC diagnostic pop
i=3;
}
and then be surprised that we still get a warning about an unused variable. The reason is, that GCC doesn't know the variable is unused until it hits the closing brace. That means the pop has to come after the closing brace:
foo()
{
#pragma GCC diagnostic push
#pragma GCC diagnostic "-Wunused-variable"
int unused,i;
i=3;
}
#pragma GCC diagnostic pop

A handy macro to help you do some of this
Jonathan Wakely came up with a nice macro set to control this and I'm sharing a slightly modified version of it with you. It defines:
GCC_DIAG_OFF(FLAG)
For versions 4.2-4.5 will turn warnings off for a particular error if controllable via a -W command line flag. If -fdiagostics-show-option told you that the warning was controlled by [-Wsign-compare], then you could say GCC_DIAG_OFF(sign_compare). Beginning at version 4.6, it will push the current state of the diagnostic flags and then turning warning off. Prior to version 4.2 it has no effect.
GCC_DIAG_ON(FLAG)
For versions 4.2-4.5 will arbitrarily turn warnings on. This may not be what the user wanted. Beginning at version 4.6 it will simply pop the saved diagnostic stack. Prior to version 4.2 it has no effect.
Both of them should be used only at file scope for versions 4.2-4.5. Beginning at version 4.6 they can be used at any scope. If you want to turn multiple things from warning to error to ignored between the push and the pop then this will not be effective for you.
It allows you to do things like (for GCC version 4.6 or later):
GCC_DIAG_OFF(sign-compare);
    if(a < b){
    GCC_DIAG_ON(sign-compare);
to turn off warnings that you know are spurious. (Probably a cast of one to the other's type or changing the declaration of the type of one to the other's would be a better fix.)
#if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
#define GCC_DIAG_STR(s) #s
#define GCC_DIAG_JOINSTR(x,y) GCC_DIAG_STR(x ## y)
# define GCC_DIAG_DO_PRAGMA(x) _Pragma (#x)
# define GCC_DIAG_PRAGMA(x) GCC_DIAG_DO_PRAGMA(GCC diagnostic x)
# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
#  define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(push) \
          GCC_DIAG_PRAGMA(ignored GCC_DIAG_JOINSTR(-W,x))
#  define GCC_DIAG_ON(x) GCC_DIAG_PRAGMA(pop)
# else
#  define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(ignored GCC_DIAG_JOINSTR(-W,x))
#  define GCC_DIAG_ON(x)  GCC_DIAG_PRAGMA(warning GCC_DIAG_JOINSTR(-W,x))
# endif
#else
# define GCC_DIAG_OFF(x)
# define GCC_DIAG_ON(x)
#endif
These macro names won't collide with GCC macros since their's start with one or two underscores.