|  | //===--- SemaAvailability.cpp - Availability attribute handling -----------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | //  This file processes the availability attribute. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/AST/Attr.h" | 
|  | #include "clang/AST/Decl.h" | 
|  | #include "clang/AST/DeclTemplate.h" | 
|  | #include "clang/AST/DynamicRecursiveASTVisitor.h" | 
|  | #include "clang/AST/ExprObjC.h" | 
|  | #include "clang/AST/StmtObjC.h" | 
|  | #include "clang/Basic/DiagnosticSema.h" | 
|  | #include "clang/Basic/IdentifierTable.h" | 
|  | #include "clang/Basic/LangOptions.h" | 
|  | #include "clang/Basic/TargetInfo.h" | 
|  | #include "clang/Lex/Preprocessor.h" | 
|  | #include "clang/Sema/DelayedDiagnostic.h" | 
|  | #include "clang/Sema/ScopeInfo.h" | 
|  | #include "clang/Sema/Sema.h" | 
|  | #include "clang/Sema/SemaObjC.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  | #include <optional> | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace sema; | 
|  |  | 
|  | static bool hasMatchingEnvironmentOrNone(const ASTContext &Context, | 
|  | const AvailabilityAttr *AA) { | 
|  | IdentifierInfo *IIEnvironment = AA->getEnvironment(); | 
|  | auto Environment = Context.getTargetInfo().getTriple().getEnvironment(); | 
|  | if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment) | 
|  | return true; | 
|  |  | 
|  | llvm::Triple::EnvironmentType ET = | 
|  | AvailabilityAttr::getEnvironmentType(IIEnvironment->getName()); | 
|  | return Environment == ET; | 
|  | } | 
|  |  | 
|  | static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context, | 
|  | const Decl *D) { | 
|  | AvailabilityAttr const *PartialMatch = nullptr; | 
|  | // Check each AvailabilityAttr to find the one for this platform. | 
|  | // For multiple attributes with the same platform try to find one for this | 
|  | // environment. | 
|  | // The attribute is always on the FunctionDecl, not on the | 
|  | // FunctionTemplateDecl. | 
|  | if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) | 
|  | D = FTD->getTemplatedDecl(); | 
|  | for (const auto *A : D->attrs()) { | 
|  | if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) { | 
|  | // FIXME: this is copied from CheckAvailability. We should try to | 
|  | // de-duplicate. | 
|  |  | 
|  | // Check if this is an App Extension "platform", and if so chop off | 
|  | // the suffix for matching with the actual platform. | 
|  | StringRef ActualPlatform = Avail->getPlatform()->getName(); | 
|  | StringRef RealizedPlatform = ActualPlatform; | 
|  | if (Context.getLangOpts().AppExt) { | 
|  | size_t suffix = RealizedPlatform.rfind("_app_extension"); | 
|  | if (suffix != StringRef::npos) | 
|  | RealizedPlatform = RealizedPlatform.slice(0, suffix); | 
|  | } | 
|  |  | 
|  | StringRef TargetPlatform = Context.getTargetInfo().getPlatformName(); | 
|  |  | 
|  | // Match the platform name. | 
|  | if (RealizedPlatform == TargetPlatform) { | 
|  | // Find the best matching attribute for this environment | 
|  | if (hasMatchingEnvironmentOrNone(Context, Avail)) | 
|  | return Avail; | 
|  | PartialMatch = Avail; | 
|  | } | 
|  | } | 
|  | } | 
|  | return PartialMatch; | 
|  | } | 
|  |  | 
|  | /// The diagnostic we should emit for \c D, and the declaration that | 
|  | /// originated it, or \c AR_Available. | 
|  | /// | 
|  | /// \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 method of a class message | 
|  | /// send, the class. Otherwise nullptr. | 
|  | std::pair<AvailabilityResult, const NamedDecl *> | 
|  | Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message, | 
|  | ObjCInterfaceDecl *ClassReceiver) { | 
|  | 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 auto *TD = dyn_cast<TypedefNameDecl>(D)) { | 
|  | if (Result != AR_Available) | 
|  | break; | 
|  | for (const Type *T = TD->getUnderlyingType().getTypePtr(); /**/; /**/) { | 
|  | if (auto *TT = dyn_cast<TagType>(T)) { | 
|  | D = TT->getDecl()->getDefinitionOrSelf(); | 
|  | } else if (isa<SubstTemplateTypeParmType>(T)) { | 
|  | // A Subst* node represents a use through a template. | 
|  | // Any uses of the underlying declaration happened through it's template | 
|  | // specialization. | 
|  | goto done; | 
|  | } else { | 
|  | const Type *NextT = | 
|  | T->getLocallyUnqualifiedSingleStepDesugaredType().getTypePtr(); | 
|  | if (NextT == T) | 
|  | goto done; | 
|  | T = NextT; | 
|  | continue; | 
|  | } | 
|  | Result = D->getAvailability(Message); | 
|  | break; | 
|  | } | 
|  | } | 
|  | done: | 
|  | // For alias templates, get the underlying declaration. | 
|  | if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) { | 
|  | D = ADecl->getTemplatedDecl(); | 
|  | Result = D->getAvailability(Message); | 
|  | } | 
|  |  | 
|  | // Forward class declarations get their attributes from their definition. | 
|  | if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { | 
|  | if (IDecl->getDefinition()) { | 
|  | D = IDecl->getDefinition(); | 
|  | Result = D->getAvailability(Message); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) | 
|  | if (Result == AR_Available) { | 
|  | const DeclContext *DC = ECD->getDeclContext(); | 
|  | if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) { | 
|  | Result = TheEnumDecl->getAvailability(Message); | 
|  | D = TheEnumDecl; | 
|  | } | 
|  | } | 
|  |  | 
|  | // For +new, infer availability from -init. | 
|  | if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { | 
|  | if (ObjC().NSAPIObj && ClassReceiver) { | 
|  | ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod( | 
|  | ObjC().NSAPIObj->getInitSelector()); | 
|  | if (Init && Result == AR_Available && MD->isClassMethod() && | 
|  | MD->getSelector() == ObjC().NSAPIObj->getNewSelector() && | 
|  | MD->definedInNSObject(getASTContext())) { | 
|  | Result = Init->getAvailability(Message); | 
|  | D = Init; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return {Result, 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, | 
|  | const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) { | 
|  | assert(K != AR_Available && "Expected an unavailable declaration here!"); | 
|  |  | 
|  | // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic. | 
|  | auto DeclLoc = Ctx->getBeginLoc(); | 
|  | // This is only a problem in Foundation's C++ implementation for CF_OPTIONS. | 
|  | if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus && | 
|  | isa<TypedefDecl>(OffendingDecl)) { | 
|  | StringRef MacroName = S.getPreprocessor().getImmediateMacroName(DeclLoc); | 
|  | if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" || | 
|  | MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to | 
|  | // strict (-fhlsl-strict-availability), or if the target is library and the | 
|  | // availability is restricted to a specific environment/shader stage. | 
|  | // For libraries the availability will be checked later in | 
|  | // DiagnoseHLSLAvailability class once where the specific environment/shader | 
|  | // stage of the caller is known. | 
|  | // We only do this for APIs that are not explicitly deprecated. Any API that | 
|  | // is explicitly deprecated we always issue a diagnostic on. | 
|  | if (S.getLangOpts().HLSL && K != AR_Deprecated) { | 
|  | if (!S.getLangOpts().HLSLStrictAvailability || | 
|  | (DeclEnv != nullptr && | 
|  | S.getASTContext().getTargetInfo().getTriple().getEnvironment() == | 
|  | llvm::Triple::EnvironmentType::Library)) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (K == AR_Deprecated) { | 
|  | if (const auto *VD = dyn_cast<VarDecl>(OffendingDecl)) | 
|  | if (VD->isLocalVarDeclOrParm() && VD->isDeprecated()) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // 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 && | 
|  | AA->getEnvironment() == DeclEnv) | 
|  | return true; | 
|  | } 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 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()) | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (C->isUnavailable()) | 
|  | return true; | 
|  | return false; | 
|  | }; | 
|  |  | 
|  | do { | 
|  | if (CheckContext(Ctx)) | 
|  | return false; | 
|  |  | 
|  | // An implementation implicitly has the availability of the interface. | 
|  | // Unless it is "+load" method. | 
|  | if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx)) | 
|  | if (MethodD->isClassMethod() && | 
|  | MethodD->getSelector().getAsString() == "load") | 
|  | return true; | 
|  |  | 
|  | if (const 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 (const 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 unsigned getAvailabilityDiagnosticKind( | 
|  | const ASTContext &Context, const VersionTuple &DeploymentVersion, | 
|  | const VersionTuple &DeclVersion, bool HasMatchingEnv) { | 
|  | const auto &Triple = Context.getTargetInfo().getTriple(); | 
|  | VersionTuple ForceAvailabilityFromVersion; | 
|  | switch (Triple.getOS()) { | 
|  | // For iOS, emit the diagnostic even if -Wunguarded-availability is | 
|  | // not specified for deployment targets >= to iOS 11 or equivalent or | 
|  | // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or | 
|  | // later. | 
|  | case llvm::Triple::IOS: | 
|  | case llvm::Triple::TvOS: | 
|  | ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11); | 
|  | break; | 
|  | case llvm::Triple::WatchOS: | 
|  | ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4); | 
|  | break; | 
|  | case llvm::Triple::Darwin: | 
|  | case llvm::Triple::MacOSX: | 
|  | ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13); | 
|  | break; | 
|  | // For HLSL, use diagnostic from HLSLAvailability group which | 
|  | // are reported as errors by default and in strict diagnostic mode | 
|  | // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic | 
|  | // mode (-Wno-error=hlsl-availability) | 
|  | case llvm::Triple::ShaderModel: | 
|  | return HasMatchingEnv ? diag::warn_hlsl_availability | 
|  | : diag::warn_hlsl_availability_unavailable; | 
|  | default: | 
|  | // New Apple targets should always warn about availability. | 
|  | ForceAvailabilityFromVersion = | 
|  | (Triple.getVendor() == llvm::Triple::Apple) | 
|  | ? VersionTuple(/*Major=*/0, 0) | 
|  | : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1); | 
|  | } | 
|  | if (DeploymentVersion >= ForceAvailabilityFromVersion || | 
|  | DeclVersion >= ForceAvailabilityFromVersion) | 
|  | return HasMatchingEnv ? diag::warn_unguarded_availability_new | 
|  | : diag::warn_unguarded_availability_unavailable_new; | 
|  | return HasMatchingEnv ? diag::warn_unguarded_availability | 
|  | : diag::warn_unguarded_availability_unavailable; | 
|  | } | 
|  |  | 
|  | static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { | 
|  | for (Decl *Ctx = OrigCtx; Ctx; | 
|  | Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) { | 
|  | if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx)) | 
|  | return cast<NamedDecl>(Ctx); | 
|  | if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) { | 
|  | if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx)) | 
|  | return Imp->getClassInterface(); | 
|  | return CD; | 
|  | } | 
|  | } | 
|  |  | 
|  | return dyn_cast<NamedDecl>(OrigCtx); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | struct AttributeInsertion { | 
|  | StringRef Prefix; | 
|  | SourceLocation Loc; | 
|  | StringRef Suffix; | 
|  |  | 
|  | static AttributeInsertion createInsertionAfter(const NamedDecl *D) { | 
|  | return {" ", D->getEndLoc(), ""}; | 
|  | } | 
|  | static AttributeInsertion createInsertionAfter(SourceLocation Loc) { | 
|  | return {" ", Loc, ""}; | 
|  | } | 
|  | static AttributeInsertion createInsertionBefore(const NamedDecl *D) { | 
|  | return {"", D->getBeginLoc(), "\n"}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // end anonymous namespace | 
|  |  | 
|  | /// Tries to parse a string as ObjC method name. | 
|  | /// | 
|  | /// \param Name The string to parse. Expected to originate from availability | 
|  | /// attribute argument. | 
|  | /// \param SlotNames The vector that will be populated with slot names. In case | 
|  | /// of unsuccessful parsing can contain invalid data. | 
|  | /// \returns A number of method parameters if parsing was successful, | 
|  | /// std::nullopt otherwise. | 
|  | static std::optional<unsigned> | 
|  | tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames, | 
|  | const LangOptions &LangOpts) { | 
|  | // Accept replacements starting with - or + as valid ObjC method names. | 
|  | if (!Name.empty() && (Name.front() == '-' || Name.front() == '+')) | 
|  | Name = Name.drop_front(1); | 
|  | if (Name.empty()) | 
|  | return std::nullopt; | 
|  | Name.split(SlotNames, ':'); | 
|  | unsigned NumParams; | 
|  | if (Name.back() == ':') { | 
|  | // Remove an empty string at the end that doesn't represent any slot. | 
|  | SlotNames.pop_back(); | 
|  | NumParams = SlotNames.size(); | 
|  | } else { | 
|  | if (SlotNames.size() != 1) | 
|  | // Not a valid method name, just a colon-separated string. | 
|  | return std::nullopt; | 
|  | NumParams = 0; | 
|  | } | 
|  | // Verify all slot names are valid. | 
|  | bool AllowDollar = LangOpts.DollarIdents; | 
|  | for (StringRef S : SlotNames) { | 
|  | if (S.empty()) | 
|  | continue; | 
|  | if (!isValidAsciiIdentifier(S, AllowDollar)) | 
|  | return std::nullopt; | 
|  | } | 
|  | return NumParams; | 
|  | } | 
|  |  | 
|  | /// Returns a source location in which it's appropriate to insert a new | 
|  | /// attribute for the given declaration \D. | 
|  | static std::optional<AttributeInsertion> | 
|  | createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, | 
|  | const LangOptions &LangOpts) { | 
|  | if (isa<ObjCPropertyDecl>(D)) | 
|  | return AttributeInsertion::createInsertionAfter(D); | 
|  | if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { | 
|  | if (MD->hasBody()) | 
|  | return std::nullopt; | 
|  | return AttributeInsertion::createInsertionAfter(D); | 
|  | } | 
|  | if (const auto *TD = dyn_cast<TagDecl>(D)) { | 
|  | SourceLocation Loc = | 
|  | Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts); | 
|  | if (Loc.isInvalid()) | 
|  | return std::nullopt; | 
|  | // Insert after the 'struct'/whatever keyword. | 
|  | return AttributeInsertion::createInsertionAfter(Loc); | 
|  | } | 
|  | return AttributeInsertion::createInsertionBefore(D); | 
|  | } | 
|  |  | 
|  | /// Actually emit an availability diagnostic for a reference to an unavailable | 
|  | /// decl. | 
|  | /// | 
|  | /// \param Ctx The context that the reference occurred in | 
|  | /// \param ReferringDecl The exact declaration that was referenced. | 
|  | /// \param OffendingDecl A related decl to \c ReferringDecl that has an | 
|  | /// availability attribute corresponding to \c K attached to it. Note that this | 
|  | /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and | 
|  | /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl | 
|  | /// and OffendingDecl is the EnumDecl. | 
|  | static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, | 
|  | Decl *Ctx, const NamedDecl *ReferringDecl, | 
|  | const NamedDecl *OffendingDecl, | 
|  | StringRef Message, | 
|  | ArrayRef<SourceLocation> Locs, | 
|  | const ObjCInterfaceDecl *UnknownObjCClass, | 
|  | const ObjCPropertyDecl *ObjCProperty, | 
|  | bool ObjCPropertyAccess) { | 
|  | // Diagnostics for deprecated or unavailable. | 
|  | unsigned diag, diag_message, diag_fwdclass_message; | 
|  | unsigned diag_available_here = diag::note_availability_specified_here; | 
|  | SourceLocation NoteLocation = OffendingDecl->getLocation(); | 
|  |  | 
|  | // Matches 'diag::note_property_attribute' options. | 
|  | unsigned property_note_select; | 
|  |  | 
|  | // Matches diag::note_availability_specified_here. | 
|  | unsigned available_here_select_kind; | 
|  |  | 
|  | VersionTuple DeclVersion; | 
|  | const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl); | 
|  | const IdentifierInfo *IIEnv = nullptr; | 
|  | if (AA) { | 
|  | DeclVersion = AA->getIntroduced(); | 
|  | IIEnv = AA->getEnvironment(); | 
|  | } | 
|  |  | 
|  | if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx, | 
|  | OffendingDecl)) | 
|  | return; | 
|  |  | 
|  | SourceLocation Loc = Locs.front(); | 
|  |  | 
|  | // The declaration can have multiple availability attributes, we are looking | 
|  | // at one of them. | 
|  | if (AA && AA->isInherited()) { | 
|  | for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; | 
|  | Redecl = Redecl->getPreviousDecl()) { | 
|  | const AvailabilityAttr *AForRedecl = | 
|  | getAttrForPlatform(S.Context, Redecl); | 
|  | if (AForRedecl && !AForRedecl->isInherited()) { | 
|  | // If D is a declaration with inherited attributes, the note should | 
|  | // point to the declaration with actual attributes. | 
|  | NoteLocation = Redecl->getLocation(); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | switch (K) { | 
|  | case AR_NotYetIntroduced: { | 
|  | // We would like to emit the diagnostic even if -Wunguarded-availability is | 
|  | // not specified for deployment targets >= to iOS 11 or equivalent or | 
|  | // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or | 
|  | // later. | 
|  | assert(AA != nullptr && "expecting valid availability attribute"); | 
|  | VersionTuple Introduced = AA->getIntroduced(); | 
|  | bool EnvironmentMatchesOrNone = | 
|  | hasMatchingEnvironmentOrNone(S.getASTContext(), AA); | 
|  |  | 
|  | const TargetInfo &TI = S.getASTContext().getTargetInfo(); | 
|  | std::string PlatformName( | 
|  | AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); | 
|  | llvm::StringRef TargetEnvironment( | 
|  | llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment())); | 
|  | llvm::StringRef AttrEnvironment = | 
|  | AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; | 
|  | bool UseEnvironment = | 
|  | (!AttrEnvironment.empty() && !TargetEnvironment.empty()); | 
|  |  | 
|  | unsigned DiagKind = getAvailabilityDiagnosticKind( | 
|  | S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), | 
|  | Introduced, EnvironmentMatchesOrNone); | 
|  |  | 
|  | S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName | 
|  | << Introduced.getAsString() << UseEnvironment | 
|  | << TargetEnvironment; | 
|  |  | 
|  | S.Diag(OffendingDecl->getLocation(), | 
|  | diag::note_partial_availability_specified_here) | 
|  | << OffendingDecl << PlatformName << Introduced.getAsString() | 
|  | << S.Context.getTargetInfo().getPlatformMinVersion().getAsString() | 
|  | << UseEnvironment << AttrEnvironment << TargetEnvironment; | 
|  |  | 
|  | // Do not offer to silence the warning or fixits for HLSL | 
|  | if (S.getLangOpts().HLSL) | 
|  | return; | 
|  |  | 
|  | if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { | 
|  | if (const auto *TD = dyn_cast<TagDecl>(Enclosing)) | 
|  | if (TD->getDeclName().isEmpty()) { | 
|  | S.Diag(TD->getLocation(), | 
|  | diag::note_decl_unguarded_availability_silence) | 
|  | << /*Anonymous*/ 1 << TD->getKindName(); | 
|  | return; | 
|  | } | 
|  | auto FixitNoteDiag = | 
|  | S.Diag(Enclosing->getLocation(), | 
|  | diag::note_decl_unguarded_availability_silence) | 
|  | << /*Named*/ 0 << Enclosing; | 
|  | // Don't offer a fixit for declarations with availability attributes. | 
|  | if (Enclosing->hasAttr<AvailabilityAttr>()) | 
|  | return; | 
|  | Preprocessor &PP = S.getPreprocessor(); | 
|  | if (!PP.isMacroDefined("API_AVAILABLE")) | 
|  | return; | 
|  | std::optional<AttributeInsertion> Insertion = createAttributeInsertion( | 
|  | Enclosing, S.getSourceManager(), S.getLangOpts()); | 
|  | if (!Insertion) | 
|  | return; | 
|  | StringRef PlatformName = | 
|  | S.getASTContext().getTargetInfo().getPlatformName(); | 
|  |  | 
|  | // Apple's API_AVAILABLE macro expands roughly like this. | 
|  | // API_AVAILABLE(ios(17.0)) | 
|  | // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0))) | 
|  | // __attribute__((availability(ios,introduced=17.0))) | 
|  | // In order to figure out which platform name to use in the API_AVAILABLE | 
|  | // macro, the associated __API_AVAILABLE_PLATFORM_ macro needs to be | 
|  | // found. The __API_AVAILABLE_PLATFORM_ macros aren't consistent about | 
|  | // using the canonical platform name, source spelling name, or one of the | 
|  | // other supported names (i.e. one of the keys in canonicalizePlatformName | 
|  | // that's neither). Check all of the supported names for a match. | 
|  | std::vector<StringRef> EquivalentPlatforms = | 
|  | AvailabilityAttr::equivalentPlatformNames(PlatformName); | 
|  | llvm::Twine MacroPrefix = "__API_AVAILABLE_PLATFORM_"; | 
|  | auto AvailablePlatform = | 
|  | llvm::find_if(EquivalentPlatforms, [&](StringRef EquivalentPlatform) { | 
|  | return PP.isMacroDefined((MacroPrefix + EquivalentPlatform).str()); | 
|  | }); | 
|  | if (AvailablePlatform == EquivalentPlatforms.end()) | 
|  | return; | 
|  | std::string Introduced = | 
|  | OffendingDecl->getVersionIntroduced().getAsString(); | 
|  | FixitNoteDiag << FixItHint::CreateInsertion( | 
|  | Insertion->Loc, | 
|  | (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + | 
|  | *AvailablePlatform + "(" + Introduced + "))" + Insertion->Suffix) | 
|  | .str()); | 
|  | } | 
|  | return; | 
|  | } | 
|  | case AR_Deprecated: | 
|  | // Suppress -Wdeprecated-declarations in implicit | 
|  | // functions. | 
|  | if (const auto *FD = dyn_cast_or_null<FunctionDecl>(S.getCurFunctionDecl()); | 
|  | FD && FD->isImplicit()) | 
|  | return; | 
|  |  | 
|  | if (ObjCPropertyAccess) | 
|  | diag = diag::warn_property_method_deprecated; | 
|  | else if (S.currentEvaluationContext().IsCaseExpr) | 
|  | diag = diag::warn_deprecated_switch_case; | 
|  | else | 
|  | diag = diag::warn_deprecated; | 
|  |  | 
|  | diag_message = diag::warn_deprecated_message; | 
|  | diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; | 
|  | property_note_select = /* deprecated */ 0; | 
|  | available_here_select_kind = /* deprecated */ 2; | 
|  | if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>()) | 
|  | NoteLocation = AL->getLocation(); | 
|  | break; | 
|  |  | 
|  | case AR_Unavailable: | 
|  | diag = !ObjCPropertyAccess ? diag::err_unavailable | 
|  | : diag::err_property_method_unavailable; | 
|  | diag_message = diag::err_unavailable_message; | 
|  | diag_fwdclass_message = diag::warn_unavailable_fwdclass_message; | 
|  | property_note_select = /* unavailable */ 1; | 
|  | available_here_select_kind = /* unavailable */ 0; | 
|  |  | 
|  | if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) { | 
|  | if (AL->isImplicit() && AL->getImplicitReason()) { | 
|  | // Most of these failures are due to extra restrictions in ARC; | 
|  | // reflect that in the primary diagnostic when applicable. | 
|  | auto flagARCError = [&] { | 
|  | if (S.getLangOpts().ObjCAutoRefCount && | 
|  | S.getSourceManager().isInSystemHeader( | 
|  | OffendingDecl->getLocation())) | 
|  | diag = diag::err_unavailable_in_arc; | 
|  | }; | 
|  |  | 
|  | switch (AL->getImplicitReason()) { | 
|  | case UnavailableAttr::IR_None: break; | 
|  |  | 
|  | case UnavailableAttr::IR_ARCForbiddenType: | 
|  | flagARCError(); | 
|  | diag_available_here = diag::note_arc_forbidden_type; | 
|  | break; | 
|  |  | 
|  | case UnavailableAttr::IR_ForbiddenWeak: | 
|  | if (S.getLangOpts().ObjCWeakRuntime) | 
|  | diag_available_here = diag::note_arc_weak_disabled; | 
|  | else | 
|  | diag_available_here = diag::note_arc_weak_no_runtime; | 
|  | break; | 
|  |  | 
|  | case UnavailableAttr::IR_ARCForbiddenConversion: | 
|  | flagARCError(); | 
|  | diag_available_here = diag::note_performs_forbidden_arc_conversion; | 
|  | break; | 
|  |  | 
|  | case UnavailableAttr::IR_ARCInitReturnsUnrelated: | 
|  | flagARCError(); | 
|  | diag_available_here = diag::note_arc_init_returns_unrelated; | 
|  | break; | 
|  |  | 
|  | case UnavailableAttr::IR_ARCFieldWithOwnership: | 
|  | flagARCError(); | 
|  | diag_available_here = diag::note_arc_field_with_ownership; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | case AR_Available: | 
|  | llvm_unreachable("Warning for availability of available declaration?"); | 
|  | } | 
|  |  | 
|  | SmallVector<FixItHint, 12> FixIts; | 
|  | if (K == AR_Deprecated) { | 
|  | StringRef Replacement; | 
|  | if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>()) | 
|  | Replacement = AL->getReplacement(); | 
|  | if (auto AL = getAttrForPlatform(S.Context, OffendingDecl)) | 
|  | Replacement = AL->getReplacement(); | 
|  |  | 
|  | CharSourceRange UseRange; | 
|  | if (!Replacement.empty()) | 
|  | UseRange = | 
|  | CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); | 
|  | if (UseRange.isValid()) { | 
|  | if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) { | 
|  | Selector Sel = MethodDecl->getSelector(); | 
|  | SmallVector<StringRef, 12> SelectorSlotNames; | 
|  | std::optional<unsigned> NumParams = tryParseObjCMethodName( | 
|  | Replacement, SelectorSlotNames, S.getLangOpts()); | 
|  | if (NumParams && *NumParams == Sel.getNumArgs()) { | 
|  | assert(SelectorSlotNames.size() == Locs.size()); | 
|  | for (unsigned I = 0; I < Locs.size(); ++I) { | 
|  | if (!Sel.getNameForSlot(I).empty()) { | 
|  | CharSourceRange NameRange = CharSourceRange::getCharRange( | 
|  | Locs[I], S.getLocForEndOfToken(Locs[I])); | 
|  | FixIts.push_back(FixItHint::CreateReplacement( | 
|  | NameRange, SelectorSlotNames[I])); | 
|  | } else | 
|  | FixIts.push_back( | 
|  | FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I])); | 
|  | } | 
|  | } else | 
|  | FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); | 
|  | } else | 
|  | FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // We emit deprecation warning for deprecated specializations | 
|  | // when their instantiation stacks originate outside | 
|  | // of a system header, even if the diagnostics is suppresed at the | 
|  | // point of definition. | 
|  | SourceLocation InstantiationLoc = | 
|  | S.getTopMostPointOfInstantiation(ReferringDecl); | 
|  | bool ShouldAllowWarningInSystemHeader = | 
|  | InstantiationLoc != Loc && | 
|  | !S.getSourceManager().isInSystemHeader(InstantiationLoc); | 
|  | struct AllowWarningInSystemHeaders { | 
|  | AllowWarningInSystemHeaders(DiagnosticsEngine &E, | 
|  | bool AllowWarningInSystemHeaders) | 
|  | : Engine(E), Prev(E.getSuppressSystemWarnings()) { | 
|  | E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders); | 
|  | } | 
|  | ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); } | 
|  |  | 
|  | private: | 
|  | DiagnosticsEngine &Engine; | 
|  | bool Prev; | 
|  | } SystemWarningOverrideRAII(S.getDiagnostics(), | 
|  | ShouldAllowWarningInSystemHeader); | 
|  |  | 
|  | if (!Message.empty()) { | 
|  | S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts; | 
|  | if (ObjCProperty) | 
|  | S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) | 
|  | << ObjCProperty->getDeclName() << property_note_select; | 
|  | } else if (!UnknownObjCClass) { | 
|  | S.Diag(Loc, diag) << ReferringDecl << FixIts; | 
|  | if (ObjCProperty) | 
|  | S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) | 
|  | << ObjCProperty->getDeclName() << property_note_select; | 
|  | } else { | 
|  | S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts; | 
|  | S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); | 
|  | } | 
|  |  | 
|  | S.Diag(NoteLocation, diag_available_here) | 
|  | << OffendingDecl << available_here_select_kind; | 
|  | } | 
|  |  | 
|  | void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) { | 
|  | assert(DD.Kind == DelayedDiagnostic::Availability && | 
|  | "Expected an availability diagnostic here"); | 
|  |  | 
|  | DD.Triggered = true; | 
|  | DoEmitAvailabilityWarning( | 
|  | *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), | 
|  | DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), | 
|  | DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(), | 
|  | DD.getObjCProperty(), false); | 
|  | } | 
|  |  | 
|  | static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR, | 
|  | const NamedDecl *ReferringDecl, | 
|  | const NamedDecl *OffendingDecl, | 
|  | StringRef Message, | 
|  | ArrayRef<SourceLocation> Locs, | 
|  | const ObjCInterfaceDecl *UnknownObjCClass, | 
|  | const ObjCPropertyDecl *ObjCProperty, | 
|  | bool ObjCPropertyAccess) { | 
|  | // Delay if we're currently parsing a declaration. | 
|  | if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { | 
|  | S.DelayedDiagnostics.add( | 
|  | DelayedDiagnostic::makeAvailability( | 
|  | AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, | 
|  | ObjCProperty, Message, ObjCPropertyAccess)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Decl *Ctx = cast<Decl>(S.getCurLexicalContext()); | 
|  | DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, | 
|  | Message, Locs, UnknownObjCClass, ObjCProperty, | 
|  | ObjCPropertyAccess); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | /// Returns true if the given statement can be a body-like child of \p Parent. | 
|  | bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) { | 
|  | switch (Parent->getStmtClass()) { | 
|  | case Stmt::IfStmtClass: | 
|  | return cast<IfStmt>(Parent)->getThen() == S || | 
|  | cast<IfStmt>(Parent)->getElse() == S; | 
|  | case Stmt::WhileStmtClass: | 
|  | return cast<WhileStmt>(Parent)->getBody() == S; | 
|  | case Stmt::DoStmtClass: | 
|  | return cast<DoStmt>(Parent)->getBody() == S; | 
|  | case Stmt::ForStmtClass: | 
|  | return cast<ForStmt>(Parent)->getBody() == S; | 
|  | case Stmt::CXXForRangeStmtClass: | 
|  | return cast<CXXForRangeStmt>(Parent)->getBody() == S; | 
|  | case Stmt::ObjCForCollectionStmtClass: | 
|  | return cast<ObjCForCollectionStmt>(Parent)->getBody() == S; | 
|  | case Stmt::CaseStmtClass: | 
|  | case Stmt::DefaultStmtClass: | 
|  | return cast<SwitchCase>(Parent)->getSubStmt() == S; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | class StmtUSEFinder : public DynamicRecursiveASTVisitor { | 
|  | const Stmt *Target; | 
|  |  | 
|  | public: | 
|  | bool VisitStmt(Stmt *S) override { return S != Target; } | 
|  |  | 
|  | /// Returns true if the given statement is present in the given declaration. | 
|  | static bool isContained(const Stmt *Target, const Decl *D) { | 
|  | StmtUSEFinder Visitor; | 
|  | Visitor.Target = Target; | 
|  | return !Visitor.TraverseDecl(const_cast<Decl *>(D)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Traverses the AST and finds the last statement that used a given | 
|  | /// declaration. | 
|  | class LastDeclUSEFinder : public DynamicRecursiveASTVisitor { | 
|  | const Decl *D; | 
|  |  | 
|  | public: | 
|  | bool VisitDeclRefExpr(DeclRefExpr *DRE) override { | 
|  | if (DRE->getDecl() == D) | 
|  | return false; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static const Stmt *findLastStmtThatUsesDecl(const Decl *D, | 
|  | const CompoundStmt *Scope) { | 
|  | LastDeclUSEFinder Visitor; | 
|  | Visitor.D = D; | 
|  | for (const Stmt *S : llvm::reverse(Scope->body())) { | 
|  | if (!Visitor.TraverseStmt(const_cast<Stmt *>(S))) | 
|  | return S; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// This class implements -Wunguarded-availability. | 
|  | /// | 
|  | /// This is done with a traversal of the AST of a function that makes reference | 
|  | /// to a partially available declaration. Whenever we encounter an \c if of the | 
|  | /// form: \c if(@available(...)), we use the version from the condition to visit | 
|  | /// the then statement. | 
|  | class DiagnoseUnguardedAvailability : public DynamicRecursiveASTVisitor { | 
|  | Sema &SemaRef; | 
|  | Decl *Ctx; | 
|  |  | 
|  | /// Stack of potentially nested 'if (@available(...))'s. | 
|  | SmallVector<VersionTuple, 8> AvailabilityStack; | 
|  | SmallVector<const Stmt *, 16> StmtStack; | 
|  |  | 
|  | void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range, | 
|  | ObjCInterfaceDecl *ClassReceiver = nullptr); | 
|  |  | 
|  | public: | 
|  | DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx) | 
|  | : SemaRef(SemaRef), Ctx(Ctx) { | 
|  | AvailabilityStack.push_back( | 
|  | SemaRef.Context.getTargetInfo().getPlatformMinVersion()); | 
|  | } | 
|  |  | 
|  | bool TraverseStmt(Stmt *S) override { | 
|  | if (!S) | 
|  | return true; | 
|  | StmtStack.push_back(S); | 
|  | bool Result = DynamicRecursiveASTVisitor::TraverseStmt(S); | 
|  | StmtStack.pop_back(); | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | void IssueDiagnostics(Stmt *S) { TraverseStmt(S); } | 
|  |  | 
|  | bool TraverseIfStmt(IfStmt *If) override; | 
|  |  | 
|  | // for 'case X:' statements, don't bother looking at the 'X'; it can't lead | 
|  | // to any useful diagnostics. | 
|  | bool TraverseCaseStmt(CaseStmt *CS) override { | 
|  | return TraverseStmt(CS->getSubStmt()); | 
|  | } | 
|  |  | 
|  | bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) override { | 
|  | 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()), ID); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitDeclRefExpr(DeclRefExpr *DRE) override { | 
|  | DiagnoseDeclAvailability(DRE->getDecl(), | 
|  | SourceRange(DRE->getBeginLoc(), DRE->getEndLoc())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitMemberExpr(MemberExpr *ME) override { | 
|  | DiagnoseDeclAvailability(ME->getMemberDecl(), | 
|  | SourceRange(ME->getBeginLoc(), ME->getEndLoc())); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) override { | 
|  | SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use) | 
|  | << (!SemaRef.getLangOpts().ObjC); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitTypeLoc(TypeLoc Ty) override; | 
|  | }; | 
|  |  | 
|  | void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( | 
|  | NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) { | 
|  | AvailabilityResult Result; | 
|  | const NamedDecl *OffendingDecl; | 
|  | std::tie(Result, OffendingDecl) = | 
|  | SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr, ReceiverClass); | 
|  | if (Result != AR_Available) { | 
|  | // All other diagnostic kinds have already been handled in | 
|  | // DiagnoseAvailabilityOfDecl. | 
|  | if (Result != AR_NotYetIntroduced) | 
|  | return; | 
|  |  | 
|  | const AvailabilityAttr *AA = | 
|  | getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); | 
|  | assert(AA != nullptr && "expecting valid availability attribute"); | 
|  | bool EnvironmentMatchesOrNone = | 
|  | hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA); | 
|  | VersionTuple Introduced = AA->getIntroduced(); | 
|  |  | 
|  | if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced) | 
|  | return; | 
|  |  | 
|  | // If the context of this function is less available than D, we should not | 
|  | // emit a diagnostic. | 
|  | if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, | 
|  | AA->getEnvironment(), Ctx, | 
|  | OffendingDecl)) | 
|  | return; | 
|  |  | 
|  | const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo(); | 
|  | std::string PlatformName( | 
|  | AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName())); | 
|  | llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName()); | 
|  | llvm::StringRef AttrEnvironment = | 
|  | AA->getEnvironment() ? AA->getEnvironment()->getName() : ""; | 
|  | bool UseEnvironment = | 
|  | (!AttrEnvironment.empty() && !TargetEnvironment.empty()); | 
|  |  | 
|  | unsigned DiagKind = getAvailabilityDiagnosticKind( | 
|  | SemaRef.Context, | 
|  | SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced, | 
|  | EnvironmentMatchesOrNone); | 
|  |  | 
|  | SemaRef.Diag(Range.getBegin(), DiagKind) | 
|  | << Range << D << PlatformName << Introduced.getAsString() | 
|  | << UseEnvironment << TargetEnvironment; | 
|  |  | 
|  | SemaRef.Diag(OffendingDecl->getLocation(), | 
|  | diag::note_partial_availability_specified_here) | 
|  | << OffendingDecl << PlatformName << Introduced.getAsString() | 
|  | << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString() | 
|  | << UseEnvironment << AttrEnvironment << TargetEnvironment; | 
|  |  | 
|  | // Do not offer to silence the warning or fixits for HLSL | 
|  | if (SemaRef.getLangOpts().HLSL) | 
|  | return; | 
|  |  | 
|  | auto FixitDiag = | 
|  | SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) | 
|  | << Range << D | 
|  | << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0 | 
|  | : /*__builtin_available*/ 1); | 
|  |  | 
|  | // Find the statement which should be enclosed in the if @available check. | 
|  | if (StmtStack.empty()) | 
|  | return; | 
|  | const Stmt *StmtOfUse = StmtStack.back(); | 
|  | const CompoundStmt *Scope = nullptr; | 
|  | for (const Stmt *S : llvm::reverse(StmtStack)) { | 
|  | if (const auto *CS = dyn_cast<CompoundStmt>(S)) { | 
|  | Scope = CS; | 
|  | break; | 
|  | } | 
|  | if (isBodyLikeChildStmt(StmtOfUse, S)) { | 
|  | // The declaration won't be seen outside of the statement, so we don't | 
|  | // have to wrap the uses of any declared variables in if (@available). | 
|  | // Therefore we can avoid setting Scope here. | 
|  | break; | 
|  | } | 
|  | StmtOfUse = S; | 
|  | } | 
|  | const Stmt *LastStmtOfUse = nullptr; | 
|  | if (isa<DeclStmt>(StmtOfUse) && Scope) { | 
|  | for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) { | 
|  | if (StmtUSEFinder::isContained(StmtStack.back(), D)) { | 
|  | LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | const SourceManager &SM = SemaRef.getSourceManager(); | 
|  | SourceLocation IfInsertionLoc = | 
|  | SM.getExpansionLoc(StmtOfUse->getBeginLoc()); | 
|  | SourceLocation StmtEndLoc = | 
|  | SM.getExpansionRange( | 
|  | (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc()) | 
|  | .getEnd(); | 
|  | if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc)) | 
|  | return; | 
|  |  | 
|  | StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM); | 
|  | const char *ExtraIndentation = "    "; | 
|  | std::string FixItString; | 
|  | llvm::raw_string_ostream FixItOS(FixItString); | 
|  | FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available" | 
|  | : "__builtin_available") | 
|  | << "(" | 
|  | << AvailabilityAttr::getPlatformNameSourceSpelling( | 
|  | SemaRef.getASTContext().getTargetInfo().getPlatformName()) | 
|  | << " " << Introduced.getAsString() << ", *)) {\n" | 
|  | << Indentation << ExtraIndentation; | 
|  | FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str()); | 
|  | SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken( | 
|  | StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(), | 
|  | /*SkipTrailingWhitespaceAndNewLine=*/false); | 
|  | if (ElseInsertionLoc.isInvalid()) | 
|  | ElseInsertionLoc = | 
|  | Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts()); | 
|  | FixItOS.str().clear(); | 
|  | FixItOS << "\n" | 
|  | << Indentation << "} else {\n" | 
|  | << Indentation << ExtraIndentation | 
|  | << "// Fallback on earlier versions\n" | 
|  | << Indentation << "}"; | 
|  | FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) { | 
|  | const Type *TyPtr = Ty.getTypePtr(); | 
|  | SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()}; | 
|  |  | 
|  | if (Range.isInvalid()) | 
|  | return true; | 
|  |  | 
|  | if (const auto *TT = dyn_cast<TagType>(TyPtr)) { | 
|  | TagDecl *TD = TT->getDecl()->getDefinitionOrSelf(); | 
|  | DiagnoseDeclAvailability(TD, Range); | 
|  |  | 
|  | } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) { | 
|  | TypedefNameDecl *D = TD->getDecl(); | 
|  | DiagnoseDeclAvailability(D, Range); | 
|  |  | 
|  | } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) { | 
|  | if (NamedDecl *D = ObjCO->getInterface()) | 
|  | DiagnoseDeclAvailability(D, Range); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | struct ExtractedAvailabilityExpr { | 
|  | const ObjCAvailabilityCheckExpr *E = nullptr; | 
|  | bool isNegated = false; | 
|  | }; | 
|  |  | 
|  | ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) { | 
|  | const auto *E = IfCond; | 
|  | bool IsNegated = false; | 
|  | while (true) { | 
|  | E = E->IgnoreParens(); | 
|  | if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(E)) { | 
|  | return ExtractedAvailabilityExpr{AE, IsNegated}; | 
|  | } | 
|  |  | 
|  | const auto *UO = dyn_cast<UnaryOperator>(E); | 
|  | if (!UO || UO->getOpcode() != UO_LNot) { | 
|  | return ExtractedAvailabilityExpr{}; | 
|  | } | 
|  | E = UO->getSubExpr(); | 
|  | IsNegated = !IsNegated; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { | 
|  | ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond()); | 
|  | if (!IfCond.E) { | 
|  | // This isn't an availability checking 'if', we can just continue. | 
|  | return DynamicRecursiveASTVisitor::TraverseIfStmt(If); | 
|  | } | 
|  |  | 
|  | VersionTuple CondVersion = IfCond.E->getVersion(); | 
|  | // If we're using the '*' case here or if this check is redundant, then we | 
|  | // use the enclosing version to check both branches. | 
|  | if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) { | 
|  | return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); | 
|  | } | 
|  |  | 
|  | auto *Guarded = If->getThen(); | 
|  | auto *Unguarded = If->getElse(); | 
|  | if (IfCond.isNegated) { | 
|  | std::swap(Guarded, Unguarded); | 
|  | } | 
|  |  | 
|  | AvailabilityStack.push_back(CondVersion); | 
|  | bool ShouldContinue = TraverseStmt(Guarded); | 
|  | AvailabilityStack.pop_back(); | 
|  |  | 
|  | return ShouldContinue && TraverseStmt(Unguarded); | 
|  | } | 
|  |  | 
|  | } // end anonymous namespace | 
|  |  | 
|  | void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) { | 
|  | Stmt *Body = nullptr; | 
|  |  | 
|  | if (auto *FD = D->getAsFunction()) { | 
|  | Body = FD->getBody(); | 
|  |  | 
|  | if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) | 
|  | for (const CXXCtorInitializer *CI : CD->inits()) | 
|  | DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit()); | 
|  |  | 
|  | } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) | 
|  | Body = MD->getBody(); | 
|  | else if (auto *BD = dyn_cast<BlockDecl>(D)) | 
|  | Body = BD->getBody(); | 
|  |  | 
|  | assert(Body && "Need a body here!"); | 
|  |  | 
|  | DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body); | 
|  | } | 
|  |  | 
|  | FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() { | 
|  | if (FunctionScopes.empty()) | 
|  | return nullptr; | 
|  |  | 
|  | // Conservatively search the entire current function scope context for | 
|  | // availability violations. This ensures we always correctly analyze nested | 
|  | // classes, blocks, lambdas, etc. that may or may not be inside if(@available) | 
|  | // checks themselves. | 
|  | return FunctionScopes.front(); | 
|  | } | 
|  |  | 
|  | void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, | 
|  | ArrayRef<SourceLocation> Locs, | 
|  | const ObjCInterfaceDecl *UnknownObjCClass, | 
|  | bool ObjCPropertyAccess, | 
|  | 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, ClassReceiver); | 
|  | if (Result == AR_Available) | 
|  | return; | 
|  |  | 
|  | if (Result == AR_NotYetIntroduced) { | 
|  | if (AvoidPartialAvailabilityChecks) | 
|  | return; | 
|  |  | 
|  | // We need to know the @available context in the current function to | 
|  | // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that | 
|  | // when we're done parsing the current function. | 
|  | if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { | 
|  | Context->HasPotentialAvailabilityViolations = true; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | const ObjCPropertyDecl *ObjCPDecl = nullptr; | 
|  | if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { | 
|  | if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { | 
|  | AvailabilityResult PDeclResult = PD->getAvailability(nullptr); | 
|  | if (PDeclResult == Result) | 
|  | ObjCPDecl = PD; | 
|  | } | 
|  | } | 
|  |  | 
|  | EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, | 
|  | UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); | 
|  | } | 
|  |  | 
|  | void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D, | 
|  | ArrayRef<SourceLocation> Locs) { | 
|  | DiagnoseAvailabilityOfDecl(D, Locs, /*UnknownObjCClass=*/nullptr, | 
|  | /*ObjCPropertyAccess=*/false, | 
|  | /*AvoidPartialAvailabilityChecks=*/false, | 
|  | /*ClassReceiver=*/nullptr); | 
|  | } |