nil check written as an assignment or with misplaced negation
ID |
objectivec.correctness.broken_nil_check |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Suspicious Code |
Language |
Objective-C |
Description
Reports two ways a nil check is written wrongly in the condition of an if or a while: assigning nil instead of comparing to it (if (x = nil)), and negating the operand before the comparison runs (if (!x == nil)). The deliberate assign-and-then-compare idiom if ((x = [self lookup]) == nil) is not reported, and neither is a correct == nil, != nil or !x. This is about how the check is written; a check written correctly but placed after the pointer has already been used is reported by misplaced-nil-check.
Rationale
if (x = nil) stores nil into the variable and then tests that value, so the branch never runs and the object the caller passed in is destroyed on the way past. The compiler warning for it is easy to lose in a noisy build, and the line reads like a comparison to everyone who reviews it afterwards. if (!x == nil) is worse, because it compiles cleanly and the precedence is not what it looks like: ! binds tighter than ==, so the test is (!x) == nil, which asks whether the boolean 0 or 1 equals nil — it is true exactly when x is non-nil, the opposite of the intent, and the guard lets nil straight through.
The following code illustrates the pattern detected by this rule:
@implementation Loader
- (void)loadNamed:(NSString *)name {
// FLAGGED: nil check written as an assignment or with misplaced negation
if (name = nil) {
return;
}
[self load:name];
}
- (NSString *)lookup {
return nil;
}
- (void)load:(NSString *)name {
NSLog(@"%@", name);
}
@end
Remediation
Write if (x == nil) or the idiomatic if (!x) for the nil case, and if (x != nil) for the non-nil case. Where the assignment is deliberate, make it explicit by parenthesising it and comparing the result — if ((x = [self lookup]) == nil) — or better, split it into an assignment statement followed by the check on its own line.