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