r341874 - [Sema][ObjC] Infer availability of +new from availability of -init.

Erik Pilkington via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 10 15:20:09 PDT 2018


Author: epilk
Date: Mon Sep 10 15:20:09 2018
New Revision: 341874

URL: http://llvm.org/viewvc/llvm-project?rev=341874&view=rev
Log:
[Sema][ObjC] Infer availability of +new from availability of -init.

When defined in NSObject, +new will call -init. If -init has been marked
unavailable, diagnose uses of +new.

rdar://18335828

Differential revision: https://reviews.llvm.org/D51189

Added:
    cfe/trunk/test/SemaObjC/infer-availability-from-init.m
Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/include/clang/AST/DeclObjC.h
    cfe/trunk/include/clang/AST/NSAPI.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/DeclObjC.cpp
    cfe/trunk/lib/AST/NSAPI.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprObjC.cpp

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Mon Sep 10 15:20:09 2018
@@ -336,7 +336,7 @@ private:
   mutable IdentifierInfo *BoolName = nullptr;
 
   /// The identifier 'NSObject'.
-  IdentifierInfo *NSObjectName = nullptr;
+  mutable IdentifierInfo *NSObjectName = nullptr;
 
   /// The identifier 'NSCopying'.
   IdentifierInfo *NSCopyingName = nullptr;
@@ -1676,7 +1676,7 @@ public:
   }
 
   /// Retrieve the identifier 'NSObject'.
-  IdentifierInfo *getNSObjectName() {
+  IdentifierInfo *getNSObjectName() const {
     if (!NSObjectName) {
       NSObjectName = &Idents.get("NSObject");
     }

Modified: cfe/trunk/include/clang/AST/DeclObjC.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclObjC.h?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclObjC.h (original)
+++ cfe/trunk/include/clang/AST/DeclObjC.h Mon Sep 10 15:20:09 2018
@@ -506,6 +506,9 @@ public:
   /// Returns whether this specific method is a definition.
   bool isThisDeclarationADefinition() const { return hasBody(); }
 
+  /// Is this method defined in the NSObject base class?
+  bool definedInNSObject(const ASTContext &) const;
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == ObjCMethod; }

Modified: cfe/trunk/include/clang/AST/NSAPI.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/NSAPI.h?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/NSAPI.h (original)
+++ cfe/trunk/include/clang/AST/NSAPI.h Mon Sep 10 15:20:09 2018
@@ -166,6 +166,14 @@ public:
     return getOrInitSelector(StringRef("isEqual"), isEqualSel);
   }
 
+  Selector getNewSelector() const {
+    return getOrInitNullarySelector("new", NewSel);
+  }
+
+  Selector getInitSelector() const {
+    return getOrInitNullarySelector("init", InitSel);
+  }
+
   /// Enumerates the NSNumber methods used to generate literals.
   enum NSNumberLiteralMethodKind {
     NSNumberWithChar,
@@ -229,6 +237,7 @@ private:
   bool isObjCEnumerator(const Expr *E,
                         StringRef name, IdentifierInfo *&II) const;
   Selector getOrInitSelector(ArrayRef<StringRef> Ids, Selector &Sel) const;
+  Selector getOrInitNullarySelector(StringRef Id, Selector &Sel) const;
 
   ASTContext &Ctx;
 
@@ -251,7 +260,7 @@ private:
 
   mutable Selector objectForKeyedSubscriptSel, objectAtIndexedSubscriptSel,
                    setObjectForKeyedSubscriptSel,setObjectAtIndexedSubscriptSel,
-                   isEqualSel;
+                   isEqualSel, InitSel, NewSel;
 
   mutable IdentifierInfo *BOOLId, *NSIntegerId, *NSUIntegerId;
   mutable IdentifierInfo *NSASCIIStringEncodingId, *NSUTF8StringEncodingId;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Sep 10 15:20:09 2018
@@ -3967,7 +3967,8 @@ public:
   void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
                                   const ObjCInterfaceDecl *UnknownObjCClass,
                                   bool ObjCPropertyAccess,
-                                  bool AvoidPartialAvailabilityChecks = false);
+                                  bool AvoidPartialAvailabilityChecks = false,
+                                  ObjCInterfaceDecl *ClassReceiver = nullptr);
 
   bool makeUnavailableInSystemHeader(SourceLocation loc,
                                      UnavailableAttr::ImplicitReason reason);
@@ -3982,7 +3983,8 @@ public:
   bool DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
                          const ObjCInterfaceDecl *UnknownObjCClass = nullptr,
                          bool ObjCPropertyAccess = false,
