In Objective-C, method calls with nil receivers are essentially no-ops. They do not fault (although the returned value may be garbage depending on the declared return type and architecture). Programmers are aware of this behavior and will complain about a false alarm when the analyzer diagnoses API violations for method calls when the receiver is known to be nil. Rather than require each individual checker to be aware of this behavior and suppress a warning when the receiver is nil, this commit changes ExprEngineObjC so that VisitObjCMessage skips calling checker pre/post handlers when the receiver is definitely nil. Instead, it adds a new event, ObjCMessageNil, that is only called in that case. The CallAndMessageChecker explicitly cares about this case, so I've changed it to add a callback for ObjCMessageNil and moved the logic in PreObjCMessage that handles nil receivers to the new callback. rdar://problem/18092611 Differential Revision: http://reviews.llvm.org/D12123 llvm-svn: 247653
41 lines
1.3 KiB
Objective-C
41 lines
1.3 KiB
Objective-C
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s
|
|
|
|
extern void clang_analyzer_warnIfReached();
|
|
void clang_analyzer_eval(int);
|
|
|
|
@interface SomeClass
|
|
-(id)someMethodWithReturn;
|
|
-(void)someMethod;
|
|
@end
|
|
|
|
void consistencyOfReturnWithNilReceiver(SomeClass *o) {
|
|
id result = [o someMethodWithReturn];
|
|
if (result) {
|
|
if (!o) {
|
|
// It is impossible for both o to be nil and result to be non-nil,
|
|
// so this should not be reached.
|
|
clang_analyzer_warnIfReached(); // no-warning
|
|
}
|
|
}
|
|
}
|
|
|
|
void maybeNilReceiverIsNotNilAfterMessage(SomeClass *o) {
|
|
[o someMethod];
|
|
|
|
// We intentionally drop the nil flow (losing coverage) after a method
|
|
// call when the receiver may be nil in order to avoid inconsistencies of
|
|
// the kind tested for in consistencyOfReturnWithNilReceiver().
|
|
clang_analyzer_eval(o != 0); // expected-warning{{TRUE}}
|
|
}
|
|
|
|
void nilReceiverIsStillNilAfterMessage(SomeClass *o) {
|
|
if (o == 0) {
|
|
id result = [o someMethodWithReturn];
|
|
|
|
// Both the receiver and the result should be nil after a message
|
|
// sent to a nil receiver returning a value of type id.
|
|
clang_analyzer_eval(o == 0); // expected-warning{{TRUE}}
|
|
clang_analyzer_eval(result == 0); // expected-warning{{TRUE}}
|
|
}
|
|
}
|