diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index e91b7756278e..dce637fbd2f4 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9890,16 +9890,23 @@ public: return OriginalLexicalContext ? OriginalLexicalContext : CurContext; } + AvailabilityResult getCurContextAvailability() const; + + /// \brief Get the verison that this context implies. + /// For instance, a method in an interface that is annotated with an + /// availability attribuite effectively has the availability of the interface. + VersionTuple getVersionForDecl(const Decl *Ctx) const; + /// \brief The diagnostic we should emit for \c D, or \c AR_Available. /// /// \param D The declaration to check. Note that this may be altered to point /// to another declaration that \c D gets it's availability from. i.e., we /// walk the list of typedefs to find an availability attribute. /// - /// \param Message If non-null, this will be populated with the message from - /// the availability attribute that is selected. - AvailabilityResult ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, - std::string *Message); + /// \param ContextVersion The version to compare availability against. + AvailabilityResult + ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, VersionTuple ContextVersion, + std::string *Message); const DeclContext *getCurObjCLexicalContext() const { const DeclContext *DC = getCurLexicalContext(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 54b56b3b594a..af1ac443baeb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15628,3 +15628,29 @@ void Sema::ActOnPragmaWeakAlias(IdentifierInfo* Name, Decl *Sema::getObjCDeclContext() const { return (dyn_cast_or_null(CurContext)); } + +AvailabilityResult Sema::getCurContextAvailability() const { + const Decl *D = cast_or_null(getCurObjCLexicalContext()); + if (!D) + return AR_Available; + + // If we are within an Objective-C method, we should consult + // both the availability of the method as well as the + // enclosing class. If the class is (say) deprecated, + // the entire method is considered deprecated from the + // purpose of checking if the current context is deprecated. + if (const ObjCMethodDecl *MD = dyn_cast(D)) { + AvailabilityResult R = MD->getAvailability(); + if (R != AR_Available) + return R; + D = MD->getClassInterface(); + } + // If we are within an Objective-c @implementation, it + // gets the same availability context as the @interface. + else if (const ObjCImplementationDecl *ID = + dyn_cast(D)) { + D = ID->getClassInterface(); + } + // Recover from user error. + return D ? D->getAvailability() : AR_Available; +} diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 15f3139e1a21..ee995e0702e6 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6327,6 +6327,30 @@ static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag, diag.Triggered = true; } +static bool isDeclDeprecated(Decl *D) { + do { + if (D->isDeprecated()) + return true; + // A category implicitly has the availability of the interface. + if (const ObjCCategoryDecl *CatD = dyn_cast(D)) + if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) + return Interface->isDeprecated(); + } while ((D = cast_or_null(D->getDeclContext()))); + return false; +} + +static bool isDeclUnavailable(Decl *D) { + do { + if (D->isUnavailable()) + return true; + // A category implicitly has the availability of the interface. + if (const ObjCCategoryDecl *CatD = dyn_cast(D)) + if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) + return Interface->isUnavailable(); + } while ((D = cast_or_null(D->getDeclContext()))); + return false; +} + static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, const Decl *D) { // Check each AvailabilityAttr to find the one for this platform. @@ -6355,49 +6379,6 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, return nullptr; } -/// \brief 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) { - assert(K != AR_Available && "Expected an unavailable declaration here!"); - - // Checks if we should emit the availability diagnostic in the context of C. - auto CheckContext = [&](const Decl *C) { - if (K == AR_NotYetIntroduced) { - if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C)) - if (AA->getIntroduced() >= DeclVersion) - return true; - } else if (K == AR_Deprecated) - if (C->isDeprecated()) - return true; - - if (C->isUnavailable()) - return true; - return false; - }; - - do { - if (CheckContext(Ctx)) - return false; - - // An implementation implicitly has the availability of the interface. - if (auto *CatOrImpl = dyn_cast(Ctx)) { - if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface()) - if (CheckContext(Interface)) - return false; - } - // A category implicitly has the availability of the interface. - else if (auto *CatD = dyn_cast(Ctx)) - if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) - if (CheckContext(Interface)) - return false; - } while ((Ctx = cast_or_null(Ctx->getDeclContext()))); - - return true; -} - static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, Decl *Ctx, const NamedDecl *D, StringRef Message, SourceLocation Loc, @@ -6414,15 +6395,11 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, // Matches diag::note_availability_specified_here. unsigned available_here_select_kind; - VersionTuple DeclVersion; - if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, D)) - DeclVersion = AA->getIntroduced(); - - if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx)) - return; - + // Don't warn if our current context is deprecated or unavailable. switch (K) { case AR_Deprecated: + if (isDeclDeprecated(Ctx) || isDeclUnavailable(Ctx)) + return; diag = !ObjCPropertyAccess ? diag::warn_deprecated : diag::warn_property_method_deprecated; diag_message = diag::warn_deprecated_message; @@ -6432,6 +6409,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, break; case AR_Unavailable: + if (isDeclUnavailable(Ctx)) + return; diag = !ObjCPropertyAccess ? diag::err_unavailable : diag::err_property_method_unavailable; diag_message = diag::err_unavailable_message; @@ -6646,6 +6625,29 @@ void Sema::EmitAvailabilityWarning(AvailabilityResult AR, ObjCProperty, ObjCPropertyAccess); } +VersionTuple Sema::getVersionForDecl(const Decl *D) const { + assert(D && "Expected a declaration here!"); + + VersionTuple DeclVersion; + if (const auto *AA = getAttrForPlatform(getASTContext(), D)) + DeclVersion = AA->getIntroduced(); + + const ObjCInterfaceDecl *Interface = nullptr; + + if (const auto *MD = dyn_cast(D)) + Interface = MD->getClassInterface(); + else if (const auto *ID = dyn_cast(D)) + Interface = ID->getClassInterface(); + + if (Interface) { + if (const auto *AA = getAttrForPlatform(getASTContext(), Interface)) + if (AA->getIntroduced() > DeclVersion) + DeclVersion = AA->getIntroduced(); + } + + return std::max(DeclVersion, Context.getTargetInfo().getPlatformMinVersion()); +} + namespace { /// \brief This class implements -Wunguarded-availability. @@ -6659,7 +6661,6 @@ class DiagnoseUnguardedAvailability typedef RecursiveASTVisitor Base; Sema &SemaRef; - Decl *Ctx; /// Stack of potentially nested 'if (@available(...))'s. SmallVector AvailabilityStack; @@ -6667,10 +6668,9 @@ class DiagnoseUnguardedAvailability void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range); public: - DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) - : SemaRef(SemaRef), Ctx(Ctx) { - AvailabilityStack.push_back( - SemaRef.Context.getTargetInfo().getPlatformMinVersion()); + DiagnoseUnguardedAvailability(Sema &SemaRef, VersionTuple BaseVersion) + : SemaRef(SemaRef) { + AvailabilityStack.push_back(BaseVersion); } void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } @@ -6703,8 +6703,8 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( NamedDecl *D, SourceRange Range) { VersionTuple ContextVersion = AvailabilityStack.back(); - if (AvailabilityResult Result = - SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr)) { + if (AvailabilityResult Result = SemaRef.ShouldDiagnoseAvailabilityOfDecl( + D, ContextVersion, nullptr)) { // All other diagnostic kinds have already been handled in // DiagnoseAvailabilityOfDecl. if (Result != AR_NotYetIntroduced) @@ -6713,14 +6713,6 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( const AvailabilityAttr *AA = getAttrForPlatform(SemaRef.getASTContext(), D); VersionTuple Introduced = AA->getIntroduced(); - if (ContextVersion >= Introduced) - return; - - // If the context of this function is less available than D, we should not - // emit a diagnostic. - if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx)) - return; - SemaRef.Diag(Range.getBegin(), diag::warn_unguarded_availability) << Range << D << AvailabilityAttr::getPrettyPlatformName( @@ -6795,5 +6787,6 @@ void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { assert(Body && "Need a body here!"); - DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); + VersionTuple BaseVersion = getVersionForDecl(D); + DiagnoseUnguardedAvailability(*this, BaseVersion).IssueDiagnostics(Body); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 206a704fdce7..43638856fa86 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -103,9 +103,9 @@ static bool HasRedeclarationWithoutAvailabilityInCategory(const Decl *D) { return false; } -AvailabilityResult -Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { - AvailabilityResult Result = D->getAvailability(Message); +AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl( + NamedDecl *&D, VersionTuple ContextVersion, std::string *Message) { + AvailabilityResult Result = D->getAvailability(Message, ContextVersion); // For typedefs, if the typedef declaration appears available look // to the underlying type to see if it is more restrictive. @@ -113,7 +113,7 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { if (Result == AR_Available) { if (const TagType *TT = TD->getUnderlyingType()->getAs()) { D = TT->getDecl(); - Result = D->getAvailability(Message); + Result = D->getAvailability(Message, ContextVersion); continue; } } @@ -124,7 +124,7 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { if (ObjCInterfaceDecl *IDecl = dyn_cast(D)) { if (IDecl->getDefinition()) { D = IDecl->getDefinition(); - Result = D->getAvailability(Message); + Result = D->getAvailability(Message, ContextVersion); } } @@ -132,10 +132,18 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { if (Result == AR_Available) { const DeclContext *DC = ECD->getDeclContext(); if (const EnumDecl *TheEnumDecl = dyn_cast(DC)) - Result = TheEnumDecl->getAvailability(Message); + Result = TheEnumDecl->getAvailability(Message, ContextVersion); } - if (Result == AR_NotYetIntroduced) { + switch (Result) { + case AR_Available: + return Result; + + case AR_Unavailable: + case AR_Deprecated: + return getCurContextAvailability() != Result ? Result : AR_Available; + + case AR_NotYetIntroduced: { // Don't do this for enums, they can't be redeclared. if (isa(D) || isa(D)) return AR_Available; @@ -158,18 +166,23 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { return Warn ? AR_NotYetIntroduced : AR_Available; } - - return Result; + } + llvm_unreachable("Unknown availability result!"); } static void DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, const ObjCInterfaceDecl *UnknownObjCClass, bool ObjCPropertyAccess) { + VersionTuple ContextVersion; + if (const DeclContext *DC = S.getCurObjCLexicalContext()) + ContextVersion = S.getVersionForDecl(cast(DC)); + std::string Message; - // See if this declaration is unavailable, deprecated, or partial. + // See if this declaration is unavailable, deprecated, or partial in the + // current context. if (AvailabilityResult Result = - S.ShouldDiagnoseAvailabilityOfDecl(D, &Message)) { + S.ShouldDiagnoseAvailabilityOfDecl(D, ContextVersion, &Message)) { if (Result == AR_NotYetIntroduced && S.getCurFunctionOrMethodDecl()) { S.getEnclosingFunction()->HasPotentialAvailabilityViolations = true; @@ -179,7 +192,8 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, const ObjCPropertyDecl *ObjCPDecl = nullptr; if (const ObjCMethodDecl *MD = dyn_cast(D)) { if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { - AvailabilityResult PDeclResult = PD->getAvailability(nullptr); + AvailabilityResult PDeclResult = + PD->getAvailability(nullptr, ContextVersion); if (PDeclResult == Result) ObjCPDecl = PD; } diff --git a/clang/test/SemaObjC/class-unavail-warning.m b/clang/test/SemaObjC/class-unavail-warning.m index 86f0b53d0b37..6a683dd349c2 100644 --- a/clang/test/SemaObjC/class-unavail-warning.m +++ b/clang/test/SemaObjC/class-unavail-warning.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -fblocks -triple x86_64-apple-darwin10 -verify %s +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-apple-darwin10 -verify %s // rdar://9092208 __attribute__((unavailable("not available"))) @@ -98,19 +98,3 @@ UNAVAILABLE @end @interface UnavailSub(cat) // no error @end - -int unavail_global UNAVAILABLE; - -UNAVAILABLE __attribute__((objc_root_class)) -@interface TestAttrContext --meth; -@end - -@implementation TestAttrContext --meth { - unavail_global = 2; // no warn - (void) ^{ - unavail_global = 4; // no warn - }; -} -@end