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