[patch/rfc] An opt-in warning for functions whose availability(introduced) is newer than the deployment target

Nico Weber thakis at chromium.org
Sun Mar 1 19:52:52 PST 2015


On Mon, Feb 2, 2015 at 10:26 PM, Douglas Gregor <dgregor at apple.com> wrote:

> Hi Nico,
>
> On Jan 8, 2015, at 6:14 PM, Nico Weber <thakis at chromium.org> wrote:
>
> Hi,
>
> the Mac OS X and iOS SDKs add new functions in new releases. Apple
> recommends using the newest SDK and setting the deployment target to
> whatever old OS version one wants to support, and only calling new
> functions after checking that they are available at runtime.
>
> In practice, we (Chromium) get this wrong. Others who support old OS X
> versions get this wrong too. Hence, we (Chromium) use a very old SDK and
> then manually declare new functions when we want to call them – this
> reduces the chance of us forgetting if they are available at runtime
> considerably, in practice. But using an old SDK has its problems –
> sometimes the frameworks check which SDK an app was linked against and only
> then activate bug fixes, and newer Xcodes don't ship include old SDKs.
>
>
> That’s an interesting approach to handling the availability problem; I
> hadn’t heard of it before, but I see the logic there.
>
> Ideally, we could use a new SDK but get a warning when we use a new API
> without a manual redeclaration – this protects us against new APIs the same
> way using an old SDK does without the drawbacks that this brings.
>
> The attached patch is a sketch how such a warning might work. How
> repulsive is this idea? Are there other approaches to this problem? If the
> basic idea is ok:
>
>
> This is a drastically different approach than I’d imagined. Whenever I’ve
> thought about this problem, I’ve always come back to some form of dataflow
> analysis that checks whether uses of “not-yet-introduced” API is used in a
> sane way: is it dominated by some check that implies the availability,
> e.g., a -respondsToSelector: check on a method with at least that
> availability, or checking whether “[NSFoo class]” is non-null when the
> class has availability. I suspect that’s the idea behind Deploymate (
> http://www.deploymateapp.com), although I’ve never used it, and it has
> the benefit that it should make idiomatic code (that does the right
> checking) just work.
>
> It’s also a heck of a lot more work to implement than the approach you’re
> using.
>

Right :-)


>
> Any comments on the implementation?
>
>
> The implementation generally looks fine. One minor comment:
>
> +    case AR_NotYetIntroduced: {
> +      // don't do this for enums, they can't be redeclared.
> +      if (isa<EnumConstantDecl>(D) || isa<EnumDecl>(D))
> +        break;
> +      bool FoundRedecl = false;
> +      for (Decl *Redecl = D->getMostRecentDecl(); Redecl && !FoundRedecl;
> +           Redecl = Redecl->getPreviousDecl()) {
> +        if (Redecl->getAttr<AvailabilityAttr>()->isInherited())
> +          FoundRedecl = true;
> +      }
> +      if (!FoundRedecl)
> +        S.EmitAvailabilityWarning(Sema::AD_Partial, D, Message, Loc,
> +                                  UnknownObjCClass, ObjCPDecl,
> +                                  ObjCPropertyAccess);
> +      break;
> +    }
>
> Generally speaking, name lookup will always find the most recent
> declaration, so you might be able to skip the D->getMostRecentDecl() bit
> entirely and just check that the availability attribute was inherited.
>

That worked, done.

I also had to add some explicit code for handling redeclarations in
@interfaces, as these aren't modeled as redeclarations in the AST. I also
added the property note used in the other availability warnings, and I
added lots of tests.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20150301/86566eeb/attachment.html>
-------------- next part --------------
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td	(revision 230929)
+++ include/clang/Basic/DiagnosticGroups.td	(working copy)
@@ -80,6 +80,7 @@
 
 def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">;
 def UnavailableDeclarations : DiagGroup<"unavailable-declarations">;
