Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt	(revision 189585)
+++ 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/Checkers.td
===================================================================
--- lib/StaticAnalyzer/Checkers/Checkers.td	(revision 189585)
+++ 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,386 @@
+#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,
+                                                   check::BranchCondition,
+                                                   eval::Assume > {
+    mutable OwningPtr<BugType> BT;
+  public:
+    void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
+    void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const;
+    void checkDeadSymbols(SymbolReaper &SR, 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;
+
+  private:
+    void displayError(const ProgramStateRef &state, SourceRange range,
+                      StringRef errorMsg, CheckerContext &Ctx) const;
+    VersionTuple versionIntroduced(const Decl *D) const;
+    VersionTuple versionCheckForCondition(const Stmt *Condition,
+                                          bool &Assumption,
+                                          CheckerContext &Ctx) const;
+    VersionTuple getCheckedForVersion(ProgramStateRef State) const;
+    ProgramStateRef setCheckedForVersion(ProgramStateRef State,
+                                         VersionTuple V) const;
+  };
+
+  struct AvailabilityState {
+    VersionTuple Version;
+    bool Assumption;
+
+    AvailabilityState(VersionTuple V, bool A) { Version = V; Assumption = A; }
+
+    bool operator==(const AvailabilityState &A) const {
+      return Version == A.Version && Assumption == A.Assumption;
+    }
+
+    void Profile(llvm::FoldingSetNodeID &ID) const {
+      ID.AddString(Version.getAsString());
+      ID.AddBoolean(Assumption);
+    }
+  };
+
+  static const SymbolRef CheckedAvailability = NULL;
+}
+
+REGISTER_MAP_WITH_PROGRAMSTATE(AvailabilityMap, SymbolRef, AvailabilityState)
+
+void UnavailableMethodChecker::checkBranchCondition(const Stmt *Condition,
+                                                    CheckerContext &Ctx) const
+{
+  // Bail early if we have no deployment target set.
+  VersionTuple TargetMinVersion =
+    Ctx.getASTContext().getTargetInfo().getPlatformMinVersion();
+  if (TargetMinVersion.empty())
+    return;
+
+  // Check if the condition is checking for availability
+  // If so, start tracking the symbol for evaluation.
+  bool Assumption = true;
+  VersionTuple CheckedVersion = versionCheckForCondition(Condition,
+                                                         Assumption,
+                                                         Ctx);
+  if (CheckedVersion > TargetMinVersion &&
+      CheckedVersion > getCheckedForVersion(Ctx.getState())) {
+    ProgramStateRef State = Ctx.getState();
+    SVal S = State->getSVal(Condition, Ctx.getLocationContext());
+    if (!S.getAs<DefinedSVal>())
+      return;
+
+    // Check if this Stmt has already been bound
+    // or we have a constant folded SVal.
+    ProgramStateRef TrueState, FalseState;
+    llvm::tie(TrueState, FalseState) = State->assume(S.castAs<DefinedSVal>());
+    if ((TrueState && !FalseState && Assumption) ||
+        (!TrueState && FalseState && !Assumption) ||
+        S.isConstant(Assumption)) {
+      State = setCheckedForVersion(State, CheckedVersion);
+      Ctx.addTransition(State);
+    } else if (TrueState && FalseState) {
+      // Track the symbol until it is assumed.
+      SymbolRef Sym = S.getAsSymbol();
+      if (!Sym) {
+        // If this is a weak function, we have to cast the
+        // region SVal to bool to get a symbol.
+        ASTContext &ACtx = Ctx.getASTContext();
+        S = Ctx.getSValBuilder().evalCast(S, ACtx.BoolTy, ACtx.VoidPtrTy);
+        Sym = S.getAsSymbol();
+        if (!Sym)
+          return;
+      }
+
+      AvailabilityState A = AvailabilityState(CheckedVersion, Assumption);
+      State = State->set<AvailabilityMap>(Sym, A);
+      Ctx.addTransition(State);
+    }
+  }
+}
+
+void UnavailableMethodChecker::checkPreCall(const CallEvent &Call,
+                                            CheckerContext &Ctx) const
+{
+  VersionTuple TargetMinVersion =
+    Ctx.getASTContext().getTargetInfo().getPlatformMinVersion();
+  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(Ctx.getState());
+  if (CheckedVersion >= Introduced)
+    return;
+
+  SmallString<256> sbuf;
+  llvm::raw_svector_ostream os(sbuf);
+  os << "The method is introduced in "
+     << Introduced
+     << ", later than the deployment target "
+     << TargetMinVersion;
+  displayError(Call.getState(), Call.getSourceRange(), os.str(), Ctx);
+}
+
+ProgramStateRef UnavailableMethodChecker::evalAssume(ProgramStateRef State,
+                                                     SVal Cond,
+                                                     bool Assumption) const
+{
+  // If this is a tracked SVal, check if the assumption agrees with the
+  // availability condition. If so set it as global state for this branch.
+  SymbolRef S = Cond.getAsSymbol();
+  if (!S)
+    return State;
+
+  const AvailabilityState *A = State->get<AvailabilityMap>(S);
+  if (!A) {
+    // If we have a SymInt expression comparing against 0,
+    // then pull out the LHS and look for state again.
+    if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(S)) {
+      if (SE->getRHS() == 0) {
+        switch (SE->getOpcode()) {
+          case BO_EQ:
+            Assumption = !Assumption;
+            // FALL-THROUGH
+          case BO_NE:
+            return evalAssume(State,
+                              nonloc::SymbolVal(SE->getLHS()),
+                              Assumption);
+          default:
+            break;
+        }
+      }
+    }
+
+    // Otherwise there is no state for this symbol.
+    return State;
+  }
+
+  if (A->Assumption == Assumption)
+    State = setCheckedForVersion(State, A->Version);
+
+  // We can stop tracking the SVal now.
+  return State->remove<AvailabilityMap>(S);
+}
+
+void UnavailableMethodChecker::printState(raw_ostream &Out,
+                                          ProgramStateRef State,
+                                          const char *NL,
+                                          const char *Sep) const
+{
+  AvailabilityMapTy M = State->get<AvailabilityMap>();
+  if (M.isEmpty())
+    return;
+
+  Out << Sep;
+  for (AvailabilityMapTy::iterator I = M.begin(), E = M.end(); I != E; ++I) {
+    if (I != M.begin())
+      Out << NL;
+
+    const llvm::FoldingSetNode * N = I->first;
+    AvailabilityState A = I->second;
+
+    if (N == CheckedAvailability) {
+      Out << "Checked availability: " << A.Version.getAsString();
+    } else {
+      Out << "  ";
+      ((SymbolRef)N)->dumpToStream(Out);
+      Out << " has availability " << A.Version.getAsString();
+    }
+  }
+}
+
+void UnavailableMethodChecker::displayError(const ProgramStateRef &state,
+                                            SourceRange range,
+                                            StringRef errorMsg,
+                                            CheckerContext &Ctx) const
+{
+  if (ExplodedNode *N = Ctx.addTransition(state)) {
+    if (!BT)
+      BT.reset(new BugType("API Unavailable", "API Misuse"));
+
+    BugReport *R = new BugReport(*BT, errorMsg, N);
+    R->addRange(range);
+    Ctx.emitReport(R);
+  }
+}
+
+VersionTuple UnavailableMethodChecker::versionIntroduced(const Decl *D) const
+{
+  if (const AvailabilityAttr *Availability = D->getAttr<AvailabilityAttr>())
+    return Availability->getIntroduced();
+
+  return VersionTuple();
+}
+
+VersionTuple
+UnavailableMethodChecker::versionCheckForCondition(const Stmt *Condition,
+                                                   bool &Assumption,
+                                                   CheckerContext &Ctx) const
+{
+  Stmt *S = (Stmt *)Condition;
+
+  // Walk down the condition looking for a DeclRef, a [ respondsToSelector:]
+  // or a [ class] method
+  while (S) {
+    if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(S)) {
+      if (UnOp->getOpcode() != UO_LNot)
+        break;
+
+      Assumption = !Assumption;
+      S = UnOp->getSubExpr();
+    }
+
+    else if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
+      BinaryOperatorKind Op = BinOp->getOpcode();
+      if (!(Op == BO_EQ || Op == BO_NE))
+        break;
+
+      Expr *LHS = BinOp->getLHS();
+      Expr *RHS = BinOp->getRHS();
+      bool Result;
+      ASTContext& ASTCtx = Ctx.getASTContext();
+
+      if (LHS->EvaluateAsBooleanCondition(Result, ASTCtx))
+        S = RHS;
+      else if (RHS->EvaluateAsBooleanCondition(Result, ASTCtx))
+        S = LHS;
+      else
+        break;
+
+      if ((Op == BO_EQ && Result == false) || (Op == BO_NE && Result == true))
+        Assumption = !Assumption;
+    }
+
+    else if (ImplicitCastExpr *ImpExpr = dyn_cast<ImplicitCastExpr>(S))
+      S = ImpExpr->IgnoreImpCasts();
+
+    else if (CallExpr *CExpr = dyn_cast<CallExpr>(S))
+      S = CExpr->getCallee();
+
+    else if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
+      // First check for the class introduced version.
+      ObjCInterfaceDecl *CD = ME->getReceiverInterface();
+      VersionTuple VC = versionIntroduced(CD);
+
+      // If this is checking for availability, return the max of the
+      // class version and the checked for method version.
+      std::string Sel = ME->getSelector().getAsString();
+      if (Sel == "respondsToSelector:" ||
+          Sel == "instancesRespondToSelector:") {
+        const ObjCSelectorExpr *SelExpr = cast<ObjCSelectorExpr>(ME->getArg(0));
+        ObjCMethodDecl *D = CD->lookupInstanceMethod(SelExpr->getSelector());
+        if (!D)
+          D = CD->lookupClassMethod(SelExpr->getSelector());
+
+        if (D) 
+          return std::max(VC, versionIntroduced(D));
+      }
+      else if (Sel == "class")
+        return VC;
+
+      break;
+    }
+
+    else if (DeclRefExpr *DeclExpr = dyn_cast<DeclRefExpr>(S)) {
+      ValueDecl *ValDecl = DeclExpr->getDecl();
+      if (FunctionDecl *FnDecl = dyn_cast<FunctionDecl>(ValDecl)) {
+        if (FnDecl->isWeak())
+          // We have a weak function, check for availability.
+          return versionIntroduced(FnDecl);
+
+        // Check the return Stmt of the function.
+        if (!FnDecl->hasBody())
+          break;
+
+        S = NULL;
+        if (CompoundStmt *C = dyn_cast<CompoundStmt>(FnDecl->getBody())) {
+          // Strip away the Compound and Return Stmts.
+          for (Stmt::child_range I = C->children(); I; ++I) {
+            if (*I)
+              if (ReturnStmt *RS = dyn_cast<ReturnStmt>(*I)) {
+                S = RS->getRetValue();
+                break;
+              }
+          }
+        }
+      }
+      else if (const VarDecl *VrDecl = dyn_cast<VarDecl>(ValDecl)) {
+        // We have a variable, get the Decl for
+        // the Expr that is was assigned from.
+        S = (Stmt *)VrDecl->getAnyInitializer();
+      }
+      else 
+        break;
+    }
+
+    else
+      // This is a Stmt that we don't handle.
+      break;
+  }
+
+  return VersionTuple();
+}
+
+VersionTuple
+UnavailableMethodChecker::getCheckedForVersion(ProgramStateRef State) const
+{
+  const AvailabilityState *A = State->get<AvailabilityMap>(CheckedAvailability);
+  if (A)
+    return A->Version;
+
+  return VersionTuple();
+}
+
+ProgramStateRef
+UnavailableMethodChecker::setCheckedForVersion(ProgramStateRef State,
+                                               VersionTuple V) const
+{
+  VersionTuple CheckedVersion = getCheckedForVersion(State);
+  if (CheckedVersion >= V)
+    return State;
+
+  return State->set<AvailabilityMap>(CheckedAvailability,
+                                     AvailabilityState(V, true));
+}
+
+// register plugin
+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,129 @@
+// 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 struct objc_selector *SEL;
+typedef char BOOL;
+@interface NSObject {}
++ (id)class;
++ (id)alloc;
+- (id)init;
++ (BOOL)instancesRespondToSelector:(SEL)selector;
+- (BOOL)respondsToSelector:(SEL)selector;
+@end
+
+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();
+  }
+}
+
+@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 testVariableIndirection()
+{
+  BOOL isAvailable = [Version20Class class] != nil;
+  if (isAvailable) {
+    [Version20Class doStuff];
+  }
+}
+
+BOOL version20IsAvailable()
+{
+  return [Version20Class class] != nil;
+}
+
+void testHelperMethodIndirection()
+{
+  if (version20IsAvailable()) {
+    [Version20Class doStuff];
+  }
+}
+
+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];
+  }
+}
