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