+def PartialAvailability : DiagGroup<"partial-availability">;
 def DeprecatedImplementations :DiagGroup<"deprecated-implementations">;
 def DeprecatedIncrementBool : DiagGroup<"deprecated-increment-bool">;
 def DeprecatedRegister : DiagGroup<"deprecated-register">;
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 230929)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -731,7 +731,7 @@
 def not_conv_function_declared_at : Note<"type conversion function declared here">;
 def note_method_declared_at : Note<"method %0 declared here">;
 def note_property_attribute : Note<"property %0 is declared "
-  "%select{deprecated|unavailable}1 here">;
+  "%select{deprecated|unavailable|partial}1 here">;
 def err_setter_type_void : Error<"type of setter must be void">;
 def err_duplicate_method_decl : Error<"duplicate declaration of method %0">;
 def warn_duplicate_method_decl : 
@@ -3871,6 +3871,15 @@
 def note_not_found_by_two_phase_lookup : Note<"%0 should be declared prior to the "
     "call site%select{| or in %2| or in an associated namespace of one of its arguments}1">;
 def err_undeclared_use : Error<"use of undeclared %0">;
+def warn_partial_availability : Warning<"%0 is only available conditionally">,
+    InGroup<PartialAvailability>, DefaultIgnore;
+def note_partial_availability_silence : Note<
+  "explicitly redeclare %0 to silence this warning">;
+def warn_partial_message : Warning<"%0 is partial: %1">,
+    InGroup<PartialAvailability>, DefaultIgnore;
+def warn_partial_fwdclass_message : Warning<
+    "%0 may be partial because the receiver type is unknown">,
+    InGroup<PartialAvailability>, DefaultIgnore;
 def warn_deprecated : Warning<"%0 is deprecated">,
     InGroup<DeprecatedDeclarations>;
 def warn_property_method_deprecated :
@@ -3896,7 +3905,7 @@
     InGroup<UnavailableDeclarations>;
 def note_availability_specified_here : Note<
   "%0 has been explicitly marked "
-  "%select{unavailable|deleted|deprecated}1 here">;
+  "%select{unavailable|deleted|deprecated|partial}1 here">;
 def note_implicitly_deleted : Note<
   "explicitly defaulted function was implicitly deleted here">;
 def note_inherited_deleted_here : Note<
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 230929)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -3355,7 +3355,7 @@
 
   void redelayDiagnostics(sema::DelayedDiagnosticPool &pool);
 
-  enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable };
+  enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable, AD_Partial };
 
   void EmitAvailabilityWarning(AvailabilityDiagnostic AD,
                                NamedDecl *D, StringRef Message,
Index: lib/Sema/DelayedDiagnostic.cpp
===================================================================
--- lib/Sema/DelayedDiagnostic.cpp	(revision 230929)
+++ lib/Sema/DelayedDiagnostic.cpp	(working copy)
@@ -35,6 +35,8 @@
     case Sema::AD_Unavailable:
       DD.Kind = Unavailable;
       break;
+    case Sema::AD_Partial:
+      llvm_unreachable("AD_Partial diags should not be delayed");
   }
   DD.Triggered = false;
   DD.Loc = Loc;
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp	(revision 230929)
+++ lib/Sema/SemaExpr.cpp	(working copy)
@@ -82,10 +82,26 @@
   }
 }
 
