views:

194

answers:

1

It doesn't care about this:

NSString* leaker()
{
 return [[NSString alloc] init];
}

I thought it would have been smart enough to check if any code paths could call that function without releasing its return value (I wouldn't normally code this way, I'm just testing the analyzer).

It reports this as a leak:

NSString* leaker()
{
 NSString* s = [[NSString alloc] init];
 [s retain];
 return s;
}

but NOT this:

NSString* leaker()
{
 NSString* s = [[NSString alloc] init];
// [s retain];
 return s;
}

which seems particularly weak to me. Does it only analyze within the local scope? If the tool can't pick up on things like this, how can I expect it to pick up on actual mistakes that I might make?

+3  A: 

clang does not perform any inter-procedure analysis, at least not yet. Even if it did, it might not necessarily catch this "error"- the permutations of potential code paths tends to rise super exponentially, making it a practical impossibility.

clang works with a set of heuristics that work "most of the time". Thankfully, Cocoa memory management rules tend to be fairly uniform, so the heuristics work for most uses. The specific example that you've given isn't really covered by the memory management rules, but I think most people (which includes myself) would tend to classify your example as "You've documented via the API that the caller of leaker() is responsible for releaseing the returned object". This is essentially analogous to - (NSString *)init... style methods.

clang knows that methods that begin with init... return an 'unreleased' object, and it is the callers responsibility to ensure that it is properly released. This forms part of the core of the heuristics- it doesn't need whole program or inter-procedural analysis to do the bulk of reference count checking- if a local block of code gets an object via an init... method, that local block of code needs to make sure it is properly released. Naturally, if the local block of code, and the object in question, is part of an init... method itself, it is covered by the same "rule", so it gets an exception.

What you probably want is something like:

NSString* leaker() __attribute__((ns_returns_retained))
{
 return [[NSString alloc] init];
}

This lets the analyzer know that leaker() returns a 'retained' object, that the caller is responsible for properly releasing it. Although I haven't tested this, I strongly suspect that the 'leak' would be detected at the point where leaker() is called, i.e.:

void test(void)
{
  NSString *leaked = leaker();
  // Previous line should be caught as a "leak" by clang.
}

This is one of the unfortunate limitations of any static analyzer, not just clang.

johne