Home | History | Annotate | Line # | Download | only in Sema
      1 //===--- SemaAvailability.cpp - Availability attribute handling -----------===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 //  This file processes the availability attribute.
     10 //
     11 //===----------------------------------------------------------------------===//
     12 
     13 #include "clang/AST/Attr.h"
     14 #include "clang/AST/Decl.h"
     15 #include "clang/AST/RecursiveASTVisitor.h"
     16 #include "clang/Basic/DiagnosticSema.h"
     17 #include "clang/Basic/TargetInfo.h"
     18 #include "clang/Lex/Preprocessor.h"
     19 #include "clang/Sema/DelayedDiagnostic.h"
     20 #include "clang/Sema/ScopeInfo.h"
     21 #include "clang/Sema/Sema.h"
     22 
     23 using namespace clang;
     24 using namespace sema;
     25 
     26 static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
     27                                                   const Decl *D) {
     28   // Check each AvailabilityAttr to find the one for this platform.
     29   for (const auto *A : D->attrs()) {
     30     if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
     31       // FIXME: this is copied from CheckAvailability. We should try to
     32       // de-duplicate.
     33 
     34       // Check if this is an App Extension "platform", and if so chop off
     35       // the suffix for matching with the actual platform.
     36       StringRef ActualPlatform = Avail->getPlatform()->getName();
     37       StringRef RealizedPlatform = ActualPlatform;
     38       if (Context.getLangOpts().AppExt) {
     39         size_t suffix = RealizedPlatform.rfind("_app_extension");
     40         if (suffix != StringRef::npos)
     41           RealizedPlatform = RealizedPlatform.slice(0, suffix);
     42       }
     43 
     44       StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
     45 
     46       // Match the platform name.
     47       if (RealizedPlatform == TargetPlatform)
     48         return Avail;
     49     }
     50   }
     51   return nullptr;
     52 }
     53 
     54 /// The diagnostic we should emit for \c D, and the declaration that
     55 /// originated it, or \c AR_Available.
     56 ///
     57 /// \param D The declaration to check.
     58 /// \param Message If non-null, this will be populated with the message from
     59 /// the availability attribute that is selected.
     60 /// \param ClassReceiver If we're checking the the method of a class message
     61 /// send, the class. Otherwise nullptr.
     62 static std::pair<AvailabilityResult, const NamedDecl *>
     63 ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
     64                                  std::string *Message,
     65                                  ObjCInterfaceDecl *ClassReceiver) {
     66   AvailabilityResult Result = D->getAvailability(Message);
     67 
     68   // For typedefs, if the typedef declaration appears available look
     69   // to the underlying type to see if it is more restrictive.
     70   while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
     71     if (Result == AR_Available) {
     72       if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) {
     73         D = TT->getDecl();
     74         Result = D->getAvailability(Message);
     75         continue;
     76       }
     77     }
     78     break;
     79   }
     80 
     81   // Forward class declarations get their attributes from their definition.
     82   if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {
     83     if (IDecl->getDefinition()) {
     84       D = IDecl->getDefinition();
     85       Result = D->getAvailability(Message);
     86     }
     87   }
     88 
     89   if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
     90     if (Result == AR_Available) {
     91       const DeclContext *DC = ECD->getDeclContext();
     92       if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) {
     93         Result = TheEnumDecl->getAvailability(Message);
     94         D = TheEnumDecl;
     95       }
     96     }
     97 
     98   // For +new, infer availability from -init.
     99   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
    100     if (S.NSAPIObj && ClassReceiver) {
    101       ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
    102           S.NSAPIObj->getInitSelector());
    103       if (Init && Result == AR_Available && MD->isClassMethod() &&
    104           MD->getSelector() == S.NSAPIObj->getNewSelector() &&
    105           MD->definedInNSObject(S.getASTContext())) {
    106         Result = Init->getAvailability(Message);
    107         D = Init;
    108       }
    109     }
    110   }
    111 
    112   return {Result, D};
    113 }
    114 
    115 
    116 /// whether we should emit a diagnostic for \c K and \c DeclVersion in
    117 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
    118 /// in a deprecated context, but not the other way around.
    119 static bool
    120 ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
    121                                     VersionTuple DeclVersion, Decl *Ctx,
    122                                     const NamedDecl *OffendingDecl) {
    123   assert(K != AR_Available && "Expected an unavailable declaration here!");
    124 
    125   // Checks if we should emit the availability diagnostic in the context of C.
    126   auto CheckContext = [&](const Decl *C) {
    127     if (K == AR_NotYetIntroduced) {
    128       if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
    129         if (AA->getIntroduced() >= DeclVersion)
    130           return true;
    131     } else if (K == AR_Deprecated) {
    132       if (C->isDeprecated())
    133         return true;
    134     } else if (K == AR_Unavailable) {
    135       // It is perfectly fine to refer to an 'unavailable' Objective-C method
    136       // when it is referenced from within the @implementation itself. In this
    137       // context, we interpret unavailable as a form of access control.
    138       if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) {
    139         if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) {
    140           if (MD->getClassInterface() == Impl->getClassInterface())
    141             return true;
    142         }
    143       }
    144     }
    145 
    146     if (C->isUnavailable())
    147       return true;
    148     return false;
    149   };
    150 
    151   do {
    152     if (CheckContext(Ctx))
    153       return false;
    154 
    155     // An implementation implicitly has the availability of the interface.
    156     // Unless it is "+load" method.
    157     if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx))
    158       if (MethodD->isClassMethod() &&
    159           MethodD->getSelector().getAsString() == "load")
    160         return true;
    161 
    162     if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) {
    163       if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())
    164         if (CheckContext(Interface))
    165           return false;
    166     }
    167     // A category implicitly has the availability of the interface.
    168     else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx))
    169       if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
    170         if (CheckContext(Interface))
    171           return false;
    172   } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));
    173 
    174   return true;
    175 }
    176 
    177 static bool
    178 shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
    179                                     const VersionTuple &DeploymentVersion,
    180                                     const VersionTuple &DeclVersion) {
    181   const auto &Triple = Context.getTargetInfo().getTriple();
    182   VersionTuple ForceAvailabilityFromVersion;
    183   switch (Triple.getOS()) {
    184   case llvm::Triple::IOS:
    185   case llvm::Triple::TvOS:
    186     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
    187     break;
    188   case llvm::Triple::WatchOS:
    189     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4);
    190     break;
    191   case llvm::Triple::Darwin:
    192   case llvm::Triple::MacOSX:
    193     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
    194     break;
    195   default:
    196     // New targets should always warn about availability.
    197     return Triple.getVendor() == llvm::Triple::Apple;
    198   }
    199   return DeploymentVersion >= ForceAvailabilityFromVersion ||
    200          DeclVersion >= ForceAvailabilityFromVersion;
    201 }
    202 
    203 static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
    204   for (Decl *Ctx = OrigCtx; Ctx;
    205        Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) {
    206     if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx))
    207       return cast<NamedDecl>(Ctx);
    208     if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) {
    209       if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx))
    210         return Imp->getClassInterface();
    211       return CD;
    212     }
    213   }
    214 
    215   return dyn_cast<NamedDecl>(OrigCtx);
    216 }
    217 
    218 namespace {
    219 
    220 struct AttributeInsertion {
    221   StringRef Prefix;
    222   SourceLocation Loc;
    223   StringRef Suffix;
    224 
    225   static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
    226     return {" ", D->getEndLoc(), ""};
    227   }
    228   static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
    229     return {" ", Loc, ""};
    230   }
    231   static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
    232     return {"", D->getBeginLoc(), "\n"};
    233   }
    234 };
    235 
    236 } // end anonymous namespace
    237 
    238 /// Tries to parse a string as ObjC method name.
    239 ///
    240 /// \param Name The string to parse. Expected to originate from availability
    241 /// attribute argument.
    242 /// \param SlotNames The vector that will be populated with slot names. In case
    243 /// of unsuccessful parsing can contain invalid data.
    244 /// \returns A number of method parameters if parsing was successful, None
    245 /// otherwise.
    246 static Optional<unsigned>
    247 tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames,
    248                        const LangOptions &LangOpts) {
    249   // Accept replacements starting with - or + as valid ObjC method names.
    250   if (!Name.empty() && (Name.front() == '-' || Name.front() == '+'))
    251     Name = Name.drop_front(1);
    252   if (Name.empty())
    253     return None;
    254   Name.split(SlotNames, ':');
    255   unsigned NumParams;
    256   if (Name.back() == ':') {
    257     // Remove an empty string at the end that doesn't represent any slot.
    258     SlotNames.pop_back();
    259     NumParams = SlotNames.size();
    260   } else {
    261     if (SlotNames.size() != 1)
    262       // Not a valid method name, just a colon-separated string.
    263       return None;
    264     NumParams = 0;
    265   }
    266   // Verify all slot names are valid.
    267   bool AllowDollar = LangOpts.DollarIdents;
    268   for (StringRef S : SlotNames) {
    269     if (S.empty())
    270       continue;
    271     if (!isValidIdentifier(S, AllowDollar))
    272       return None;
    273   }
    274   return NumParams;
    275 }
    276 
    277 /// Returns a source location in which it's appropriate to insert a new
    278 /// attribute for the given declaration \D.
    279 static Optional<AttributeInsertion>
    280 createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
    281                          const LangOptions &LangOpts) {
    282   if (isa<ObjCPropertyDecl>(D))
    283     return AttributeInsertion::createInsertionAfter(D);
    284   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
    285     if (MD->hasBody())
    286       return None;
    287     return AttributeInsertion::createInsertionAfter(D);
    288   }
    289   if (const auto *TD = dyn_cast<TagDecl>(D)) {
    290     SourceLocation Loc =
    291         Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts);
    292     if (Loc.isInvalid())
    293       return None;
    294     // Insert after the 'struct'/whatever keyword.
    295     return AttributeInsertion::createInsertionAfter(Loc);
    296   }
    297   return AttributeInsertion::createInsertionBefore(D);
    298 }
    299 
    300 /// Actually emit an availability diagnostic for a reference to an unavailable
    301 /// decl.
    302 ///
    303 /// \param Ctx The context that the reference occurred in
    304 /// \param ReferringDecl The exact declaration that was referenced.
    305 /// \param OffendingDecl A related decl to \c ReferringDecl that has an
    306 /// availability attribute corresponding to \c K attached to it. Note that this
    307 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
    308 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
    309 /// and OffendingDecl is the EnumDecl.
    310 static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
    311                                       Decl *Ctx, const NamedDecl *ReferringDecl,
    312                                       const NamedDecl *OffendingDecl,
    313                                       StringRef Message,
    314                                       ArrayRef<SourceLocation> Locs,
    315                                       const ObjCInterfaceDecl *UnknownObjCClass,
    316                                       const ObjCPropertyDecl *ObjCProperty,
    317                                       bool ObjCPropertyAccess) {
    318   // Diagnostics for deprecated or unavailable.
    319   unsigned diag, diag_message, diag_fwdclass_message;
    320   unsigned diag_available_here = diag::note_availability_specified_here;
    321   SourceLocation NoteLocation = OffendingDecl->getLocation();
    322 
    323   // Matches 'diag::note_property_attribute' options.
    324   unsigned property_note_select;
    325 
    326   // Matches diag::note_availability_specified_here.
    327   unsigned available_here_select_kind;
    328 
    329   VersionTuple DeclVersion;
    330   if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl))
    331     DeclVersion = AA->getIntroduced();
    332 
    333   if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx,
    334                                            OffendingDecl))
    335     return;
    336 
    337   SourceLocation Loc = Locs.front();
    338 
    339   // The declaration can have multiple availability attributes, we are looking
    340   // at one of them.
    341   const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl);
    342   if (A && A->isInherited()) {
    343     for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
    344          Redecl = Redecl->getPreviousDecl()) {
    345       const AvailabilityAttr *AForRedecl =
    346           getAttrForPlatform(S.Context, Redecl);
    347       if (AForRedecl && !AForRedecl->isInherited()) {
    348         // If D is a declaration with inherited attributes, the note should
    349         // point to the declaration with actual attributes.
    350         NoteLocation = Redecl->getLocation();
    351         break;
    352       }
    353     }
    354   }
    355 
    356   switch (K) {
    357   case AR_NotYetIntroduced: {
    358     // We would like to emit the diagnostic even if -Wunguarded-availability is
    359     // not specified for deployment targets >= to iOS 11 or equivalent or
    360     // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
    361     // later.
    362     const AvailabilityAttr *AA =
    363         getAttrForPlatform(S.getASTContext(), OffendingDecl);
    364     VersionTuple Introduced = AA->getIntroduced();
    365 
    366     bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
    367         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
    368         Introduced);
    369     unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new
    370                                      : diag::warn_unguarded_availability;
    371 
    372     std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
    373         S.getASTContext().getTargetInfo().getPlatformName()));
    374 
    375     S.Diag(Loc, Warning) << OffendingDecl << PlatformName
    376                          << Introduced.getAsString();
    377 
    378     S.Diag(OffendingDecl->getLocation(),
    379            diag::note_partial_availability_specified_here)
    380         << OffendingDecl << PlatformName << Introduced.getAsString()
    381         << S.Context.getTargetInfo().getPlatformMinVersion().getAsString();
    382 
    383     if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
    384       if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
    385         if (TD->getDeclName().isEmpty()) {
    386           S.Diag(TD->getLocation(),
    387                  diag::note_decl_unguarded_availability_silence)
    388               << /*Anonymous*/ 1 << TD->getKindName();
    389           return;
    390         }
    391       auto FixitNoteDiag =
    392           S.Diag(Enclosing->getLocation(),
    393                  diag::note_decl_unguarded_availability_silence)
    394           << /*Named*/ 0 << Enclosing;
    395       // Don't offer a fixit for declarations with availability attributes.
    396       if (Enclosing->hasAttr<AvailabilityAttr>())
    397         return;
    398       if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE"))
    399         return;
    400       Optional<AttributeInsertion> Insertion = createAttributeInsertion(
    401           Enclosing, S.getSourceManager(), S.getLangOpts());
    402       if (!Insertion)
    403         return;
    404       std::string PlatformName =
    405           AvailabilityAttr::getPlatformNameSourceSpelling(
    406               S.getASTContext().getTargetInfo().getPlatformName())
    407               .lower();
    408       std::string Introduced =
    409           OffendingDecl->getVersionIntroduced().getAsString();
    410       FixitNoteDiag << FixItHint::CreateInsertion(
    411           Insertion->Loc,
    412           (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName +
    413            "(" + Introduced + "))" + Insertion->Suffix)
    414               .str());
    415     }
    416     return;
    417   }
    418   case AR_Deprecated:
    419     diag = !ObjCPropertyAccess ? diag::warn_deprecated
    420                                : diag::warn_property_method_deprecated;
    421     diag_message = diag::warn_deprecated_message;
    422     diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;
    423     property_note_select = /* deprecated */ 0;
    424     available_here_select_kind = /* deprecated */ 2;
    425     if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())
    426       NoteLocation = AL->getLocation();
    427     break;
    428 
    429   case AR_Unavailable:
    430     diag = !ObjCPropertyAccess ? diag::err_unavailable
    431                                : diag::err_property_method_unavailable;
    432     diag_message = diag::err_unavailable_message;
    433     diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;
    434     property_note_select = /* unavailable */ 1;
    435     available_here_select_kind = /* unavailable */ 0;
    436 
    437     if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {
    438       if (AL->isImplicit() && AL->getImplicitReason()) {
    439         // Most of these failures are due to extra restrictions in ARC;
    440         // reflect that in the primary diagnostic when applicable.
    441         auto flagARCError = [&] {
    442           if (S.getLangOpts().ObjCAutoRefCount &&
    443               S.getSourceManager().isInSystemHeader(
    444                   OffendingDecl->getLocation()))
    445             diag = diag::err_unavailable_in_arc;
    446         };
    447 
    448         switch (AL->getImplicitReason()) {
    449         case UnavailableAttr::IR_None: break;
    450 
    451         case UnavailableAttr::IR_ARCForbiddenType:
    452           flagARCError();
    453           diag_available_here = diag::note_arc_forbidden_type;
    454           break;
    455 
    456         case UnavailableAttr::IR_ForbiddenWeak:
    457           if (S.getLangOpts().ObjCWeakRuntime)
    458             diag_available_here = diag::note_arc_weak_disabled;
    459           else
    460             diag_available_here = diag::note_arc_weak_no_runtime;
    461           break;
    462 
    463         case UnavailableAttr::IR_ARCForbiddenConversion:
    464           flagARCError();
    465           diag_available_here = diag::note_performs_forbidden_arc_conversion;
    466           break;
    467 
    468         case UnavailableAttr::IR_ARCInitReturnsUnrelated:
    469           flagARCError();
    470           diag_available_here = diag::note_arc_init_returns_unrelated;
    471           break;
    472 
    473         case UnavailableAttr::IR_ARCFieldWithOwnership:
    474           flagARCError();
    475           diag_available_here = diag::note_arc_field_with_ownership;
    476           break;
    477         }
    478       }
    479     }
    480     break;
    481 
    482   case AR_Available:
    483     llvm_unreachable("Warning for availability of available declaration?");
    484   }
    485 
    486   SmallVector<FixItHint, 12> FixIts;
    487   if (K == AR_Deprecated) {
    488     StringRef Replacement;
    489     if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
    490       Replacement = AL->getReplacement();
    491     if (auto AL = getAttrForPlatform(S.Context, OffendingDecl))
    492       Replacement = AL->getReplacement();
    493 
    494     CharSourceRange UseRange;
    495     if (!Replacement.empty())
    496       UseRange =
    497           CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
    498     if (UseRange.isValid()) {
    499       if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {
    500         Selector Sel = MethodDecl->getSelector();
    501         SmallVector<StringRef, 12> SelectorSlotNames;
    502         Optional<unsigned> NumParams = tryParseObjCMethodName(
    503             Replacement, SelectorSlotNames, S.getLangOpts());
    504         if (NumParams && NumParams.getValue() == Sel.getNumArgs()) {
    505           assert(SelectorSlotNames.size() == Locs.size());
    506           for (unsigned I = 0; I < Locs.size(); ++I) {
    507             if (!Sel.getNameForSlot(I).empty()) {
    508               CharSourceRange NameRange = CharSourceRange::getCharRange(
    509                   Locs[I], S.getLocForEndOfToken(Locs[I]));
    510               FixIts.push_back(FixItHint::CreateReplacement(
    511                   NameRange, SelectorSlotNames[I]));
    512             } else
    513               FixIts.push_back(
    514                   FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I]));
    515           }
    516         } else
    517           FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
    518       } else
    519         FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
    520     }
    521   }
    522 
    523   if (!Message.empty()) {
    524     S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;
    525     if (ObjCProperty)
    526       S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
    527           << ObjCProperty->getDeclName() << property_note_select;
    528   } else if (!UnknownObjCClass) {
    529     S.Diag(Loc, diag) << ReferringDecl << FixIts;
    530     if (ObjCProperty)
    531       S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
    532           << ObjCProperty->getDeclName() << property_note_select;
    533   } else {
    534     S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts;
    535     S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
    536   }
    537 
    538   S.Diag(NoteLocation, diag_available_here)
    539     << OffendingDecl << available_here_select_kind;
    540 }
    541 
    542 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
    543   assert(DD.Kind == DelayedDiagnostic::Availability &&
    544          "Expected an availability diagnostic here");
    545 
    546   DD.Triggered = true;
    547   DoEmitAvailabilityWarning(
    548       *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),
    549       DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),
    550       DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),
    551       DD.getObjCProperty(), false);
    552 }
    553 
    554 static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
    555                                     const NamedDecl *ReferringDecl,
    556                                     const NamedDecl *OffendingDecl,
    557                                     StringRef Message,
    558                                     ArrayRef<SourceLocation> Locs,
    559                                     const ObjCInterfaceDecl *UnknownObjCClass,
    560                                     const ObjCPropertyDecl *ObjCProperty,
    561                                     bool ObjCPropertyAccess) {
    562   // Delay if we're currently parsing a declaration.
    563   if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
    564     S.DelayedDiagnostics.add(
    565         DelayedDiagnostic::makeAvailability(
    566             AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
    567             ObjCProperty, Message, ObjCPropertyAccess));
    568     return;
    569   }
    570 
    571   Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
    572   DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
    573                             Message, Locs, UnknownObjCClass, ObjCProperty,
    574                             ObjCPropertyAccess);
    575 }
    576 
    577 namespace {
    578 
    579 /// Returns true if the given statement can be a body-like child of \p Parent.
    580 bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {
    581   switch (Parent->getStmtClass()) {
    582   case Stmt::IfStmtClass:
    583     return cast<IfStmt>(Parent)->getThen() == S ||
    584            cast<IfStmt>(Parent)->getElse() == S;
    585   case Stmt::WhileStmtClass:
    586     return cast<WhileStmt>(Parent)->getBody() == S;
    587   case Stmt::DoStmtClass:
    588     return cast<DoStmt>(Parent)->getBody() == S;
    589   case Stmt::ForStmtClass:
    590     return cast<ForStmt>(Parent)->getBody() == S;
    591   case Stmt::CXXForRangeStmtClass:
    592     return cast<CXXForRangeStmt>(Parent)->getBody() == S;
    593   case Stmt::ObjCForCollectionStmtClass:
    594     return cast<ObjCForCollectionStmt>(Parent)->getBody() == S;
    595   case Stmt::CaseStmtClass:
    596   case Stmt::DefaultStmtClass:
    597     return cast<SwitchCase>(Parent)->getSubStmt() == S;
    598   default:
    599     return false;
    600   }
    601 }
    602 
    603 class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> {
    604   const Stmt *Target;
    605 
    606 public:
    607   bool VisitStmt(Stmt *S) { return S != Target; }
    608 
    609   /// Returns true if the given statement is present in the given declaration.
    610   static bool isContained(const Stmt *Target, const Decl *D) {
    611     StmtUSEFinder Visitor;
    612     Visitor.Target = Target;
    613     return !Visitor.TraverseDecl(const_cast<Decl *>(D));
    614   }
    615 };
    616 
    617 /// Traverses the AST and finds the last statement that used a given
    618 /// declaration.
    619 class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> {
    620   const Decl *D;
    621 
    622 public:
    623   bool VisitDeclRefExpr(DeclRefExpr *DRE) {
    624     if (DRE->getDecl() == D)
    625       return false;
    626     return true;
    627   }
    628 
    629   static const Stmt *findLastStmtThatUsesDecl(const Decl *D,
    630                                               const CompoundStmt *Scope) {
    631     LastDeclUSEFinder Visitor;
    632     Visitor.D = D;
    633     for (auto I = Scope->body_rbegin(), E = Scope->body_rend(); I != E; ++I) {
    634       const Stmt *S = *I;
    635       if (!Visitor.TraverseStmt(const_cast<Stmt *>(S)))
    636         return S;
    637     }
    638     return nullptr;
    639   }
    640 };
    641 
    642 /// This class implements -Wunguarded-availability.
    643 ///
    644 /// This is done with a traversal of the AST of a function that makes reference
    645 /// to a partially available declaration. Whenever we encounter an \c if of the
    646 /// form: \c if(@available(...)), we use the version from the condition to visit
    647 /// the then statement.
    648 class DiagnoseUnguardedAvailability
    649     : public RecursiveASTVisitor<DiagnoseUnguardedAvailability> {
    650   typedef RecursiveASTVisitor<DiagnoseUnguardedAvailability> Base;
    651 
    652   Sema &SemaRef;
    653   Decl *Ctx;
    654 
    655   /// Stack of potentially nested 'if (@available(...))'s.
    656   SmallVector<VersionTuple, 8> AvailabilityStack;
    657   SmallVector<const Stmt *, 16> StmtStack;
    658 
    659   void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range,
    660                                 ObjCInterfaceDecl *ClassReceiver = nullptr);
    661 
    662 public:
    663   DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
    664       : SemaRef(SemaRef), Ctx(Ctx) {
    665     AvailabilityStack.push_back(
    666         SemaRef.Context.getTargetInfo().getPlatformMinVersion());
    667   }
    668 
    669   bool TraverseDecl(Decl *D) {
    670     // Avoid visiting nested functions to prevent duplicate warnings.
    671     if (!D || isa<FunctionDecl>(D))
    672       return true;
    673     return Base::TraverseDecl(D);
    674   }
    675 
    676   bool TraverseStmt(Stmt *S) {
    677     if (!S)
    678       return true;
    679     StmtStack.push_back(S);
    680     bool Result = Base::TraverseStmt(S);
    681     StmtStack.pop_back();
    682     return Result;
    683   }
    684 
    685   void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
    686 
    687   bool TraverseIfStmt(IfStmt *If);
    688 
    689   bool TraverseLambdaExpr(LambdaExpr *E) { return true; }
    690 
    691   // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
    692   // to any useful diagnostics.
    693   bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }
    694 
    695   bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) {
    696     if (PRE->isClassReceiver())
    697       DiagnoseDeclAvailability(PRE->getClassReceiver(), PRE->getReceiverLocation());
    698     return true;
    699   }
    700 
    701   bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
    702     if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
    703       ObjCInterfaceDecl *ID = nullptr;
    704       QualType ReceiverTy = Msg->getClassReceiver();
    705       if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())
    706         ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
    707 
    708       DiagnoseDeclAvailability(
    709           D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
    710     }
    711     return true;
    712   }
    713 
    714   bool VisitDeclRefExpr(DeclRefExpr *DRE) {
    715     DiagnoseDeclAvailability(DRE->getDecl(),
    716                              SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
    717     return true;
    718   }
    719 
    720   bool VisitMemberExpr(MemberExpr *ME) {
    721     DiagnoseDeclAvailability(ME->getMemberDecl(),
    722                              SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
    723     return true;
    724   }
    725 
    726   bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
    727     SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
    728         << (!SemaRef.getLangOpts().ObjC);
    729     return true;
    730   }
    731 
    732   bool VisitTypeLoc(TypeLoc Ty);
    733 };
    734 
    735 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
    736     NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
    737   AvailabilityResult Result;
    738   const NamedDecl *OffendingDecl;
    739   std::tie(Result, OffendingDecl) =
    740       ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
    741   if (Result != AR_Available) {
    742     // All other diagnostic kinds have already been handled in
    743     // DiagnoseAvailabilityOfDecl.
    744     if (Result != AR_NotYetIntroduced)
    745       return;
    746 
    747     const AvailabilityAttr *AA =
    748       getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
    749     VersionTuple Introduced = AA->getIntroduced();
    750 
    751     if (AvailabilityStack.back() >= Introduced)
    752       return;
    753 
    754     // If the context of this function is less available than D, we should not
    755     // emit a diagnostic.
    756     if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx,
    757                                              OffendingDecl))
    758       return;
    759 
    760     // We would like to emit the diagnostic even if -Wunguarded-availability is
    761     // not specified for deployment targets >= to iOS 11 or equivalent or
    762     // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
    763     // later.
    764     unsigned DiagKind =
    765         shouldDiagnoseAvailabilityByDefault(
    766             SemaRef.Context,
    767             SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced)
    768             ? diag::warn_unguarded_availability_new
    769             : diag::warn_unguarded_availability;
    770 
    771     std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
    772         SemaRef.getASTContext().getTargetInfo().getPlatformName()));
    773 
    774     SemaRef.Diag(Range.getBegin(), DiagKind)
    775         << Range << D << PlatformName << Introduced.getAsString();
    776 
    777     SemaRef.Diag(OffendingDecl->getLocation(),
    778                  diag::note_partial_availability_specified_here)
    779         << OffendingDecl << PlatformName << Introduced.getAsString()
    780         << SemaRef.Context.getTargetInfo()
    781                .getPlatformMinVersion()
    782                .getAsString();
    783 
    784     auto FixitDiag =
    785         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
    786         << Range << D
    787         << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0
    788                                        : /*__builtin_available*/ 1);
    789 
    790     // Find the statement which should be enclosed in the if @available check.
    791     if (StmtStack.empty())
    792       return;
    793     const Stmt *StmtOfUse = StmtStack.back();
    794     const CompoundStmt *Scope = nullptr;
    795     for (const Stmt *S : llvm::reverse(StmtStack)) {
    796       if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
    797         Scope = CS;
    798         break;
    799       }
    800       if (isBodyLikeChildStmt(StmtOfUse, S)) {
    801         // The declaration won't be seen outside of the statement, so we don't
    802         // have to wrap the uses of any declared variables in if (@available).
    803         // Therefore we can avoid setting Scope here.
    804         break;
    805       }
    806       StmtOfUse = S;
    807     }
    808     const Stmt *LastStmtOfUse = nullptr;
    809     if (isa<DeclStmt>(StmtOfUse) && Scope) {
    810       for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
    811         if (StmtUSEFinder::isContained(StmtStack.back(), D)) {
    812           LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);
    813           break;
    814         }
    815       }
    816     }
    817 
    818     const SourceManager &SM = SemaRef.getSourceManager();
    819     SourceLocation IfInsertionLoc =
    820         SM.getExpansionLoc(StmtOfUse->getBeginLoc());
    821     SourceLocation StmtEndLoc =
    822         SM.getExpansionRange(
    823               (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())
    824             .getEnd();
    825     if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
    826       return;
    827 
    828     StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);
    829     const char *ExtraIndentation = "    ";
    830     std::string FixItString;
    831     llvm::raw_string_ostream FixItOS(FixItString);
    832     FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
    833                                                      : "__builtin_available")
    834             << "("
    835             << AvailabilityAttr::getPlatformNameSourceSpelling(
    836                    SemaRef.getASTContext().getTargetInfo().getPlatformName())
    837             << " " << Introduced.getAsString() << ", *)) {\n"
    838             << Indentation << ExtraIndentation;
    839     FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
    840     SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
    841         StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
    842         /*SkipTrailingWhitespaceAndNewLine=*/false);
    843     if (ElseInsertionLoc.isInvalid())
    844       ElseInsertionLoc =
    845           Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());
    846     FixItOS.str().clear();
    847     FixItOS << "\n"
    848             << Indentation << "} else {\n"
    849             << Indentation << ExtraIndentation
    850             << "// Fallback on earlier versions\n"
    851             << Indentation << "}";
    852     FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());
    853   }
    854 }
    855 
    856 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
    857   const Type *TyPtr = Ty.getTypePtr();
    858   SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
    859 
    860   if (Range.isInvalid())
    861     return true;
    862 
    863   if (const auto *TT = dyn_cast<TagType>(TyPtr)) {
    864     TagDecl *TD = TT->getDecl();
    865     DiagnoseDeclAvailability(TD, Range);
    866 
    867   } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
    868     TypedefNameDecl *D = TD->getDecl();
    869     DiagnoseDeclAvailability(D, Range);
    870 
    871   } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
    872     if (NamedDecl *D = ObjCO->getInterface())
    873       DiagnoseDeclAvailability(D, Range);
    874   }
    875 
    876   return true;
    877 }
    878 
    879 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
    880   VersionTuple CondVersion;
    881   if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
    882     CondVersion = E->getVersion();
    883 
    884     // If we're using the '*' case here or if this check is redundant, then we
    885     // use the enclosing version to check both branches.
    886     if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
    887       return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
    888   } else {
    889     // This isn't an availability checking 'if', we can just continue.
    890     return Base::TraverseIfStmt(If);
    891   }
    892 
    893   AvailabilityStack.push_back(CondVersion);
    894   bool ShouldContinue = TraverseStmt(If->getThen());
    895   AvailabilityStack.pop_back();
    896 
    897   return ShouldContinue && TraverseStmt(If->getElse());
    898 }
    899 
    900 } // end anonymous namespace
    901 
    902 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
    903   Stmt *Body = nullptr;
    904 
    905   if (auto *FD = D->getAsFunction()) {
    906     // FIXME: We only examine the pattern decl for availability violations now,
    907     // but we should also examine instantiated templates.
    908     if (FD->isTemplateInstantiation())
    909       return;
    910 
    911     Body = FD->getBody();
    912   } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
    913     Body = MD->getBody();
    914   else if (auto *BD = dyn_cast<BlockDecl>(D))
    915     Body = BD->getBody();
    916 
    917   assert(Body && "Need a body here!");
    918 
    919   DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
    920 }
    921 
    922 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
    923                                       ArrayRef<SourceLocation> Locs,
    924                                       const ObjCInterfaceDecl *UnknownObjCClass,
    925                                       bool ObjCPropertyAccess,
    926                                       bool AvoidPartialAvailabilityChecks,
    927                                       ObjCInterfaceDecl *ClassReceiver) {
    928   std::string Message;
    929   AvailabilityResult Result;
    930   const NamedDecl* OffendingDecl;
    931   // See if this declaration is unavailable, deprecated, or partial.
    932   std::tie(Result, OffendingDecl) =
    933       ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
    934   if (Result == AR_Available)
    935     return;
    936 
    937   if (Result == AR_NotYetIntroduced) {
    938     if (AvoidPartialAvailabilityChecks)
    939       return;
    940 
    941     // We need to know the @available context in the current function to
    942     // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
    943     // when we're done parsing the current function.
    944     if (getCurFunctionOrMethodDecl()) {
    945       getEnclosingFunction()->HasPotentialAvailabilityViolations = true;
    946       return;
    947     } else if (getCurBlock() || getCurLambda()) {
    948       getCurFunction()->HasPotentialAvailabilityViolations = true;
    949       return;
    950     }
    951   }
    952 
    953   const ObjCPropertyDecl *ObjCPDecl = nullptr;
    954   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
    955     if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
    956       AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
    957       if (PDeclResult == Result)
    958         ObjCPDecl = PD;
    959     }
    960   }
    961 
    962   EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
    963                           UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
    964 }
    965