-static AvailabilityResult DiagnoseAvailabilityOfDecl(Sema &S,
-                              NamedDecl *D, SourceLocation Loc,
-                              const ObjCInterfaceDecl *UnknownObjCClass,
-                              bool ObjCPropertyAccess) {
+static bool HasRedeclarationWithoutAvailabilityInCategory(const Decl *D) {
+  const auto *OMD = dyn_cast<ObjCMethodDecl>(D);
+  if (!OMD)
+    return false;
+  const ObjCInterfaceDecl *OID = OMD->getClassInterface();
+  if (!OID)
+    return false;
+
+  for (const ObjCCategoryDecl *Cat : OID->visible_categories())
+    if (ObjCMethodDecl *CatMeth =
+            Cat->getMethod(OMD->getSelector(), OMD->isInstanceMethod()))
+      if (!CatMeth->hasAttr<AvailabilityAttr>())
+        return true;
+  return false;
+}
+
+static AvailabilityResult
+DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc,
+                           const ObjCInterfaceDecl *UnknownObjCClass,
+                           bool ObjCPropertyAccess) {
   // See if this declaration is unavailable or deprecated.
   std::string Message;
     
@@ -103,7 +119,8 @@
     }
 
   const ObjCPropertyDecl *ObjCPDecl = nullptr;
-  if (Result == AR_Deprecated || Result == AR_Unavailable) {
+  if (Result == AR_Deprecated || Result == AR_Unavailable ||
+      AR_NotYetIntroduced) {
     if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
       if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
         AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
@@ -115,9 +132,26 @@
   
   switch (Result) {
     case AR_Available:
-    case AR_NotYetIntroduced:
       break;
-            
+
+    case AR_NotYetIntroduced: {
+      // Don't do this for enums, they can't be redeclared.
+      if (isa<EnumConstantDecl>(D) || isa<EnumDecl>(D))
+        break;
+      bool Warn = !D->getAttr<AvailabilityAttr>()->isInherited();
+      // Objective-C method declarations in categories are not modelled as
+      // redeclarations, so manually look for a redeclaration in a category
+      // if necessary.
+      if (Warn && HasRedeclarationWithoutAvailabilityInCategory(D))
+        Warn = false;
+
+      if (Warn)
+        S.EmitAvailabilityWarning(Sema::AD_Partial, D, Message, Loc,
+                                  UnknownObjCClass, ObjCPDecl,
+                                  ObjCPropertyAccess);
+      break;
+    }
+
     case AR_Deprecated:
       if (S.getCurContextAvailability() != AR_Deprecated)
         S.EmitAvailabilityWarning(Sema::AD_Deprecation,
@@ -307,7 +341,8 @@
         DeduceReturnType(FD, Loc))
       return true;
   }
-  DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass, ObjCPropertyAccess);
+  DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
+                             ObjCPropertyAccess);
 
   DiagnoseUnusedOfDecl(*this, D, Loc);
 
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp	(revision 230929)
+++ lib/Sema/SemaDeclAttr.cpp	(working copy)
@@ -5105,7 +5105,7 @@
   return false;
 }
 
