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