xref: /freebsd/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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/DeclTemplate.h"
16 #include "clang/AST/DynamicRecursiveASTVisitor.h"
17 #include "clang/AST/ExprObjC.h"
18 #include "clang/AST/StmtObjC.h"
19 #include "clang/Basic/DiagnosticSema.h"
20 #include "clang/Basic/IdentifierTable.h"
21 #include "clang/Basic/LangOptions.h"
22 #include "clang/Basic/TargetInfo.h"
23 #include "clang/Lex/Preprocessor.h"
24 #include "clang/Sema/DelayedDiagnostic.h"
25 #include "clang/Sema/ScopeInfo.h"
26 #include "clang/Sema/Sema.h"
27 #include "clang/Sema/SemaObjC.h"
28 #include "llvm/ADT/StringRef.h"
29 #include <optional>
30 
31 using namespace clang;
32 using namespace sema;
33 
34 static bool hasMatchingEnvironmentOrNone(const ASTContext &Context,
35                                          const AvailabilityAttr *AA) {
36   IdentifierInfo *IIEnvironment = AA->getEnvironment();
37   auto Environment = Context.getTargetInfo().getTriple().getEnvironment();
38   if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment)
39     return true;
40 
41   llvm::Triple::EnvironmentType ET =
42       AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
43   return Environment == ET;
44 }
45 
46 static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
47                                                   const Decl *D) {
48   AvailabilityAttr const *PartialMatch = nullptr;
49   // Check each AvailabilityAttr to find the one for this platform.
50   // For multiple attributes with the same platform try to find one for this
51   // environment.
52   // The attribute is always on the FunctionDecl, not on the
53   // FunctionTemplateDecl.
54   if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
55     D = FTD->getTemplatedDecl();
56   for (const auto *A : D->attrs()) {
57     if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
58       // FIXME: this is copied from CheckAvailability. We should try to
59       // de-duplicate.
60 
61       // Check if this is an App Extension "platform", and if so chop off
62       // the suffix for matching with the actual platform.
63       StringRef ActualPlatform = Avail->getPlatform()->getName();
64       StringRef RealizedPlatform = ActualPlatform;
65       if (Context.getLangOpts().AppExt) {
66         size_t suffix = RealizedPlatform.rfind("_app_extension");
67         if (suffix != StringRef::npos)
68           RealizedPlatform = RealizedPlatform.slice(0, suffix);
69       }
70 
71       StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
72 
73       // Match the platform name.
74       if (RealizedPlatform == TargetPlatform) {
75         // Find the best matching attribute for this environment
76         if (hasMatchingEnvironmentOrNone(Context, Avail))
77           return Avail;
78         PartialMatch = Avail;
79       }
80     }
81   }
82   return PartialMatch;
83 }
84 
85 /// The diagnostic we should emit for \c D, and the declaration that
86 /// originated it, or \c AR_Available.
87 ///
88 /// \param D The declaration to check.
89 /// \param Message If non-null, this will be populated with the message from
90 /// the availability attribute that is selected.
91 /// \param ClassReceiver If we're checking the method of a class message
92 /// send, the class. Otherwise nullptr.
93 std::pair<AvailabilityResult, const NamedDecl *>
94 Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message,
95                                        ObjCInterfaceDecl *ClassReceiver) {
96   AvailabilityResult Result = D->getAvailability(Message);
97 
98   // For typedefs, if the typedef declaration appears available look
99   // to the underlying type to see if it is more restrictive.
100   while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
101     if (Result != AR_Available)
102       break;
103     for (const Type *T = TD->getUnderlyingType().getTypePtr(); /**/; /**/) {
104       if (auto *TT = dyn_cast<TagType>(T)) {
105         D = TT->getDecl();
106       } else if (isa<SubstTemplateTypeParmType>(T)) {
107         // A Subst* node represents a use through a template.
108         // Any uses of the underlying declaration happened through it's template
109         // specialization.
110         goto done;
111       } else {
112         const Type *NextT =
113             T->getLocallyUnqualifiedSingleStepDesugaredType().getTypePtr();
114         if (NextT == T)
115           goto done;
116         T = NextT;
117         continue;
118       }
119       Result = D->getAvailability(Message);
120       break;
121     }
122   }
123 done:
124   // For alias templates, get the underlying declaration.
125   if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) {
126     D = ADecl->getTemplatedDecl();
127     Result = D->getAvailability(Message);
128   }
129 
130   // Forward class declarations get their attributes from their definition.
131   if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {
132     if (IDecl->getDefinition()) {
133       D = IDecl->getDefinition();
134       Result = D->getAvailability(Message);
135     }
136   }
137 
138   if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
139     if (Result == AR_Available) {
140       const DeclContext *DC = ECD->getDeclContext();
141       if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) {
142         Result = TheEnumDecl->getAvailability(Message);
143         D = TheEnumDecl;
144       }
145     }
146 
147   // For +new, infer availability from -init.
148   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
149     if (ObjC().NSAPIObj && ClassReceiver) {
150       ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
151           ObjC().NSAPIObj->getInitSelector());
152       if (Init && Result == AR_Available && MD->isClassMethod() &&
153           MD->getSelector() == ObjC().NSAPIObj->getNewSelector() &&
154           MD->definedInNSObject(getASTContext())) {
155         Result = Init->getAvailability(Message);
156         D = Init;
157       }
158     }
159   }
160 
161   return {Result, D};
162 }
163 
164 /// whether we should emit a diagnostic for \c K and \c DeclVersion in
165 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
166 /// in a deprecated context, but not the other way around.
167 static bool ShouldDiagnoseAvailabilityInContext(
168     Sema &S, AvailabilityResult K, VersionTuple DeclVersion,
169     const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) {
170   assert(K != AR_Available && "Expected an unavailable declaration here!");
171 
172   // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
173   auto DeclLoc = Ctx->getBeginLoc();
174   // This is only a problem in Foundation's C++ implementation for CF_OPTIONS.
175   if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus &&
176       isa<TypedefDecl>(OffendingDecl)) {
177     StringRef MacroName = S.getPreprocessor().getImmediateMacroName(DeclLoc);
178     if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" ||
179         MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") {
180       return false;
181     }
182   }
183 
184   // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to
185   // strict (-fhlsl-strict-availability), or if the target is library and the
186   // availability is restricted to a specific environment/shader stage.
187   // For libraries the availability will be checked later in
188   // DiagnoseHLSLAvailability class once where the specific environment/shader
189   // stage of the caller is known.
190   if (S.getLangOpts().HLSL) {
191     if (!S.getLangOpts().HLSLStrictAvailability ||
192         (DeclEnv != nullptr &&
193          S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
194              llvm::Triple::EnvironmentType::Library))
195       return false;
196   }
197 
198   if (K == AR_Deprecated) {
199     if (const auto *VD = dyn_cast<VarDecl>(OffendingDecl))
200       if (VD->isLocalVarDeclOrParm() && VD->isDeprecated())
201         return true;
202   }
203 
204   // Checks if we should emit the availability diagnostic in the context of C.
205   auto CheckContext = [&](const Decl *C) {
206     if (K == AR_NotYetIntroduced) {
207       if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
208         if (AA->getIntroduced() >= DeclVersion &&
209             AA->getEnvironment() == DeclEnv)
210           return true;
211     } else if (K == AR_Deprecated) {
212       if (C->isDeprecated())
213         return true;
214     } else if (K == AR_Unavailable) {
215       // It is perfectly fine to refer to an 'unavailable' Objective-C method
216       // when it is referenced from within the @implementation itself. In this
217       // context, we interpret unavailable as a form of access control.
218       if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) {
219         if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) {
220           if (MD->getClassInterface() == Impl->getClassInterface())
221             return true;
222         }
223       }
224     }
225 
226     if (C->isUnavailable())
227       return true;
228     return false;
229   };
230 
231   do {
232     if (CheckContext(Ctx))
233       return false;
234 
235     // An implementation implicitly has the availability of the interface.
236     // Unless it is "+load" method.
237     if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx))
238       if (MethodD->isClassMethod() &&
239           MethodD->getSelector().getAsString() == "load")
240         return true;
241 
242     if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) {
243       if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())
244         if (CheckContext(Interface))
245           return false;
246     }
247     // A category implicitly has the availability of the interface.
248     else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx))
249       if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
250         if (CheckContext(Interface))
251           return false;
252   } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));
253 
254   return true;
255 }
256 
257 static unsigned getAvailabilityDiagnosticKind(
258     const ASTContext &Context, const VersionTuple &DeploymentVersion,
259     const VersionTuple &DeclVersion, bool HasMatchingEnv) {
260   const auto &Triple = Context.getTargetInfo().getTriple();
261   VersionTuple ForceAvailabilityFromVersion;
262   switch (Triple.getOS()) {
263   // For iOS, emit the diagnostic even if -Wunguarded-availability is
264   // not specified for deployment targets >= to iOS 11 or equivalent or
265   // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
266   // later.
267   case llvm::Triple::IOS:
268   case llvm::Triple::TvOS:
269     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
270     break;
271   case llvm::Triple::WatchOS:
272     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4);
273     break;
274   case llvm::Triple::Darwin:
275   case llvm::Triple::MacOSX:
276     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
277     break;
278   // For HLSL, use diagnostic from HLSLAvailability group which
279   // are reported as errors by default and in strict diagnostic mode
280   // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
281   // mode (-Wno-error=hlsl-availability)
282   case llvm::Triple::ShaderModel:
283     return HasMatchingEnv ? diag::warn_hlsl_availability
284                           : diag::warn_hlsl_availability_unavailable;
285   default:
286     // New Apple targets should always warn about availability.
287     ForceAvailabilityFromVersion =
288         (Triple.getVendor() == llvm::Triple::Apple)
289             ? VersionTuple(/*Major=*/0, 0)
290             : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
291   }
292   if (DeploymentVersion >= ForceAvailabilityFromVersion ||
293       DeclVersion >= ForceAvailabilityFromVersion)
294     return HasMatchingEnv ? diag::warn_unguarded_availability_new
295                           : diag::warn_unguarded_availability_unavailable_new;
296   return HasMatchingEnv ? diag::warn_unguarded_availability
297                         : diag::warn_unguarded_availability_unavailable;
298 }
299 
300 static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
301   for (Decl *Ctx = OrigCtx; Ctx;
302        Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) {
303     if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx))
304       return cast<NamedDecl>(Ctx);
305     if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) {
306       if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx))
307         return Imp->getClassInterface();
308       return CD;
309     }
310   }
311 
312   return dyn_cast<NamedDecl>(OrigCtx);
313 }
314 
315 namespace {
316 
317 struct AttributeInsertion {
318   StringRef Prefix;
319   SourceLocation Loc;
320   StringRef Suffix;
321 
322   static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
323     return {" ", D->getEndLoc(), ""};
324   }
325   static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
326     return {" ", Loc, ""};
327   }
328   static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
329     return {"", D->getBeginLoc(), "\n"};
330   }
331 };
332 
333 } // end anonymous namespace
334 
335 /// Tries to parse a string as ObjC method name.
336 ///
337 /// \param Name The string to parse. Expected to originate from availability
338 /// attribute argument.
339 /// \param SlotNames The vector that will be populated with slot names. In case
340 /// of unsuccessful parsing can contain invalid data.
341 /// \returns A number of method parameters if parsing was successful,
342 /// std::nullopt otherwise.
343 static std::optional<unsigned>
344 tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames,
345                        const LangOptions &LangOpts) {
346   // Accept replacements starting with - or + as valid ObjC method names.
347   if (!Name.empty() && (Name.front() == '-' || Name.front() == '+'))
348     Name = Name.drop_front(1);
349   if (Name.empty())
350     return std::nullopt;
351   Name.split(SlotNames, ':');
352   unsigned NumParams;
353   if (Name.back() == ':') {
354     // Remove an empty string at the end that doesn't represent any slot.
355     SlotNames.pop_back();
356     NumParams = SlotNames.size();
357   } else {
358     if (SlotNames.size() != 1)
359       // Not a valid method name, just a colon-separated string.
360       return std::nullopt;
361     NumParams = 0;
362   }
363   // Verify all slot names are valid.
364   bool AllowDollar = LangOpts.DollarIdents;
365   for (StringRef S : SlotNames) {
366     if (S.empty())
367       continue;
368     if (!isValidAsciiIdentifier(S, AllowDollar))
369       return std::nullopt;
370   }
371   return NumParams;
372 }
373 
374 /// Returns a source location in which it's appropriate to insert a new
375 /// attribute for the given declaration \D.
376 static std::optional<AttributeInsertion>
377 createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
378                          const LangOptions &LangOpts) {
379   if (isa<ObjCPropertyDecl>(D))
380     return AttributeInsertion::createInsertionAfter(D);
381   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
382     if (MD->hasBody())
383       return std::nullopt;
384     return AttributeInsertion::createInsertionAfter(D);
385   }
386   if (const auto *TD = dyn_cast<TagDecl>(D)) {
387     SourceLocation Loc =
388         Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts);
389     if (Loc.isInvalid())
390       return std::nullopt;
391     // Insert after the 'struct'/whatever keyword.
392     return AttributeInsertion::createInsertionAfter(Loc);
393   }
394   return AttributeInsertion::createInsertionBefore(D);
395 }
396 
397 /// Actually emit an availability diagnostic for a reference to an unavailable
398 /// decl.
399 ///
400 /// \param Ctx The context that the reference occurred in
401 /// \param ReferringDecl The exact declaration that was referenced.
402 /// \param OffendingDecl A related decl to \c ReferringDecl that has an
403 /// availability attribute corresponding to \c K attached to it. Note that this
404 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
405 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
406 /// and OffendingDecl is the EnumDecl.
407 static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
408                                       Decl *Ctx, const NamedDecl *ReferringDecl,
409                                       const NamedDecl *OffendingDecl,
410                                       StringRef Message,
411                                       ArrayRef<SourceLocation> Locs,
412                                       const ObjCInterfaceDecl *UnknownObjCClass,
413                                       const ObjCPropertyDecl *ObjCProperty,
414                                       bool ObjCPropertyAccess) {
415   // Diagnostics for deprecated or unavailable.
416   unsigned diag, diag_message, diag_fwdclass_message;
417   unsigned diag_available_here = diag::note_availability_specified_here;
418   SourceLocation NoteLocation = OffendingDecl->getLocation();
419 
420   // Matches 'diag::note_property_attribute' options.
421   unsigned property_note_select;
422 
423   // Matches diag::note_availability_specified_here.
424   unsigned available_here_select_kind;
425 
426   VersionTuple DeclVersion;
427   const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl);
428   const IdentifierInfo *IIEnv = nullptr;
429   if (AA) {
430     DeclVersion = AA->getIntroduced();
431     IIEnv = AA->getEnvironment();
432   }
433 
434   if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx,
435                                            OffendingDecl))
436     return;
437 
438   SourceLocation Loc = Locs.front();
439 
440   // The declaration can have multiple availability attributes, we are looking
441   // at one of them.
442   if (AA && AA->isInherited()) {
443     for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
444          Redecl = Redecl->getPreviousDecl()) {
445       const AvailabilityAttr *AForRedecl =
446           getAttrForPlatform(S.Context, Redecl);
447       if (AForRedecl && !AForRedecl->isInherited()) {
448         // If D is a declaration with inherited attributes, the note should
449         // point to the declaration with actual attributes.
450         NoteLocation = Redecl->getLocation();
451         break;
452       }
453     }
454   }
455 
456   switch (K) {
457   case AR_NotYetIntroduced: {
458     // We would like to emit the diagnostic even if -Wunguarded-availability is
459     // not specified for deployment targets >= to iOS 11 or equivalent or
460     // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
461     // later.
462     assert(AA != nullptr && "expecting valid availability attribute");
463     VersionTuple Introduced = AA->getIntroduced();
464     bool EnvironmentMatchesOrNone =
465         hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
466 
467     const TargetInfo &TI = S.getASTContext().getTargetInfo();
468     std::string PlatformName(
469         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
470     llvm::StringRef TargetEnvironment(
471         llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment()));
472     llvm::StringRef AttrEnvironment =
473         AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
474     bool UseEnvironment =
475         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
476 
477     unsigned DiagKind = getAvailabilityDiagnosticKind(
478         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
479         Introduced, EnvironmentMatchesOrNone);
480 
481     S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
482                           << Introduced.getAsString() << UseEnvironment
483                           << TargetEnvironment;
484 
485     S.Diag(OffendingDecl->getLocation(),
486            diag::note_partial_availability_specified_here)
487         << OffendingDecl << PlatformName << Introduced.getAsString()
488         << S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
489         << UseEnvironment << AttrEnvironment << TargetEnvironment;
490 
491     // Do not offer to silence the warning or fixits for HLSL
492     if (S.getLangOpts().HLSL)
493       return;
494 
495     if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
496       if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
497         if (TD->getDeclName().isEmpty()) {
498           S.Diag(TD->getLocation(),
499                  diag::note_decl_unguarded_availability_silence)
500               << /*Anonymous*/ 1 << TD->getKindName();
501           return;
502         }
503       auto FixitNoteDiag =
504           S.Diag(Enclosing->getLocation(),
505                  diag::note_decl_unguarded_availability_silence)
506           << /*Named*/ 0 << Enclosing;
507       // Don't offer a fixit for declarations with availability attributes.
508       if (Enclosing->hasAttr<AvailabilityAttr>())
509         return;
510       Preprocessor &PP = S.getPreprocessor();
511       if (!PP.isMacroDefined("API_AVAILABLE"))
512         return;
513       std::optional<AttributeInsertion> Insertion = createAttributeInsertion(
514           Enclosing, S.getSourceManager(), S.getLangOpts());
515       if (!Insertion)
516         return;
517       StringRef PlatformName =
518           S.getASTContext().getTargetInfo().getPlatformName();
519 
520       // Apple's API_AVAILABLE macro expands roughly like this.
521       // API_AVAILABLE(ios(17.0))
522       // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0)))
523       // __attribute__((availability(ios,introduced=17.0)))
524       // In order to figure out which platform name to use in the API_AVAILABLE
525       // macro, the associated __API_AVAILABLE_PLATFORM_ macro needs to be
526       // found. The __API_AVAILABLE_PLATFORM_ macros aren't consistent about
527       // using the canonical platform name, source spelling name, or one of the
528       // other supported names (i.e. one of the keys in canonicalizePlatformName
529       // that's neither). Check all of the supported names for a match.
530       std::vector<StringRef> EquivalentPlatforms =
531           AvailabilityAttr::equivalentPlatformNames(PlatformName);
532       llvm::Twine MacroPrefix = "__API_AVAILABLE_PLATFORM_";
533       auto AvailablePlatform =
534           llvm::find_if(EquivalentPlatforms, [&](StringRef EquivalentPlatform) {
535             return PP.isMacroDefined((MacroPrefix + EquivalentPlatform).str());
536           });
537       if (AvailablePlatform == EquivalentPlatforms.end())
538         return;
539       std::string Introduced =
540           OffendingDecl->getVersionIntroduced().getAsString();
541       FixitNoteDiag << FixItHint::CreateInsertion(
542           Insertion->Loc,
543           (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" +
544            *AvailablePlatform + "(" + Introduced + "))" + Insertion->Suffix)
545               .str());
546     }
547     return;
548   }
549   case AR_Deprecated:
550     if (ObjCPropertyAccess)
551       diag = diag::warn_property_method_deprecated;
552     else if (S.currentEvaluationContext().IsCaseExpr)
553       diag = diag::warn_deprecated_switch_case;
554     else
555       diag = diag::warn_deprecated;
556 
557     diag_message = diag::warn_deprecated_message;
558     diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;
559     property_note_select = /* deprecated */ 0;
560     available_here_select_kind = /* deprecated */ 2;
561     if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())
562       NoteLocation = AL->getLocation();
563     break;
564 
565   case AR_Unavailable:
566     diag = !ObjCPropertyAccess ? diag::err_unavailable
567                                : diag::err_property_method_unavailable;
568     diag_message = diag::err_unavailable_message;
569     diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;
570     property_note_select = /* unavailable */ 1;
571     available_here_select_kind = /* unavailable */ 0;
572 
573     if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {
574       if (AL->isImplicit() && AL->getImplicitReason()) {
575         // Most of these failures are due to extra restrictions in ARC;
576         // reflect that in the primary diagnostic when applicable.
577         auto flagARCError = [&] {
578           if (S.getLangOpts().ObjCAutoRefCount &&
579               S.getSourceManager().isInSystemHeader(
580                   OffendingDecl->getLocation()))
581             diag = diag::err_unavailable_in_arc;
582         };
583 
584         switch (AL->getImplicitReason()) {
585         case UnavailableAttr::IR_None: break;
586 
587         case UnavailableAttr::IR_ARCForbiddenType:
588           flagARCError();
589           diag_available_here = diag::note_arc_forbidden_type;
590           break;
591 
592         case UnavailableAttr::IR_ForbiddenWeak:
593           if (S.getLangOpts().ObjCWeakRuntime)
594             diag_available_here = diag::note_arc_weak_disabled;
595           else
596             diag_available_here = diag::note_arc_weak_no_runtime;
597           break;
598 
599         case UnavailableAttr::IR_ARCForbiddenConversion:
600           flagARCError();
601           diag_available_here = diag::note_performs_forbidden_arc_conversion;
602           break;
603 
604         case UnavailableAttr::IR_ARCInitReturnsUnrelated:
605           flagARCError();
606           diag_available_here = diag::note_arc_init_returns_unrelated;
607           break;
608 
609         case UnavailableAttr::IR_ARCFieldWithOwnership:
610           flagARCError();
611           diag_available_here = diag::note_arc_field_with_ownership;
612           break;
613         }
614       }
615     }
616     break;
617 
618   case AR_Available:
619     llvm_unreachable("Warning for availability of available declaration?");
620   }
621 
622   SmallVector<FixItHint, 12> FixIts;
623   if (K == AR_Deprecated) {
624     StringRef Replacement;
625     if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
626       Replacement = AL->getReplacement();
627     if (auto AL = getAttrForPlatform(S.Context, OffendingDecl))
628       Replacement = AL->getReplacement();
629 
630     CharSourceRange UseRange;
631     if (!Replacement.empty())
632       UseRange =
633           CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
634     if (UseRange.isValid()) {
635       if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {
636         Selector Sel = MethodDecl->getSelector();
637         SmallVector<StringRef, 12> SelectorSlotNames;
638         std::optional<unsigned> NumParams = tryParseObjCMethodName(
639             Replacement, SelectorSlotNames, S.getLangOpts());
640         if (NumParams && *NumParams == Sel.getNumArgs()) {
641           assert(SelectorSlotNames.size() == Locs.size());
642           for (unsigned I = 0; I < Locs.size(); ++I) {
643             if (!Sel.getNameForSlot(I).empty()) {
644               CharSourceRange NameRange = CharSourceRange::getCharRange(
645                   Locs[I], S.getLocForEndOfToken(Locs[I]));
646               FixIts.push_back(FixItHint::CreateReplacement(
647                   NameRange, SelectorSlotNames[I]));
648             } else
649               FixIts.push_back(
650                   FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I]));
651           }
652         } else
653           FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
654       } else
655         FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
656     }
657   }
658 
659   // We emit deprecation warning for deprecated specializations
660   // when their instantiation stacks originate outside
661   // of a system header, even if the diagnostics is suppresed at the
662   // point of definition.
663   SourceLocation InstantiationLoc =
664       S.getTopMostPointOfInstantiation(ReferringDecl);
665   bool ShouldAllowWarningInSystemHeader =
666       InstantiationLoc != Loc &&
667       !S.getSourceManager().isInSystemHeader(InstantiationLoc);
668   struct AllowWarningInSystemHeaders {
669     AllowWarningInSystemHeaders(DiagnosticsEngine &E,
670                                 bool AllowWarningInSystemHeaders)
671         : Engine(E), Prev(E.getSuppressSystemWarnings()) {
672       E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders);
673     }
674     ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); }
675 
676   private:
677     DiagnosticsEngine &Engine;
678     bool Prev;
679   } SystemWarningOverrideRAII(S.getDiagnostics(),
680                               ShouldAllowWarningInSystemHeader);
681 
682   if (!Message.empty()) {
683     S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;
684     if (ObjCProperty)
685       S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
686           << ObjCProperty->getDeclName() << property_note_select;
687   } else if (!UnknownObjCClass) {
688     S.Diag(Loc, diag) << ReferringDecl << FixIts;
689     if (ObjCProperty)
690       S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
691           << ObjCProperty->getDeclName() << property_note_select;
692   } else {
693     S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts;
694     S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
695   }
696 
697   S.Diag(NoteLocation, diag_available_here)
698     << OffendingDecl << available_here_select_kind;
699 }
700 
701 void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
702   assert(DD.Kind == DelayedDiagnostic::Availability &&
703          "Expected an availability diagnostic here");
704 
705   DD.Triggered = true;
706   DoEmitAvailabilityWarning(
707       *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),
708       DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),
709       DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),
710       DD.getObjCProperty(), false);
711 }
712 
713 static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
714                                     const NamedDecl *ReferringDecl,
715                                     const NamedDecl *OffendingDecl,
716                                     StringRef Message,
717                                     ArrayRef<SourceLocation> Locs,
718                                     const ObjCInterfaceDecl *UnknownObjCClass,
719                                     const ObjCPropertyDecl *ObjCProperty,
720                                     bool ObjCPropertyAccess) {
721   // Delay if we're currently parsing a declaration.
722   if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
723     S.DelayedDiagnostics.add(
724         DelayedDiagnostic::makeAvailability(
725             AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
726             ObjCProperty, Message, ObjCPropertyAccess));
727     return;
728   }
729 
730   Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
731   DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
732                             Message, Locs, UnknownObjCClass, ObjCProperty,
733                             ObjCPropertyAccess);
734 }
735 
736 namespace {
737 
738 /// Returns true if the given statement can be a body-like child of \p Parent.
739 bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {
740   switch (Parent->getStmtClass()) {
741   case Stmt::IfStmtClass:
742     return cast<IfStmt>(Parent)->getThen() == S ||
743            cast<IfStmt>(Parent)->getElse() == S;
744   case Stmt::WhileStmtClass:
745     return cast<WhileStmt>(Parent)->getBody() == S;
746   case Stmt::DoStmtClass:
747     return cast<DoStmt>(Parent)->getBody() == S;
748   case Stmt::ForStmtClass:
749     return cast<ForStmt>(Parent)->getBody() == S;
750   case Stmt::CXXForRangeStmtClass:
751     return cast<CXXForRangeStmt>(Parent)->getBody() == S;
752   case Stmt::ObjCForCollectionStmtClass:
753     return cast<ObjCForCollectionStmt>(Parent)->getBody() == S;
754   case Stmt::CaseStmtClass:
755   case Stmt::DefaultStmtClass:
756     return cast<SwitchCase>(Parent)->getSubStmt() == S;
757   default:
758     return false;
759   }
760 }
761 
762 class StmtUSEFinder : public DynamicRecursiveASTVisitor {
763   const Stmt *Target;
764 
765 public:
766   bool VisitStmt(Stmt *S) override { return S != Target; }
767 
768   /// Returns true if the given statement is present in the given declaration.
769   static bool isContained(const Stmt *Target, const Decl *D) {
770     StmtUSEFinder Visitor;
771     Visitor.Target = Target;
772     return !Visitor.TraverseDecl(const_cast<Decl *>(D));
773   }
774 };
775 
776 /// Traverses the AST and finds the last statement that used a given
777 /// declaration.
778 class LastDeclUSEFinder : public DynamicRecursiveASTVisitor {
779   const Decl *D;
780 
781 public:
782   bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
783     if (DRE->getDecl() == D)
784       return false;
785     return true;
786   }
787 
788   static const Stmt *findLastStmtThatUsesDecl(const Decl *D,
789                                               const CompoundStmt *Scope) {
790     LastDeclUSEFinder Visitor;
791     Visitor.D = D;
792     for (const Stmt *S : llvm::reverse(Scope->body())) {
793       if (!Visitor.TraverseStmt(const_cast<Stmt *>(S)))
794         return S;
795     }
796     return nullptr;
797   }
798 };
799 
800 /// This class implements -Wunguarded-availability.
801 ///
802 /// This is done with a traversal of the AST of a function that makes reference
803 /// to a partially available declaration. Whenever we encounter an \c if of the
804 /// form: \c if(@available(...)), we use the version from the condition to visit
805 /// the then statement.
806 class DiagnoseUnguardedAvailability : public DynamicRecursiveASTVisitor {
807   Sema &SemaRef;
808   Decl *Ctx;
809 
810   /// Stack of potentially nested 'if (@available(...))'s.
811   SmallVector<VersionTuple, 8> AvailabilityStack;
812   SmallVector<const Stmt *, 16> StmtStack;
813 
814   void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range,
815                                 ObjCInterfaceDecl *ClassReceiver = nullptr);
816 
817 public:
818   DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
819       : SemaRef(SemaRef), Ctx(Ctx) {
820     AvailabilityStack.push_back(
821         SemaRef.Context.getTargetInfo().getPlatformMinVersion());
822   }
823 
824   bool TraverseStmt(Stmt *S) override {
825     if (!S)
826       return true;
827     StmtStack.push_back(S);
828     bool Result = DynamicRecursiveASTVisitor::TraverseStmt(S);
829     StmtStack.pop_back();
830     return Result;
831   }
832 
833   void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
834 
835   bool TraverseIfStmt(IfStmt *If) override;
836 
837   // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
838   // to any useful diagnostics.
839   bool TraverseCaseStmt(CaseStmt *CS) override {
840     return TraverseStmt(CS->getSubStmt());
841   }
842 
843   bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) override {
844     if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
845       ObjCInterfaceDecl *ID = nullptr;
846       QualType ReceiverTy = Msg->getClassReceiver();
847       if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())
848         ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
849 
850       DiagnoseDeclAvailability(
851           D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
852     }
853     return true;
854   }
855 
856   bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
857     DiagnoseDeclAvailability(DRE->getDecl(),
858                              SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
859     return true;
860   }
861 
862   bool VisitMemberExpr(MemberExpr *ME) override {
863     DiagnoseDeclAvailability(ME->getMemberDecl(),
864                              SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
865     return true;
866   }
867 
868   bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) override {
869     SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
870         << (!SemaRef.getLangOpts().ObjC);
871     return true;
872   }
873 
874   bool VisitTypeLoc(TypeLoc Ty) override;
875 };
876 
877 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
878     NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
879   AvailabilityResult Result;
880   const NamedDecl *OffendingDecl;
881   std::tie(Result, OffendingDecl) =
882       SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr, ReceiverClass);
883   if (Result != AR_Available) {
884     // All other diagnostic kinds have already been handled in
885     // DiagnoseAvailabilityOfDecl.
886     if (Result != AR_NotYetIntroduced)
887       return;
888 
889     const AvailabilityAttr *AA =
890       getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
891     assert(AA != nullptr && "expecting valid availability attribute");
892     bool EnvironmentMatchesOrNone =
893         hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);
894     VersionTuple Introduced = AA->getIntroduced();
895 
896     if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced)
897       return;
898 
899     // If the context of this function is less available than D, we should not
900     // emit a diagnostic.
901     if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced,
902                                              AA->getEnvironment(), Ctx,
903                                              OffendingDecl))
904       return;
905 
906     const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
907     std::string PlatformName(
908         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
909     llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());
910     llvm::StringRef AttrEnvironment =
911         AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
912     bool UseEnvironment =
913         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
914 
915     unsigned DiagKind = getAvailabilityDiagnosticKind(
916         SemaRef.Context,
917         SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,
918         EnvironmentMatchesOrNone);
919 
920     SemaRef.Diag(Range.getBegin(), DiagKind)
921         << Range << D << PlatformName << Introduced.getAsString()
922         << UseEnvironment << TargetEnvironment;
923 
924     SemaRef.Diag(OffendingDecl->getLocation(),
925                  diag::note_partial_availability_specified_here)
926         << OffendingDecl << PlatformName << Introduced.getAsString()
927         << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()
928         << UseEnvironment << AttrEnvironment << TargetEnvironment;
929 
930     // Do not offer to silence the warning or fixits for HLSL
931     if (SemaRef.getLangOpts().HLSL)
932       return;
933 
934     auto FixitDiag =
935         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
936         << Range << D
937         << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0
938                                        : /*__builtin_available*/ 1);
939 
940     // Find the statement which should be enclosed in the if @available check.
941     if (StmtStack.empty())
942       return;
943     const Stmt *StmtOfUse = StmtStack.back();
944     const CompoundStmt *Scope = nullptr;
945     for (const Stmt *S : llvm::reverse(StmtStack)) {
946       if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
947         Scope = CS;
948         break;
949       }
950       if (isBodyLikeChildStmt(StmtOfUse, S)) {
951         // The declaration won't be seen outside of the statement, so we don't
952         // have to wrap the uses of any declared variables in if (@available).
953         // Therefore we can avoid setting Scope here.
954         break;
955       }
956       StmtOfUse = S;
957     }
958     const Stmt *LastStmtOfUse = nullptr;
959     if (isa<DeclStmt>(StmtOfUse) && Scope) {
960       for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
961         if (StmtUSEFinder::isContained(StmtStack.back(), D)) {
962           LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);
963           break;
964         }
965       }
966     }
967 
968     const SourceManager &SM = SemaRef.getSourceManager();
969     SourceLocation IfInsertionLoc =
970         SM.getExpansionLoc(StmtOfUse->getBeginLoc());
971     SourceLocation StmtEndLoc =
972         SM.getExpansionRange(
973               (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())
974             .getEnd();
975     if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
976       return;
977 
978     StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);
979     const char *ExtraIndentation = "    ";
980     std::string FixItString;
981     llvm::raw_string_ostream FixItOS(FixItString);
982     FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
983                                                      : "__builtin_available")
984             << "("
985             << AvailabilityAttr::getPlatformNameSourceSpelling(
986                    SemaRef.getASTContext().getTargetInfo().getPlatformName())
987             << " " << Introduced.getAsString() << ", *)) {\n"
988             << Indentation << ExtraIndentation;
989     FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
990     SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
991         StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
992         /*SkipTrailingWhitespaceAndNewLine=*/false);
993     if (ElseInsertionLoc.isInvalid())
994       ElseInsertionLoc =
995           Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());
996     FixItOS.str().clear();
997     FixItOS << "\n"
998             << Indentation << "} else {\n"
999             << Indentation << ExtraIndentation
1000             << "// Fallback on earlier versions\n"
1001             << Indentation << "}";
1002     FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());
1003   }
1004 }
1005 
1006 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
1007   const Type *TyPtr = Ty.getTypePtr();
1008   SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
1009 
1010   if (Range.isInvalid())
1011     return true;
1012 
1013   if (const auto *TT = dyn_cast<TagType>(TyPtr)) {
1014     TagDecl *TD = TT->getDecl();
1015     DiagnoseDeclAvailability(TD, Range);
1016 
1017   } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
1018     TypedefNameDecl *D = TD->getDecl();
1019     DiagnoseDeclAvailability(D, Range);
1020 
1021   } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
1022     if (NamedDecl *D = ObjCO->getInterface())
1023       DiagnoseDeclAvailability(D, Range);
1024   }
1025 
1026   return true;
1027 }
1028 
1029 struct ExtractedAvailabilityExpr {
1030   const ObjCAvailabilityCheckExpr *E = nullptr;
1031   bool isNegated = false;
1032 };
1033 
1034 ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) {
1035   const auto *E = IfCond;
1036   bool IsNegated = false;
1037   while (true) {
1038     E = E->IgnoreParens();
1039     if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(E)) {
1040       return ExtractedAvailabilityExpr{AE, IsNegated};
1041     }
1042 
1043     const auto *UO = dyn_cast<UnaryOperator>(E);
1044     if (!UO || UO->getOpcode() != UO_LNot) {
1045       return ExtractedAvailabilityExpr{};
1046     }
1047     E = UO->getSubExpr();
1048     IsNegated = !IsNegated;
1049   }
1050 }
1051 
1052 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
1053   ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond());
1054   if (!IfCond.E) {
1055     // This isn't an availability checking 'if', we can just continue.
1056     return DynamicRecursiveASTVisitor::TraverseIfStmt(If);
1057   }
1058 
1059   VersionTuple CondVersion = IfCond.E->getVersion();
1060   // If we're using the '*' case here or if this check is redundant, then we
1061   // use the enclosing version to check both branches.
1062   if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) {
1063     return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
1064   }
1065 
1066   auto *Guarded = If->getThen();
1067   auto *Unguarded = If->getElse();
1068   if (IfCond.isNegated) {
1069     std::swap(Guarded, Unguarded);
1070   }
1071 
1072   AvailabilityStack.push_back(CondVersion);
1073   bool ShouldContinue = TraverseStmt(Guarded);
1074   AvailabilityStack.pop_back();
1075 
1076   return ShouldContinue && TraverseStmt(Unguarded);
1077 }
1078 
1079 } // end anonymous namespace
1080 
1081 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
1082   Stmt *Body = nullptr;
1083 
1084   if (auto *FD = D->getAsFunction()) {
1085     Body = FD->getBody();
1086 
1087     if (auto *CD = dyn_cast<CXXConstructorDecl>(FD))
1088       for (const CXXCtorInitializer *CI : CD->inits())
1089         DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit());
1090 
1091   } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
1092     Body = MD->getBody();
1093   else if (auto *BD = dyn_cast<BlockDecl>(D))
1094     Body = BD->getBody();
1095 
1096   assert(Body && "Need a body here!");
1097 
1098   DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
1099 }
1100 
1101 FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() {
1102   if (FunctionScopes.empty())
1103     return nullptr;
1104 
1105   // Conservatively search the entire current function scope context for
1106   // availability violations. This ensures we always correctly analyze nested
1107   // classes, blocks, lambdas, etc. that may or may not be inside if(@available)
1108   // checks themselves.
1109   return FunctionScopes.front();
1110 }
1111 
1112 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
1113                                       ArrayRef<SourceLocation> Locs,
1114                                       const ObjCInterfaceDecl *UnknownObjCClass,
1115                                       bool ObjCPropertyAccess,
1116                                       bool AvoidPartialAvailabilityChecks,
1117                                       ObjCInterfaceDecl *ClassReceiver) {
1118 
1119   std::string Message;
1120   AvailabilityResult Result;
1121   const NamedDecl* OffendingDecl;
1122   // See if this declaration is unavailable, deprecated, or partial.
1123   std::tie(Result, OffendingDecl) =
1124       ShouldDiagnoseAvailabilityOfDecl(D, &Message, ClassReceiver);
1125   if (Result == AR_Available)
1126     return;
1127 
1128   if (Result == AR_NotYetIntroduced) {
1129     if (AvoidPartialAvailabilityChecks)
1130       return;
1131 
1132     // We need to know the @available context in the current function to
1133     // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
1134     // when we're done parsing the current function.
1135     if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) {
1136       Context->HasPotentialAvailabilityViolations = true;
1137       return;
1138     }
1139   }
1140 
1141   const ObjCPropertyDecl *ObjCPDecl = nullptr;
1142   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
1143     if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
1144       AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
1145       if (PDeclResult == Result)
1146         ObjCPDecl = PD;
1147     }
1148   }
1149 
1150   EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
1151                           UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
1152 }
1153 
1154 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
1155                                       ArrayRef<SourceLocation> Locs) {
1156   DiagnoseAvailabilityOfDecl(D, Locs, /*UnknownObjCClass=*/nullptr,
1157                              /*ObjCPropertyAccess=*/false,
1158                              /*AvoidPartialAvailabilityChecks=*/false,
1159                              /*ClassReceiver=*/nullptr);
1160 }
1161