This revision was automatically updated to reflect the committed changes. Closed by commit rL284265: [Sema] Refactor context checking for availability diagnostics (authored by epilk).
Changed prior to commit: https://reviews.llvm.org/D25283?vs=74547&id=74729#toc Repository: rL LLVM https://reviews.llvm.org/D25283 Files: cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaDeclAttr.cpp cfe/trunk/lib/Sema/SemaExpr.cpp cfe/trunk/test/SemaObjC/class-unavail-warning.m
Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -9889,23 +9889,16 @@ 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 ContextVersion The version to compare availability against. - AvailabilityResult - ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, VersionTuple ContextVersion, - std::string *Message); + /// \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); const DeclContext *getCurObjCLexicalContext() const { const DeclContext *DC = getCurLexicalContext(); Index: cfe/trunk/test/SemaObjC/class-unavail-warning.m =================================================================== --- cfe/trunk/test/SemaObjC/class-unavail-warning.m +++ cfe/trunk/test/SemaObjC/class-unavail-warning.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -triple x86_64-apple-darwin10 -verify %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -triple x86_64-apple-darwin10 -verify %s // rdar://9092208 __attribute__((unavailable("not available"))) @@ -98,3 +98,19 @@ @end @interface UnavailSub(cat)<SomeProto> // 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 Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -15615,29 +15615,3 @@ Decl *Sema::getObjCDeclContext() const { return (dyn_cast_or_null<ObjCContainerDecl>(CurContext)); } - -AvailabilityResult Sema::getCurContextAvailability() const { - const Decl *D = cast_or_null<Decl>(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<ObjCMethodDecl>(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<ObjCImplementationDecl>(D)) { - D = ID->getClassInterface(); - } - // Recover from user error. - return D ? D->getAvailability() : AR_Available; -} Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp @@ -6317,30 +6317,6 @@ 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<ObjCCategoryDecl>(D)) - if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) - return Interface->isDeprecated(); - } while ((D = cast_or_null<Decl>(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<ObjCCategoryDecl>(D)) - if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) - return Interface->isUnavailable(); - } while ((D = cast_or_null<Decl>(D->getDeclContext()))); - return false; -} - static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, const Decl *D) { // Check each AvailabilityAttr to find the one for this platform. @@ -6369,6 +6345,49 @@ 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<ObjCImplDecl>(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<ObjCCategoryDecl>(Ctx)) + if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface()) + if (CheckContext(Interface)) + return false; + } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext()))); + + return true; +} + static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, Decl *Ctx, const NamedDecl *D, StringRef Message, SourceLocation Loc, @@ -6385,11 +6404,15 @@ // Matches diag::note_availability_specified_here. unsigned available_here_select_kind; - // Don't warn if our current context is deprecated or unavailable. + VersionTuple DeclVersion; + if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, D)) + DeclVersion = AA->getIntroduced(); + + if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx)) + return; + 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; @@ -6399,8 +6422,6 @@ break; case AR_Unavailable: - if (isDeclUnavailable(Ctx)) - return; diag = !ObjCPropertyAccess ? diag::err_unavailable : diag::err_property_method_unavailable; diag_message = diag::err_unavailable_message; @@ -6615,29 +6636,6 @@ 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<ObjCMethodDecl>(D)) - Interface = MD->getClassInterface(); - else if (const auto *ID = dyn_cast<ObjCImplementationDecl>(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. @@ -6651,16 +6649,18 @@ typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base; Sema &SemaRef; + Decl *Ctx; /// Stack of potentially nested 'if (@available(...))'s. SmallVector<VersionTuple, 8> AvailabilityStack; void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range); public: - DiagnoseUnguardedAvailability(Sema &SemaRef, VersionTuple BaseVersion) - : SemaRef(SemaRef) { - AvailabilityStack.push_back(BaseVersion); + DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) + : SemaRef(SemaRef), Ctx(Ctx) { + AvailabilityStack.push_back( + SemaRef.Context.getTargetInfo().getPlatformMinVersion()); } void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } @@ -6693,16 +6693,24 @@ NamedDecl *D, SourceRange Range) { VersionTuple ContextVersion = AvailabilityStack.back(); - if (AvailabilityResult Result = SemaRef.ShouldDiagnoseAvailabilityOfDecl( - D, ContextVersion, nullptr)) { + if (AvailabilityResult Result = + SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr)) { // All other diagnostic kinds have already been handled in // DiagnoseAvailabilityOfDecl. if (Result != AR_NotYetIntroduced) return; 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( @@ -6777,6 +6785,5 @@ assert(Body && "Need a body here!"); - VersionTuple BaseVersion = getVersionForDecl(D); - DiagnoseUnguardedAvailability(*this, BaseVersion).IssueDiagnostics(Body); + DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); } Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -103,17 +103,17 @@ return false; } -AvailabilityResult Sema::ShouldDiagnoseAvailabilityOfDecl( - NamedDecl *&D, VersionTuple ContextVersion, std::string *Message) { - AvailabilityResult Result = D->getAvailability(Message, ContextVersion); +AvailabilityResult +Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { + AvailabilityResult Result = D->getAvailability(Message); // For typedefs, if the typedef declaration appears available look // to the underlying type to see if it is more restrictive. while (const TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { if (Result == AR_Available) { if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) { D = TT->getDecl(); - Result = D->getAvailability(Message, ContextVersion); + Result = D->getAvailability(Message); continue; } } @@ -124,26 +124,18 @@ if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { if (IDecl->getDefinition()) { D = IDecl->getDefinition(); - Result = D->getAvailability(Message, ContextVersion); + Result = D->getAvailability(Message); } } if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) if (Result == AR_Available) { const DeclContext *DC = ECD->getDeclContext(); if (const EnumDecl *TheEnumDecl = dyn_cast<EnumDecl>(DC)) - Result = TheEnumDecl->getAvailability(Message, ContextVersion); + Result = TheEnumDecl->getAvailability(Message); } - switch (Result) { - case AR_Available: - return Result; - - case AR_Unavailable: - case AR_Deprecated: - return getCurContextAvailability() != Result ? Result : AR_Available; - - case AR_NotYetIntroduced: { + if (Result == AR_NotYetIntroduced) { // Don't do this for enums, they can't be redeclared. if (isa<EnumConstantDecl>(D) || isa<EnumDecl>(D)) return AR_Available; @@ -166,23 +158,18 @@ return Warn ? AR_NotYetIntroduced : AR_Available; } - } - llvm_unreachable("Unknown availability result!"); + + return 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<Decl>(DC)); - std::string Message; - // See if this declaration is unavailable, deprecated, or partial in the - // current context. + // See if this declaration is unavailable, deprecated, or partial. if (AvailabilityResult Result = - S.ShouldDiagnoseAvailabilityOfDecl(D, ContextVersion, &Message)) { + S.ShouldDiagnoseAvailabilityOfDecl(D, &Message)) { if (Result == AR_NotYetIntroduced && S.getCurFunctionOrMethodDecl()) { S.getEnclosingFunction()->HasPotentialAvailabilityViolations = true; @@ -192,8 +179,7 @@ const ObjCPropertyDecl *ObjCPDecl = nullptr; if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { - AvailabilityResult PDeclResult = - PD->getAvailability(nullptr, ContextVersion); + AvailabilityResult PDeclResult = PD->getAvailability(nullptr); if (PDeclResult == Result) ObjCPDecl = PD; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits