more info ⬇

@amattn

subscribe for the best
iOS code & business tips:

iOS, server tech, and the business of software



2014 02 04

Better Apps with Clang's Weverything or Wall is a Lie!

It sure would be nice if there was a code fairy flying over your shoulder that could sound a dulcet chime every time suspect code was introduced. Even better would be a swarm of fae to keep the entire project free of dubious practice. No such creatures seem to exist, but I will make the somewhat farcical claim that compiler warnings are a poor man’s army of code fairies and that enabling most all of them will eventually resolve into better apps. First, some backstory on how not to proceed.

-Wall is a lie!

The -Wall flag instructs the compiler to Enable All Warnings. However, gcc has an interesting definition of “All”, which is “Not All”. This is unfortunate as a codebase that compiles warning-free tends to behave better and have less uncovered edge cases.

The historical reason is that some warnings are intended to be more like notices where human judgement is required. The compiler is basically saying, “Watch out! Something questionable is happening here”.

Everyone’s definition of questionable is a bit different however, and there were some compiler warnings deemed as more informational than true exclamations of potential danger. Many of those warnings were excluded from -Wall, and as a compromise some of them were shoehorned into -Wpedantic and -Wextra.

After gcc usage became more and more widespread, it actually got harder to get new warnings included in the -Wall umbrella as this started to break toolchains. Once you get government standards bodies involved with the application of the -Wall flag, you more or less stop improving the -Wall flag.

Clang to the rescue

This brings us to Clang. Clang developers understand most of this history and decided that since -Wall and it’s ilk were mired in political and technical muck, they needed a new flag that is defined as “all current and future warnings forever”. They called it -Weverything which is pronounced “wevrything” and typically accompanied by large wavy outstretched arm gestures.

I’ll wait for that mental image to settle. Okay moving on.

But You Promised Better Apps!

This brings us to Better Apps.

Here’s a non-exhaustive, off-the-top-of-my-head list of edge cases and questionable behavior that -Wevertyhing can help diagnose:

  1. non-existent selector warning
  2. lossy integer conversion
  3. enum usage in switch statements
  4. memory leaks due to usage of self in blocks
  5. preprocessor and pragma issues
  6. slow builds due to not using modules

The list is roughly sorted in order of severity of potential problems. Stuff high on the list could cause runtime crashes and widespread decaffeination of coffee-stores around the world. Integer conversion edge cases in particular are a big source of crashes on 64bit iOS devices. Stuff towards the bottom simply wastes time & energy and likely induces hair loss.

-Weverything in Action

A small prototype project that I implemented in a few weeks that uses Core Data, some GCD, and some collection views and animations was my initial stab at -Weverything.

A clean build turned into a build with 307 warnings.

Many of these warnings are actually pretty nice and resolving them means my app is a little bit more resilient, a little bit more future-proof, and even builds a little faster.

Resolving them ended up only taking about 20 minutes or so, mostly thanks to Xcode under-appreciated Fix-It feature. The warnings spanned the range of important, nice, helpful, benign-but-I-just-want-the-warning-to-disappear stuff. At the end, I’m left with a handful of warnings I can’t or won’t fix.

At this point, we have to start disabling warnings. Because what -Weverything really means is “all current and future warnings forever, except those that I disable because of reasons”.

A Brief Interlude into Xcode

In Xcode, to enable -Weverything you need to go to the “Build Settings” pane. This can be done project-wide or only for specific targets. I tend to keep them strict for my app targets, then loosen them for test or tangential targets. The app gets a curfew, but the red-headed test targets can go out rutting like a drunken sailor for all I care.

The correct place to set -Weverything is in the “Other Warning Flags” line item.

If you wish to disable a certain warning, you first find out the warning flag’s root name then prepend -Wno- to it.

To get the warning flag’s root name in the issue navigator, right-click on a warning you’d like to disable, and select “Reveal in Log”. You should see something like this:

warning: enumeration values 'XXModeCount' not explicitly handled in switch [-Wswitch-enum]

The warning flag is in the square brackets at the end. Just swap out the -W with -Wno- to get the correct flag to disable the warning.

You can then add the disabling flag into “Other Warning Flags” and they should stop bugging you.

Just a note – if you have both target and project-wide settings, the target setting will override the project-wide setting. The “Resolved” column is handy to sort out the final settings.

Weverything But The Girl

Here’s what I ended up with:

-Weverything 
-Wno-objc-missing-property-synthesis 
-Wno-semicolon-before-method-body

The objc-missing-property-synthesis is a no-brainer one to disable. This is one of those informative warnings; the compiler is telling you that it is auto-synthesizing the property. We love that the compiler does this for us because manually synthesizing properties is not just boilerplate but also more tedious than doing Quality Testing on the Tickle Me Elmo assembly line.

The other one made me think quite a bit before disabling.

If you are going to disable warnings, make sure you have rational other than “I want the warnings to go away.” or “Stop breaking my beautiful build you bad compiler!”. Here’s an example of the thought process went through.

semicolon-before-method-body: I’ve long, long had the habit of adding semicolons after method name implementations

                        ⤹ HERE! 
- (void)doAction:(id)arg;
{
    //  
}

It makes copying and pasting between header and source files easier. It is optional in ObjC, but a syntax error in plain old C. I’ve found it on the whole to be a good little character, and greater than 99% of the methods I write have it. That being said, I don’t see it that much in the greater ObjC world and could be convinced to change my habits in the future.

For the vast majority of the Xcode-using population, I recommend starting with the following and then disabling warnings as necessary for your own project requirements and coding standards.

-Weverything
-Wno-objc-missing-property-synthesis

Temporarily Overriding Warnings With Our Friend #pragma

Sometimes you just want to disable warnings for a small section of code. This is done with the magic of #pragma. Specifically, the very nice diagnostic push & pop commands.

Here are some examples pulled from real code:

The direct ivar access warning:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdirect-ivar-access"
    if (_fetchedResultsController) // direct access to not trigger the lazy loader
        [self.collectionView reloadData];
#pragma clang diagnostic pop

The assign-enum warning when you just want to give it a 0:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wassign-enum"
// This is not a great example, but gets the point across
NSString *sig = [sigData base64EncodedStringWithOptions:0];
#pragma clang diagnostic pop

The “I’m so sorry, it’s really not my fault, but I really, really need that deprecated method” aka “Guilt-trip, stealing time from your future self” warning:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Come on, you know better.  Stop using this:
[NSThing someDeprecatedMethod];
#pragma clang diagnostic pop

Finally, He’s Almost Done

Will your project still crash after enabling -Weverything? Probably.

Will it crash less? Probably.

If I had a moral of the story, it would probably be this variant of “trust but verify”:

Discipline is nice, but tool-assisted enforcement is even better.

In Summary:

Start with this in your app target’s Build Settings > Other Warning Flags:

-Weverything 
-Wno-objc-missing-property-synthesis 

And use this on a case-by-case basis and only when absolutely necessary:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-W<SOME_FLAG>"
// code...
#pragma clang diagnostic pop


the fine print:
aboutarchive@amattn
© matt nunogawa 2010 - 2014
back ⬆