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.

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: Any comments on the implementation?

I'm not sure who should look at this. dgregor, you wrote r128127 which
looks in the same area.

Thanks,
Nico
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td     (revision 225408)
+++ 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 225408)
+++ include/clang/Basic/DiagnosticSemaKinds.td  (working copy)
@@ -730,7 +730,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 : 
@@ -3842,6 +3842,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 declare %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 :
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h   (revision 225408)
+++ include/clang/Sema/Sema.h   (working copy)
@@ -3341,7 +3341,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 225408)
+++ 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/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp   (revision 225414)
+++ lib/Sema/SemaDeclAttr.cpp   (working copy)
@@ -5071,7 +5071,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,
@@ -5088,7 +5088,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
@@ -5099,7 +5099,7 @@
     available_here_select_kind = /* deprecated */ 2;
     break;
 
-  case DelayedDiagnostic::Unavailable:
+  case Sema::AD_Unavailable:
     if (isDeclUnavailable(Ctx))
       return;
     diag = !ObjCPropertyAccess ? diag::err_unavailable
@@ -5110,8 +5110,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 = /* unavailable */ 2;
+    available_here_select_kind = /* unavailable */ 0;
+    break;
   }
 
   if (!Message.empty()) {
@@ -5131,15 +5136,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) {
@@ -5205,7 +5216,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));
@@ -5213,16 +5224,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: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp       (revision 225408)
+++ lib/Sema/SemaExpr.cpp       (working copy)
@@ -82,10 +82,10 @@
   }
 }
 
-static AvailabilityResult DiagnoseAvailabilityOfDecl(Sema &S,
-                              NamedDecl *D, SourceLocation Loc,
-                              const ObjCInterfaceDecl *UnknownObjCClass,
-                              bool ObjCPropertyAccess) {
+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;
     
@@ -115,9 +115,27 @@
   
   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 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;
+    }
+
     case AR_Deprecated:
       if (S.getCurContextAvailability() != AR_Deprecated)
         S.EmitAvailabilityWarning(Sema::AD_Deprecation,
@@ -307,7 +325,8 @@
         DeduceReturnType(FD, Loc))
       return true;
   }
-  DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass, 
ObjCPropertyAccess);
+  DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
+                             ObjCPropertyAccess);
 
   DiagnoseUnusedOfDecl(*this, D, Loc);
 
Index: test/Sema/attr-availability.c
===================================================================
--- test/Sema/attr-availability.c       (revision 225408)
+++ test/Sema/attr-availability.c       (working copy)
@@ -1,4 +1,5 @@
 // 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 +14,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@+3 {{has been explicitly marked unavailable 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@+2 {{is partial: introduced in OS X 10.8}} 
expected-note@+2 {{explicitly declare '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 {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to