[analyzer] Check for return of nil in ObjC methods with nonnull return type.

Update NullabilityChecker so that it checks return statements in ObjC methods.
Previously it was returning early because methods do not have a function type.

Also update detection of violated parameter _Nonnull preconditions to handle
ObjC methods.

rdar://problem/24200560

llvm-svn: 257938
This commit is contained in:
Devin Coughlin 2016-01-15 21:35:40 +00:00
parent 22c5af782a
commit 851da71c8f
2 changed files with 60 additions and 20 deletions

View File

@ -366,27 +366,23 @@ static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N,
if (!D) if (!D)
return false; return false;
if (const auto *BlockD = dyn_cast<BlockDecl>(D)) { ArrayRef<ParmVarDecl*> Params;
if (checkParamsForPreconditionViolation(BlockD->parameters(), State, if (const auto *BD = dyn_cast<BlockDecl>(D))
LocCtxt)) { Params = BD->parameters();
if (!N->isSink()) else if (const auto *FD = dyn_cast<FunctionDecl>(D))
C.addTransition(State->set<PreconditionViolated>(true), N); Params = FD->parameters();
return true; else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
} Params = MD->parameters();
else
return false; return false;
}
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(D)) { if (checkParamsForPreconditionViolation(Params, State, LocCtxt)) {
if (checkParamsForPreconditionViolation(FuncDecl->parameters(), State,
LocCtxt)) {
if (!N->isSink()) if (!N->isSink())
C.addTransition(State->set<PreconditionViolated>(true), N); C.addTransition(State->set<PreconditionViolated>(true), N);
return true; return true;
} }
return false; return false;
} }
return false;
}
void NullabilityChecker::reportBugIfPreconditionHolds( void NullabilityChecker::reportBugIfPreconditionHolds(
ErrorKind Error, ExplodedNode *N, const MemRegion *Region, ErrorKind Error, ExplodedNode *N, const MemRegion *Region,
@ -484,16 +480,20 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
if (!RetSVal) if (!RetSVal)
return; return;
QualType RequiredRetType;
AnalysisDeclContext *DeclCtxt = AnalysisDeclContext *DeclCtxt =
C.getLocationContext()->getAnalysisDeclContext(); C.getLocationContext()->getAnalysisDeclContext();
const FunctionType *FuncType = DeclCtxt->getDecl()->getFunctionType(); const Decl *D = DeclCtxt->getDecl();
if (!FuncType) if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
RequiredRetType = MD->getReturnType();
else if (auto *FD = dyn_cast<FunctionDecl>(D))
RequiredRetType = FD->getReturnType();
else
return; return;
NullConstraint Nullness = getNullConstraint(*RetSVal, State); NullConstraint Nullness = getNullConstraint(*RetSVal, State);
Nullability RequiredNullability = Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType);
getNullabilityAnnotation(FuncType->getReturnType());
// If the returned value is null but the type of the expression // If the returned value is null but the type of the expression
// generating it is nonnull then we will suppress the diagnostic. // generating it is nonnull then we will suppress the diagnostic.

View File

@ -1,5 +1,21 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -verify %s // RUN: %clang_cc1 -analyze -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -verify %s
#define nil 0
#define BOOL int
@protocol NSObject
+ (id)alloc;
- (id)init;
@end
@protocol NSCopying
@end
__attribute__((objc_root_class))
@interface
NSObject<NSObject>
@end
int getRandom(); int getRandom();
typedef struct Dummy { int val; } Dummy; typedef struct Dummy { int val; } Dummy;
@ -85,3 +101,27 @@ Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) {
takesNonnull(p); takesNonnull(p);
return p; return p;
} }
@interface SomeClass : NSObject
@end
@implementation SomeClass (MethodReturn)
- (SomeClass * _Nonnull)testReturnsNilInNonnull {
SomeClass *local = nil;
return local; // expected-warning {{Null is returned from a function that is expected to return a non-null value}}
}
- (SomeClass * _Nonnull)testReturnsCastSuppressedNilInNonnull {
SomeClass *local = nil;
return (SomeClass * _Nonnull)local; // no-warning
}
- (SomeClass * _Nonnull)testReturnsNilInNonnullWhenPreconditionViolated:(SomeClass * _Nonnull) p {
SomeClass *local = nil;
if (!p) // Pre-condition violated here.
return local; // no-warning
else
return p; // no-warning
}
@end