I like warnings. I really do. It reminds me that the compiler loves me and is looking out for me. (OK, the compiler at least tolerates me.)
What is a warning? It’s when the compiler notices that you’re doing something that might not be what you really intend. The compiler’s job, ultimately, is to do exactly what you tell it to do, but with being slabs of meat instead of computational machines we make mistakes. We make assumptions. Sometimes we’re careless. Sometimes we hold on to outdated ideas or techniques. If the compiler notices that might be sketchy, it lets you know.
Nothing makes me more depressed than joining a project where every build emits a stream of warnings. “Oh, you can ignore those” or “Yeah, we’re going to get around to fixing that.” And then I check the subversion log and see the warnings been in there for a year.
I think that’s a huge mistake. Warnings should either be fixed (they are showing a legitimate problem) or suppressed (we “know” this is OK, stop bugging me). Over my too-many years in this industry, I’ve had the opportunity to work on a lot of platforms. When I started my first job, our software ran on about 30 different Unix variants with at least a dozen different chip architectures. (Anyone remember Clipper?). A subsequent developer’s toolkit we sold supported about a dozen Unix variants, Mac, Windows, OS/2 (anyone remember that?), and VMS (anyone remember that?). I’ll let you ponder the testing matrix for that product.
Needless to say, I got to see a lot of cross-platform code. One interesting behavior we saw was that a warning on one platform’s compiler usually ended up as an error (or worse, a bug) on another platform, or sometimes even on a different rev of a vendor’s operating system. That’s what cemented my opinion of warnings: They’re there for a reason. Fix them.
The biggest problem I see with projects with large numbers of warnings, is that you might never notice new, legitimate warnings. Going from zero warnings to one warning is frightfully obvious. Going from one hundred warnings to one hundred and one warnings is much tougher to catch.
I’m not the only one who meditates on the warning. For example, Peter Hosey has a list of warnings he uses (and why).
For my own code, I tend to turn on
-Wall, with turns on a decent set of stable warnings. Weirdly,
-Wall does not actually mean all warnings. You can check out the docs at http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html for the complete list.
-Wall, and friends like
-pedantic, include a well-defined set of warnings, so you can assured that your code won’t generate new warnings with new versions of your compiler.
With Xcode 4.4, the Apple LLVM compiler introduced
-Weverything, which really means, “warn me about everything.” If the team adds new warnings and they get triggered by your code, you’ll see new warnings when you upgrade. I think this is wonderful. You should be careful anyway when upgrading compiler versions (software is software after all, and has bugs or new interpretations for corner-case behavior.) If I turn on
-Weverything I’m willing to take on that burden. I ported the Advanced Mac OS X Programming sample code to the latest Apple LLVM compiler and
-Weverything, and it was an eye-opening experience. It found all sorts of things.
In the real world, I probably won’t turn on
-Weverything for day-to-day work on a large project with lots of programmers. It’s very picky. But I’ve started going on occasional warning fix-its - turn it on, build, see what’s warned, and then fix it if appropriate. Then turn it off before committing the changes.
You can turn individual warnings on as well. Xcode provides checkboxes you can set, and you can supply them as command line parameters. Say you’re using
-Wall, but also wanted the warning that gives a little extra security on your
printf statements. You’d add
-Wformat-security to your build settings
Sometimes, though, you want to suppress some warnings. They might not make sense. For example, I consider
-Wunused-parameter to be worthless. It warns if you don’t consume all of the parameters passed to a function or method. More often than not, you’re passed more parameters than you need to do your work, especially in callback functions, or methods intending to be overridden.
There’s several ways to turn off warnings. You can uncheck the box in Xcode. Another is including the flag
-Wno-unused-parameter. The pattern is: for a warning called “
badger”, you turn it on with
-Wbadger, and suppress it with
The Apple LLVM compiler is very helpful when it generates warnings - it tells you what warning triggered the message:
BNRExtremeGrooviness.m:181:69: warning: will never be executed [-Wunreachable-code] awesome = [[BNRAwesome alloc] initWithName:@"Strongbad"]; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
That way, if you wanted to suppress this warning, you just need to stick a “
no-” in there, and supply the compiler the
In fact, my usual compiler line for the updated Advanced book source code is something like
clang -g -Weverything -Wno-unused-parameter rest of the stuff
So why is this warning-flag-stuff useful information in the clicky-clicky world of Xcode project configuration? You might want to provide a company-wide set of warnings in
.xcconfig files. You can also suppress warnings on a case-by-case basis.
Consider this not untypical sockets code:
struct sockaddr_storage *serverAddress = ...; struct hostent *host = ...; serverAddress->ss_family = host->h_addrtype;
You get the struct hostent from a DNS lookup call like
gethostbyname2, and you create a server address, copying over the address family (such as
AF_INET for IPv4 or
AF_INET6 for IPv6).
The problem is,
h_addrtype is an int, and
ss_family is a
uint8_t. So the top three (or more) bytes of the address type will be chopped off. As far as the networking API is concerned, that’s groovy.
AF_INET and friends are small values. But not as far as the compiler is concerned if you have
-Weverything turned on. It thinks that you might be making a mistake slicing off the top bytes (see the problems it can cause with BOOLS). So, with
-Weverything turned on, you’ll get this:
blorf.m:27:38: warning: implicit conversion loses integer precision: 'int' to 'sa_family_t' (aka 'unsigned char') [-Wconversion] serverAddress->ss_family = host->h_addrtype; ~ ~~~~~~^~~~~~~~~~
The compiler tells you the flag which would turn this warning on (so to turn it off just use
-Wno-conversion), but you might not want to turn it off project-wide (or even source-file wide), because there may be legitimate problems involving integer mutilation happening. You can turn warnings off on a case by case basis, by using compiler pragmas:
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" serverAddress->ss_family = host->h_addrtype; #pragma clang diagnostic pop
The syntax is pretty straightforward.
#pragma clang diagnostic says that you’re doing stuff with
clang, the name of the command-line Apple LLVM compiler tool.
The compiler keeps a stack of diagnostics, similar to Quartz graphic states. Push the current configuration, make some changes, enjoy the changes, then get rid of them, restoring the previous state. You can find full information at http://clang.llvm.org/docs/UsersManual.html
So here, I’m turning off the conversion warning just for this line of code. Of course, I could just cast it, but then I couldn’t use the cool
Lastly, in my personal projects, I turn on the big warning hammer.
-Werror. Treat warnings as errors. This forces me to fix warnings as they happen. It forces strict warning hygiene.
(If you’re in Columbus, OH this weekend, come hear me speak at CocoaConf on the riveting topics of debugging and performance tuning!)