-static void DoEmitAvailabilityWarning(Sema &S, DelayedDiagnostic::DDKind K,
+static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K,
                                       Decl *Ctx, const NamedDecl *D,
                                       StringRef Message, SourceLocation Loc,
                                       const ObjCInterfaceDecl *UnknownObjCClass,
@@ -5122,7 +5122,7 @@
 
   // Don't warn if our current context is deprecated or unavailable.
   switch (K) {
-  case DelayedDiagnostic::Deprecation:
+  case Sema::AD_Deprecation:
     if (isDeclDeprecated(Ctx))
       return;
     diag = !ObjCPropertyAccess ? diag::warn_deprecated
@@ -5133,7 +5133,7 @@
     available_here_select_kind = /* deprecated */ 2;
     break;
 
-  case DelayedDiagnostic::Unavailable:
+  case Sema::AD_Unavailable:
     if (isDeclUnavailable(Ctx))
       return;
     diag = !ObjCPropertyAccess ? diag::err_unavailable
@@ -5144,8 +5144,13 @@
     available_here_select_kind = /* unavailable */ 0;
     break;
 
-  default:
-    llvm_unreachable("Neither a deprecation or unavailable kind");
+  case Sema::AD_Partial:
+    diag = diag::warn_partial_availability;
+    diag_message = diag::warn_partial_message;
+    diag_fwdclass_message = diag::warn_partial_fwdclass_message;
+    property_note_select = /* partial */ 2;
+    available_here_select_kind = /* partial */ 3;
+    break;
   }
 
   if (!Message.empty()) {
@@ -5165,15 +5170,21 @@
 
   S.Diag(D->getLocation(), diag::note_availability_specified_here)
       << D << available_here_select_kind;
+  if (K == Sema::AD_Partial)
+    S.Diag(Loc, diag::note_partial_availability_silence) << D;
 }
 
 static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD,
                                            Decl *Ctx) {
+  assert(DD.Kind == DelayedDiagnostic::Deprecation ||
+         DD.Kind == DelayedDiagnostic::Unavailable);
+  Sema::AvailabilityDiagnostic AD = DD.Kind == DelayedDiagnostic::Deprecation
+                                        ? Sema::AD_Deprecation
+                                        : Sema::AD_Unavailable;
   DD.Triggered = true;
-  DoEmitAvailabilityWarning(S, (DelayedDiagnostic::DDKind)DD.Kind, Ctx,
-                            DD.getDeprecationDecl(), DD.getDeprecationMessage(),
-                            DD.Loc, DD.getUnknownObjCClass(),
-                            DD.getObjCProperty(), false);
+  DoEmitAvailabilityWarning(
+      S, AD, Ctx, DD.getDeprecationDecl(), DD.getDeprecationMessage(), DD.Loc,
+      DD.getUnknownObjCClass(), DD.getObjCProperty(), false);
 }
 
 void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) {
@@ -5239,7 +5250,7 @@
                                    const ObjCPropertyDecl  *ObjCProperty,
                                    bool ObjCPropertyAccess) {
   // Delay if we're currently parsing a declaration.
-  if (DelayedDiagnostics.shouldDelayDiagnostics()) {
+  if (DelayedDiagnostics.shouldDelayDiagnostics() && AD != AD_Partial) {
     DelayedDiagnostics.add(DelayedDiagnostic::makeAvailability(
         AD, Loc, D, UnknownObjCClass, ObjCProperty, Message,
         ObjCPropertyAccess));
@@ -5247,16 +5258,6 @@
   }
 
   Decl *Ctx = cast<Decl>(getCurLexicalContext());
-  DelayedDiagnostic::DDKind K;
-  switch (AD) {
-    case AD_Deprecation:
-      K = DelayedDiagnostic::Deprecation;
-      break;
-    case AD_Unavailable:
-      K = DelayedDiagnostic::Unavailable;
-      break;
-  }
-
-  DoEmitAvailabilityWarning(*this, K, Ctx, D, Message, Loc,
-                            UnknownObjCClass, ObjCProperty, ObjCPropertyAccess);
+  DoEmitAvailabilityWarning(*this, AD, Ctx, D, Message, Loc, UnknownObjCClass,
+                            ObjCProperty, ObjCPropertyAccess);
 }
Index: test/Sema/attr-availability.c
===================================================================
--- test/Sema/attr-availability.c	(revision 230929)
+++ test/Sema/attr-availability.c	(working copy)
@@ -1,4 +1,6 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fsyntax-only -fblocks -verify %s
+// RUN: %clang_cc1 -D WARN_PARTIAL -Wpartial-availability -triple x86_64-apple-darwin9 -fsyntax-only -fblocks -verify %s
+// 
 
 void f0() __attribute__((availability(macosx,introduced=10.4,deprecated=10.2))); // expected-warning{{feature cannot be deprecated in OS X version 10.2 before it was introduced in version 10.4; attribute ignored}}
 void f1() __attribute__((availability(ios,obsoleted=2.1,deprecated=3.0)));  // expected-warning{{feature cannot be obsoleted in iOS version 2.1 before it was deprecated in version 3.0; attribute ignored}}
@@ -13,11 +15,34 @@
 extern void
 ATSFontGetPostScriptName(int flags) __attribute__((availability(macosx,introduced=8.0,obsoleted=9.0, message="use ATSFontGetFullPostScriptName"))); // expected-note {{'ATSFontGetPostScriptName' has been explicitly marked unavailable here}}
 
+#if defined(WARN_PARTIAL)
+// expected-note at +3 {{has been explicitly marked partial here}}
+#endif
+extern void
+PartiallyAvailable() __attribute__((availability(macosx,introduced=10.8)));
+
+enum __attribute__((availability(macosx,introduced=10.8))) PartialEnum {
+  kPartialEnumConstant,
+};
+
 void test_10095131() {
   ATSFontGetName("Hello"); // expected-warning {{'ATSFontGetName' is deprecated: first deprecated in OS X 9.0 - use CTFontCopyFullName}}
   ATSFontGetPostScriptName(100); // expected-error {{'ATSFontGetPostScriptName' is unavailable: obsoleted in OS X 9.0 - use ATSFontGetFullPostScriptName}}
+
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{is partial: introduced in OS X 10.8}} expected-note at +2 {{explicitly redeclare 'PartiallyAvailable' to silence this warning}}
+#endif
+  PartiallyAvailable();
 }
 
