Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt	(revision 197336)
+++ lib/StaticAnalyzer/Checkers/CMakeLists.txt	(working copy)
@@ -66,6 +66,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 197336)
+++ 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,19 @@
         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.
+    static SimpleProgramPointTag Tag("CallAndMessageChecker : NullDereference");
+    if (ExplodedNode *N = C.generateSink(StNull, 0, &Tag)) {
+      ImplicitNullDerefEvent Event = { L, false, N, &C.getBugReporter() };
+      dispatchEvent(Event);
+    }
   }
 
-  C.addTransition(StNonNull);
+  // There may be a sink generated for the implicit null deference, so we need
+  // to ensure a transition to the non null state here.
+  C.addTransition(StNonNull, this);
 }
 
 void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
Index: lib/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- lib/StaticAnalyzer/Checkers/Checkers.td	(revision 197336)
+++ lib/StaticAnalyzer/Checkers/Checkers.td	(working copy)
@@ -452,6 +452,10 @@
   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"
 
 let ParentPackage = CoreFoundation in {
Index: lib/StaticAnalyzer/Checkers/UnavailableMethodChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/UnavailableMethodChecker.cpp	(revision 0)
+++ lib/StaticAnalyzer/Checkers/UnavailableMethodChecker.cpp	(working copy)
@@ -0,0 +1,391 @@
+//== UnavailableMethodChecker.cpp -------------------------------*- C++ -*--==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines UnavailableMethodChecker, which is a path-sensitive check
+// which looks for the use of functions or methods that are implemented later
+// than the build deployment target.
+//
+//===----------------------------------------------------------------------===//
+
+#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"
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+typedef std::pair<VersionTuple, const NamedDecl *> VersionPair;
+
+namespace {
+  class UnavailableMethodChecker
+      : public Checker< check::PreCall,
+                        eval::Assume,
+                        check::Event<ImplicitNullDerefEvent> > {
+    mutable OwningPtr<BugType> BT;
+  public:
+    /// \brief Called before each function or method call to check if the Decl
+    /// for this call was introduced later than the deployment target.
+    void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const;
+                          
+    /// \brief Called every time an SVal is assumed to have a value. If the
+    /// SVal contains a version check that matches the Assumption, the checked
+    /// version number along this path is updated.
+    ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+                               bool Assumption) const;
+                          
+    /// \brief Outputs the checked availability along the current path.
+    void printState(raw_ostream &Out, ProgramStateRef State,
+                    const char *NL, const char *Sep) const;
+                          
+    /// \brief Called when another checker performs an implicit null
+    /// dereference. If the location being dereferenced is a weakly linked
+    /// function, this will cause an error if the checked for version along
+    /// the current path is less than the introduced version of the function.
+    void checkEvent(ImplicitNullDerefEvent Event) const;
+
+  private:
+    /// \brief Outputs a bug report for the given node.
+    void displayError(ProgramStateRef State, VersionTuple Introduced,
+                      const NamedDecl *D, ExplodedNode *N,
+                      BugReporter *BR) const;
+                          
+    /// \brief Tries to extract a weakly linked FunctionDecl from a symbol.
+    /// \return A weak FunctionDecl on success, otherwise NULL.
+    const Decl *getWeakDeclForSymbol(SymbolRef Sym) const;
+                          
+    /// \brief Tries to extract a weakly linked FunctionDecl from a MemRegion.
+    /// \return A weak FunctionDecl on success, otherwise NULL.
+    const Decl *getWeakDeclForRegion(const MemRegion *MR) const;
+                          
+    /// \brief Gets the version number for when a Decl was first introduced.
+    /// For Objective-C methods, this will also check the protocol or class.
+    /// \return A pair of the version and the Decl that had the version info.
+    VersionPair getVersionIntroduced(const Decl *D) const;
+                          
+    /// \brief Gets the deployment target of the current build.
+    VersionTuple getDeploymentTarget(ProgramStateRef State) const;
+                          
+    /// \brief The given symbol is examined for any version checks that match
+    /// the Assumption. This includes checking if a C function is NULL,
+    /// calling one of the Objective-C respondsToSelector: methods or
+    /// checking if an Objective-C class is nil.
+    /// \return If a successful check is found, the introduced version number
+    /// for the class, method or Decl being checked.
+    VersionTuple getVersionCheckForSymbol(SymbolRef S, bool Assumption) const;
+    
+    /// \brief Gets the current checked for version along this path.
+    VersionTuple getCheckedForVersion(ProgramStateRef State) const;
+                          
+    /// \brief Sets the current checked for version along this path to the
+    /// maximum of the current checked version and V.
+    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 {
+  if (!(isa<SimpleCall>(Call) || isa<ObjCMethodCall>(Call)))
+    return;
+
+  if (const ObjCMethodCall *ObjCCall = dyn_cast<ObjCMethodCall>(&Call)) {
+    // ObjC methods with no type information about the receiver cannot be
+    // reasoned about accurately.
+    const ObjCMessageExpr *ME = ObjCCall->getOriginExpr();
+    if (!ME->getReceiverInterface())
+      return;
+
+    // Array and dictionary subscripts can be ignored, they are implemented
+    // for lower deployment targets by the runtime.
+    if (ObjCCall->getMessageKind() == OCM_Subscript)
+      return;
+    
+    // If this method is calling self of super with the same selector, then we
+    // can ignore any version checking.
+    if (ObjCCall->isReceiverSelfOrSuper()) {
+      const StackFrameContext *SFC = Ctx.getStackFrame();
+      if (const ObjCMethodDecl *D = dyn_cast<ObjCMethodDecl>(SFC->getDecl())) {
+        if (D->getSelector() == ME->getSelector())
+          return;
+      }
+    }
+  }
+
+  const Decl *D = Call.getDecl();
+  if (!D)
+    return;
+  
+  ProgramStateRef State = Ctx.getState();
+  VersionPair VI = getVersionIntroduced(D);
+  VersionTuple Introduced = VI.first;
+  VersionTuple DeploymentTarget = getDeploymentTarget(State);
+  if (Introduced <= DeploymentTarget)
+    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 (Introduced <= CheckedVersion)
+    return;
+
+  if (ExplodedNode *N = Ctx.generateSink(State))
+    displayError(State, Introduced, VI.second, N, &Ctx.getBugReporter());
+}
+
+ProgramStateRef UnavailableMethodChecker::evalAssume(ProgramStateRef State,
+                                                     SVal Cond,
+                                                     bool Assumption) const {
+  SymbolRef S = Cond.getAsSymbol();
+  if (!S)
+    return State;
+
+  // Walk through the symbol's children looking for a version check.
+  VersionTuple V = getVersionCheckForSymbol(S, Assumption);
+  return setCheckedForVersion(State, V);
+}
+
+void UnavailableMethodChecker::checkEvent(ImplicitNullDerefEvent Event) const {
+  if (Event.IsLoad)
+    return;
+
+  const MemRegion *MR = Event.Location.getAsRegion();
+  if (!MR)
+    return;
+
+  // If this is a weak function, it is not OK to dereference if availability
+  // has not been checked for.
+  if (const Decl *D = getWeakDeclForRegion(MR)) {
+    ProgramStateRef State = Event.SinkNode->getState();
+    VersionTuple Introduced = getVersionIntroduced(D).first;
+    VersionTuple Checked = getCheckedForVersion(State);
+    if (Introduced > Checked)
+      // TODO: get the highlight range for the event.
+      displayError(State, Introduced, cast<NamedDecl>(D),
+                   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.empty()) {
+    Out << Sep;
+    Out << "Checked availability: " << CheckedVersion.getAsString();
+  }
+}
+
+void UnavailableMethodChecker::displayError(ProgramStateRef State,
+                                            VersionTuple Introduced,
+                                            const NamedDecl *D,
+                                            ExplodedNode *N,
+                                            BugReporter *BR) const {
+  if (!BT)
+    BT.reset(new BugType("API Unavailable", "API Misuse"));
+
+  SmallString<128> sbuf;
+  llvm::raw_svector_ostream os(sbuf);
+  switch (D->getKind()) {
+  case Decl::Function:
+    os << "Calling function";
+    break;
+
+  case Decl::ObjCMethod:
+    os << "Calling method";
+    break;
+
+  case Decl::ObjCProtocol:
+    os << "Calling method belonging to protocol " << "'" << *D << "'";
+    break;
+
+  case Decl::ObjCInterface:
+    os << "Calling method belonging to class " << "'" << *D << "'";
+    break;
+
+  default:
+    llvm_unreachable("Unknown Decl kind.");
+  }
+
+  ASTContext &Ctx = State->getStateManager().getContext();
+  StringRef TargetPlatform = Ctx.getTargetInfo().getPlatformName();
+  StringRef PrettyPlatformName =
+    AvailabilityAttr::getPrettyPlatformName(TargetPlatform);
+  if (PrettyPlatformName.empty())
+    PrettyPlatformName = TargetPlatform;
+
+  os << " introduced in " << PrettyPlatformName << " " << Introduced;
+  os << " (deployment target is " << getDeploymentTarget(State) << ")";
+  BugReport *R = new BugReport(*BT, os.str(), N);
+  BR->emitReport(R);
+}
+
+VersionPair
+UnavailableMethodChecker::getVersionIntroduced(const Decl *D) const {
+   if (const AvailabilityAttr *A = D->getAttr<AvailabilityAttr>())
+     return VersionPair(A->getIntroduced(), cast<NamedDecl>(D));
+  
+  // If we have an ObjectiveC method decl, we should also check its context.
+  // The class or protocol that it belongs to could also have an introduced
+  // version, which should be returned.
+  if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+    const DeclContext *DC = MD->getDeclContext();
+    if (const ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(DC)) {
+      if (const AvailabilityAttr *A = PD->getAttr<AvailabilityAttr>()) {
+        return VersionPair(A->getIntroduced(), PD);
+      }
+    } else {
+      const ObjCInterfaceDecl *ID = MD->getClassInterface();
+      if (const AvailabilityAttr *A = ID->getAttr<AvailabilityAttr>()) {
+        return VersionPair(A->getIntroduced(), ID);
+      }
+    }
+  }
+
+  return VersionPair(VersionTuple(), NULL);
+}
+
+VersionTuple
+UnavailableMethodChecker::getDeploymentTarget(ProgramStateRef State) const {
+  ProgramStateManager &PSM = State->getStateManager();
+  return PSM.getContext().getTargetInfo().getPlatformMinVersion();
+}
+
+const Decl
+*UnavailableMethodChecker::getWeakDeclForSymbol(SymbolRef Sym) const {
+  if (const SymbolExtent *SE = dyn_cast<SymbolExtent>(Sym))
+    return getWeakDeclForRegion(SE->getRegion());
+    
+  return NULL;
+}
+
+const Decl
+*UnavailableMethodChecker::getWeakDeclForRegion(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::getVersionCheckForSymbol(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) {
+    // Look for symbol int comparsions, eg functionPtr != NULL or
+    // [object respondsToSelector:SEL] == YES
+    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();
+      if (op != BO_EQ && op != BO_NE)
+          break;
+
+      bool RHS = SIExpr->getRHS() != 0;
+      if ((op == BO_EQ && !RHS) || (op == BO_NE && RHS))
+        Assumption = !Assumption;
+
+      continue;
+    }
+
+    // We are only interested in checks that a symbol is not NULL or that
+    // an instance does respond to a selector. Any false assumptions can
+    // be ignored, as they are not checking for availability.
+    if (!Assumption)
+      break;
+
+    // We have a true assumption for a symbol. First check for weak function
+    // symbols, which are wrapped by SymbolExtents.
+    if (const SymbolExtent *SE = dyn_cast<SymbolExtent>(*I)) {
+      if (const Decl *D = getWeakDeclForSymbol(SE))
+        // We have a weakly linked function, return its introduced version.
+        return getVersionIntroduced(D).first;
+    } 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. We are looking for the following
+      // availability checks:
+      //  * [Classname class] != nil
+      //  * [Classname instancesRespondToSelector:SEL]
+      //  * [Instance respondsToSelector:SEL]
+      Selector MsgSel = ME->getSelector();
+      if (MsgSel.getNumArgs() > 1)
+        break;
+      
+      // If we have no class here, we cannot reason about the version checking,
+      // so give up.
+      const ObjCInterfaceDecl *CD = ME->getReceiverInterface();
+      if (!CD)
+        break;
+      
+      StringRef SelStr = MsgSel.getNameForSlot(0);
+      if (SelStr == "respondsToSelector" ||
+          SelStr == "instancesRespondToSelector") {
+        // The argument could be a selector expression or a SEL variable.
+        // TODO: make this work for SEL variables.
+        if (const ObjCSelectorExpr *SExp =
+            dyn_cast<ObjCSelectorExpr>(ME->getArg(0))) {
+          Selector ArgSel = SExp->getSelector();
+          const ObjCMethodDecl *D = CD->lookupInstanceMethod(ArgSel);
+          if (!D)
+            D = CD->lookupClassMethod(ArgSel);
+          
+          if (D)
+            return getVersionIntroduced(D).first;
+        }
+      } else if (SelStr == "class") {
+        return getVersionIntroduced(CD).first;
+      }
+    }
+  }
+
+  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) {
+  // TODO: get the deployment target here, if there isn't one then don't register
+  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,175 @@
+// 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 {{Calling function introduced in OS X 20.0 (deployment target is 10.5.0)}}
+}
+
+void testCFunctionAvailability() {
+  if (version20Function) {
+    version20Function();
+  }
+
+  if (version40Function != NULL) {
+    version30Function();
+    version40Function();
+  } else {
+    version30Function();  // expected-warning {{Calling function introduced in OS X 30.0 (deployment target is 10.5.0)}}
+  }
+}
+
+void testUnary() {
+  if (!version30Function)
+    availableFunction();
+  else
+    version30Function();
+}
+
+void testVariables() {
+  BOOL available = version20Function != NULL;
+  if (available) {
+    version20Function();
+  }
+}
+
+BOOL version20IsAvailable() {
+  return version20Function != NULL;
+}
+
+void testHelperMethod() {
+  if (version20IsAvailable())
+    version20Function();
+}
+
+void testOnlySingleWarning() {
+  version20Function();  // expected-warning {{Calling function introduced in OS X 20.0 (deployment target is 10.5.0)}}
+  version20Function();  // no warning
+}
+
+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(40.0)
+@protocol Version40Protocol
++ (void)doV40ProtocolStuff;
+@end
+
+AVAILABLE(20.0)
+@interface Version20Class : NSObject <VersionProtocol, Version40Protocol>  {}
+@property (strong) Version20Class *myProperty;
++ (void)doStuff;
++ (void)doVersion30Stuff AVAILABLE(30.0);
++ (void)doVersion40Stuff AVAILABLE(40.0);
+- (void)doVersion30InstanceStuff AVAILABLE(30.0);
+- (id)objectAtIndexedSubscript:(unsigned)idx;
+- (id)objectForKeyedSubscript:(id)key;
+- (void)setObject:(id)object atIndexedSubscript:(unsigned)idx;
+- (void)setObject:(id)object forKeyedSubscript:(id)key;
+@end
+
+@interface Version20Class (CategoryMethods)
++ (void)doVersion50Stuff AVAILABLE(50.0);
+@end
+
+void testUnavailableClass() {
+  [Version20Class class];
+  [Version20Class doStuff]; // expected-warning {{Calling method belonging to class 'Version20Class' introduced in OS X 20.0 (deployment target is 10.5.0)}}
+}
+
+void testOnlySingleClassWarning() {
+  [Version20Class doStuff]; // expected-warning {{Calling method belonging to class 'Version20Class' introduced in OS X 20.0 (deployment target is 10.5.0)}}
+  [Version20Class doStuff]; // no warning
+}
+
+void testClassMethodAvailability() {
+  if ([Version20Class class] != nil) {
+    [Version20Class doStuff];
+    [Version20Class doVersion30Stuff]; // expected-warning {{Calling method introduced in OS X 30.0 (deployment target is 10.5.0)}}
+  }
+}
+
+void testRespondsToSelector() {
+  Version20Class *T = [[Version20Class alloc] init];
+  if ([T respondsToSelector:@selector(doVersion30InstanceStuff)]) {
+    [Version20Class doStuff];
+    [T doVersion30InstanceStuff];
+    [Version20Class doVersion40Stuff]; // expected-warning {{Calling method introduced in OS X 40.0 (deployment target is 10.5.0)}}
+  }
+}
+
+void testInstancesRespondToSelector() {
+  if ([Version20Class instancesRespondToSelector:@selector(doVersion30Stuff)]) {
+    [Version20Class doStuff];
+    [Version20Class doVersion30Stuff];
+    [Version20Class doVersion40Stuff]; // expected-warning {{Calling method introduced in OS X 40.0 (deployment target is 10.5.0)}}
+  }
+}
+
+void testCategoryMethods() {
+  [Version20Class doVersion50Stuff]; // expected-warning {{Calling method introduced in OS X 50.0 (deployment target is 10.5.0)}}
+}
+
+void testProtocolMethodWithAvailability() {
+  [Version20Class doProtocolStuff];
+  [Version20Class doProtocolVersion30Stuff]; // expected-warning {{Calling method introduced in OS X 30.0 (deployment target is 10.5.0)}}
+}
+
+void testProtocolWithAvailability() {
+  if ([Version20Class respondsToSelector:@selector(doVersion30Stuff)]) {
+    [Version20Class doProtocolVersion30Stuff];
+  }
+  [Version20Class doV40ProtocolStuff]; // expected-warning {{Calling method belonging to protocol 'Version40Protocol' introduced in OS X 40.0 (deployment target is 10.5.0)}}
+}
+
+void testIDs(id obj) {
+  [obj doVersion30InstanceStuff];  // no warning
+}
+
+void testObjectSubscripts() {
+  // no warnings are issued for object subscripting
+  Version20Class *v = [[Version20Class alloc] init];
+  id obj = v[0];
+  obj = v[obj];
+  v[0] = obj;
+  v[obj] = obj;
+}
+
+@interface TestSuperCallClass : Version20Class
+- (void)doVersion40InstanceStuff AVAILABLE(40.0);
+@end
+@implementation TestSuperCallClass
+
+// calling super should be allowed from self
+// regardless of the deployment target
++ (void)doVersion30Stuff {
+  [super doVersion30Stuff];  // no warning
+  [super doVersion40Stuff];  // expected-warning {{Calling method introduced in OS X 40.0 (deployment target is 10.5.0)}}
+}
+
+- (void)doVersion30InstanceStuff {
+  [super doVersion30InstanceStuff]; // no warning
+  [self doVersion40InstanceStuff];  // expected-warning {{Calling method introduced in OS X 40.0 (deployment target is 10.5.0)}}
+}
+
+@end
