From 05fcbd3dc28f4cba4a6d33e7aeaabb5f6f7837e3 Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Thu, 30 Aug 2012 19:40:52 +0000 Subject: [PATCH] [analyzer] Do not propagate the [super init] could be nil assumption from callee to caller. radar://12109638 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162935 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Core/PathSensitive/CheckerContext.h | 5 +++ .../Checkers/BasicObjCFoundationChecks.cpp | 40 ++++++++++++++---- .../assume-super-init-does-not-return-nil.m | 41 +++++++++++++++++++ 3 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 test/Analysis/inlining/assume-super-init-does-not-return-nil.m diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 13bb3f6a90..9ba2f8c21a 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -100,6 +100,11 @@ public: return Pred->getStackFrame(); } + /// Returns true if the predecessor is within an inlined function/method. + bool isWithinInlined() { + return (getStackFrame() != 0); + } + BugReporter &getBugReporter() { return Eng.getBugReporter(); } diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index a09324ee6b..e5fbb832a9 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -718,8 +718,8 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, namespace { /// \class ObjCNonNilReturnValueChecker -/// \brief The checker restricts the return values of APIs known to never -/// return nil. +/// \brief The checker restricts the return values of APIs known to +/// never (or almost never) return 'nil'. class ObjCNonNilReturnValueChecker : public Checker { mutable bool Initialized; @@ -732,9 +732,18 @@ public: }; } +ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, + ProgramStateRef State, + CheckerContext &C) { + SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); + if (!isa(Val)) + return State; + return State->assume(cast(Val), true); +} + void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) - const { + const { ProgramStateRef State = C.getState(); if (!Initialized) { @@ -745,19 +754,34 @@ void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, // Check the receiver type. if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { + + // Assume that object returned from '[self init]' or '[super init]' is not + // 'nil' if we are processing an inlined function/method. + // + // A defensive callee will (and should) check if the object returned by + // '[super init]' is 'nil' before doing it's own initialization. However, + // since 'nil' is rarely returned in practice, we should not warn when the + // caller to the defensive constructor uses the object in contexts where + // 'nil' is not accepted. + if (C.isWithinInlined() && + M.getDecl()->getMethodFamily() == OMF_init && + M.isReceiverSelfOrSuper()) { + State = assumeExprIsNonNull(M.getOriginExpr(), State, C); + } + + // Objects returned from + // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] + // are never 'nil'. FoundationClass Cl = findKnownClass(Interface); if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { Selector Sel = M.getSelector(); if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { // Go ahead and assume the value is non-nil. - SVal Val = State->getSVal(M.getOriginExpr(), C.getLocationContext()); - if (!isa(Val)) - return; - State = State->assume(cast(Val), true); - C.addTransition(State); + State = assumeExprIsNonNull(M.getOriginExpr(), State, C); } } } + C.addTransition(State); } //===----------------------------------------------------------------------===// diff --git a/test/Analysis/inlining/assume-super-init-does-not-return-nil.m b/test/Analysis/inlining/assume-super-init-does-not-return-nil.m new file mode 100644 index 0000000000..d17835e1fa --- /dev/null +++ b/test/Analysis/inlining/assume-super-init-does-not-return-nil.m @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -analyze -analyzer-ipa=dynamic-bifurcate -analyzer-checker=core,osx -verify %s + +typedef signed char BOOL; + +@protocol NSObject - (BOOL)isEqual:(id)object; @end +@interface NSObject {} ++(id)alloc; ++(id)new; +-(id)init; +-(id)autorelease; +-(id)copy; +- (Class)class; +-(id)retain; +- (oneway void)release; +@end + +@interface Cell : NSObject { + int x; +} +- (id) init; +- (void)test; +@end + +@implementation Cell +- (id) init { + if ((self = [super init])) { + ; + } + // Test that this is being analyzed. + int m; + m = m + 1; //expected-warning {{The left operand of '+' is a garbage value}} + return self; +} + +// Make sure that we do not propagate the 'nil' check from inlined 'init' to 'test'. +- (void) test { + Cell *newCell = [[Cell alloc] init]; + newCell->x = 5; // no-warning + [newCell release]; +} +@end -- 2.50.1