+extern void PartiallyAvailable() ;
+void with_redeclaration() {
+  PartiallyAvailable();  // Don't warn.
+
+  // enums should never warn.
+  enum PartialEnum p = kPartialEnumConstant;
+}
+
 // rdar://10711037
 __attribute__((availability(macos, unavailable))) // expected-warning {{attribute 'availability' is ignored}}
 enum {
Index: test/SemaObjC/attr-availability.m
===================================================================
--- test/SemaObjC/attr-availability.m	(revision 230929)
+++ test/SemaObjC/attr-availability.m	(working copy)
@@ -1,11 +1,21 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin9.0.0 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -D WARN_PARTIAL -Wpartial-availability -triple x86_64-apple-darwin9.0.0 -fsyntax-only -verify %s
 
 @protocol P
 - (void)proto_method __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 2 {{'proto_method' has been explicitly marked deprecated here}}
+
+#if defined(WARN_PARTIAL)
+  // expected-note at +2 2 {{'partial_proto_method' has been explicitly marked partial here}}
+#endif
+- (void)partial_proto_method __attribute__((availability(macosx,introduced=10.8)));
 @end
 
 @interface A <P>
 - (void)method __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note {{'method' has been explicitly marked deprecated here}}
+#if defined(WARN_PARTIAL)
+  // expected-note at +2 {{'partialMethod' has been explicitly marked partial here}}
+#endif
+- (void)partialMethod __attribute__((availability(macosx,introduced=10.8)));
 
 - (void)overridden __attribute__((availability(macosx,introduced=10.3))); // expected-note{{overridden method is here}}
 - (void)overridden2 __attribute__((availability(macosx,introduced=10.3)));
@@ -18,6 +28,7 @@
 // rdar://11475360
 @interface B : A
 - (void)method; // NOTE: we expect 'method' to *not* inherit availability.
+- (void)partialMethod; // Likewise.
 - (void)overridden __attribute__((availability(macosx,introduced=10.4))); // expected-warning{{overriding method introduced after overridden method on OS X (10.4 vs. 10.3)}}
 - (void)overridden2 __attribute__((availability(macosx,introduced=10.2)));
 - (void)overridden3 __attribute__((availability(macosx,deprecated=10.4)));
@@ -31,8 +42,34 @@
   [b method]; // no-warning
   [a proto_method]; // expected-warning{{'proto_method' is deprecated: first deprecated in OS X 10.2}}
   [b proto_method]; // expected-warning{{'proto_method' is deprecated: first deprecated in OS X 10.2}}
+
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'partialMethod' is partial: introduced in OS X 10.8}} expected-note at +2 {{explicitly redeclare 'partialMethod' to silence this warning}}
+#endif
+  [a partialMethod];
+  [b partialMethod];  // no warning
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'partial_proto_method' is partial: introduced in OS X 10.8}} expected-note at +2 {{explicitly redeclare 'partial_proto_method' to silence this warning}}
+#endif
+  [a partial_proto_method];
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'partial_proto_method' is partial: introduced in OS X 10.8}} expected-note at +2 {{explicitly redeclare 'partial_proto_method' to silence this warning}}
+#endif
+  [b partial_proto_method];
 }
 
