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