mutable IdentifierInfo *BoolName = nullptr;
/// The identifier 'NSObject'.
- IdentifierInfo *NSObjectName = nullptr;
+ mutable IdentifierInfo *NSObjectName = nullptr;
/// The identifier 'NSCopying'.
IdentifierInfo *NSCopyingName = nullptr;
}
/// Retrieve the identifier 'NSObject'.
- IdentifierInfo *getNSObjectName() {
+ IdentifierInfo *getNSObjectName() const {
if (!NSObjectName) {
NSObjectName = &Idents.get("NSObject");
}
/// 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; }
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,
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;
mutable Selector objectForKeyedSubscriptSel, objectAtIndexedSubscriptSel,
setObjectForKeyedSubscriptSel,setObjectAtIndexedSubscriptSel,
- isEqualSel;
+ isEqualSel, InitSel, NewSel;
mutable IdentifierInfo *BOOLId, *NSIntegerId, *NSUIntegerId;
mutable IdentifierInfo *NSASCIIStringEncodingId, *NSUTF8StringEncodingId;
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);
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);
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)
}
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;
+}
/// \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
}
}
+ // 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};
}
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)
}
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;
}
};
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.
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;
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,
}
DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess,
- AvoidPartialAvailabilityChecks);
+ AvoidPartialAvailabilityChecks, ClassReceiver);
DiagnoseUnusedOfDecl(*this, D, Loc);
if (!Method)
Method = Class->lookupPrivateClassMethod(Sel);
- if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs))
+ if (Method && DiagnoseUseOfDecl(Method, SelectorSlotLocs,
+ nullptr, false, false, Class))
return ExprError();
}
} 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'.
--- /dev/null
+// 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