+ at interface A (NewAPI)
+- (void)partialMethod;
+- (void)partial_proto_method;
+ at end
+
+void f_after_redecl(A *a, B *b) {
+  [a partialMethod]; // no warning
+  [b partialMethod]; // no warning
+  [a partial_proto_method]; // no warning
+  [b partial_proto_method]; // no warning
+}
+
 // Test case for <rdar://problem/11627873>.  Warn about
 // using a deprecated method when that method is re-implemented in a
 // subclass where the redeclared method is not deprecated.
@@ -87,3 +124,52 @@
 }
 
 @end
+
+ at protocol PartialProt
+- (void)ppartialMethod __attribute__((availability(macosx,introduced=10.8)));
++ (void)ppartialMethod __attribute__((availability(macosx,introduced=10.8)));
+ at end
+
+ at interface PartialI <PartialProt>
+- (void)partialMethod __attribute__((availability(macosx,introduced=10.8)));
++ (void)partialMethod __attribute__((availability(macosx,introduced=10.8)));
+ at end
+
+ at interface PartialI ()
+- (void)ipartialMethod1 __attribute__((availability(macosx,introduced=10.8)));
+#if defined(WARN_PARTIAL)
+  // expected-note at +2 {{'ipartialMethod2' has been explicitly marked partial here}}
+#endif
+- (void)ipartialMethod2 __attribute__((availability(macosx,introduced=10.8)));
++ (void)ipartialMethod1 __attribute__((availability(macosx,introduced=10.8)));
+#if defined(WARN_PARTIAL)
+  // expected-note at +2 {{'ipartialMethod2' has been explicitly marked partial here}}
+#endif
++ (void)ipartialMethod2 __attribute__((availability(macosx,introduced=10.8)));
+ at end
+
+ at interface PartialI (Redecls)
+- (void)partialMethod;
+- (void)ipartialMethod1;
+- (void)ppartialMethod;
++ (void)partialMethod;
++ (void)ipartialMethod1;
++ (void)ppartialMethod;
+ at end
+
+void partialfun(PartialI* a) {
+  [a partialMethod]; // no warning
+  [a ipartialMethod1]; // no warning
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'ipartialMethod2' is partial: introduced in OS X 10.8}} expected-note at +2 {{explicitly redeclare 'ipartialMethod2' to silence this warning}}
+#endif
+  [a ipartialMethod2];
+  [a ppartialMethod]; // no warning
+  [PartialI partialMethod]; // no warning
+  [PartialI ipartialMethod1]; // no warning
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'ipartialMethod2' is partial: introduced in OS X 10.8}} expected-note at +2 {{explicitly redeclare 'ipartialMethod2' to silence this warning}}
+#endif
+  [PartialI ipartialMethod2];
+  [PartialI ppartialMethod]; // no warning
+}
Index: test/SemaObjC/property-deprecated-warning.m
===================================================================
--- test/SemaObjC/property-deprecated-warning.m	(revision 230929)
+++ test/SemaObjC/property-deprecated-warning.m	(working copy)
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1  -fsyntax-only -triple thumbv6-apple-ios3.0 -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -D WARN_PARTIAL -Wpartial-availability -fsyntax-only -triple thumbv6-apple-ios3.0 -verify -Wno-objc-root-class %s
 // RUN: %clang_cc1 -x objective-c++ -fsyntax-only -triple thumbv6-apple-ios3.0 -verify -Wno-objc-root-class %s
 // rdar://12324295
 
