forked from OSchip/llvm-project
[ObjC] Allow the use of implemented unavailable methods from within
the @implementation context In Objective-C, it's common for some frameworks to mark some methods like init as unavailable in the @interface to prohibit their usage. However, these frameworks then often implemented said method and refer to it in another method that acts as a factory for that object. The recent change to how messages to self are type checked in clang (r349841) introduced a regression which started to prohibit this pattern with an X is unavailable error. This commit addresses the aforementioned regression. rdar://47134898 Differential Revision: https://reviews.llvm.org/D56469 llvm-svn: 350768
This commit is contained in:
parent
c58fbe3c71
commit
4e3c0bdf6f
|
@ -7269,9 +7269,10 @@ ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
|
|||
/// whether we should emit a diagnostic for \c K and \c DeclVersion in
|
||||
/// the context of \c Ctx. For example, we should emit an unavailable diagnostic
|
||||
/// in a deprecated context, but not the other way around.
|
||||
static bool ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
|
||||
VersionTuple DeclVersion,
|
||||
Decl *Ctx) {
|
||||
static bool
|
||||
ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
|
||||
VersionTuple DeclVersion, Decl *Ctx,
|
||||
const NamedDecl *OffendingDecl) {
|
||||
assert(K != AR_Available && "Expected an unavailable declaration here!");
|
||||
|
||||
// Checks if we should emit the availability diagnostic in the context of C.
|
||||
|
@ -7280,9 +7281,22 @@ static bool ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
|
|||
if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
|
||||
if (AA->getIntroduced() >= DeclVersion)
|
||||
return true;
|
||||
} else if (K == AR_Deprecated)
|
||||
} else if (K == AR_Deprecated) {
|
||||
if (C->isDeprecated())
|
||||
return true;
|
||||
} else if (K == AR_Unavailable) {
|
||||
// It is perfectly fine to refer to an 'unavailable' Objective-C method
|
||||
// when it's actually defined and is referenced from within the
|
||||
// @implementation itself. In this context, we interpret unavailable as a
|
||||
// form of access control.
|
||||
if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) {
|
||||
if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) {
|
||||
if (MD->getClassInterface() == Impl->getClassInterface() &&
|
||||
MD->isDefined())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (C->isUnavailable())
|
||||
return true;
|
||||
|
@ -7471,7 +7485,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
|
|||
if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl))
|
||||
DeclVersion = AA->getIntroduced();
|
||||
|
||||
if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx))
|
||||
if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx,
|
||||
OffendingDecl))
|
||||
return;
|
||||
|
||||
SourceLocation Loc = Locs.front();
|
||||
|
@ -7955,7 +7970,8 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
|
|||
|
||||
// If the context of this function is less available than D, we should not
|
||||
// emit a diagnostic.
|
||||
if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx))
|
||||
if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx,
|
||||
OffendingDecl))
|
||||
return;
|
||||
|
||||
// We would like to emit the diagnostic even if -Wunguarded-availability is
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// RUN: %clang_cc1 -x objective-c -verify -fobjc-arc %s
|
||||
|
||||
@interface NSObject
|
||||
|
||||
+ (instancetype)new;
|
||||
+ (instancetype)alloc;
|
||||
|
||||
@end
|
||||
|
||||
@interface Sub: NSObject
|
||||
|
||||
- (instancetype)init __attribute__((unavailable)); // expected-note 4 {{'init' has been explicitly marked unavailable here}}
|
||||
|
||||
- (void)notImplemented __attribute__((unavailable)); // expected-note {{'notImplemented' has been explicitly marked unavailable here}}
|
||||
|
||||
@end
|
||||
|
||||
@implementation Sub
|
||||
|
||||
+ (Sub *)create {
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
+ (Sub *)create2 {
|
||||
return [self new];
|
||||
}
|
||||
|
||||
+ (Sub *)create3 {
|
||||
return [Sub new];
|
||||
}
|
||||
|
||||
- (instancetype) init {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reportUseOfUnimplemented {
|
||||
[self notImplemented]; // expected-error {{'notImplemented' is unavailable}}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface SubClassContext: Sub
|
||||
@end
|
||||
|
||||
@implementation SubClassContext
|
||||
|
||||
- (void)subClassContext {
|
||||
(void)[[Sub alloc] init]; // expected-error {{'init' is unavailable}}
|
||||
(void)[Sub new]; // expected-error {{'new' is unavailable}}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void unrelatedContext() {
|
||||
(void)[[Sub alloc] init]; // expected-error {{'init' is unavailable}}
|
||||
(void)[Sub new]; // expected-error {{'new' is unavailable}}
|
||||
}
|
||||
|
||||
@interface X @end
|
||||
|
||||
@interface X (Foo)
|
||||
-(void)meth __attribute__((unavailable));
|
||||
@end
|
||||
|
||||
@implementation X (Foo)
|
||||
-(void)meth {}
|
||||
-(void)call_it { [self meth]; }
|
||||
@end
|
Loading…
Reference in New Issue