Skip to content
Soutaro Matsumoto edited this page Jun 11, 2016 · 4 revisions

Nullarihyon has some assumptions on analysis to minimize number of false positives.

Variable Nullability Propagation

If local variable declaration is with initial value, and without explicit nullability annotation, the nullability of the variable is propagated from nullability of initial value.

NSNumber *x = @1;  // x is _Nonnull
NSNumber * _Nullable y = @3;  // y is _Nullable because the declaration has nullability annotation
NSNumber *z = y;  // z is _Nullable because y is _Nullable

This helps analyzing without nullability on variables. The following program would have warning on dictionary[key] without nullability propagation.

NSDictionary *dictionary;

NSNumber *key = condition ? @1 : @2;
NSString *value = dictionary[key];

The nullability propagation also would make extra warning however.

NSDictionary *dictionary;

NSString *name = @"initial value";
name = dictionary[@"name"];

The dictionary[@"name"] is _Nullable, and then name is assumed to be _Nonnull by nullability propagation, it would have an extra warning, which would not be reported without nullability propagation. For that case, you can explicitly declare variable with nullability.

NSDictionary *dictionary;

NSString * _Nullable name = @"initial value";
name = dictionary[@"name"];

self is _Nonnull, on RHS

self is assumed to be _Nonnull in Nullarihyon on right hand side. And on LHS, self is treated as _Nullable.

This assumption makes

  • Having _Nonnull returning method call _Nonnull
  • Assigning [super init] things to self without warning, even if it returns _Nullable

Assumption on class, alloc, and init

The following methods are assumed having _Nonnull return types, if no nullability is specified.

  • -[C init]
  • -[C class]
  • +[C alloc]
  • +[C class]

Loop variables are _Nonnull

Loop variables are assumed to be _Nonnull.

for (NSString *x in collection) {
  // x is _Nonnull
}

I believe this makes sense because most collections including NSArray and NSDictionary do not allow having nil. However, you can define custom collection which may contain nil. For that, just add nullability annotation.

for (NSString * _Nullable x in collection) {
  // x is _Nullable
}

if and logical and

When condition clause of if is reference to variable, the variable is treated as _Nonnull in then clause of the if.

NSString * _Nullable x;

if (x) {
  // x is _Nonnull
} else {
  // x is _Nullable
}

// x is _Nullable

This rule also applies to logical and && expression.

NSNumber * _Nullable x;
NSString * _Nullable y;

if (x && y) {
  // x and y are _Nonnull
}

BOOL flag = x && [y isEqualToString:[x stringValue]];   // [x stringValue] is _Nonnull

This makes a warning if you have assignment in then clause of if.

NSNumber * _Nullable x;
NSNumber * _Nullable y;

if (x) {
  x = y;  // Warning: x is _Nonnull here
}

Add cast for that case.

Casting

Using Nullarihyon would make programmers to write many casts. Nullarihyon takes responsibility for that situation.

It checks

  • If the cast is not incorrect
  • If the cast is required

Feel free to write casts if you need.

Incorrect casts

It also checks casts and report warnings when:

  • The cast is to _Nonnull type, and
  • The cast changes type
NSString * _Nullable a;

NSString * _Nonnull x = (NSString * _Nonnull)a;  // This is ok
NSObject * _Nonnull y = (NSObject * _Nonnull)a;  // Warning: changes type from NSString to NSObject
NSObject *z = (NSObject *)a;                     // This is ok; not a cast to _Nonnull

The exception is when source type is id.

id _Nullable a;

NSString * _Nonnull x = (NSString * _Nonnull)a;  // This is ok

Unnecessary casts

Nullarihyon also checks unnecessary casts. This is introduced by changing method type to return from _Nullable to _Nonnull.

NSString *x = [YouClass someMethod];  // Assume someMethod returns _Nullable

// Have to write cast because nullability of x is propagated as _Nullable
NSString * _Nonnull y = (NSString * _Nonnull)x;

However, you will change the type of someMethod to _Nonnull. And then nullability of x will be _Nonnull. Now, the cast to NSString * _Nonnull is unnecessary. Nullarihyon will report warning for that.

Casts are unnecessary when

  • The destination type is _Nonnull
  • The source type is _Nonnull
  • It does not change other than nullability

and they will be reported.