@@ -6,29 +7,47 @@
 
 @protocol P
 @property(nonatomic,assign) id ptarget __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{property 'ptarget' is declared deprecated here}} expected-note {{'ptarget' has been explicitly marked deprecated here}}
+
+#if defined(WARN_PARTIAL)
+// expected-note at +2 {{property 'partialPtarget' is declared partial here}} expected-note at +2 {{'partialPtarget' has been explicitly marked partial here}}
+#endif
+ at property(nonatomic,assign) id partialPtarget __attribute__((availability(ios,introduced=5.0)));
 @end
 
 @protocol P1<P>
 - (void)setPtarget:(id)arg;
+- (void)setPartialPtarget:(id)arg;
 @end
 
 
 @interface UITableViewCell<P1>
 @property(nonatomic,assign) id target __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{property 'target' is declared deprecated here}} expected-note {{'setTarget:' has been explicitly marked deprecated here}}
+
+#if defined(WARN_PARTIAL)
+// expected-note at +2 {{property 'partialTarget' is declared partial here}} expected-note at +2 {{'setPartialTarget:' has been explicitly marked partial here}}
+#endif
+ at property(nonatomic,assign) id partialTarget __attribute__((availability(ios,introduced=5.0)));
 @end
 
 @interface PSTableCell : UITableViewCell
  - (void)setTarget:(id)target;
+ - (void)setPartialTarget:(id)target;
 @end
 
 @interface UITableViewCell(UIDeprecated)
 @property(nonatomic,assign) id dep_target  __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note 2 {{'dep_target' has been explicitly marked deprecated here}} \
                                                                                     // expected-note 4 {{property 'dep_target' is declared deprecated here}} \
                                                                                     // expected-note 2 {{'setDep_target:' has been explicitly marked deprecated here}}
+
+#if defined(WARN_PARTIAL)
+// expected-note at +2 4 {{property 'partial_dep_target' is declared partial here}} expected-note at +2 2 {{'partial_dep_target' has been explicitly marked partial here}} expected-note at +2 2 {{'setPartial_dep_target:' has been explicitly marked partial here}}
+#endif
+ at property(nonatomic,assign) id partial_dep_target  __attribute__((availability(ios,introduced=5.0)));
 @end
 
 @implementation PSTableCell
 - (void)setTarget:(id)target {};
+- (void)setPartialTarget:(id)target {};
 - (void)setPtarget:(id)val {};
 - (void) Meth {
   [self setTarget: (id)0]; // no-warning
@@ -36,20 +55,41 @@
                                            // expected-warning {{'setDep_target:' is deprecated: first deprecated in iOS 3.0}}
 					   
   [self setPtarget: (id)0]; // no-warning
+  [self setPartialTarget: (id)0]; // no-warning
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'partial_dep_target' is partial: introduced in iOS 5.0}} expected-warning at +2 {{'setPartial_dep_target:' is partial: introduced in iOS 5.0}} expected-note at +2 {{explicitly redeclare 'partial_dep_target' to silence this warning}} expected-note at +2 {{explicitly redeclare 'setPartial_dep_target:' to silence this warning}}
+#endif
+  [self setPartial_dep_target: [self partial_dep_target]];
+
+  [self setPartialPtarget: (id)0]; // no-warning
 }
 @end
 
 @implementation UITableViewCell
 @synthesize target;
+ at synthesize partialTarget;
 @synthesize ptarget;
+ at synthesize partialPtarget;
 - (void)setPtarget:(id)val {};
+- (void)setPartialPtarget:(id)val {};
 - (void)setTarget:(id)target {};
