51

I keep getting Clang errors on the following type of code and I can't figure out why they're erroneous or how to resolve them to Clang's satisfaction:

+ (NSString *)checkForLength: (NSString *)theString error: (NSError **)error {
    BOOL hasLength = ([theString length] > 0);
    if (hasLength) return theString;
    else {
        *error = [NSError errorWithDomain:@"ErrorDomain" code:hasLength userInfo:nil];
        return nil;
    }
}

Leaving aside the utterly-contrived nature of the example (which Clang did object to so it's illustrative enough), Clang balks at the error assignment line with the following objection:

Potential null dereference. According to coding standards in 'Creating and Returning NSError Objects' the parameter 'error' may be null.

I like having a pristine Clang report. I've read the cited document and I can't see a way to do what's expected; I checked some open-source Cocoa libraries and this seems to be a common idiom. Any ideas?

Constantino Tsarouhas
  • 6,846
  • 6
  • 43
  • 54
bbrown
  • 6,370
  • 5
  • 37
  • 43

2 Answers2

103

The way to do what's expected is shown in listing 3-5 in that document. With your example code:

+ (NSString *)checkForLength: (NSString *)theString error: (NSError **)error {
    BOOL hasLength = ([theString length] > 0);
    if (hasLength) return theString;
    else {
        if (error != NULL) *error = [NSError errorWithDomain:@"ErrorDomain" code:hasLength userInfo:nil];
        return nil;
    }
}
Constantino Tsarouhas
  • 6,846
  • 6
  • 43
  • 54
Daniel Martin
  • 23,083
  • 6
  • 50
  • 70
  • 1
    Ugh, I can't believe I missed that. Thanks! – bbrown Jul 27 '09 at 22:37
  • I used a million times `if (error) *error = …` instead, without any crashes or analyser errors/warnings. Can I keep going this way? – Constantino Tsarouhas Aug 18 '11 at 09:27
  • Yes, the explicit equivalence of a null pointer to 0 and false is something objective C kept from plain old C. I personally think it's bad style, but yeah you can simply use `if (error)` if you really want to. – Daniel Martin Oct 09 '11 at 17:30
  • NSError * error = [NSError errorWithDomain:@"Profile" code:100 userInfo:dictionary]; if (error) { *perror = error; } I'm still getting a static analyzer warning on that *perror = error line – quantumpotato Nov 20 '14 at 23:39
  • Well why on earth are you doing that? If the output parameter is `perror` then the variable in the `if` guard needs to be `perror` as well, as in: NSError * error = [NSError errorWithDomain:@"Profile" code:100 userInfo:dictionary]; if (perror) { *perror = error; } But I don't understand why you're introducing a new variable `error` here; just set it inside the `if`. – Daniel Martin Nov 27 '14 at 14:10
  • I don't understand why we have to check `error` *isn't* nil instead of *is* nil... Surely a null pointer is being sent into the method? – jowie Mar 02 '15 at 17:09
  • Sorry, I get it now... It has to check in case the object being passed in is just nil. Nothing to see here, move along... – jowie Mar 02 '15 at 17:29
17

The Cocoa convention is that the return value should indicate success or failure (in this case, you return nil for failure) and the error is filled in with additional information, but only when the caller requests it.

In other words

NSError *error = nil;
NSString *result = [self checkForLength: aString error: &error];

and

NSString *result = [self checkForLength: aString error: NULL];

are both valid ways to invoke the method. So the method body should always check for a NULL error param:

if (error != NULL)
    *error = ...;
Jim Correia
  • 7,064
  • 1
  • 33
  • 24
  • Actually, if the clang static analyser doesn't complain about your second snippit, I'd file a bug :-) –  Jul 27 '09 at 18:29
  • Are you referring to the snippet where NULL is passed for the error param? This is a documented pattern for using NSError. See . – Jim Correia Jul 27 '09 at 20:53
  • If I could've marked two answers, I would have. Thanks for explaining the context in which it might happen! – bbrown Jul 27 '09 at 22:38
  • 3
    By the way, is it valid to pass in nil instead of NULL? Both values are equal to (void *)0 — that's at least what preprocessed Objective-C files say. – Constantino Tsarouhas Aug 18 '11 at 09:23