forked from OSchip/llvm-project
[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 llvm-svn: 341874
This commit is contained in:
parent
afb8b5d954
commit
4257857bf8
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -829,6 +829,14 @@ bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
|
|||
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)
|
||||
|
|
|
@ -607,3 +607,11 @@ Selector NSAPI::getOrInitSelector(ArrayRef<StringRef> Ids,
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -6967,8 +6967,12 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
|
|||
/// \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 NamedDecl *D, std::string *Message) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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(NamedDecl *D,
|
|||
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;
|
||||
|
||||
|
|
|
@ -206,7 +206,8 @@ void Sema::MaybeSuggestAddingStaticToDecl(const FunctionDecl *Cur) {
|
|||
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 *D, ArrayRef<SourceLocation> Locs,
|
|||
}
|
||||
|
||||
DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess,
|
||||
AvoidPartialAvailabilityChecks);
|
||||
AvoidPartialAvailabilityChecks, ClassReceiver);
|
||||
|
||||
DiagnoseUnusedOfDecl(*this, D, Loc);
|
||||
|
||||
|
|
|
@ -2471,7 +2471,8 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo,
|
|||
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(Expr *Receiver,
|
|||
} 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'.
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue