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

Nico Weber thakis at chromium.org
Mon Mar 2 14:25:01 PST 2015


One more tweak: Apparently for `@interface A @end @class A;`, a lookup for
A finds the @interface decl, not the @class redecl. So for
ObjCInterfaceDecls, the explicit loop is necessary – this patch adds it
back for that case. With this change, I can build most of chrome with this
warning enabled (and some declaration tweaks in chrome). (Only "most of"
because the build is still running – no known bugs).

On Sun, Mar 1, 2015 at 7:52 PM, Nico Weber <thakis at chromium.org> wrote:

> 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/20150302/7be9e7f3/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,36 @@
   
   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;
+      // In general, D will point to the most recent redeclaration. However,
+      // for `@class A;` decls, this isn't true -- manually go through the
+      // redecl chain in that case.
+      if (Warn && isa<ObjCInterfaceDecl>(D))
+        for (Decl *Redecl = D->getMostRecentDecl(); Redecl && Warn;
+             Redecl = Redecl->getPreviousDecl())
+          if (!Redecl->hasAttr<AvailabilityAttr>() ||
+              Redecl->getAttr<AvailabilityAttr>()->isInherited())
+            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 +351,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,69 @@
 }
 
 @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
+}
+
+#if defined(WARN_PARTIAL)
+  // expected-note at +2 {{'PartialI2' has been explicitly marked partial here}}
+#endif
+__attribute__((availability(macosx, introduced = 10.8))) @interface PartialI2
+ at end
+
+#if defined(WARN_PARTIAL)
+  // expected-warning at +2 {{'PartialI2' is partial: introduced in OS X 10.8}} expected-note at +2 {{explicitly redeclare 'PartialI2' to silence this warning}}
+#endif
+void partialinter1(PartialI2* p) {
+}
+
+ at class PartialI2;
+
+void partialinter2(PartialI2* p) { // 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