[analyzer] Check for +raise:format: on subclasses of NSException as well.

We don't handle exceptions yet, so we treat them as sinks. ExprEngine
hardcodes messages that are known to raise Objective-C exceptions like -raise,
but it was only checking for +raise:format: and +raise:format:arguments: on
NSException itself, not subclasses.

<rdar://problem/11724201>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159010 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 45ba044..be9a26b 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -132,6 +132,14 @@
   getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
 }
 
+static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) {
+  if (!Class)
+    return false;
+  if (Class->getIdentifier() == II)
+    return true;
+  return isSubclass(Class->getSuperClass(), II);
+}
+
 void ExprEngine::VisitObjCMessage(const ObjCMessage &msg,
                                   ExplodedNode *Pred,
                                   ExplodedNodeSet &Dst) {
@@ -177,47 +185,50 @@
         // Dispatch to plug-in transfer function.
         evalObjCMessage(Bldr, msg, Pred, notNilState, RaisesException);
       }
-    }
-    else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
-      IdentifierInfo* ClsName = Iface->getIdentifier();
-      Selector S = msg.getSelector();
-      
-      // Check for special instance methods.
-      if (!NSExceptionII) {
-        ASTContext &Ctx = getContext();
-        NSExceptionII = &Ctx.Idents.get("NSException");
-      }
-      
-      if (ClsName == NSExceptionII) {
-        enum { NUM_RAISE_SELECTORS = 2 };
-        
-        // Lazily create a cache of the selectors.
-        if (!NSExceptionInstanceRaiseSelectors) {
+    } else if (const ObjCInterfaceDecl *Iface = msg.getReceiverInterface()) {
+      // Note that this branch also handles messages to super, not just
+      // class methods!
+
+      // Check for special class methods.
+      if (!msg.isInstanceMessage()) {
+        if (!NSExceptionII) {
           ASTContext &Ctx = getContext();
-          NSExceptionInstanceRaiseSelectors =
-          new Selector[NUM_RAISE_SELECTORS];
-          SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
-          unsigned idx = 0;
-          
-          // raise:format:
-          II.push_back(&Ctx.Idents.get("raise"));
-          II.push_back(&Ctx.Idents.get("format"));
-          NSExceptionInstanceRaiseSelectors[idx++] =
-          Ctx.Selectors.getSelector(II.size(), &II[0]);
-          
-          // raise:format::arguments:
-          II.push_back(&Ctx.Idents.get("arguments"));
-          NSExceptionInstanceRaiseSelectors[idx++] =
-          Ctx.Selectors.getSelector(II.size(), &II[0]);
+          NSExceptionII = &Ctx.Idents.get("NSException");
         }
         
-        for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i)
-          if (S == NSExceptionInstanceRaiseSelectors[i]) {
-            RaisesException = true;
-            break;
+        if (isSubclass(Iface, NSExceptionII)) {
+          enum { NUM_RAISE_SELECTORS = 2 };
+          
+          // Lazily create a cache of the selectors.
+          if (!NSExceptionInstanceRaiseSelectors) {
+            ASTContext &Ctx = getContext();
+            NSExceptionInstanceRaiseSelectors =
+              new Selector[NUM_RAISE_SELECTORS];
+            SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II;
+            unsigned idx = 0;
+            
+            // raise:format:
+            II.push_back(&Ctx.Idents.get("raise"));
+            II.push_back(&Ctx.Idents.get("format"));
+            NSExceptionInstanceRaiseSelectors[idx++] =
+              Ctx.Selectors.getSelector(II.size(), &II[0]);
+            
+            // raise:format:arguments:
+            II.push_back(&Ctx.Idents.get("arguments"));
+            NSExceptionInstanceRaiseSelectors[idx++] =
+              Ctx.Selectors.getSelector(II.size(), &II[0]);
           }
+          
+          Selector S = msg.getSelector();
+          for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) {
+            if (S == NSExceptionInstanceRaiseSelectors[i]) {
+              RaisesException = true;
+              break;
+            }
+          }
+        }
       }
-      
+
       // If we raise an exception, for now treat it as a sink.
       // Eventually we will want to handle exceptions properly.
       // Dispatch to plug-in transfer function.
diff --git a/test/Analysis/NoReturn.m b/test/Analysis/NoReturn.m
index ea2efd0..1d948fa 100644
--- a/test/Analysis/NoReturn.m
+++ b/test/Analysis/NoReturn.m
@@ -76,3 +76,15 @@
   return *x; // no-warning
 }
 
+
+@interface CustomException : NSException
+@end
+
+int testCustomException(int *x) {
+  if (x != 0) return 0;
+
+  [CustomException raise:@"Blah" format:@"abc"];
+
+  return *x; // no-warning
+}
+