Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt	(revision 191843)
+++ lib/StaticAnalyzer/Checkers/CMakeLists.txt	(working copy)
@@ -62,6 +62,7 @@
   StreamChecker.cpp
   TaintTesterChecker.cpp
   TraversalChecker.cpp
+  UnavailableMethodChecker.cpp
   UndefBranchChecker.cpp
   UndefCapturedBlockVarChecker.cpp
   UndefResultChecker.cpp
Index: lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp	(revision 191843)
+++ lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp	(working copy)
@@ -31,7 +31,8 @@
   : public Checker< check::PreStmt<CallExpr>,
                     check::PreStmt<CXXDeleteExpr>,
                     check::PreObjCMessage,
-                    check::PreCall > {
+                    check::PreCall,
+                    EventDispatcher<ImplicitNullDerefEvent> > {
   mutable OwningPtr<BugType> BT_call_null;
   mutable OwningPtr<BugType> BT_call_undef;
   mutable OwningPtr<BugType> BT_cxx_call_null;
@@ -226,7 +227,6 @@
 
 void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
                                          CheckerContext &C) const{
-
   const Expr *Callee = CE->getCallee()->IgnoreParens();
   ProgramStateRef State = C.getState();
   const LocationContext *LCtx = C.getLocationContext();
@@ -250,9 +250,16 @@
         new BuiltinBug("Called function pointer is null (null dereference)"));
     emitBadCall(BT_call_null.get(), C, Callee);
     return;
+  } else if (StNull && StNonNull) {
+    // Called functions are assumed to be non-null, so this is
+    // an implicit null dereference.
+    if (ExplodedNode *N = C.generateSink(StNull)) {
+        ImplicitNullDerefEvent Event = { L, false, N, &C.getBugReporter() };
+        dispatchEvent(Event);
+    }
   }
 
-  C.addTransition(StNonNull);
+  C.addTransition(StNonNull, this);
 }
 
 void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
Index: lib/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- lib/StaticAnalyzer/Checkers/Checkers.td	(revision 191843)
+++ lib/StaticAnalyzer/Checkers/Checkers.td	(working copy)
@@ -447,6 +447,10 @@
 def ObjCSuperCallChecker : Checker<"MissingSuperCall">,
   HelpText<"Warn about Objective-C methods that lack a necessary call to super">,
   DescFile<"ObjCMissingSuperCallChecker.cpp">;
+  
+def UnavailableMethodChecker : Checker<"UnavailableMethodChecker">,
+  HelpText<"Check for use of unavailable methods">,
+  DescFile<"UnavailableMethodChecker.cpp">;
 
 } // end "alpha.osx.cocoa"
 
