Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -7269,9 +7269,10 @@ /// 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 @@ 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(OffendingDecl)) { + if (const auto *Impl = dyn_cast(C)) { + if (MD->getClassInterface() == Impl->getClassInterface() && + MD->isDefined()) + return true; + } + } + } if (C->isUnavailable()) return true; @@ -7471,7 +7485,8 @@ 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 @@ // 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 Index: test/SemaObjC/call-unavailable-init-in-self.m =================================================================== --- test/SemaObjC/call-unavailable-init-in-self.m +++ test/SemaObjC/call-unavailable-init-in-self.m @@ -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