CLANG WARN NULLABLE TO NONNULL CONVERSION

Xcode 7 includes a project setting named "Apple LLVM 7.0 - Warnings - All languages > Incorrect Uses of Nullable Values", or CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION.

You can pass it as a compiler flag using -Wnullable-to-nonnull-conversion.

What does it do?

To demonstrate, consider the following command-line app:

#import <Foundation/Foundation.h>

void welcomeToClowntown(NSString *_Nonnull greeting) {
  NSLog(@"%@, welcome to clowntown!", greeting);
}

int main(int argc, const char * argv[]) {
  // Warning: Null passed to a callee that requires 
  //          a non-null argument
  welcomeToClowntown(nil); 
  return 0;
}

Notice that we're passing a nil literal to our function welcomeToClowntown(), even though it takes a parameter of type NSString *_Nonnull. This always emits a warning, regardless of whether CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION is enabled.

But what about the following case?

#import <Foundation/Foundation.h>

void welcomeToClowntown(NSString *_Nonnull greeting) {
  NSLog(@"%@, welcome to clowntown!", greeting);
}

NSString *_Nullable clownyGreeting() {
  if (arc4random() % 2 == 0) {
    return @"Hola";
  } else {
    return nil;
  }
}

int main(int argc, const char * argv[]) {
  // Emits a warning *only if*
  // CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION
  // is enabled.
  //
  // Warning: Implicit conversion from nullable pointer 
  //          'NSString * _Nullable' to non-nullable pointer
  //          type 'NSString * _Nonnull'
  welcomeToClowntown(clownyGreeting());
  return 0;
}

welcomeToClowntown() insists upon being given an NSString *_Nonnull parameter, but we're passing it the return value from clownyGreeting(), which is of type NSString *_Nullable. In other words, we're passing a potentially nil value to a function that requires non-nil values. CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION emits a warning for code such as this.

When should I use it?

I recommend enabling this setting before adding nullability identifiers to your Objective-C headers. That way, as you add restrictions on the non-nullability of your interfaces, the compiler will warn you of callsites that violate these restrictions.

Is it fool-proof?

No. Like many compiler warnings, it can be side-stepped:

#import <Foundation/Foundation.h>

void welcomeToClowntown(NSString *_Nonnull greeting) {
  NSLog(@"%@, welcome to clowntown!", greeting);
}

NSString *_Nullable clownyGreeting() {
  if (arc4random() % 2 == 0) {
    return @"Hola";
  } else {
    return nil;
  }
}

int main(int argc, const char * argv[]) {
  // No longer emits a warning, since the return type
  // is "cast" as an (implicitly non-null) `NSString *`.
  NSString *greeting = clownyGreeting();
  welcomeToClowntown(greeting);
  return 0;
}

When we assign the return value of clownyGreeting(), an NSString *_Nonnull object, to a variable typed as NSString *, the compiler no longer emits a warning. I believe this is because the NSString *_Nonnull object being returned is "cast" as an NSString *, which is implicitly regarded as _Nonnull.

Why shouldn't I enable this warning?

Nullability identifiers have been added to Foundation classes like NSString, and some of these are very strict. For example, -[NSString isEqualToString:] takes an argument of NSString *_Nonnull. If you have a lot of callsites to this method that expect -isEqualToString: to return NO when passed a nil parameter (JSON parsing, perhaps?), you'll have to modify each of those callsites.

#import <Foundation/Foundation.h>

NSString *_Nullable clownyGreeting() {
  if (arc4random() % 2 == 0) {
    return @"Hola";
  } else {
    return nil;
  }
}

int main(int argc, const char * argv[]) {
  // Emits a warning *only if*
  // CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION
  // is enabled.
  //
  // Warning: Implicit conversion from nullable pointer 
  //          'NSString * _Nullable' to non-nullable pointer
  //          type 'NSString * _Nonnull'
  [@"Hola" isEqualToString:clownyGreeting()];
  return 0;
}

This gets pretty tedious, so beware: turning CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION on in your project could lead to a lot of extra warnings!

Still, all the warnings demonstrate spots where nil may be propagating throughout your system. Try turning it on to see if the results surprise you!

Thanks to @nlutsenko for introducing me to this setting!

UPDATE: Clang static analyzer improvements

The latest version of the Clang static analyzer was released on October 28, 2015. It catches the "side-step" I point out above:

#import <Foundation/Foundation.h>

void welcomeToClowntown(NSString *_Nonnull greeting) {
  NSLog(@"%@, welcome to clowntown!", greeting);
}

NSString *_Nullable clownyGreeting() {
  if (arc4random() % 2 == 0) {
    return @"Hola";
  } else {
    return nil;
  }
}

int main(int argc, const char * argv[]) {
  // Previously, this would not emit a warning, since
  // the return type is "cast" as an
  // (implicitly non-null) `NSString *`.
  //
  // The latest Clang static analyzer emits:
  // Warning: Nullable pointer is passed to a callee
  //          that requires a non-null argument
  NSString *greeting = clownyGreeting();
  welcomeToClowntown(greeting);
  return 0;
}

This is great work, and I can't wait for this new analyzer to be packaged with Xcode. Until then, you can run the analyzer yourself by downloading the latest release and running:

$ checker-277/scan-build \
      -enable-checker nullability.NullPassedToNonnull \
      -enable-checker nullability.NullReturnedFromNonnull \
      -enable-checker nullability.NullableDereferenced \
      -enable-checker nullability.NullablePassedToNonnull \
      xcodebuild