Index: lib/StaticAnalyzer/Checkers/UnavailableMethodChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/UnavailableMethodChecker.cpp	(revision 0)
+++ lib/StaticAnalyzer/Checkers/UnavailableMethodChecker.cpp	(working copy)
@@ -0,0 +1,270 @@
+#include "ClangSACheckers.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "llvm/ADT/SmallString.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+  class UnavailableMethodChecker
+      : public Checker< check::PreCall,
+                        eval::Assume,
+                        check::Event<ImplicitNullDerefEvent> > {
+    mutable OwningPtr<BugType> BT;
+  public:
+    void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const;
+    ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+                               bool Assumption) const;
+    void printState(raw_ostream &Out, ProgramStateRef State,
+                    const char *NL, const char *Sep) const;
+    void checkEvent(ImplicitNullDerefEvent Event) const;
+
+  private:
+    void displayError(const ProgramStateRef &State, VersionTuple Introduced,
+                      ExplodedNode *N, BugReporter *BR) const;
+    const Decl *weakDeclForSymbol(SymbolRef Sym) const;
+    const Decl *weakDeclForRegion(const MemRegion *MR) const;
+    VersionTuple deploymentTarget(ProgramStateRef State) const;
+    VersionTuple versionIntroduced(const Decl *D) const;
+    VersionTuple versionCheckForSymbol(SymbolRef S, bool Assumption) const;
+    VersionTuple getCheckedForVersion(ProgramStateRef State) const;
+    ProgramStateRef setCheckedForVersion(ProgramStateRef State,
+                                         VersionTuple V) const;
+  };
+}
+
+REGISTER_TRAIT_WITH_PROGRAMSTATE(CheckedVersionMajor, unsigned)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(CheckedVersionMinor, unsigned)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(CheckedVersionSubminor, unsigned)
+
+void UnavailableMethodChecker::checkPreCall(const CallEvent &Call,
+                                            CheckerContext &Ctx) const {
+  // Bail early if there is no deployment target.
+  ProgramStateRef State = Ctx.getState();
+  VersionTuple TargetMinVersion = deploymentTarget(State);
+  if (TargetMinVersion.empty())
+    return;
+
+  if (!(isa<SimpleCall>(&Call) || isa<ObjCMethodCall>(&Call)))
+    return;
+
+  const Decl *D = Call.getDecl();
+  if (!D)
+    return;
+
+  VersionTuple Introduced = versionIntroduced(D);
+
+  // If we have an objc method, check for the classes introduced version.
+  if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+    VersionTuple ClassIntroduced;
+    const DeclContext *DC = MD->getDeclContext();
+    if (const ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(DC))
+      ClassIntroduced = versionIntroduced(PD);
+    else
+      ClassIntroduced = versionIntroduced(MD->getClassInterface());
+
+    Introduced = std::max(Introduced, ClassIntroduced);
+  }
+
+  if (TargetMinVersion >= Introduced)
+    return;
+
+  // We have a method being called that is available later than the minimum
+  // deployment target, so we need to check for any availability checks.
+  VersionTuple CheckedVersion = getCheckedForVersion(State);
+  if (CheckedVersion >= Introduced)
+    return;
+
+  if (ExplodedNode *N = Ctx.addTransition(State)) {
+    displayError(State, Introduced, N, &Ctx.getBugReporter());
+  }
+}
+
+ProgramStateRef UnavailableMethodChecker::evalAssume(ProgramStateRef State,
+                                                     SVal Cond,
+                                                     bool Assumption) const {
+  // Bail early if there is no deployment target set.
+  VersionTuple TargetMinVersion = deploymentTarget(State);
+  if (TargetMinVersion.empty())
+    return State;
+
+  SymbolRef S = Cond.getAsSymbol();
+  if (!S)
+    return State;
+
+  // Walk through the symbols children looking for a version check.
+  VersionTuple V = versionCheckForSymbol(S, Assumption);
+  return setCheckedForVersion(State, V);
+}
+
+void UnavailableMethodChecker::checkEvent(ImplicitNullDerefEvent Event) const {
+  if (Event.IsLoad)
+    return;
+
+  // Bail early if there is no deployment target.
+  ProgramStateRef State = Event.SinkNode->getState();
+  VersionTuple TargetMinVersion = deploymentTarget(State);
+  if (TargetMinVersion.empty())
+    return;
+
+  const MemRegion *MR = Event.Location.getAsRegion();
+  if (!MR)
+    return;
+
+  // If this is a weak function, it is not OK to derefence if availability
+  // has not been checked for.
+  if (const Decl *D = weakDeclForRegion(MR)) {
+    VersionTuple Introduced = versionIntroduced(D);
+    VersionTuple Checked = getCheckedForVersion(State);
+    if (Introduced > Checked)
+      displayError(State, Introduced, Event.SinkNode, Event.BR);
+  }
+}
+
+void UnavailableMethodChecker::printState(raw_ostream &Out,
+                                          ProgramStateRef State,
+                                          const char *NL,
+                                          const char *Sep) const {
+  VersionTuple CheckedVersion = getCheckedForVersion(State);
+  if (CheckedVersion > VersionTuple()) {
+    Out << Sep;
+    Out << "Checked availability: " << CheckedVersion.getAsString();
+  }
+}
+
+void UnavailableMethodChecker::displayError(const ProgramStateRef &State,
+                                            VersionTuple Introduced,
+                                            ExplodedNode *N,
+                                            BugReporter *BR) const {
+  if (!BT)
+    BT.reset(new BugType("API Unavailable", "API Misuse"));
+
+  SmallString<128> sbuf;
+  llvm::raw_svector_ostream os(sbuf);
+  os << "The method is introduced in ";
+  os << Introduced;
+  os << ", later than the deployment target ";
+  os << deploymentTarget(State);
+
+  BugReport *R = new BugReport(*BT, os.str(), N);
+  BR->emitReport(R);
+}
+
+VersionTuple
+UnavailableMethodChecker::deploymentTarget(ProgramStateRef State) const {
+  ProgramStateManager &PSM = State->getStateManager();
+  return PSM.getContext().getTargetInfo().getPlatformMinVersion();
+}
+
+VersionTuple UnavailableMethodChecker::versionIntroduced(const Decl *D) const {
+  if (const AvailabilityAttr *Availability = D->getAttr<AvailabilityAttr>())
+    return Availability->getIntroduced();
+
+  return VersionTuple();
+}
+
+const Decl* UnavailableMethodChecker::weakDeclForSymbol(SymbolRef Sym) const {
+  const SymbolExtent *SE = dyn_cast<SymbolExtent>(Sym);
+  if (!SE)
+    return NULL;
+
+  return weakDeclForRegion(SE->getRegion());
+}
+
+const Decl*
+UnavailableMethodChecker::weakDeclForRegion(const MemRegion *R) const {
+  if (!isa<FunctionTextRegion>(R))
+    return NULL;
+
+  const FunctionTextRegion *FTR = cast<FunctionTextRegion>(R);
+  const FunctionDecl *D = cast<FunctionDecl>(FTR->getDecl());
+  return D->isWeak() ? D : NULL;
+}
+
+VersionTuple
+UnavailableMethodChecker::versionCheckForSymbol(SymbolRef Sym,
+                                                bool Assumption) const {
+  // Iterate over the expressions symbols looking for version checks.
+  for (SymExpr::symbol_iterator I = Sym->symbol_begin(), E = Sym->symbol_end();
+       I != E; ++I) {
+    if (const SymIntExpr *SIExpr = dyn_cast<SymIntExpr>(*I)) {
+      // A symbol is being compared to an int value. The Assumption value
+      // is adjusted so that it is checking the symbol exists.
+      BinaryOperatorKind op = SIExpr->getOpcode();
+      bool rhs = SIExpr->getRHS() != 0;
+      if ((op == BO_EQ && rhs == false) || (op == BO_NE && rhs == true))
+        Assumption = !Assumption;
+    }
+
+    // We are only interested in checks for existence.
+    if (!Assumption)
+      break;
+
+    if (const SymbolExtent *SE = dyn_cast<SymbolExtent>(*I)) {
+      if (const Decl *D = weakDeclForSymbol(SE))
+        // We have a weakly linked function, return its introduced version.
+        return versionIntroduced(D);
+    }
+
+    else if (const SymbolConjured *SConj = dyn_cast<SymbolConjured>(*I)) {
+      const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(SConj->getStmt());
+      if (!ME)
+        continue;
+
+      // This is an objective C message, first get the class availability.
+      const ObjCInterfaceDecl *CD = ME->getReceiverInterface();
+      VersionTuple ClassVersion = versionIntroduced(CD);
+
+      // If this message is checking for availability, return the max of the
+      // class version and the checked for method version.
+      std::string SelStr = ME->getSelector().getAsString();
+      if (SelStr == "respondsToSelector:" ||
+          SelStr == "instancesRespondToSelector:") {
+        const ObjCSelectorExpr *SelExpr = cast<ObjCSelectorExpr>(ME->getArg(0));
+        Selector Sel = SelExpr->getSelector();
+        const ObjCMethodDecl *D = CD->lookupInstanceMethod(Sel);
+        if (!D)
+          D = CD->lookupClassMethod(Sel);
+
+        if (D)
+          return std::max(ClassVersion, versionIntroduced(D));
+      } else if (SelStr == "class") {
+        return ClassVersion;
+      }
+    }
+  }
+
+  return VersionTuple();
+}
+
+VersionTuple
+UnavailableMethodChecker::getCheckedForVersion(ProgramStateRef State) const {
+  unsigned major = State->get<CheckedVersionMajor>();
+  unsigned minor = State->get<CheckedVersionMinor>();
+  unsigned subminor = State->get<CheckedVersionSubminor>();
+  return VersionTuple(major, minor, subminor);
+}
+
+ProgramStateRef
+UnavailableMethodChecker::setCheckedForVersion(ProgramStateRef State,
+                                               VersionTuple V) const {
+  VersionTuple CheckedVersion = getCheckedForVersion(State);
+  if (CheckedVersion >= V)
+    return State;
+
+  State = State->set<CheckedVersionMajor>(V.getMajor());
+  State = State->set<CheckedVersionMinor>(V.getMinor());
+  State = State->set<CheckedVersionSubminor>(V.getSubminor());
+  return State;
+}
+
+void ento::registerUnavailableMethodChecker(CheckerManager &mgr) {
+  mgr.registerChecker<UnavailableMethodChecker>();
+}
Index: test/Analysis/unavailable-methods.m
===================================================================
--- test/Analysis/unavailable-methods.m	(revision 0)
+++ test/Analysis/unavailable-methods.m	(working copy)
@@ -0,0 +1,127 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -analyze -analyzer-checker=core,alpha.osx.cocoa.UnavailableMethodChecker -analyzer-store=region -fblocks -verify %s
+
+#define nil (void*)0
+#define NULL nil
+#define AVAILABLE(_version) __attribute__((availability(macosx,introduced=_version)))
+#define FN_AVAILABLE(_version) __attribute__((weak_import)) AVAILABLE(_version)
+typedef char BOOL;
+
+void availableFunction();
+void version20Function() FN_AVAILABLE(20.0);
+void version30Function() FN_AVAILABLE(30.0);
+void version40Function() FN_AVAILABLE(40.0);
+
+void testCallUnavailableFunction() {
+  version20Function();  // expected-warning {{The method is introduced in 20.0, later than the deployment target 10.5.0}}
+}
+
+void testCFunctionAvailability() {
+  if (version20Function) {
+    version20Function();
+  }
+
+  if (version40Function != NULL) {
+    version30Function();
+    version40Function();
+  } else {
+    version30Function();  // expected-warning {{The method is introduced in 30.0, later than the deployment target 10.5.0}}
+  }
+}
+
+void testUnary() {
+  if (!version30Function)
+    availableFunction();
+  else
+    version30Function();
+}
+
+void testCaching() {
+  BOOL available = version20Function != NULL;
+  if (available) {
+    version20Function();
+  }
+}
+
+BOOL version20IsAvailable() {
+  return version20Function != NULL;
+}
+
+void testHelperMethod() {
+  if (version20IsAvailable())
+    version20Function();
+}
+
+typedef struct objc_selector *SEL;
+@interface NSObject {}
++ (id)class;
++ (id)alloc;
+- (id)init;
++ (BOOL)instancesRespondToSelector:(SEL)selector;
+- (BOOL)respondsToSelector:(SEL)selector;
+@end
+
+@protocol VersionProtocol
++ (void)doProtocolStuff;
++ (void)doProtocolVersion30Stuff AVAILABLE(30.0);
+@end
+
+AVAILABLE(30.0)
+@protocol Version30Protocol
++ (void)doV30ProtocolStuff;
+@end
+
+AVAILABLE(20.0)
+@interface Version20Class : NSObject <VersionProtocol, Version30Protocol>  {}
+@property (strong) Version20Class *myProperty;
++ (void)doStuff;
++ (void)doVersion30Stuff AVAILABLE(30.0);
++ (void)doVersion40Stuff AVAILABLE(40.0);
+- (void)doVersion30InstanceStuff AVAILABLE(30.0);
+@end
+
+@interface Version20Class (CategoryMethods)
++ (void)doVersion50Stuff AVAILABLE(50.0);
+@end
+
+void testUnavailableClass() {
+  [Version20Class class];
+  [Version20Class doStuff]; // expected-warning {{The method is introduced in 20.0, later than the deployment target 10.5.0}}
+}
+
+void testClassMethodAvailability() {
+  if ([Version20Class class]) {
+    [Version20Class doStuff];
+    [Version20Class doVersion30Stuff]; // expected-warning {{The method is introduced in 30.0, later than the deployment target 10.5.0}}
+  }
+}
+
+void testRespondsToSelector() {
+  Version20Class *T = [[Version20Class alloc] init];
+  if ([T respondsToSelector:@selector(doVersion30InstanceStuff)]) {
+    [Version20Class doStuff];
+    [T doVersion30InstanceStuff];
+    [Version20Class doVersion40Stuff]; // expected-warning {{The method is introduced in 40.0, later than the deployment target 10.5.0}}
+  }
+}
+
+void testInstancesRespondToSelector() {
+  if ([Version20Class instancesRespondToSelector:@selector(doVersion30Stuff)]) {
+    [Version20Class doStuff];
+    [Version20Class doVersion30Stuff];
+    [Version20Class doVersion40Stuff]; // expected-warning {{The method is introduced in 40.0, later than the deployment target 10.5.0}}
+  }
+}
+
+void testCategoryMethods() {
+  [Version20Class doVersion50Stuff]; // expected-warning {{The method is introduced in 50.0, later than the deployment target 10.5.0}}
+}
+
+void testProtocolMethods() {
+  [Version20Class doProtocolStuff];
+  [Version20Class doProtocolVersion30Stuff]; // expected-warning {{The method is introduced in 30.0, later than the deployment target 10.5.0}}
+
+  [Version20Class doV30ProtocolStuff]; // expected-warning {{The method is introduced in 30.0, later than the deployment target 10.5.0}}
+  if ([Version20Class respondsToSelector:@selector(doVersion30Stuff)]) {
+    [Version20Class doV30ProtocolStuff];
+  }
+}