-                         bool AvoidPartialAvailabilityChecks = false);
+                         bool AvoidPartialAvailabilityChecks = false,
+                         ObjCInterfaceDecl *ClassReciever = nullptr);
   void NoteDeletedFunction(FunctionDecl *FD);
   void NoteDeletedInheritingConstructor(CXXConstructorDecl *CD);
   std::string getDeletedOrUnavailableSuffix(const FunctionDecl *FD);

Modified: cfe/trunk/lib/AST/DeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclObjC.cpp?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclObjC.cpp (original)
+++ cfe/trunk/lib/AST/DeclObjC.cpp Mon Sep 10 15:20:09 2018
@@ -829,6 +829,14 @@ bool ObjCMethodDecl::isThisDeclarationAD
       hasAttr<ObjCDesignatedInitializerAttr>();
 }
 
+bool ObjCMethodDecl::definedInNSObject(const ASTContext &Ctx) const {
+  if (const auto *PD = dyn_cast<const ObjCProtocolDecl>(getDeclContext()))
+    return PD->getIdentifier() == Ctx.getNSObjectName();
+  if (const auto *ID = dyn_cast<const ObjCInterfaceDecl>(getDeclContext()))
+    return ID->getIdentifier() == Ctx.getNSObjectName();
+  return false;
+}
+
 bool ObjCMethodDecl::isDesignatedInitializerForTheInterface(
     const ObjCMethodDecl **InitMethod) const {
   if (getMethodFamily() != OMF_init)

Modified: cfe/trunk/lib/AST/NSAPI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/NSAPI.cpp?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/lib/AST/NSAPI.cpp (original)
+++ cfe/trunk/lib/AST/NSAPI.cpp Mon Sep 10 15:20:09 2018
@@ -607,3 +607,11 @@ Selector NSAPI::getOrInitSelector(ArrayR
   }
   return Sel;
 }
+
+Selector NSAPI::getOrInitNullarySelector(StringRef Id, Selector &Sel) const {
+  if (Sel.isNull()) {
+    IdentifierInfo *Ident = &Ctx.Idents.get(Id);
+    Sel = Ctx.Selectors.getSelector(0, &Ident);
+  }
+  return Sel;
+}

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon Sep 10 15:20:09 2018
@@ -6967,8 +6967,12 @@ static const AvailabilityAttr *getAttrFo
 /// \param D The declaration to check.
 /// \param Message If non-null, this will be populated with the message from
 /// the availability attribute that is selected.
+/// \param ClassReceiver If we're checking the the method of a class message
+/// send, the class. Otherwise nullptr.
 static std::pair<AvailabilityResult, const NamedDecl *>
-ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message) {
+ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
+                                 std::string *Message,
+                                 ObjCInterfaceDecl *ClassReceiver) {
   AvailabilityResult Result = D->getAvailability(Message);
 
   // For typedefs, if the typedef declaration appears available look
@@ -7001,6 +7005,20 @@ ShouldDiagnoseAvailabilityOfDecl(const N
       }
     }
 
+  // For +new, infer availability from -init.
+  if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+    if (S.NSAPIObj && ClassReceiver) {
+      ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
+          S.NSAPIObj->getInitSelector());
+      if (Init && Result == AR_Available && MD->isClassMethod() &&
+          MD->getSelector() == S.NSAPIObj->getNewSelector() &&
+          MD->definedInNSObject(S.getASTContext())) {
+        Result = Init->getAvailability(Message);
+        D = Init;
+      }
+    }
+  }
+
   return {Result, D};
 }
 
@@ -7589,7 +7607,8 @@ class DiagnoseUnguardedAvailability
   SmallVector<VersionTuple, 8> AvailabilityStack;
   SmallVector<const Stmt *, 16> StmtStack;
 
-  void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range);
+  void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range,
+                                ObjCInterfaceDecl *ClassReceiver = nullptr);
 
 public:
   DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
@@ -7631,9 +7650,15 @@ public:
   }
 
   bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
-    if (ObjCMethodDecl *D = Msg->getMethodDecl())
+    if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
+      ObjCInterfaceDecl *ID = nullptr;
+      QualType ReceiverTy = Msg->getClassReceiver();
+      if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())
+        ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
+
       DiagnoseDeclAvailability(
-          D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()));
+          D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
+    }
     return true;
   }
 
@@ -7659,11 +7684,11 @@ public:
 };
 
 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
