forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			965 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			965 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- 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/RecursiveASTVisitor.h"
 | |
| #include "clang/Basic/DiagnosticSema.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"
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace sema;
 | |
| 
 | |
| static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
 | |
|                                                   const Decl *D) {
 | |
|   // Check each AvailabilityAttr to find the one for this platform.
 | |
|   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)
 | |
|         return Avail;
 | |
|     }
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| /// 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 the method of a class message
 | |
| /// send, the class. Otherwise nullptr.
 | |
| static std::pair<AvailabilityResult, const NamedDecl *>
 | |
| ShouldDiagnoseAvailabilityOfDecl(Sema &S, 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) {
 | |
|       if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) {
 | |
|         D = TT->getDecl();
 | |
|         Result = D->getAvailability(Message);
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   // 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 (S.NSAPIObj && ClassReceiver) {
 | |
|       ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
 | |
|           S.NSAPIObj->getInitSelector());
 | |
|       if (Init && Result == AR_Available && MD->isClassMethod() &&
 | |
|           MD->getSelector() == S.NSAPIObj->getNewSelector() &&
 | |
|           MD->definedInNSObject(S.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, 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.
 | |
|   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;
 | |
|     } 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 bool
 | |
| shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
 | |
|                                     const VersionTuple &DeploymentVersion,
 | |
|                                     const VersionTuple &DeclVersion) {
 | |
|   const auto &Triple = Context.getTargetInfo().getTriple();
 | |
|   VersionTuple ForceAvailabilityFromVersion;
 | |
|   switch (Triple.getOS()) {
 | |
|   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;
 | |
|   default:
 | |
|     // New targets should always warn about availability.
 | |
|     return Triple.getVendor() == llvm::Triple::Apple;
 | |
|   }
 | |
|   return DeploymentVersion >= ForceAvailabilityFromVersion ||
 | |
|          DeclVersion >= ForceAvailabilityFromVersion;
 | |
| }
 | |
| 
 | |
| 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, None
 | |
| /// otherwise.
 | |
| static 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 None;
 | |
|   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 None;
 | |
|     NumParams = 0;
 | |
|   }
 | |
|   // Verify all slot names are valid.
 | |
|   bool AllowDollar = LangOpts.DollarIdents;
 | |
|   for (StringRef S : SlotNames) {
 | |
|     if (S.empty())
 | |
|       continue;
 | |
|     if (!isValidIdentifier(S, AllowDollar))
 | |
|       return None;
 | |
|   }
 | |
|   return NumParams;
 | |
| }
 | |
| 
 | |
| /// Returns a source location in which it's appropriate to insert a new
 | |
| /// attribute for the given declaration \D.
 | |
| static 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 None;
 | |
|     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 None;
 | |
|     // 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;
 | |
|   if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl))
 | |
|     DeclVersion = AA->getIntroduced();
 | |
| 
 | |
|   if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx,
 | |
|                                            OffendingDecl))
 | |
|     return;
 | |
| 
 | |
|   SourceLocation Loc = Locs.front();
 | |
| 
 | |
|   // The declaration can have multiple availability attributes, we are looking
 | |
|   // at one of them.
 | |
|   const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl);
 | |
|   if (A && A->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.
 | |
|     const AvailabilityAttr *AA =
 | |
|         getAttrForPlatform(S.getASTContext(), OffendingDecl);
 | |
|     VersionTuple Introduced = AA->getIntroduced();
 | |
| 
 | |
|     bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
 | |
|         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
 | |
|         Introduced);
 | |
|     unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new
 | |
|                                      : diag::warn_unguarded_availability;
 | |
| 
 | |
|     std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
 | |
|         S.getASTContext().getTargetInfo().getPlatformName()));
 | |
| 
 | |
|     S.Diag(Loc, Warning) << OffendingDecl << PlatformName
 | |
|                          << Introduced.getAsString();
 | |
| 
 | |
|     S.Diag(OffendingDecl->getLocation(),
 | |
|            diag::note_partial_availability_specified_here)
 | |
|         << OffendingDecl << PlatformName << Introduced.getAsString()
 | |
|         << S.Context.getTargetInfo().getPlatformMinVersion().getAsString();
 | |
| 
 | |
|     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;
 | |
|       if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE"))
 | |
|         return;
 | |
|       Optional<AttributeInsertion> Insertion = createAttributeInsertion(
 | |
|           Enclosing, S.getSourceManager(), S.getLangOpts());
 | |
|       if (!Insertion)
 | |
|         return;
 | |
|       std::string PlatformName =
 | |
|           AvailabilityAttr::getPlatformNameSourceSpelling(
 | |
|               S.getASTContext().getTargetInfo().getPlatformName())
 | |
|               .lower();
 | |
|       std::string Introduced =
 | |
|           OffendingDecl->getVersionIntroduced().getAsString();
 | |
|       FixitNoteDiag << FixItHint::CreateInsertion(
 | |
|           Insertion->Loc,
 | |
|           (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName +
 | |
|            "(" + Introduced + "))" + Insertion->Suffix)
 | |
|               .str());
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
|   case AR_Deprecated:
 | |
|     diag = !ObjCPropertyAccess ? diag::warn_deprecated
 | |
|                                : diag::warn_property_method_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;
 | |
|         Optional<unsigned> NumParams = tryParseObjCMethodName(
 | |
|             Replacement, SelectorSlotNames, S.getLangOpts());
 | |