+- (void)setPartialTarget:(id)target {};
 - (void) Meth {
   [self setTarget: (id)0]; // expected-warning {{'setTarget:' is deprecated: first deprecated in iOS 3.0}}
   [self setDep_target: [self dep_target]]; // expected-warning {{'dep_target' is deprecated: first deprecated in iOS 3.0}} \
                                            // expected-warning {{'setDep_target:' is deprecated: first deprecated in iOS 3.0}}
-					   
+
   [self setPtarget: (id)0]; // no-warning
+
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'setPartialTarget:' is partial: introduced in iOS 5.0}} expected-note at +2 {{explicitly redeclare 'setPartialTarget:' to silence this warning}}
+#endif
+  [self setPartialTarget: (id)0];
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'partial_dep_target' is partial: introduced in iOS 5.0}} expected-warning at +2 {{'setPartial_dep_target:' is partial: introduced in iOS 5.0}} expected-note at +2 {{explicitly redeclare 'partial_dep_target' to silence this warning}} expected-note at +2 {{explicitly redeclare 'setPartial_dep_target:' to silence this warning}}
+#endif
+  [self setPartial_dep_target: [self partial_dep_target]];
+  [self setPartialPtarget: (id)0]; // no-warning
 }
 @end
 
@@ -58,11 +98,27 @@
 @property(getter=isEnabled,assign) BOOL enabled __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{'isEnabled' has been explicitly marked deprecated here}} expected-note {{property 'enabled' is declared deprecated here}}
 
 @property(setter=setNewDelegate:,assign) id delegate __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{'setNewDelegate:' has been explicitly marked deprecated here}} expected-note {{property 'delegate' is declared deprecated here}}
+
+#if defined(WARN_PARTIAL)
+// expected-note at +2 {{property 'partialEnabled' is declared partial here}} expected-note at +2 {{'partialIsEnabled' has been explicitly marked partial here}}
+#endif
+ at property(getter=partialIsEnabled,assign) BOOL partialEnabled __attribute__((availability(ios,introduced=5.0)));
+
+#if defined(WARN_PARTIAL)
+// expected-note at +2 {{property 'partialDelegate' is declared partial here}} expected-note at +2 {{'partialSetNewDelegate:' has been explicitly marked partial here}}
+#endif
+ at property(setter=partialSetNewDelegate:,assign) id partialDelegate __attribute__((availability(ios,introduced=5.0)));
 @end
 
 void testCustomAccessorNames(CustomAccessorNames *obj) {
   if ([obj isEnabled]) // expected-warning {{'isEnabled' is deprecated: first deprecated in iOS 3.0}}
     [obj setNewDelegate:0]; // expected-warning {{'setNewDelegate:' is deprecated: first deprecated in iOS 3.0}}
+
+#if defined(WARN_PARTIAL)
+// expected-warning at +2 {{'partialIsEnabled' is partial: introduced in iOS 5.0}} expected-warning at +3 {{'partialSetNewDelegate:' is partial: introduced in iOS 5.0}} expected-note at +2 {{explicitly redeclare 'partialIsEnabled' to silence this warning}} expected-note at +3 {{explicitly redeclare 'partialSetNewDelegate:' to silence this warning}}
+#endif
+  if ([obj partialIsEnabled])
+    [obj partialSetNewDelegate:0];
 }
 
 
@@ -71,12 +127,20 @@
 
 @interface ProtocolInCategory (TheCategory) <P1>
 - (id)ptarget;
+- (id)partialPtarget;
 @end
 
 id useDeprecatedProperty(ProtocolInCategory *obj, id<P> obj2, int flag) {
   if (flag)
     return [obj ptarget];  // no-warning
   return [obj2 ptarget];   // expected-warning {{'ptarget' is deprecated: first deprecated in iOS 3.0}}
+
+  if (flag)
+    return [obj partialPtarget];  // no-warning
+#if defined(WARN_PARTIAL)
+// expected-warning at +2 {{'partialPtarget' is partial: introduced in iOS 5.0}} expected-note at +2 {{explicitly redeclare 'partialPtarget' to silence this warning}}
+#endif
+  return [obj2 partialPtarget];
 }
 
 // rdar://15951801


More information about the cfe-commits mailing list