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