Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -336,7 +336,7 @@ mutable IdentifierInfo *BoolName = nullptr; /// The identifier 'NSObject'. - IdentifierInfo *NSObjectName = nullptr; + mutable IdentifierInfo *NSObjectName = nullptr; /// The identifier 'NSCopying'. IdentifierInfo *NSCopyingName = nullptr; @@ -1676,7 +1676,7 @@ } /// Retrieve the identifier 'NSObject'. - IdentifierInfo *getNSObjectName() { + IdentifierInfo *getNSObjectName() const { if (!NSObjectName) { NSObjectName = &Idents.get("NSObject"); } Index: clang/include/clang/AST/DeclObjC.h =================================================================== --- clang/include/clang/AST/DeclObjC.h +++ clang/include/clang/AST/DeclObjC.h @@ -506,6 +506,9 @@ /// 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; } Index: clang/include/clang/AST/NSAPI.h =================================================================== --- clang/include/clang/AST/NSAPI.h +++ clang/include/clang/AST/NSAPI.h @@ -166,6 +166,14 @@ 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 @@ bool isObjCEnumerator(const Expr *E, StringRef name, IdentifierInfo *&II) const; Selector getOrInitSelector(ArrayRef Ids, Selector &Sel) const; + Selector getOrInitNullarySelector(StringRef Id, Selector &Sel) const; ASTContext &Ctx; @@ -251,7 +260,7 @@ mutable Selector objectForKeyedSubscriptSel, objectAtIndexedSubscriptSel, setObjectForKeyedSubscriptSel,setObjectAtIndexedSubscriptSel, - isEqualSel; + isEqualSel, InitSel, NewSel; mutable IdentifierInfo *BOOLId, *NSIntegerId, *NSUIntegerId; mutable IdentifierInfo *NSASCIIStringEncodingId, *NSUTF8StringEncodingId; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3965,7 +3965,8 @@ void DiagnoseAvailabilityOfDecl(NamedDecl *D, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, - bool AvoidPartialAvailabilityChecks = false); + bool AvoidPartialAvailabilityChecks = false, + ObjCInterfaceDecl *ClassReceiver = nullptr); bool makeUnavailableInSystemHeader(SourceLocation loc, UnavailableAttr::ImplicitReason reason); @@ -3980,7 +3981,8 @@ bool DiagnoseUseOfDecl(NamedDecl *D, ArrayRef 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); Index: clang/lib/AST/DeclObjC.cpp =================================================================== --- clang/lib/AST/DeclObjC.cpp +++ clang/lib/AST/DeclObjC.cpp @@ -829,6 +829,14 @@ hasAttr(); } +bool ObjCMethodDecl::definedInNSObject(const ASTContext &Ctx) const { + if (const auto *PD = dyn_cast(getDeclContext())) + return PD->getIdentifier() == Ctx.getNSObjectName(); + if (const auto *ID = dyn_cast(getDeclContext())) + return ID->getIdentifier() == Ctx.getNSObjectName(); + return false; +} + bool ObjCMethodDecl::isDesignatedInitializerForTheInterface( const ObjCMethodDecl **InitMethod) const { if (getMethodFamily() != OMF_init) Index: clang/lib/AST/NSAPI.cpp =================================================================== --- clang/lib/AST/NSAPI.cpp +++ clang/lib/AST/NSAPI.cpp @@ -607,3 +607,11 @@ } 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; +} Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -6959,8 +6959,12 @@ /// \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 -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 @@ -6993,6 +6997,20 @@ } } + // For +new, infer availability from -init. + if (const auto *MD = dyn_cast(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}; } @@ -7581,7 +7599,8 @@ SmallVector AvailabilityStack; SmallVector StmtStack; - void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range); + void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, + ObjCInterfaceDecl *ClassReceiver = nullptr); public: DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) @@ -7623,9 +7642,15 @@ } 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; } @@ -7651,11 +7676,11 @@ }; 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. @@ -7837,12 +7862,14 @@ ArrayRef 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; Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -206,7 +206,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess, - bool AvoidPartialAvailabilityChecks) { + bool AvoidPartialAvailabilityChecks, + ObjCInterfaceDecl *ClassReceiver) { SourceLocation Loc = Locs.front(); if (getLangOpts().CPlusPlus && isa(D)) { // If there were any diagnostics suppressed by template argument deduction, @@ -292,7 +293,7 @@ } DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess, - AvoidPartialAvailabilityChecks); + AvoidPartialAvailabilityChecks, ClassReceiver); DiagnoseUnusedOfDecl(*this, D, Loc); Index: clang/lib/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -2471,7 +2471,8 @@ 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 @@ } 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'. Index: clang/test/SemaObjC/infer-availability-from-init.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/infer-availability-from-init.m @@ -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)) +@interface NSObject ++(instancetype)new; +-(instancetype)init; +@end + +@interface MyObject : NSObject +-(instancetype)init __attribute__((unavailable)); // expected-note{{'init' has been explicitly marked unavailable here}} +@end + +void usemyobject() { + [MyObject new]; // expected-error{{'new' is unavailable}} +} + +@interface MyOtherObject : NSObject ++(instancetype)init __attribute__((unavailable)); ++(instancetype)new; +@end + +void usemyotherobject() { + [MyOtherObject new]; // no error; new is overrideen. +} + +@interface NotGoodOverride : NSObject ++(instancetype)init __attribute__((unavailable)); +-(instancetype)new; ++(instancetype)new: (int)x; +@end + +void usenotgoodoverride() { + [NotGoodOverride new]; // no error +} + +@interface NotNSObject ++(instancetype)new; +-(instancetype)init; +@end + +@interface NotMyObject : NotNSObject +-(instancetype)init __attribute__((unavailable)); +@end + +void usenotmyobject() { + [NotMyObject new]; // no error; this isn't NSObject +} + +@interface FromSelf : NSObject +-(instancetype)init __attribute__((unavailable)); // expected-note {{'init' has been explicitly marked unavailable here}} ++(FromSelf*)another_one; +@end + +@implementation FromSelf ++(FromSelf*)another_one { + [self new]; // expected-error{{'new' is unavailable}} +} +@end