]> granicus.if.org Git - clang/commitdiff
[analyzer] Check for return of nil in ObjC methods with nonnull return type.
authorDevin Coughlin <dcoughlin@apple.com>
Fri, 15 Jan 2016 21:35:40 +0000 (21:35 +0000)
committerDevin Coughlin <dcoughlin@apple.com>
Fri, 15 Jan 2016 21:35:40 +0000 (21:35 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@257938 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
test/Analysis/nullability_nullonly.mm

index bb86ea401df508a889d22eef9ec85d9b23d7b771..01c7287c97f454e1fad2c28447f01d32b72c0593 100644 (file)
@@ -366,24 +366,20 @@ static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N,
   if (!D)
     return false;
 
-  if (const auto *BlockD = dyn_cast<BlockDecl>(D)) {
-    if (checkParamsForPreconditionViolation(BlockD->parameters(), State,
-                                            LocCtxt)) {
-      if (!N->isSink())
-        C.addTransition(State->set<PreconditionViolated>(true), N);
-      return true;
-    }
+  ArrayRef<ParmVarDecl*> Params;
+  if (const auto *BD = dyn_cast<BlockDecl>(D))
+    Params = BD->parameters();
+  else if (const auto *FD = dyn_cast<FunctionDecl>(D))
+    Params = FD->parameters();
+  else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+    Params = MD->parameters();
+  else
     return false;
-  }
 
-  if (const auto *FuncDecl = dyn_cast<FunctionDecl>(D)) {
-    if (checkParamsForPreconditionViolation(FuncDecl->parameters(), State,
-                                            LocCtxt)) {
-      if (!N->isSink())
-        C.addTransition(State->set<PreconditionViolated>(true), N);
-      return true;
-    }
-    return false;
+  if (checkParamsForPreconditionViolation(Params, State, LocCtxt)) {
+    if (!N->isSink())
+      C.addTransition(State->set<PreconditionViolated>(true), N);
+    return true;
   }
   return false;
 }
@@ -484,16 +480,20 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
   if (!RetSVal)
     return;
 
+  QualType RequiredRetType;
   AnalysisDeclContext *DeclCtxt =
       C.getLocationContext()->getAnalysisDeclContext();
-  const FunctionType *FuncType = DeclCtxt->getDecl()->getFunctionType();
-  if (!FuncType)
+  const Decl *D = DeclCtxt->getDecl();
+  if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
+    RequiredRetType = MD->getReturnType();
+  else if (auto *FD = dyn_cast<FunctionDecl>(D))
+    RequiredRetType = FD->getReturnType();
+  else
     return;
 
   NullConstraint Nullness = getNullConstraint(*RetSVal, State);
 
-  Nullability RequiredNullability =
-      getNullabilityAnnotation(FuncType->getReturnType());
+  Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType);
 
   // If the returned value is null but the type of the expression
   // generating it is nonnull then we will suppress the diagnostic.
index 56b3f9e14490250c83e3442366ccbd0a5e01868a..6479d67bda61ccf4a2f7d1cb424e343cbeccce58 100644 (file)
@@ -1,5 +1,21 @@
 // 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();
 
 typedef struct Dummy { int val; } Dummy;
@@ -85,3 +101,27 @@ Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) {
     takesNonnull(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