retain/release checker: When determining whether an analyzed method can return

an owned object, consult its summary instead of inspecting the selector. This
picks up annotations, and is just more general.

llvm-svn: 70429
This commit is contained in:
Ted Kremenek 2009-04-29 23:03:22 +00:00
parent f4031bd01e
commit 223a7d5445
2 changed files with 49 additions and 31 deletions

View File

@ -149,9 +149,14 @@ static bool followsFundamentalRule(const char* s) {
return deriveNamingConvention(s) == CreateRule;
}
static bool followsReturnRule(const char* s) {
NamingConvention C = deriveNamingConvention(s);
return C == CreateRule || C == InitRule;
static const ObjCMethodDecl*
ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD, ASTContext &Context) {
ObjCInterfaceDecl *ID =
const_cast<ObjCInterfaceDecl*>(MD->getClassInterface());
return MD->isInstanceMethod()
? ID->lookupInstanceMethod(Context, MD->getSelector())
: ID->lookupClassMethod(Context, MD->getSelector());
}
//===----------------------------------------------------------------------===//
@ -269,6 +274,10 @@ public:
return index;
}
bool isOwned() const {
return K == OwnedSymbol || K == OwnedAllocatedSymbol;
}
static RetEffect MakeAlias(unsigned Idx) {
return RetEffect(Alias, Idx);
}
@ -399,7 +408,7 @@ public:
ObjCSummaryKey(IdentifierInfo* ii, Selector s)
: II(ii), S(s) {}
ObjCSummaryKey(ObjCInterfaceDecl* d, Selector s)
ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s)
: II(d ? d->getIdentifier() : 0), S(s) {}
ObjCSummaryKey(Selector s)
@ -452,14 +461,14 @@ public:
typedef MapTy::iterator iterator;
iterator find(ObjCInterfaceDecl* D, IdentifierInfo *ClsName, Selector S) {
iterator find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName,
Selector S) {
// Lookup the method using the decl for the class @interface. If we
// have no decl, lookup using the class name.
return D ? find(D, S) : find(ClsName, S);
}
iterator find(ObjCInterfaceDecl* D, Selector S) {
iterator find(const ObjCInterfaceDecl* D, Selector S) {
// Do a lookup with the (D,S) pair. If we find a match return
// the iterator.
ObjCSummaryKey K(D, S);
@ -730,18 +739,21 @@ public:
RetainSummary* getSummary(FunctionDecl* FD);
RetainSummary* getInstanceMethodSummary(ObjCMessageExpr* ME, ObjCInterfaceDecl* ID) {
RetainSummary* getInstanceMethodSummary(ObjCMessageExpr* ME,
const ObjCInterfaceDecl* ID) {
return getInstanceMethodSummary(ME->getSelector(), ME->getClassName(),
ID, ME->getMethodDecl(), ME->getType());
}
RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName,
ObjCInterfaceDecl* ID,
ObjCMethodDecl *MD, QualType RetTy);
const ObjCInterfaceDecl* ID,
const ObjCMethodDecl *MD,
QualType RetTy);
RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
ObjCInterfaceDecl *ID,
ObjCMethodDecl *MD, QualType RetTy);
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy);
RetainSummary *getClassMethodSummary(ObjCMessageExpr *ME) {
return getClassMethodSummary(ME->getSelector(), ME->getClassName(),
@ -751,9 +763,12 @@ public:
/// getMethodSummary - This version of getMethodSummary is used to query
/// the summary for the current method being analyzed.
RetainSummary *getMethodSummary(ObjCMethodDecl *MD) {
RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
// FIXME: Eventually this should be unneeded.
MD = ResolveToInterfaceMethodDecl(MD, Ctx);
Selector S = MD->getSelector();
ObjCInterfaceDecl *ID = MD->getClassInterface();
const ObjCInterfaceDecl *ID = MD->getClassInterface();
IdentifierInfo *ClsName = ID->getIdentifier();
QualType ResultTy = MD->getResultType();
@ -763,9 +778,10 @@ public:
return getClassMethodSummary(S, ClsName, ID, MD, ResultTy);
}
RetainSummary* getCommonMethodSummary(ObjCMethodDecl* MD, Selector S,
QualType RetTy);
RetainSummary* getMethodSummaryFromAnnotations(ObjCMethodDecl *MD);
RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD,
Selector S, QualType RetTy);
RetainSummary* getMethodSummaryFromAnnotations(const ObjCMethodDecl *MD);
bool isGCEnabled() const { return GCEnabled; }
};
@ -1113,7 +1129,7 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
}
RetainSummary*
RetainSummaryManager::getMethodSummaryFromAnnotations(ObjCMethodDecl *MD) {
RetainSummaryManager::getMethodSummaryFromAnnotations(const ObjCMethodDecl *MD){
if (!MD)
return 0;
@ -1169,8 +1185,8 @@ RetainSummaryManager::getMethodSummaryFromAnnotations(ObjCMethodDecl *MD) {
}
RetainSummary*
RetainSummaryManager::getCommonMethodSummary(ObjCMethodDecl* MD, Selector S,
QualType RetTy) {
RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,
Selector S, QualType RetTy) {
if (MD) {
// Scan the method decl for 'void*' arguments. These should be treated
@ -1224,8 +1240,8 @@ RetainSummaryManager::getCommonMethodSummary(ObjCMethodDecl* MD, Selector S,
RetainSummary*
RetainSummaryManager::getInstanceMethodSummary(Selector S,
IdentifierInfo *ClsName,
ObjCInterfaceDecl* ID,
ObjCMethodDecl *MD,
const ObjCInterfaceDecl* ID,
const ObjCMethodDecl *MD,
QualType RetTy) {
// Look up a summary in our summary cache.
@ -1255,8 +1271,9 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,
RetainSummary*
RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName,
ObjCInterfaceDecl *ID,
ObjCMethodDecl *MD, QualType RetTy){
const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy) {
assert(ClsName && "Class name must be specified.");
ObjCMethodSummariesTy::iterator I =
@ -2448,7 +2465,7 @@ CFRefLeakReport::getEndPath(BugReporter& br, const ExplodedNode<GRState>* EndN){
// to the caller for NS objects.
ObjCMethodDecl& MD = cast<ObjCMethodDecl>(BR.getGraph().getCodeDecl());
os << " is returned from a method whose name ('"
<< MD.getSelector().getAsString()
<< MD.getSelector().getAsString()
<< "') does not contain 'copy' or otherwise starts with"
" 'new' or 'alloc'. This violates the naming convention rules given"
" in the Memory Management Guide for Cocoa (object leaked)";
@ -2992,13 +3009,11 @@ void CFRefCount::EvalReturn(ExplodedNodeSet<GRState>& Dst,
if (X.isReturnedOwned() && X.getCount() == 0) {
const Decl *CD = &Eng.getStateManager().getCodeDecl();
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
std::string s = MD->getSelector().getAsString();
// FIXME: Use method summary.
if (!followsReturnRule(s.c_str())) {
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
RetainSummary *Summ = Summaries.getMethodSummary(MD);
if (!GetRetEffect(Summ).isOwned()) {
static int ReturnOwnLeakTag = 0;
state = state.set<RefBindings>(Sym, X ^ RefVal::ErrorLeakReturned);
// Generate an error node.
ExplodedNode<GRState> *N =
Builder.generateNode(PostStmt(S, &ReturnOwnLeakTag), state, Pred);

View File

@ -440,13 +440,16 @@ void rdar6704930(unsigned char *s, unsigned int length) {
@interface TestAttrHelper : NSObject
- (NSString*) createString:(TestOwnershipAttr*)X;
- (NSString*) createStringAttr:(TestOwnershipAttr*)X __attribute__((objc_ownership_returns));
@end
@implementation TestAttrHelper
- (NSString*) createString:(TestOwnershipAttr*)X {
return [X returnsAnOwnedString]; // expected-warning{{leak}}
}
- (NSString*) createStringAttr:(TestOwnershipAttr*)X {
return [X returnsAnOwnedString]; // no-warning
}
@end
void test_attr_1(TestOwnershipAttr *X) {