-    NamedDecl *D, SourceRange Range) {
+    NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
   AvailabilityResult Result;
   const NamedDecl *OffendingDecl;
   std::tie(Result, OffendingDecl) =
-    ShouldDiagnoseAvailabilityOfDecl(D, nullptr);
+      ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
   if (Result != AR_Available) {
     // All other diagnostic kinds have already been handled in
     // DiagnoseAvailabilityOfDecl.
@@ -7845,12 +7870,14 @@ void Sema::DiagnoseAvailabilityOfDecl(Na
                                       ArrayRef<SourceLocation> Locs,
                                       const ObjCInterfaceDecl *UnknownObjCClass,
                                       bool ObjCPropertyAccess,
-                                      bool AvoidPartialAvailabilityChecks) {
+                                      bool AvoidPartialAvailabilityChecks,
+                                      ObjCInterfaceDecl *ClassReceiver) {
   std::string Message;
   AvailabilityResult Result;
   const NamedDecl* OffendingDecl;
   // See if this declaration is unavailable, deprecated, or partial.
-  std::tie(Result, OffendingDecl) = ShouldDiagnoseAvailabilityOfDecl(D, &Message);
+  std::tie(Result, OffendingDecl) =
+      ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
   if (Result == AR_Available)
     return;
 

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon Sep 10 15:20:09 2018
@@ -206,7 +206,8 @@ void Sema::MaybeSuggestAddingStaticToDec
 bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
                              const ObjCInterfaceDecl *UnknownObjCClass,
                              bool ObjCPropertyAccess,
-                             bool AvoidPartialAvailabilityChecks) {
+                             bool AvoidPartialAvailabilityChecks,
+                             ObjCInterfaceDecl *ClassReceiver) {
   SourceLocation Loc = Locs.front();
   if (getLangOpts().CPlusPlus && isa<FunctionDecl>(D)) {
     // If there were any diagnostics suppressed by template argument deduction,
@@ -292,7 +293,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *
   }
 
   DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess,
-                             AvoidPartialAvailabilityChecks);
+                             AvoidPartialAvailabilityChecks, ClassReceiver);
 
   DiagnoseUnusedOfDecl(*this, D, Loc);
 

Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=341874&r1=341873&r2=341874&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Mon Sep 10 15:20:09 2018
@@ -2471,7 +2471,8 @@ ExprResult Sema::BuildClassMessage(TypeS
     if (!Method)
       Method = Class->lookupPrivateClassMethod(Sel);
 
-    if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs))
+    if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs,
+                                    nullptr, false, false, Class))
       return ExprError();
   }
 
@@ -2784,14 +2785,19 @@ ExprResult Sema::BuildInstanceMessage(Ex
       } else {
         if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) {
           if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface()) {
+            // FIXME: Is this correct? Why are we assuming that a message to
+            // Class will call a method in the current interface?
+
             // First check the public methods in the class interface.
             Method = ClassDecl->lookupClassMethod(Sel);
 
             if (!Method)
               Method = ClassDecl->lookupPrivateClassMethod(Sel);
+
+            if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs, nullptr,
+                                            false, false, ClassDecl))
+              return ExprError();
           }
-          if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs))
-            return ExprError();
         }
         if (!Method) {
           // If not messaging 'self', look for any factory method named 'Sel'.

Added: cfe/trunk/test/SemaObjC/infer-availability-from-init.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/infer-availability-from-init.m?rev=341874&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/infer-availability-from-init.m (added)
+++ cfe/trunk/test/SemaObjC/infer-availability-from-init.m Mon Sep 10 15:20:09 2018
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -triple x86_64-apple-macosx-10.9 -Wunguarded-availability -fblocks -fsyntax-only -verify %s
+
+__attribute__((objc_root_class))
+ at interface NSObject
++(instancetype)new;
+-(instancetype)init;
+ at end
+
+ at interface MyObject : NSObject
+-(instancetype)init __attribute__((unavailable)); // expected-note{{'init' has been explicitly marked unavailable here}}
+ at end
+
+void usemyobject() {
+  [MyObject new]; // expected-error{{'new' is unavailable}}
+}
+
+ at interface MyOtherObject : NSObject
++(instancetype)init __attribute__((unavailable));
++(instancetype)new;
+ at end
+
+void usemyotherobject() {
+  [MyOtherObject new]; // no error; new is overrideen.
+}
+
+ at interface NotGoodOverride : NSObject
++(instancetype)init __attribute__((unavailable));
+-(instancetype)new;
++(instancetype)new: (int)x;
+ at end
+
+void usenotgoodoverride() {
+  [NotGoodOverride new]; // no error
+}
+
+ at interface NotNSObject
++(instancetype)new;
+-(instancetype)init;
+ at end
+
+ at interface NotMyObject : NotNSObject
+-(instancetype)init __attribute__((unavailable));
+ at end
+
+void usenotmyobject() {
+  [NotMyObject new]; // no error; this isn't NSObject
+}
+
+ at interface FromSelf : NSObject
+-(instancetype)init __attribute__((unavailable)); // expected-note {{'init' has been explicitly marked unavailable here}}
++(FromSelf*)another_one;
+ at end
+
+ at implementation FromSelf
++(FromSelf*)another_one {
+  [self new]; // expected-error{{'new' is unavailable}}
+}
+ at end




More information about the cfe-commits mailing list