mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-03 19:32:35 +00:00
[analyzer] Add osx.cocoa.NonNilReturnValue checker.
The checker adds assumptions that the return values from the known APIs are non-nil. Teach the checker about NSArray/NSMutableArray/NSOrderedSet objectAtIndex, objectAtIndexedSubscript. llvm-svn: 162398
This commit is contained in:
parent
26d3b2a272
commit
5a5a1755f2
@ -716,6 +716,47 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// \class ObjCNonNilReturnValueChecker
|
||||
/// \brief The checker restricts the return values of APIs known to never
|
||||
/// return nil.
|
||||
class ObjCNonNilReturnValueChecker
|
||||
: public Checker<check::PostObjCMessage> {
|
||||
mutable bool Initialized;
|
||||
mutable Selector ObjectAtIndex;
|
||||
mutable Selector ObjectAtIndexedSubscript;
|
||||
public:
|
||||
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
|
||||
};
|
||||
}
|
||||
|
||||
void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
|
||||
CheckerContext &C)
|
||||
const {
|
||||
ProgramStateRef State = C.getState();
|
||||
|
||||
if (!Initialized) {
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
|
||||
ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
|
||||
}
|
||||
|
||||
// Check the receiver type.
|
||||
if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
|
||||
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<DefinedOrUnknownSVal>(Val))
|
||||
return;
|
||||
State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
|
||||
C.addTransition(State);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Check registration.
|
||||
@ -744,3 +785,7 @@ void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
|
||||
void ento::registerObjCLoopChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<ObjCLoopChecker>();
|
||||
}
|
||||
|
||||
void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
|
||||
mgr.registerChecker<ObjCNonNilReturnValueChecker>();
|
||||
}
|
||||
|
@ -397,6 +397,10 @@ def ObjCLoopChecker : Checker<"Loops">,
|
||||
HelpText<"Improved modeling of loops using Cocoa collection types">,
|
||||
DescFile<"BasicObjCFoundationChecks.cpp">;
|
||||
|
||||
def ObjCNonNilReturnValueChecker : Checker<"NonNilReturnValue">,
|
||||
HelpText<"Model the APIs that are guaranteed to return a non-nil value">,
|
||||
DescFile<"BasicObjCFoundationChecks.cpp">;
|
||||
|
||||
def NSErrorChecker : Checker<"NSError">,
|
||||
HelpText<"Check usage of NSError** parameters">,
|
||||
DescFile<"NSErrorChecker.cpp">;
|
||||
|
50
clang/test/Analysis/test-objc-non-nil-return-value-checker.m
Normal file
50
clang/test/Analysis/test-objc-non-nil-return-value-checker.m
Normal file
@ -0,0 +1,50 @@
|
||||
// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.NonNilReturnValue,debug.ExprInspection -verify %s
|
||||
|
||||
typedef unsigned int NSUInteger;
|
||||
typedef signed char BOOL;
|
||||
|
||||
@protocol NSObject - (BOOL)isEqual:(id)object; @end
|
||||
|
||||
@interface NSObject <NSObject> {}
|
||||
+(id)alloc;
|
||||
+(id)new;
|
||||
-(id)init;
|
||||
-(id)autorelease;
|
||||
-(id)copy;
|
||||
- (Class)class;
|
||||
-(id)retain;
|
||||
@end
|
||||
|
||||
@interface NSArray : NSObject
|
||||
- (id)objectAtIndex:(unsigned long)index;
|
||||
@end
|
||||
|
||||
@interface NSArray (NSExtendedArray)
|
||||
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
|
||||
@end
|
||||
|
||||
@interface NSMutableArray : NSArray
|
||||
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
|
||||
@end
|
||||
|
||||
@interface NSOrderedSet : NSObject
|
||||
@end
|
||||
@interface NSOrderedSet (NSOrderedSetCreation)
|
||||
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
|
||||
@end
|
||||
|
||||
void clang_analyzer_eval(id);
|
||||
|
||||
void assumeThatNSArrayObjectAtIndexIsNeverNull(NSArray *A, NSUInteger i) {
|
||||
clang_analyzer_eval([A objectAtIndex: i]); // expected-warning {{TRUE}}
|
||||
id subscriptObj = A[1];
|
||||
clang_analyzer_eval(subscriptObj); // expected-warning {{TRUE}}
|
||||
}
|
||||
|
||||
void assumeThatNSMutableArrayObjectAtIndexIsNeverNull(NSMutableArray *A, NSUInteger i) {
|
||||
clang_analyzer_eval([A objectAtIndex: i]); // expected-warning {{TRUE}}
|
||||
}
|
||||
|
||||
void assumeThatNSArrayObjectAtIndexedSubscriptIsNeverNull(NSOrderedSet *A, NSUInteger i) {
|
||||
clang_analyzer_eval(A[i]); // expected-warning {{TRUE}}
|
||||
}
|
Loading…
Reference in New Issue
Block a user