|         if (NumParams && NumParams.getValue() == 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));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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 RecursiveASTVisitor<StmtUSEFinder> {
 | |
|   const Stmt *Target;
 | |
| 
 | |
| public:
 | |
|   bool VisitStmt(Stmt *S) { 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 RecursiveASTVisitor<LastDeclUSEFinder> {
 | |
|   const Decl *D;
 | |
| 
 | |
| public:
 | |
|   bool VisitDeclRefExpr(DeclRefExpr *DRE) {
 | |
|     if (DRE->getDecl() == D)
 | |
|       return false;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   static const Stmt *findLastStmtThatUsesDecl(const Decl *D,
 | |
|                                               const CompoundStmt *Scope) {
 | |
|     LastDeclUSEFinder Visitor;
 | |
|     Visitor.D = D;
 | |
|     for (auto I = Scope->body_rbegin(), E = Scope->body_rend(); I != E; ++I) {
 | |
|       const Stmt *S = *I;
 | |
|       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 RecursiveASTVisitor<DiagnoseUnguardedAvailability> {
 | |
|   typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base;
 | |
| 
 | |
|   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 TraverseDecl(Decl *D) {
 | |
|     // Avoid visiting nested functions to prevent duplicate warnings.
 | |
|     if (!D || isa<FunctionDecl>(D))
 | |
|       return true;
 | |
|     return Base::TraverseDecl(D);
 | |
|   }
 | |
| 
 | |
|   bool TraverseStmt(Stmt *S) {
 | |
|     if (!S)
 | |
|       return true;
 | |
|     StmtStack.push_back(S);
 | |
|     bool Result = Base::TraverseStmt(S);
 | |
|     StmtStack.pop_back();
 | |
|     return Result;
 | |
|   }
 | |
| 
 | |
|   void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
 | |
| 
 | |
|   bool TraverseIfStmt(IfStmt *If);
 | |
| 
 | |
|   bool TraverseLambdaExpr(LambdaExpr *E) { return true; }
 | |
| 
 | |
|   // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
 | |
|   // to any useful diagnostics.
 | |
|   bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }
 | |
| 
 | |
|   bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) {
 | |
|     if (PRE->isClassReceiver())
 | |
|       DiagnoseDeclAvailability(PRE->getClassReceiver(), PRE->getReceiverLocation());
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
 | |
|     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) {
 | |
|     DiagnoseDeclAvailability(DRE->getDecl(),
 | |
|                              SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool VisitMemberExpr(MemberExpr *ME) {
 | |
|     DiagnoseDeclAvailability(ME->getMemberDecl(),
 | |
|                              SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
 | |
|     SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
 | |
|         << (!SemaRef.getLangOpts().ObjC);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   bool VisitTypeLoc(TypeLoc Ty);
 | |
| };
 | |
| 
 | |
| void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
 | |
|     NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
 | |
|   AvailabilityResult Result;
 | |
|   const NamedDecl *OffendingDecl;
 | |
|   std::tie(Result, OffendingDecl) =
 | |
|       ShouldDiagnoseAvailabilityOfDecl(SemaRef, 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);
 | |
|     VersionTuple Introduced = AA->getIntroduced();
 | |
| 
 | |
|     if (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, Ctx,
 | |
|                                              OffendingDecl))
 | |
|       return;
 | |
| 
 | |
|     // 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.
 | |
|     unsigned DiagKind =
 | |
|         shouldDiagnoseAvailabilityByDefault(
 | |
|             SemaRef.Context,
 | |
|             SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced)
 | |
|             ? diag::warn_unguarded_availability_new
 | |
|             : diag::warn_unguarded_availability;
 | |
| 
 | |
|     std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
 | |
|         SemaRef.getASTContext().getTargetInfo().getPlatformName()));
 | |
| 
 | |
|     SemaRef.Diag(Range.getBegin(), DiagKind)
 | |
|         << Range << D << PlatformName << Introduced.getAsString();
 | |
| 
 | |
|     SemaRef.Diag(OffendingDecl->getLocation(),
 | |
|                  diag::note_partial_availability_specified_here)
 | |
|         << OffendingDecl << PlatformName << Introduced.getAsString()
 | |
|         << SemaRef.Context.getTargetInfo()
 | |
|                .getPlatformMinVersion()
 | |
|                .getAsString();
 | |
| 
 | |
|     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();
 | |
|     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;
 | |
| }
 | |
| 
 | |
| bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
 | |
|   VersionTuple CondVersion;
 | |
|   if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
 | |
|     CondVersion = 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());
 | |
|   } else {
 | |
|     // This isn't an availability checking 'if', we can just continue.
 | |
|     return Base::TraverseIfStmt(If);
 | |
|   }
 | |
| 
 | |
|   AvailabilityStack.push_back(CondVersion);
 | |
|   bool ShouldContinue = TraverseStmt(If->getThen());
 | |
|   AvailabilityStack.pop_back();
 | |
| 
 | |
|   return ShouldContinue && TraverseStmt(If->getElse());
 | |
| }
 | |
| 
 | |
| } // end anonymous namespace
 | |
| 
 | |
| void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
 | |
|   Stmt *Body = nullptr;
 | |
| 
 | |
|   if (auto *FD = D->getAsFunction()) {
 | |
|     // FIXME: We only examine the pattern decl for availability violations now,
 | |
|     // but we should also examine instantiated templates.
 | |
|     if (FD->isTemplateInstantiation())
 | |
|       return;
 | |
| 
 | |
|     Body = FD->getBody();
 | |
|   } 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);
 | |
| }
 | |
| 
 | |
| 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(*this, 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 (getCurFunctionOrMethodDecl()) {
 | |
|       getEnclosingFunction()->HasPotentialAvailabilityViolations = true;
 | |
|       return;
 | |
|     } else if (getCurBlock() || getCurLambda()) {
 | |
|       getCurFunction()->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);
 | |
| }
 |