forked from OSchip/llvm-project
[analyzer] Nullability: Look through implicit casts when suppressing warnings on return.
In r256567 I changed the nullability checker to suppress warnings about returning a null value from a function/method with a non-null return type when the type of the returned expression is itself nonnull. This enables the programmer to silence nullability warnings by casting to _Nonnull: return (SomeObject * _Nonnull)nil; Unfortunately, under ObjC automated reference counting, Sema adds implicit casts to _Nonnull to return expressions of nullable or unspecified types in functions with non-null function/method return types. With r256567, these casts cause all nullability warnings for returns of reference-counted types to be suppressed under ARC, leading to false negatives. This commit updates the nullability checker to look through implicit casts before determining the type of the returned expression. It also updates the tests to turn on ARC for the nullability_nullonly.mm testfile and adds a new testfile to test when ARC is turned off. rdar://problem/24200117 llvm-svn: 258061
This commit is contained in:
parent
e03126aea4
commit
5a3843e506
|
@ -456,6 +456,20 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the outermost subexpression of E that is not an implicit cast.
|
||||||
|
/// This looks through the implicit casts to _Nonnull that ARC adds to
|
||||||
|
/// return expressions of ObjC types when the return type of the function or
|
||||||
|
/// method is non-null but the express is not.
|
||||||
|
static const Expr *lookThroughImplicitCasts(const Expr *E) {
|
||||||
|
assert(E);
|
||||||
|
|
||||||
|
while (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
|
||||||
|
E = ICE->getSubExpr();
|
||||||
|
}
|
||||||
|
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
|
||||||
/// This method check when nullable pointer or null value is returned from a
|
/// This method check when nullable pointer or null value is returned from a
|
||||||
/// function that has nonnull return type.
|
/// function that has nonnull return type.
|
||||||
///
|
///
|
||||||
|
@ -501,7 +515,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
|
||||||
// function with a _Nonnull return type:
|
// function with a _Nonnull return type:
|
||||||
// return (NSString * _Nonnull)0;
|
// return (NSString * _Nonnull)0;
|
||||||
Nullability RetExprTypeLevelNullability =
|
Nullability RetExprTypeLevelNullability =
|
||||||
getNullabilityAnnotation(RetExpr->getType());
|
getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType());
|
||||||
|
|
||||||
if (Filter.CheckNullReturnedFromNonnull &&
|
if (Filter.CheckNullReturnedFromNonnull &&
|
||||||
Nullness == NullConstraint::IsNull &&
|
Nullness == NullConstraint::IsNull &&
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
// RUN: %clang_cc1 -analyze -analyzer-checker=core,nullability -verify %s
|
||||||
|
|
||||||
|
#define nil 0
|
||||||
|
|
||||||
|
@protocol NSObject
|
||||||
|
+ (id)alloc;
|
||||||
|
- (id)init;
|
||||||
|
@end
|
||||||
|
|
||||||
|
__attribute__((objc_root_class))
|
||||||
|
@interface
|
||||||
|
NSObject<NSObject>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface TestObject : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceIndirectly() {
|
||||||
|
TestObject *local = 0;
|
||||||
|
return local; // expected-warning {{Null is returned from a function that is expected to return a non-null value}}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceIndirectlyWithSupressingCast() {
|
||||||
|
TestObject *local = 0;
|
||||||
|
return (TestObject * _Nonnull)local; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceDirectly() {
|
||||||
|
// The first warning is from Sema. The second is from the static analyzer.
|
||||||
|
return nil; // expected-warning {{null returned from function that requires a non-null return value}}
|
||||||
|
// expected-warning@-1 {{Null is returned from a function that is expected to return a non-null value}}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceDirectlyWithSuppressingCast() {
|
||||||
|
return (TestObject * _Nonnull)nil; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
void testObjCNonARCNoInitialization(TestObject * _Nonnull p) {
|
||||||
|
TestObject * _Nonnull implicitlyZeroInitialized; // no-warning
|
||||||
|
implicitlyZeroInitialized = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void testObjCNonARCExplicitZeroInitialization() {
|
||||||
|
TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}}
|
||||||
|
}
|
|
@ -120,12 +120,12 @@ void testArgumentTracking(Dummy *_Nonnull nonnull, Dummy *_Nullable nullable) {
|
||||||
|
|
||||||
Dummy *_Nonnull testNullableReturn(Dummy *_Nullable a) {
|
Dummy *_Nonnull testNullableReturn(Dummy *_Nullable a) {
|
||||||
Dummy *p = a;
|
Dummy *p = a;
|
||||||
return p; // expected-warning {{}}
|
return p; // expected-warning {{Nullable pointer is returned from a function that is expected to return a non-null value}}
|
||||||
}
|
}
|
||||||
|
|
||||||
Dummy *_Nonnull testNullReturn() {
|
Dummy *_Nonnull testNullReturn() {
|
||||||
Dummy *p = 0;
|
Dummy *p = 0;
|
||||||
return p; // expected-warning {{}}
|
return p; // expected-warning {{Null is returned from a function that is expected to return a non-null value}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testObjCMessageResultNullability() {
|
void testObjCMessageResultNullability() {
|
||||||
|
@ -279,11 +279,25 @@ Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void testObjCARCImplicitZeroInitialization() {
|
@interface SomeClass : NSObject
|
||||||
TestObject * _Nonnull implicitlyZeroInitialized; // no-warning
|
@end
|
||||||
implicitlyZeroInitialized = getNonnullTestObject();
|
|
||||||
|
@implementation SomeClass (MethodReturn)
|
||||||
|
- (TestObject * _Nonnull)testReturnsNullableInNonnullIndirectly {
|
||||||
|
TestObject *local = getNullableTestObject();
|
||||||
|
return local; // expected-warning {{Nullable pointer is returned from a function that is expected to return a non-null value}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testObjCARCExplicitZeroInitialization() {
|
- (TestObject * _Nonnull)testReturnsCastSuppressedNullableInNonnullIndirectly {
|
||||||
TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}}
|
TestObject *local = getNullableTestObject();
|
||||||
|
return (TestObject * _Nonnull)local; // no-warning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (TestObject * _Nonnull)testReturnsNullableInNonnullWhenPreconditionViolated:(TestObject * _Nonnull) p {
|
||||||
|
TestObject *local = getNullableTestObject();
|
||||||
|
if (!p) // Pre-condition violated here.
|
||||||
|
return local; // no-warning
|
||||||
|
else
|
||||||
|
return p; // no-warning
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// RUN: %clang_cc1 -analyze -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -verify %s
|
// RUN: %clang_cc1 -analyze -fobjc-arc -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -verify %s
|
||||||
|
|
||||||
#define nil 0
|
#define nil 0
|
||||||
#define BOOL int
|
#define BOOL int
|
||||||
|
@ -102,6 +102,40 @@ Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface TestObject : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
TestObject *_Nonnull getNonnullTestObject();
|
||||||
|
|
||||||
|
void testObjCARCImplicitZeroInitialization() {
|
||||||
|
TestObject * _Nonnull implicitlyZeroInitialized; // no-warning
|
||||||
|
implicitlyZeroInitialized = getNonnullTestObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
void testObjCARCExplicitZeroInitialization() {
|
||||||
|
TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Under ARC, returned expressions of ObjC objects types are are implicitly
|
||||||
|
// cast to _Nonnull when the functions return type is _Nonnull, so make
|
||||||
|
// sure this doesn't implicit cast doesn't suppress a legitimate warning.
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceIndirectly() {
|
||||||
|
TestObject *local = 0;
|
||||||
|
return local; // expected-warning {{Null is returned from a function that is expected to return a non-null value}}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceIndirectlyWithSupressingCast() {
|
||||||
|
TestObject *local = 0;
|
||||||
|
return (TestObject * _Nonnull)local; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceDirectly() {
|
||||||
|
return nil; // expected-warning {{Null is returned from a function that is expected to return a non-null value}}
|
||||||
|
}
|
||||||
|
|
||||||
|
TestObject * _Nonnull returnsNilObjCInstanceDirectlyWithSuppressingCast() {
|
||||||
|
return (TestObject * _Nonnull)nil; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
@interface SomeClass : NSObject
|
@interface SomeClass : NSObject
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue