xref: /freebsd/contrib/llvm-project/clang/lib/Sema/SemaAvailability.cpp (revision 6be3386466ab79a84b48429ae66244f21526d3df)
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 TraverseDecl(Decl *D) {
670     // Avoid visiting nested functions to prevent duplicate warnings.
671     if (!D || isa<FunctionDecl>(D))
672       return true;
673     return Base::TraverseDecl(D);
674   }
675 
676   bool TraverseStmt(Stmt *S) {
677     if (!S)
678       return true;
679     StmtStack.push_back(S);
680     bool Result = Base::TraverseStmt(S);
681     StmtStack.pop_back();
682     return Result;
683   }
684 
685   void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
686 
687   bool TraverseIfStmt(IfStmt *If);
688 
689   bool TraverseLambdaExpr(LambdaExpr *E) { return true; }
690 
691   // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
692   // to any useful diagnostics.
693   bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }
694 
695   bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) {
696     if (PRE->isClassReceiver())
697       DiagnoseDeclAvailability(PRE->getClassReceiver(), PRE->getReceiverLocation());
698     return true;
699   }
700 
701   bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
702     if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
703       ObjCInterfaceDecl *ID = nullptr;
704       QualType ReceiverTy = Msg->getClassReceiver();
705       if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())
706         ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
707 
708       DiagnoseDeclAvailability(
709           D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
710     }
711     return true;
712   }
713 
714   bool VisitDeclRefExpr(DeclRefExpr *DRE) {
715     DiagnoseDeclAvailability(DRE->getDecl(),
716                              SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
717     return true;
718   }
719 
720   bool VisitMemberExpr(MemberExpr *ME) {
721     DiagnoseDeclAvailability(ME->getMemberDecl(),
722                              SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
723     return true;
724   }
725 
726   bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
727     SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
728         << (!SemaRef.getLangOpts().ObjC);
729     return true;
730   }
731 
732   bool VisitTypeLoc(TypeLoc Ty);
733 };
734 
735 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
736     NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
737   AvailabilityResult Result;
738   const NamedDecl *OffendingDecl;
739   std::tie(Result, OffendingDecl) =
740       ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
741   if (Result != AR_Available) {
742     // All other diagnostic kinds have already been handled in
743     // DiagnoseAvailabilityOfDecl.
744     if (Result != AR_NotYetIntroduced)
745       return;
746 
747     const AvailabilityAttr *AA =
748       getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
749     VersionTuple Introduced = AA->getIntroduced();
750 
751     if (AvailabilityStack.back() >= Introduced)
752       return;
753 
754     // If the context of this function is less available than D, we should not
755     // emit a diagnostic.
756     if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced, Ctx,
757                                              OffendingDecl))
758       return;
759 
760     // We would like to emit the diagnostic even if -Wunguarded-availability is
761     // not specified for deployment targets >= to iOS 11 or equivalent or
762     // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
763     // later.
764     unsigned DiagKind =
765         shouldDiagnoseAvailabilityByDefault(
766             SemaRef.Context,
767             SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced)
768             ? diag::warn_unguarded_availability_new
769             : diag::warn_unguarded_availability;
770 
771     std::string PlatformName(AvailabilityAttr::getPrettyPlatformName(
772         SemaRef.getASTContext().getTargetInfo().getPlatformName()));
773 
774     SemaRef.Diag(Range.getBegin(), DiagKind)
775         << Range << D << PlatformName << Introduced.getAsString();
776 
777     SemaRef.Diag(OffendingDecl->getLocation(),
778                  diag::note_partial_availability_specified_here)
779         << OffendingDecl << PlatformName << Introduced.getAsString()
780         << SemaRef.Context.getTargetInfo()
781                .getPlatformMinVersion()
782                .getAsString();
783 
784     auto FixitDiag =
785         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
786         << Range << D
787         << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0
788                                        : /*__builtin_available*/ 1);
789 
790     // Find the statement which should be enclosed in the if @available check.
791     if (StmtStack.empty())
792       return;
793     const Stmt *StmtOfUse = StmtStack.back();
794     const CompoundStmt *Scope = nullptr;
795     for (const Stmt *S : llvm::reverse(StmtStack)) {
796       if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
797         Scope = CS;
798         break;
799       }
800       if (isBodyLikeChildStmt(StmtOfUse, S)) {
801         // The declaration won't be seen outside of the statement, so we don't
802         // have to wrap the uses of any declared variables in if (@available).
803         // Therefore we can avoid setting Scope here.
804         break;
805       }
806       StmtOfUse = S;
807     }
808     const Stmt *LastStmtOfUse = nullptr;
809     if (isa<DeclStmt>(StmtOfUse) && Scope) {
810       for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
811         if (StmtUSEFinder::isContained(StmtStack.back(), D)) {
812           LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);
813           break;
814         }
815       }
816     }
817 
818     const SourceManager &SM = SemaRef.getSourceManager();
819     SourceLocation IfInsertionLoc =
820         SM.getExpansionLoc(StmtOfUse->getBeginLoc());
821     SourceLocation StmtEndLoc =
822         SM.getExpansionRange(
823               (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())
824             .getEnd();
825     if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
826       return;
827 
828     StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);
829     const char *ExtraIndentation = "    ";
830     std::string FixItString;
831     llvm::raw_string_ostream FixItOS(FixItString);
832     FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
833                                                      : "__builtin_available")
834             << "("
835             << AvailabilityAttr::getPlatformNameSourceSpelling(
836                    SemaRef.getASTContext().getTargetInfo().getPlatformName())
837             << " " << Introduced.getAsString() << ", *)) {\n"
838             << Indentation << ExtraIndentation;
839     FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
840     SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
841         StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
842         /*SkipTrailingWhitespaceAndNewLine=*/false);
843     if (ElseInsertionLoc.isInvalid())
844       ElseInsertionLoc =
845           Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());
846     FixItOS.str().clear();
847     FixItOS << "\n"
848             << Indentation << "} else {\n"
849             << Indentation << ExtraIndentation
850             << "// Fallback on earlier versions\n"
851             << Indentation << "}";
852     FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());
853   }
854 }
855 
856 bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
857   const Type *TyPtr = Ty.getTypePtr();
858   SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
859 
860   if (Range.isInvalid())
861     return true;
862 
863   if (const auto *TT = dyn_cast<TagType>(TyPtr)) {
864     TagDecl *TD = TT->getDecl();
865     DiagnoseDeclAvailability(TD, Range);
866 
867   } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
868     TypedefNameDecl *D = TD->getDecl();
869     DiagnoseDeclAvailability(D, Range);
870 
871   } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
872     if (NamedDecl *D = ObjCO->getInterface())
873       DiagnoseDeclAvailability(D, Range);
874   }
875 
876   return true;
877 }
878 
879 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
880   VersionTuple CondVersion;
881   if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(If->getCond())) {
882     CondVersion = E->getVersion();
883 
884     // If we're using the '*' case here or if this check is redundant, then we
885     // use the enclosing version to check both branches.
886     if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
887       return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
888   } else {
889     // This isn't an availability checking 'if', we can just continue.
890     return Base::TraverseIfStmt(If);
891   }
892 
893   AvailabilityStack.push_back(CondVersion);
894   bool ShouldContinue = TraverseStmt(If->getThen());
895   AvailabilityStack.pop_back();
896 
897   return ShouldContinue && TraverseStmt(If->getElse());
898 }
899 
900 } // end anonymous namespace
901 
902 void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
903   Stmt *Body = nullptr;
904 
905   if (auto *FD = D->getAsFunction()) {
906     // FIXME: We only examine the pattern decl for availability violations now,
907     // but we should also examine instantiated templates.
908     if (FD->isTemplateInstantiation())
909       return;
910 
911     Body = FD->getBody();
912   } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
913     Body = MD->getBody();
914   else if (auto *BD = dyn_cast<BlockDecl>(D))
915     Body = BD->getBody();
916 
917   assert(Body && "Need a body here!");
918 
919   DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
920 }
921 
922 void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
923                                       ArrayRef<SourceLocation> Locs,
924                                       const ObjCInterfaceDecl *UnknownObjCClass,
925                                       bool ObjCPropertyAccess,
926                                       bool AvoidPartialAvailabilityChecks,
927                                       ObjCInterfaceDecl *ClassReceiver) {
928   std::string Message;
929   AvailabilityResult Result;
930   const NamedDecl* OffendingDecl;
931   // See if this declaration is unavailable, deprecated, or partial.
932   std::tie(Result, OffendingDecl) =
933       ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
934   if (Result == AR_Available)
935     return;
936 
937   if (Result == AR_NotYetIntroduced) {
938     if (AvoidPartialAvailabilityChecks)
939       return;
940 
941     // We need to know the @available context in the current function to
942     // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
943     // when we're done parsing the current function.
944     if (getCurFunctionOrMethodDecl()) {
945       getEnclosingFunction()->HasPotentialAvailabilityViolations = true;
946       return;
947     } else if (getCurBlock() || getCurLambda()) {
948       getCurFunction()->HasPotentialAvailabilityViolations = true;
949       return;
950     }
951   }
952 
953   const ObjCPropertyDecl *ObjCPDecl = nullptr;
954   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
955     if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
956       AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
957       if (PDeclResult == Result)
958         ObjCPDecl = PD;
959     }
960   }
961 
962   EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
963                           UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
964 }
965