xref: /llvm-project/clang/lib/Sema/SemaAvailability.cpp (revision dde802b153d5cb41505bf4d377be753576991297)
1dd8e0a0aSReid Kleckner //===--- SemaAvailability.cpp - Availability attribute handling -----------===//
2dd8e0a0aSReid Kleckner //
3dd8e0a0aSReid Kleckner // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4dd8e0a0aSReid Kleckner // See https://llvm.org/LICENSE.txt for license information.
5dd8e0a0aSReid Kleckner // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dd8e0a0aSReid Kleckner //
7dd8e0a0aSReid Kleckner //===----------------------------------------------------------------------===//
8dd8e0a0aSReid Kleckner //
9dd8e0a0aSReid Kleckner //  This file processes the availability attribute.
10dd8e0a0aSReid Kleckner //
11dd8e0a0aSReid Kleckner //===----------------------------------------------------------------------===//
12dd8e0a0aSReid Kleckner 
13dd8e0a0aSReid Kleckner #include "clang/AST/Attr.h"
14dd8e0a0aSReid Kleckner #include "clang/AST/Decl.h"
15670fa2bdSGábor Horváth #include "clang/AST/DeclTemplate.h"
16*dde802b1SSirraide #include "clang/AST/DynamicRecursiveASTVisitor.h"
17*dde802b1SSirraide #include "clang/AST/ExprObjC.h"
18*dde802b1SSirraide #include "clang/AST/StmtObjC.h"
19dd8e0a0aSReid Kleckner #include "clang/Basic/DiagnosticSema.h"
203f33c4c1SHelena Kotas #include "clang/Basic/IdentifierTable.h"
218890209eSHelena Kotas #include "clang/Basic/LangOptions.h"
22d7c5037eSReid Kleckner #include "clang/Basic/TargetInfo.h"
23dd8e0a0aSReid Kleckner #include "clang/Lex/Preprocessor.h"
24dd8e0a0aSReid Kleckner #include "clang/Sema/DelayedDiagnostic.h"
25dd8e0a0aSReid Kleckner #include "clang/Sema/ScopeInfo.h"
26dd8e0a0aSReid Kleckner #include "clang/Sema/Sema.h"
2731a203faSVlad Serebrennikov #include "clang/Sema/SemaObjC.h"
283f33c4c1SHelena Kotas #include "llvm/ADT/StringRef.h"
29a1580d7bSKazu Hirata #include <optional>
30dd8e0a0aSReid Kleckner 
31dd8e0a0aSReid Kleckner using namespace clang;
32dd8e0a0aSReid Kleckner using namespace sema;
33dd8e0a0aSReid Kleckner 
343f33c4c1SHelena Kotas static bool hasMatchingEnvironmentOrNone(const ASTContext &Context,
353f33c4c1SHelena Kotas                                          const AvailabilityAttr *AA) {
363f33c4c1SHelena Kotas   IdentifierInfo *IIEnvironment = AA->getEnvironment();
373f33c4c1SHelena Kotas   auto Environment = Context.getTargetInfo().getTriple().getEnvironment();
383f33c4c1SHelena Kotas   if (!IIEnvironment || Environment == llvm::Triple::UnknownEnvironment)
393f33c4c1SHelena Kotas     return true;
403f33c4c1SHelena Kotas 
413f33c4c1SHelena Kotas   llvm::Triple::EnvironmentType ET =
423f33c4c1SHelena Kotas       AvailabilityAttr::getEnvironmentType(IIEnvironment->getName());
433f33c4c1SHelena Kotas   return Environment == ET;
443f33c4c1SHelena Kotas }
453f33c4c1SHelena Kotas 
46dd8e0a0aSReid Kleckner static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
47dd8e0a0aSReid Kleckner                                                   const Decl *D) {
483f33c4c1SHelena Kotas   AvailabilityAttr const *PartialMatch = nullptr;
49dd8e0a0aSReid Kleckner   // Check each AvailabilityAttr to find the one for this platform.
503f33c4c1SHelena Kotas   // For multiple attributes with the same platform try to find one for this
513f33c4c1SHelena Kotas   // environment.
52670fa2bdSGábor Horváth   // The attribute is always on the FunctionDecl, not on the
53670fa2bdSGábor Horváth   // FunctionTemplateDecl.
54670fa2bdSGábor Horváth   if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
55670fa2bdSGábor Horváth     D = FTD->getTemplatedDecl();
56dd8e0a0aSReid Kleckner   for (const auto *A : D->attrs()) {
57dd8e0a0aSReid Kleckner     if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
58dd8e0a0aSReid Kleckner       // FIXME: this is copied from CheckAvailability. We should try to
59dd8e0a0aSReid Kleckner       // de-duplicate.
60dd8e0a0aSReid Kleckner 
61dd8e0a0aSReid Kleckner       // Check if this is an App Extension "platform", and if so chop off
62dd8e0a0aSReid Kleckner       // the suffix for matching with the actual platform.
63dd8e0a0aSReid Kleckner       StringRef ActualPlatform = Avail->getPlatform()->getName();
64dd8e0a0aSReid Kleckner       StringRef RealizedPlatform = ActualPlatform;
65dd8e0a0aSReid Kleckner       if (Context.getLangOpts().AppExt) {
66dd8e0a0aSReid Kleckner         size_t suffix = RealizedPlatform.rfind("_app_extension");
67dd8e0a0aSReid Kleckner         if (suffix != StringRef::npos)
68dd8e0a0aSReid Kleckner           RealizedPlatform = RealizedPlatform.slice(0, suffix);
69dd8e0a0aSReid Kleckner       }
70dd8e0a0aSReid Kleckner 
71dd8e0a0aSReid Kleckner       StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
72dd8e0a0aSReid Kleckner 
73dd8e0a0aSReid Kleckner       // Match the platform name.
743f33c4c1SHelena Kotas       if (RealizedPlatform == TargetPlatform) {
753f33c4c1SHelena Kotas         // Find the best matching attribute for this environment
763f33c4c1SHelena Kotas         if (hasMatchingEnvironmentOrNone(Context, Avail))
77dd8e0a0aSReid Kleckner           return Avail;
783f33c4c1SHelena Kotas         PartialMatch = Avail;
79dd8e0a0aSReid Kleckner       }
80dd8e0a0aSReid Kleckner     }
813f33c4c1SHelena Kotas   }
823f33c4c1SHelena Kotas   return PartialMatch;
83dd8e0a0aSReid Kleckner }
84dd8e0a0aSReid Kleckner 
85dd8e0a0aSReid Kleckner /// The diagnostic we should emit for \c D, and the declaration that
86dd8e0a0aSReid Kleckner /// originated it, or \c AR_Available.
87dd8e0a0aSReid Kleckner ///
88dd8e0a0aSReid Kleckner /// \param D The declaration to check.
89dd8e0a0aSReid Kleckner /// \param Message If non-null, this will be populated with the message from
90dd8e0a0aSReid Kleckner /// the availability attribute that is selected.
918f77dc45SKazuaki Ishizaki /// \param ClassReceiver If we're checking the method of a class message
92dd8e0a0aSReid Kleckner /// send, the class. Otherwise nullptr.
93dd8e0a0aSReid Kleckner static std::pair<AvailabilityResult, const NamedDecl *>
94dd8e0a0aSReid Kleckner ShouldDiagnoseAvailabilityOfDecl(Sema &S, const NamedDecl *D,
95dd8e0a0aSReid Kleckner                                  std::string *Message,
96dd8e0a0aSReid Kleckner                                  ObjCInterfaceDecl *ClassReceiver) {
97dd8e0a0aSReid Kleckner   AvailabilityResult Result = D->getAvailability(Message);
98dd8e0a0aSReid Kleckner 
99dd8e0a0aSReid Kleckner   // For typedefs, if the typedef declaration appears available look
100dd8e0a0aSReid Kleckner   // to the underlying type to see if it is more restrictive.
101dd8e0a0aSReid Kleckner   while (const auto *TD = dyn_cast<TypedefNameDecl>(D)) {
102dd8e0a0aSReid Kleckner     if (Result == AR_Available) {
103dd8e0a0aSReid Kleckner       if (const auto *TT = TD->getUnderlyingType()->getAs<TagType>()) {
104dd8e0a0aSReid Kleckner         D = TT->getDecl();
105dd8e0a0aSReid Kleckner         Result = D->getAvailability(Message);
106dd8e0a0aSReid Kleckner         continue;
107dd8e0a0aSReid Kleckner       }
108dd8e0a0aSReid Kleckner     }
109dd8e0a0aSReid Kleckner     break;
110dd8e0a0aSReid Kleckner   }
111dd8e0a0aSReid Kleckner 
112d8e0b0d6Spremanandrao   // For alias templates, get the underlying declaration.
113d8e0b0d6Spremanandrao   if (const auto *ADecl = dyn_cast<TypeAliasTemplateDecl>(D)) {
114d8e0b0d6Spremanandrao     D = ADecl->getTemplatedDecl();
115d8e0b0d6Spremanandrao     Result = D->getAvailability(Message);
116d8e0b0d6Spremanandrao   }
117d8e0b0d6Spremanandrao 
118dd8e0a0aSReid Kleckner   // Forward class declarations get their attributes from their definition.
119dd8e0a0aSReid Kleckner   if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) {
120dd8e0a0aSReid Kleckner     if (IDecl->getDefinition()) {
121dd8e0a0aSReid Kleckner       D = IDecl->getDefinition();
122dd8e0a0aSReid Kleckner       Result = D->getAvailability(Message);
123dd8e0a0aSReid Kleckner     }
124dd8e0a0aSReid Kleckner   }
125dd8e0a0aSReid Kleckner 
126dd8e0a0aSReid Kleckner   if (const auto *ECD = dyn_cast<EnumConstantDecl>(D))
127dd8e0a0aSReid Kleckner     if (Result == AR_Available) {
128dd8e0a0aSReid Kleckner       const DeclContext *DC = ECD->getDeclContext();
129dd8e0a0aSReid Kleckner       if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) {
130dd8e0a0aSReid Kleckner         Result = TheEnumDecl->getAvailability(Message);
131dd8e0a0aSReid Kleckner         D = TheEnumDecl;
132dd8e0a0aSReid Kleckner       }
133dd8e0a0aSReid Kleckner     }
134dd8e0a0aSReid Kleckner 
135dd8e0a0aSReid Kleckner   // For +new, infer availability from -init.
136dd8e0a0aSReid Kleckner   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
13731a203faSVlad Serebrennikov     if (S.ObjC().NSAPIObj && ClassReceiver) {
138dd8e0a0aSReid Kleckner       ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
13931a203faSVlad Serebrennikov           S.ObjC().NSAPIObj->getInitSelector());
140dd8e0a0aSReid Kleckner       if (Init && Result == AR_Available && MD->isClassMethod() &&
14131a203faSVlad Serebrennikov           MD->getSelector() == S.ObjC().NSAPIObj->getNewSelector() &&
142dd8e0a0aSReid Kleckner           MD->definedInNSObject(S.getASTContext())) {
143dd8e0a0aSReid Kleckner         Result = Init->getAvailability(Message);
144dd8e0a0aSReid Kleckner         D = Init;
145dd8e0a0aSReid Kleckner       }
146dd8e0a0aSReid Kleckner     }
147dd8e0a0aSReid Kleckner   }
148dd8e0a0aSReid Kleckner 
149dd8e0a0aSReid Kleckner   return {Result, D};
150dd8e0a0aSReid Kleckner }
151dd8e0a0aSReid Kleckner 
152dd8e0a0aSReid Kleckner 
153dd8e0a0aSReid Kleckner /// whether we should emit a diagnostic for \c K and \c DeclVersion in
154dd8e0a0aSReid Kleckner /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
155dd8e0a0aSReid Kleckner /// in a deprecated context, but not the other way around.
1563f33c4c1SHelena Kotas static bool ShouldDiagnoseAvailabilityInContext(
1573f33c4c1SHelena Kotas     Sema &S, AvailabilityResult K, VersionTuple DeclVersion,
1583f33c4c1SHelena Kotas     const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) {
159dd8e0a0aSReid Kleckner   assert(K != AR_Available && "Expected an unavailable declaration here!");
160dd8e0a0aSReid Kleckner 
161bb58748eSzoecarver   // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
162bb58748eSzoecarver   auto DeclLoc = Ctx->getBeginLoc();
163bb58748eSzoecarver   // This is only a problem in Foundation's C++ implementation for CF_OPTIONS.
164bb58748eSzoecarver   if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus &&
165bb58748eSzoecarver       isa<TypedefDecl>(OffendingDecl)) {
166bb58748eSzoecarver     StringRef MacroName = S.getPreprocessor().getImmediateMacroName(DeclLoc);
167bb58748eSzoecarver     if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" ||
168bb58748eSzoecarver         MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") {
169bb58748eSzoecarver       return false;
170bb58748eSzoecarver     }
171bb58748eSzoecarver   }
172bb58748eSzoecarver 
17330efdce7SHelena Kotas   // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to
17430efdce7SHelena Kotas   // strict (-fhlsl-strict-availability), or if the target is library and the
17530efdce7SHelena Kotas   // availability is restricted to a specific environment/shader stage.
17630efdce7SHelena Kotas   // For libraries the availability will be checked later in
17730efdce7SHelena Kotas   // DiagnoseHLSLAvailability class once where the specific environment/shader
17830efdce7SHelena Kotas   // stage of the caller is known.
17930efdce7SHelena Kotas   if (S.getLangOpts().HLSL) {
18030efdce7SHelena Kotas     if (!S.getLangOpts().HLSLStrictAvailability ||
18130efdce7SHelena Kotas         (DeclEnv != nullptr &&
18230efdce7SHelena Kotas          S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
18330efdce7SHelena Kotas              llvm::Triple::EnvironmentType::Library))
18430efdce7SHelena Kotas       return false;
18530efdce7SHelena Kotas   }
18630efdce7SHelena Kotas 
187b89f09f5SOleksandr T.   if (K == AR_Deprecated) {
188b89f09f5SOleksandr T.     if (const auto *VD = dyn_cast<VarDecl>(OffendingDecl))
189b89f09f5SOleksandr T.       if (VD->isLocalVarDeclOrParm() && VD->isDeprecated())
190b89f09f5SOleksandr T.         return true;
191b89f09f5SOleksandr T.   }
192b89f09f5SOleksandr T. 
193dd8e0a0aSReid Kleckner   // Checks if we should emit the availability diagnostic in the context of C.
194dd8e0a0aSReid Kleckner   auto CheckContext = [&](const Decl *C) {
195dd8e0a0aSReid Kleckner     if (K == AR_NotYetIntroduced) {
196dd8e0a0aSReid Kleckner       if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, C))
1973f33c4c1SHelena Kotas         if (AA->getIntroduced() >= DeclVersion &&
1983f33c4c1SHelena Kotas             AA->getEnvironment() == DeclEnv)
199dd8e0a0aSReid Kleckner           return true;
200dd8e0a0aSReid Kleckner     } else if (K == AR_Deprecated) {
201dd8e0a0aSReid Kleckner       if (C->isDeprecated())
202dd8e0a0aSReid Kleckner         return true;
203dd8e0a0aSReid Kleckner     } else if (K == AR_Unavailable) {
204dd8e0a0aSReid Kleckner       // It is perfectly fine to refer to an 'unavailable' Objective-C method
205dd8e0a0aSReid Kleckner       // when it is referenced from within the @implementation itself. In this
206dd8e0a0aSReid Kleckner       // context, we interpret unavailable as a form of access control.
207dd8e0a0aSReid Kleckner       if (const auto *MD = dyn_cast<ObjCMethodDecl>(OffendingDecl)) {
208dd8e0a0aSReid Kleckner         if (const auto *Impl = dyn_cast<ObjCImplDecl>(C)) {
209dd8e0a0aSReid Kleckner           if (MD->getClassInterface() == Impl->getClassInterface())
210dd8e0a0aSReid Kleckner             return true;
211dd8e0a0aSReid Kleckner         }
212dd8e0a0aSReid Kleckner       }
213dd8e0a0aSReid Kleckner     }
214dd8e0a0aSReid Kleckner 
215dd8e0a0aSReid Kleckner     if (C->isUnavailable())
216dd8e0a0aSReid Kleckner       return true;
217dd8e0a0aSReid Kleckner     return false;
218dd8e0a0aSReid Kleckner   };
219dd8e0a0aSReid Kleckner 
220dd8e0a0aSReid Kleckner   do {
221dd8e0a0aSReid Kleckner     if (CheckContext(Ctx))
222dd8e0a0aSReid Kleckner       return false;
223dd8e0a0aSReid Kleckner 
224dd8e0a0aSReid Kleckner     // An implementation implicitly has the availability of the interface.
225dd8e0a0aSReid Kleckner     // Unless it is "+load" method.
226dd8e0a0aSReid Kleckner     if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Ctx))
227dd8e0a0aSReid Kleckner       if (MethodD->isClassMethod() &&
228dd8e0a0aSReid Kleckner           MethodD->getSelector().getAsString() == "load")
229dd8e0a0aSReid Kleckner         return true;
230dd8e0a0aSReid Kleckner 
231dd8e0a0aSReid Kleckner     if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Ctx)) {
232dd8e0a0aSReid Kleckner       if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())
233dd8e0a0aSReid Kleckner         if (CheckContext(Interface))
234dd8e0a0aSReid Kleckner           return false;
235dd8e0a0aSReid Kleckner     }
236dd8e0a0aSReid Kleckner     // A category implicitly has the availability of the interface.
237dd8e0a0aSReid Kleckner     else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Ctx))
238dd8e0a0aSReid Kleckner       if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
239dd8e0a0aSReid Kleckner         if (CheckContext(Interface))
240dd8e0a0aSReid Kleckner           return false;
241dd8e0a0aSReid Kleckner   } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));
242dd8e0a0aSReid Kleckner 
243dd8e0a0aSReid Kleckner   return true;
244dd8e0a0aSReid Kleckner }
245dd8e0a0aSReid Kleckner 
24630efdce7SHelena Kotas static unsigned getAvailabilityDiagnosticKind(
24730efdce7SHelena Kotas     const ASTContext &Context, const VersionTuple &DeploymentVersion,
24830efdce7SHelena Kotas     const VersionTuple &DeclVersion, bool HasMatchingEnv) {
249dd8e0a0aSReid Kleckner   const auto &Triple = Context.getTargetInfo().getTriple();
250dd8e0a0aSReid Kleckner   VersionTuple ForceAvailabilityFromVersion;
251dd8e0a0aSReid Kleckner   switch (Triple.getOS()) {
25230efdce7SHelena Kotas   // For iOS, emit the diagnostic even if -Wunguarded-availability is
25330efdce7SHelena Kotas   // not specified for deployment targets >= to iOS 11 or equivalent or
25430efdce7SHelena Kotas   // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
25530efdce7SHelena Kotas   // later.
256dd8e0a0aSReid Kleckner   case llvm::Triple::IOS:
257dd8e0a0aSReid Kleckner   case llvm::Triple::TvOS:
258dd8e0a0aSReid Kleckner     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
259dd8e0a0aSReid Kleckner     break;
260dd8e0a0aSReid Kleckner   case llvm::Triple::WatchOS:
261dd8e0a0aSReid Kleckner     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4);
262dd8e0a0aSReid Kleckner     break;
263dd8e0a0aSReid Kleckner   case llvm::Triple::Darwin:
264dd8e0a0aSReid Kleckner   case llvm::Triple::MacOSX:
265dd8e0a0aSReid Kleckner     ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
266dd8e0a0aSReid Kleckner     break;
26730efdce7SHelena Kotas   // For HLSL, use diagnostic from HLSLAvailability group which
26830efdce7SHelena Kotas   // are reported as errors by default and in strict diagnostic mode
26930efdce7SHelena Kotas   // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
27030efdce7SHelena Kotas   // mode (-Wno-error=hlsl-availability)
27110378c45SChris Bieneman   case llvm::Triple::ShaderModel:
27230efdce7SHelena Kotas     return HasMatchingEnv ? diag::warn_hlsl_availability
27330efdce7SHelena Kotas                           : diag::warn_hlsl_availability_unavailable;
274dd8e0a0aSReid Kleckner   default:
27530efdce7SHelena Kotas     // New Apple targets should always warn about availability.
27630efdce7SHelena Kotas     ForceAvailabilityFromVersion =
27730efdce7SHelena Kotas         (Triple.getVendor() == llvm::Triple::Apple)
27830efdce7SHelena Kotas             ? VersionTuple(/*Major=*/0, 0)
27930efdce7SHelena Kotas             : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
280dd8e0a0aSReid Kleckner   }
28130efdce7SHelena Kotas   if (DeploymentVersion >= ForceAvailabilityFromVersion ||
28230efdce7SHelena Kotas       DeclVersion >= ForceAvailabilityFromVersion)
28330efdce7SHelena Kotas     return HasMatchingEnv ? diag::warn_unguarded_availability_new
28430efdce7SHelena Kotas                           : diag::warn_unguarded_availability_unavailable_new;
28530efdce7SHelena Kotas   return HasMatchingEnv ? diag::warn_unguarded_availability
28630efdce7SHelena Kotas                         : diag::warn_unguarded_availability_unavailable;
287dd8e0a0aSReid Kleckner }
288dd8e0a0aSReid Kleckner 
289dd8e0a0aSReid Kleckner static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
290dd8e0a0aSReid Kleckner   for (Decl *Ctx = OrigCtx; Ctx;
291dd8e0a0aSReid Kleckner        Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) {
292dd8e0a0aSReid Kleckner     if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx))
293dd8e0a0aSReid Kleckner       return cast<NamedDecl>(Ctx);
294dd8e0a0aSReid Kleckner     if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) {
295dd8e0a0aSReid Kleckner       if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx))
296dd8e0a0aSReid Kleckner         return Imp->getClassInterface();
297dd8e0a0aSReid Kleckner       return CD;
298dd8e0a0aSReid Kleckner     }
299dd8e0a0aSReid Kleckner   }
300dd8e0a0aSReid Kleckner 
301dd8e0a0aSReid Kleckner   return dyn_cast<NamedDecl>(OrigCtx);
302dd8e0a0aSReid Kleckner }
303dd8e0a0aSReid Kleckner 
304dd8e0a0aSReid Kleckner namespace {
305dd8e0a0aSReid Kleckner 
306dd8e0a0aSReid Kleckner struct AttributeInsertion {
307dd8e0a0aSReid Kleckner   StringRef Prefix;
308dd8e0a0aSReid Kleckner   SourceLocation Loc;
309dd8e0a0aSReid Kleckner   StringRef Suffix;
310dd8e0a0aSReid Kleckner 
311dd8e0a0aSReid Kleckner   static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
312dd8e0a0aSReid Kleckner     return {" ", D->getEndLoc(), ""};
313dd8e0a0aSReid Kleckner   }
314dd8e0a0aSReid Kleckner   static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
315dd8e0a0aSReid Kleckner     return {" ", Loc, ""};
316dd8e0a0aSReid Kleckner   }
317dd8e0a0aSReid Kleckner   static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
318dd8e0a0aSReid Kleckner     return {"", D->getBeginLoc(), "\n"};
319dd8e0a0aSReid Kleckner   }
320dd8e0a0aSReid Kleckner };
321dd8e0a0aSReid Kleckner 
322dd8e0a0aSReid Kleckner } // end anonymous namespace
323dd8e0a0aSReid Kleckner 
324dd8e0a0aSReid Kleckner /// Tries to parse a string as ObjC method name.
325dd8e0a0aSReid Kleckner ///
326dd8e0a0aSReid Kleckner /// \param Name The string to parse. Expected to originate from availability
327dd8e0a0aSReid Kleckner /// attribute argument.
328dd8e0a0aSReid Kleckner /// \param SlotNames The vector that will be populated with slot names. In case
329dd8e0a0aSReid Kleckner /// of unsuccessful parsing can contain invalid data.
33037a3e98cSKazu Hirata /// \returns A number of method parameters if parsing was successful,
33137a3e98cSKazu Hirata /// std::nullopt otherwise.
3326ad0788cSKazu Hirata static std::optional<unsigned>
333dd8e0a0aSReid Kleckner tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames,
334dd8e0a0aSReid Kleckner                        const LangOptions &LangOpts) {
335dd8e0a0aSReid Kleckner   // Accept replacements starting with - or + as valid ObjC method names.
336dd8e0a0aSReid Kleckner   if (!Name.empty() && (Name.front() == '-' || Name.front() == '+'))
337dd8e0a0aSReid Kleckner     Name = Name.drop_front(1);
338dd8e0a0aSReid Kleckner   if (Name.empty())
3398595f2e5SKazu Hirata     return std::nullopt;
340dd8e0a0aSReid Kleckner   Name.split(SlotNames, ':');
341dd8e0a0aSReid Kleckner   unsigned NumParams;
342dd8e0a0aSReid Kleckner   if (Name.back() == ':') {
343dd8e0a0aSReid Kleckner     // Remove an empty string at the end that doesn't represent any slot.
344dd8e0a0aSReid Kleckner     SlotNames.pop_back();
345dd8e0a0aSReid Kleckner     NumParams = SlotNames.size();
346dd8e0a0aSReid Kleckner   } else {
347dd8e0a0aSReid Kleckner     if (SlotNames.size() != 1)
348dd8e0a0aSReid Kleckner       // Not a valid method name, just a colon-separated string.
3498595f2e5SKazu Hirata       return std::nullopt;
350dd8e0a0aSReid Kleckner     NumParams = 0;
351dd8e0a0aSReid Kleckner   }
352dd8e0a0aSReid Kleckner   // Verify all slot names are valid.
353dd8e0a0aSReid Kleckner   bool AllowDollar = LangOpts.DollarIdents;
354dd8e0a0aSReid Kleckner   for (StringRef S : SlotNames) {
355dd8e0a0aSReid Kleckner     if (S.empty())
356dd8e0a0aSReid Kleckner       continue;
357601102d2SCorentin Jabot     if (!isValidAsciiIdentifier(S, AllowDollar))
3588595f2e5SKazu Hirata       return std::nullopt;
359dd8e0a0aSReid Kleckner   }
360dd8e0a0aSReid Kleckner   return NumParams;
361dd8e0a0aSReid Kleckner }
362dd8e0a0aSReid Kleckner 
363dd8e0a0aSReid Kleckner /// Returns a source location in which it's appropriate to insert a new
364dd8e0a0aSReid Kleckner /// attribute for the given declaration \D.
3656ad0788cSKazu Hirata static std::optional<AttributeInsertion>
366dd8e0a0aSReid Kleckner createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
367dd8e0a0aSReid Kleckner                          const LangOptions &LangOpts) {
368dd8e0a0aSReid Kleckner   if (isa<ObjCPropertyDecl>(D))
369dd8e0a0aSReid Kleckner     return AttributeInsertion::createInsertionAfter(D);
370dd8e0a0aSReid Kleckner   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
371dd8e0a0aSReid Kleckner     if (MD->hasBody())
3728595f2e5SKazu Hirata       return std::nullopt;
373dd8e0a0aSReid Kleckner     return AttributeInsertion::createInsertionAfter(D);
374dd8e0a0aSReid Kleckner   }
375dd8e0a0aSReid Kleckner   if (const auto *TD = dyn_cast<TagDecl>(D)) {
376dd8e0a0aSReid Kleckner     SourceLocation Loc =
377dd8e0a0aSReid Kleckner         Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts);
378dd8e0a0aSReid Kleckner     if (Loc.isInvalid())
3798595f2e5SKazu Hirata       return std::nullopt;
380dd8e0a0aSReid Kleckner     // Insert after the 'struct'/whatever keyword.
381dd8e0a0aSReid Kleckner     return AttributeInsertion::createInsertionAfter(Loc);
382dd8e0a0aSReid Kleckner   }
383dd8e0a0aSReid Kleckner   return AttributeInsertion::createInsertionBefore(D);
384dd8e0a0aSReid Kleckner }
385dd8e0a0aSReid Kleckner 
386dd8e0a0aSReid Kleckner /// Actually emit an availability diagnostic for a reference to an unavailable
387dd8e0a0aSReid Kleckner /// decl.
388dd8e0a0aSReid Kleckner ///
389dd8e0a0aSReid Kleckner /// \param Ctx The context that the reference occurred in
390dd8e0a0aSReid Kleckner /// \param ReferringDecl The exact declaration that was referenced.
391dd8e0a0aSReid Kleckner /// \param OffendingDecl A related decl to \c ReferringDecl that has an
392dd8e0a0aSReid Kleckner /// availability attribute corresponding to \c K attached to it. Note that this
393dd8e0a0aSReid Kleckner /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
394dd8e0a0aSReid Kleckner /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
395dd8e0a0aSReid Kleckner /// and OffendingDecl is the EnumDecl.
396dd8e0a0aSReid Kleckner static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
397dd8e0a0aSReid Kleckner                                       Decl *Ctx, const NamedDecl *ReferringDecl,
398dd8e0a0aSReid Kleckner                                       const NamedDecl *OffendingDecl,
399dd8e0a0aSReid Kleckner                                       StringRef Message,
400dd8e0a0aSReid Kleckner                                       ArrayRef<SourceLocation> Locs,
401dd8e0a0aSReid Kleckner                                       const ObjCInterfaceDecl *UnknownObjCClass,
402dd8e0a0aSReid Kleckner                                       const ObjCPropertyDecl *ObjCProperty,
403dd8e0a0aSReid Kleckner                                       bool ObjCPropertyAccess) {
404dd8e0a0aSReid Kleckner   // Diagnostics for deprecated or unavailable.
405dd8e0a0aSReid Kleckner   unsigned diag, diag_message, diag_fwdclass_message;
406dd8e0a0aSReid Kleckner   unsigned diag_available_here = diag::note_availability_specified_here;
407dd8e0a0aSReid Kleckner   SourceLocation NoteLocation = OffendingDecl->getLocation();
408dd8e0a0aSReid Kleckner 
409dd8e0a0aSReid Kleckner   // Matches 'diag::note_property_attribute' options.
410dd8e0a0aSReid Kleckner   unsigned property_note_select;
411dd8e0a0aSReid Kleckner 
412dd8e0a0aSReid Kleckner   // Matches diag::note_availability_specified_here.
413dd8e0a0aSReid Kleckner   unsigned available_here_select_kind;
414dd8e0a0aSReid Kleckner 
415dd8e0a0aSReid Kleckner   VersionTuple DeclVersion;
4163f33c4c1SHelena Kotas   const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl);
4173f33c4c1SHelena Kotas   const IdentifierInfo *IIEnv = nullptr;
4183f33c4c1SHelena Kotas   if (AA) {
419dd8e0a0aSReid Kleckner     DeclVersion = AA->getIntroduced();
4203f33c4c1SHelena Kotas     IIEnv = AA->getEnvironment();
4213f33c4c1SHelena Kotas   }
422dd8e0a0aSReid Kleckner 
4233f33c4c1SHelena Kotas   if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx,
424dd8e0a0aSReid Kleckner                                            OffendingDecl))
425dd8e0a0aSReid Kleckner     return;
426dd8e0a0aSReid Kleckner 
427dd8e0a0aSReid Kleckner   SourceLocation Loc = Locs.front();
428dd8e0a0aSReid Kleckner 
429dd8e0a0aSReid Kleckner   // The declaration can have multiple availability attributes, we are looking
430dd8e0a0aSReid Kleckner   // at one of them.
4313f33c4c1SHelena Kotas   if (AA && AA->isInherited()) {
432dd8e0a0aSReid Kleckner     for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
433dd8e0a0aSReid Kleckner          Redecl = Redecl->getPreviousDecl()) {
434dd8e0a0aSReid Kleckner       const AvailabilityAttr *AForRedecl =
435dd8e0a0aSReid Kleckner           getAttrForPlatform(S.Context, Redecl);
436dd8e0a0aSReid Kleckner       if (AForRedecl && !AForRedecl->isInherited()) {
437dd8e0a0aSReid Kleckner         // If D is a declaration with inherited attributes, the note should
438dd8e0a0aSReid Kleckner         // point to the declaration with actual attributes.
439dd8e0a0aSReid Kleckner         NoteLocation = Redecl->getLocation();
440dd8e0a0aSReid Kleckner         break;
441dd8e0a0aSReid Kleckner       }
442dd8e0a0aSReid Kleckner     }
443dd8e0a0aSReid Kleckner   }
444dd8e0a0aSReid Kleckner 
445dd8e0a0aSReid Kleckner   switch (K) {
446dd8e0a0aSReid Kleckner   case AR_NotYetIntroduced: {
447dd8e0a0aSReid Kleckner     // We would like to emit the diagnostic even if -Wunguarded-availability is
448dd8e0a0aSReid Kleckner     // not specified for deployment targets >= to iOS 11 or equivalent or
449dd8e0a0aSReid Kleckner     // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
450dd8e0a0aSReid Kleckner     // later.
4513f33c4c1SHelena Kotas     assert(AA != nullptr && "expecting valid availability attribute");
452dd8e0a0aSReid Kleckner     VersionTuple Introduced = AA->getIntroduced();
4533f33c4c1SHelena Kotas     bool EnvironmentMatchesOrNone =
4543f33c4c1SHelena Kotas         hasMatchingEnvironmentOrNone(S.getASTContext(), AA);
4553f33c4c1SHelena Kotas 
4563f33c4c1SHelena Kotas     const TargetInfo &TI = S.getASTContext().getTargetInfo();
4573f33c4c1SHelena Kotas     std::string PlatformName(
4583f33c4c1SHelena Kotas         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
45930efdce7SHelena Kotas     llvm::StringRef TargetEnvironment(
46030efdce7SHelena Kotas         llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment()));
4613f33c4c1SHelena Kotas     llvm::StringRef AttrEnvironment =
46230efdce7SHelena Kotas         AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
4633f33c4c1SHelena Kotas     bool UseEnvironment =
4643f33c4c1SHelena Kotas         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
465dd8e0a0aSReid Kleckner 
46630efdce7SHelena Kotas     unsigned DiagKind = getAvailabilityDiagnosticKind(
467dd8e0a0aSReid Kleckner         S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
46830efdce7SHelena Kotas         Introduced, EnvironmentMatchesOrNone);
469dd8e0a0aSReid Kleckner 
4703f33c4c1SHelena Kotas     S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
4713f33c4c1SHelena Kotas                           << Introduced.getAsString() << UseEnvironment
4723f33c4c1SHelena Kotas                           << TargetEnvironment;
473dd8e0a0aSReid Kleckner 
474dd8e0a0aSReid Kleckner     S.Diag(OffendingDecl->getLocation(),
475dd8e0a0aSReid Kleckner            diag::note_partial_availability_specified_here)
476dd8e0a0aSReid Kleckner         << OffendingDecl << PlatformName << Introduced.getAsString()
4773f33c4c1SHelena Kotas         << S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
4783f33c4c1SHelena Kotas         << UseEnvironment << AttrEnvironment << TargetEnvironment;
479dd8e0a0aSReid Kleckner 
4808890209eSHelena Kotas     // Do not offer to silence the warning or fixits for HLSL
4818890209eSHelena Kotas     if (S.getLangOpts().HLSL)
4828890209eSHelena Kotas       return;
4838890209eSHelena Kotas 
484dd8e0a0aSReid Kleckner     if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) {
485dd8e0a0aSReid Kleckner       if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
486dd8e0a0aSReid Kleckner         if (TD->getDeclName().isEmpty()) {
487dd8e0a0aSReid Kleckner           S.Diag(TD->getLocation(),
488dd8e0a0aSReid Kleckner                  diag::note_decl_unguarded_availability_silence)
489dd8e0a0aSReid Kleckner               << /*Anonymous*/ 1 << TD->getKindName();
490dd8e0a0aSReid Kleckner           return;
491dd8e0a0aSReid Kleckner         }
492dd8e0a0aSReid Kleckner       auto FixitNoteDiag =
493dd8e0a0aSReid Kleckner           S.Diag(Enclosing->getLocation(),
494dd8e0a0aSReid Kleckner                  diag::note_decl_unguarded_availability_silence)
495dd8e0a0aSReid Kleckner           << /*Named*/ 0 << Enclosing;
496dd8e0a0aSReid Kleckner       // Don't offer a fixit for declarations with availability attributes.
497dd8e0a0aSReid Kleckner       if (Enclosing->hasAttr<AvailabilityAttr>())
498dd8e0a0aSReid Kleckner         return;
499319e8cd2SIan Anderson       Preprocessor &PP = S.getPreprocessor();
500319e8cd2SIan Anderson       if (!PP.isMacroDefined("API_AVAILABLE"))
501dd8e0a0aSReid Kleckner         return;
5026ad0788cSKazu Hirata       std::optional<AttributeInsertion> Insertion = createAttributeInsertion(
503dd8e0a0aSReid Kleckner           Enclosing, S.getSourceManager(), S.getLangOpts());
504dd8e0a0aSReid Kleckner       if (!Insertion)
505dd8e0a0aSReid Kleckner         return;
506319e8cd2SIan Anderson       StringRef PlatformName =
507319e8cd2SIan Anderson           S.getASTContext().getTargetInfo().getPlatformName();
508319e8cd2SIan Anderson 
509319e8cd2SIan Anderson       // Apple's API_AVAILABLE macro expands roughly like this.
510319e8cd2SIan Anderson       // API_AVAILABLE(ios(17.0))
511319e8cd2SIan Anderson       // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0)))
512319e8cd2SIan Anderson       // __attribute__((availability(ios,introduced=17.0)))
513319e8cd2SIan Anderson       // In order to figure out which platform name to use in the API_AVAILABLE
514319e8cd2SIan Anderson       // macro, the associated __API_AVAILABLE_PLATFORM_ macro needs to be
515319e8cd2SIan Anderson       // found. The __API_AVAILABLE_PLATFORM_ macros aren't consistent about
516319e8cd2SIan Anderson       // using the canonical platform name, source spelling name, or one of the
517319e8cd2SIan Anderson       // other supported names (i.e. one of the keys in canonicalizePlatformName
518319e8cd2SIan Anderson       // that's neither). Check all of the supported names for a match.
519319e8cd2SIan Anderson       std::vector<StringRef> EquivalentPlatforms =
520319e8cd2SIan Anderson           AvailabilityAttr::equivalentPlatformNames(PlatformName);
521319e8cd2SIan Anderson       llvm::Twine MacroPrefix = "__API_AVAILABLE_PLATFORM_";
522319e8cd2SIan Anderson       auto AvailablePlatform =
523319e8cd2SIan Anderson           llvm::find_if(EquivalentPlatforms, [&](StringRef EquivalentPlatform) {
524319e8cd2SIan Anderson             return PP.isMacroDefined((MacroPrefix + EquivalentPlatform).str());
525319e8cd2SIan Anderson           });
526319e8cd2SIan Anderson       if (AvailablePlatform == EquivalentPlatforms.end())
527319e8cd2SIan Anderson         return;
528dd8e0a0aSReid Kleckner       std::string Introduced =
529dd8e0a0aSReid Kleckner           OffendingDecl->getVersionIntroduced().getAsString();
530dd8e0a0aSReid Kleckner       FixitNoteDiag << FixItHint::CreateInsertion(
531dd8e0a0aSReid Kleckner           Insertion->Loc,
532319e8cd2SIan Anderson           (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" +
533319e8cd2SIan Anderson            *AvailablePlatform + "(" + Introduced + "))" + Insertion->Suffix)
534dd8e0a0aSReid Kleckner               .str());
535dd8e0a0aSReid Kleckner     }
536dd8e0a0aSReid Kleckner     return;
537dd8e0a0aSReid Kleckner   }
538dd8e0a0aSReid Kleckner   case AR_Deprecated:
539dd8e0a0aSReid Kleckner     diag = !ObjCPropertyAccess ? diag::warn_deprecated
540dd8e0a0aSReid Kleckner                                : diag::warn_property_method_deprecated;
541dd8e0a0aSReid Kleckner     diag_message = diag::warn_deprecated_message;
542dd8e0a0aSReid Kleckner     diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;
543dd8e0a0aSReid Kleckner     property_note_select = /* deprecated */ 0;
544dd8e0a0aSReid Kleckner     available_here_select_kind = /* deprecated */ 2;
545dd8e0a0aSReid Kleckner     if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())
546dd8e0a0aSReid Kleckner       NoteLocation = AL->getLocation();
547dd8e0a0aSReid Kleckner     break;
548dd8e0a0aSReid Kleckner 
549dd8e0a0aSReid Kleckner   case AR_Unavailable:
550dd8e0a0aSReid Kleckner     diag = !ObjCPropertyAccess ? diag::err_unavailable
551dd8e0a0aSReid Kleckner                                : diag::err_property_method_unavailable;
552dd8e0a0aSReid Kleckner     diag_message = diag::err_unavailable_message;
553dd8e0a0aSReid Kleckner     diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;
554dd8e0a0aSReid Kleckner     property_note_select = /* unavailable */ 1;
555dd8e0a0aSReid Kleckner     available_here_select_kind = /* unavailable */ 0;
556dd8e0a0aSReid Kleckner 
557dd8e0a0aSReid Kleckner     if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {
558dd8e0a0aSReid Kleckner       if (AL->isImplicit() && AL->getImplicitReason()) {
559dd8e0a0aSReid Kleckner         // Most of these failures are due to extra restrictions in ARC;
560dd8e0a0aSReid Kleckner         // reflect that in the primary diagnostic when applicable.
561dd8e0a0aSReid Kleckner         auto flagARCError = [&] {
562dd8e0a0aSReid Kleckner           if (S.getLangOpts().ObjCAutoRefCount &&
563dd8e0a0aSReid Kleckner               S.getSourceManager().isInSystemHeader(
564dd8e0a0aSReid Kleckner                   OffendingDecl->getLocation()))
565dd8e0a0aSReid Kleckner             diag = diag::err_unavailable_in_arc;
566dd8e0a0aSReid Kleckner         };
567dd8e0a0aSReid Kleckner 
568dd8e0a0aSReid Kleckner         switch (AL->getImplicitReason()) {
569dd8e0a0aSReid Kleckner         case UnavailableAttr::IR_None: break;
570dd8e0a0aSReid Kleckner 
571dd8e0a0aSReid Kleckner         case UnavailableAttr::IR_ARCForbiddenType:
572dd8e0a0aSReid Kleckner           flagARCError();
573dd8e0a0aSReid Kleckner           diag_available_here = diag::note_arc_forbidden_type;
574dd8e0a0aSReid Kleckner           break;
575dd8e0a0aSReid Kleckner 
576dd8e0a0aSReid Kleckner         case UnavailableAttr::IR_ForbiddenWeak:
577dd8e0a0aSReid Kleckner           if (S.getLangOpts().ObjCWeakRuntime)
578dd8e0a0aSReid Kleckner             diag_available_here = diag::note_arc_weak_disabled;
579dd8e0a0aSReid Kleckner           else
580dd8e0a0aSReid Kleckner             diag_available_here = diag::note_arc_weak_no_runtime;
581dd8e0a0aSReid Kleckner           break;
582dd8e0a0aSReid Kleckner 
583dd8e0a0aSReid Kleckner         case UnavailableAttr::IR_ARCForbiddenConversion:
584dd8e0a0aSReid Kleckner           flagARCError();
585dd8e0a0aSReid Kleckner           diag_available_here = diag::note_performs_forbidden_arc_conversion;
586dd8e0a0aSReid Kleckner           break;
587dd8e0a0aSReid Kleckner 
588dd8e0a0aSReid Kleckner         case UnavailableAttr::IR_ARCInitReturnsUnrelated:
589dd8e0a0aSReid Kleckner           flagARCError();
590dd8e0a0aSReid Kleckner           diag_available_here = diag::note_arc_init_returns_unrelated;
591dd8e0a0aSReid Kleckner           break;
592dd8e0a0aSReid Kleckner 
593dd8e0a0aSReid Kleckner         case UnavailableAttr::IR_ARCFieldWithOwnership:
594dd8e0a0aSReid Kleckner           flagARCError();
595dd8e0a0aSReid Kleckner           diag_available_here = diag::note_arc_field_with_ownership;
596dd8e0a0aSReid Kleckner           break;
597dd8e0a0aSReid Kleckner         }
598dd8e0a0aSReid Kleckner       }
599dd8e0a0aSReid Kleckner     }
600dd8e0a0aSReid Kleckner     break;
601dd8e0a0aSReid Kleckner 
602dd8e0a0aSReid Kleckner   case AR_Available:
603dd8e0a0aSReid Kleckner     llvm_unreachable("Warning for availability of available declaration?");
604dd8e0a0aSReid Kleckner   }
605dd8e0a0aSReid Kleckner 
606dd8e0a0aSReid Kleckner   SmallVector<FixItHint, 12> FixIts;
607dd8e0a0aSReid Kleckner   if (K == AR_Deprecated) {
608dd8e0a0aSReid Kleckner     StringRef Replacement;
609dd8e0a0aSReid Kleckner     if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
610dd8e0a0aSReid Kleckner       Replacement = AL->getReplacement();
611dd8e0a0aSReid Kleckner     if (auto AL = getAttrForPlatform(S.Context, OffendingDecl))
612dd8e0a0aSReid Kleckner       Replacement = AL->getReplacement();
613dd8e0a0aSReid Kleckner 
614dd8e0a0aSReid Kleckner     CharSourceRange UseRange;
615dd8e0a0aSReid Kleckner     if (!Replacement.empty())
616dd8e0a0aSReid Kleckner       UseRange =
617dd8e0a0aSReid Kleckner           CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
618dd8e0a0aSReid Kleckner     if (UseRange.isValid()) {
619dd8e0a0aSReid Kleckner       if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {
620dd8e0a0aSReid Kleckner         Selector Sel = MethodDecl->getSelector();
621dd8e0a0aSReid Kleckner         SmallVector<StringRef, 12> SelectorSlotNames;
6226ad0788cSKazu Hirata         std::optional<unsigned> NumParams = tryParseObjCMethodName(
623dd8e0a0aSReid Kleckner             Replacement, SelectorSlotNames, S.getLangOpts());
624ca4af13eSKazu Hirata         if (NumParams && *NumParams == Sel.getNumArgs()) {
625dd8e0a0aSReid Kleckner           assert(SelectorSlotNames.size() == Locs.size());
626dd8e0a0aSReid Kleckner           for (unsigned I = 0; I < Locs.size(); ++I) {
627dd8e0a0aSReid Kleckner             if (!Sel.getNameForSlot(I).empty()) {
628dd8e0a0aSReid Kleckner               CharSourceRange NameRange = CharSourceRange::getCharRange(
629dd8e0a0aSReid Kleckner                   Locs[I], S.getLocForEndOfToken(Locs[I]));
630dd8e0a0aSReid Kleckner               FixIts.push_back(FixItHint::CreateReplacement(
631dd8e0a0aSReid Kleckner                   NameRange, SelectorSlotNames[I]));
632dd8e0a0aSReid Kleckner             } else
633dd8e0a0aSReid Kleckner               FixIts.push_back(
634dd8e0a0aSReid Kleckner                   FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I]));
635dd8e0a0aSReid Kleckner           }
636dd8e0a0aSReid Kleckner         } else
637dd8e0a0aSReid Kleckner           FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
638dd8e0a0aSReid Kleckner       } else
639dd8e0a0aSReid Kleckner         FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
640dd8e0a0aSReid Kleckner     }
641dd8e0a0aSReid Kleckner   }
642dd8e0a0aSReid Kleckner 
643aafad2d2Scor3ntin   // We emit deprecation warning for deprecated specializations
644aafad2d2Scor3ntin   // when their instantiation stacks originate outside
645aafad2d2Scor3ntin   // of a system header, even if the diagnostics is suppresed at the
646aafad2d2Scor3ntin   // point of definition.
647aafad2d2Scor3ntin   SourceLocation InstantiationLoc =
648aafad2d2Scor3ntin       S.getTopMostPointOfInstantiation(ReferringDecl);
649aafad2d2Scor3ntin   bool ShouldAllowWarningInSystemHeader =
650aafad2d2Scor3ntin       InstantiationLoc != Loc &&
651aafad2d2Scor3ntin       !S.getSourceManager().isInSystemHeader(InstantiationLoc);
652aafad2d2Scor3ntin   struct AllowWarningInSystemHeaders {
653aafad2d2Scor3ntin     AllowWarningInSystemHeaders(DiagnosticsEngine &E,
654aafad2d2Scor3ntin                                 bool AllowWarningInSystemHeaders)
655aafad2d2Scor3ntin         : Engine(E), Prev(E.getSuppressSystemWarnings()) {
656aafad2d2Scor3ntin       E.setSuppressSystemWarnings(!AllowWarningInSystemHeaders);
657aafad2d2Scor3ntin     }
658aafad2d2Scor3ntin     ~AllowWarningInSystemHeaders() { Engine.setSuppressSystemWarnings(Prev); }
659aafad2d2Scor3ntin 
660aafad2d2Scor3ntin   private:
661aafad2d2Scor3ntin     DiagnosticsEngine &Engine;
662aafad2d2Scor3ntin     bool Prev;
663aafad2d2Scor3ntin   } SystemWarningOverrideRAII(S.getDiagnostics(),
664aafad2d2Scor3ntin                               ShouldAllowWarningInSystemHeader);
665aafad2d2Scor3ntin 
666dd8e0a0aSReid Kleckner   if (!Message.empty()) {
667dd8e0a0aSReid Kleckner     S.Diag(Loc, diag_message) << ReferringDecl << Message << FixIts;
668dd8e0a0aSReid Kleckner     if (ObjCProperty)
669dd8e0a0aSReid Kleckner       S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
670dd8e0a0aSReid Kleckner           << ObjCProperty->getDeclName() << property_note_select;
671dd8e0a0aSReid Kleckner   } else if (!UnknownObjCClass) {
672dd8e0a0aSReid Kleckner     S.Diag(Loc, diag) << ReferringDecl << FixIts;
673dd8e0a0aSReid Kleckner     if (ObjCProperty)
674dd8e0a0aSReid Kleckner       S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
675dd8e0a0aSReid Kleckner           << ObjCProperty->getDeclName() << property_note_select;
676dd8e0a0aSReid Kleckner   } else {
677dd8e0a0aSReid Kleckner     S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts;
678dd8e0a0aSReid Kleckner     S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
679dd8e0a0aSReid Kleckner   }
680dd8e0a0aSReid Kleckner 
681dd8e0a0aSReid Kleckner   S.Diag(NoteLocation, diag_available_here)
682dd8e0a0aSReid Kleckner     << OffendingDecl << available_here_select_kind;
683dd8e0a0aSReid Kleckner }
684dd8e0a0aSReid Kleckner 
685dd8e0a0aSReid Kleckner void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
686dd8e0a0aSReid Kleckner   assert(DD.Kind == DelayedDiagnostic::Availability &&
687dd8e0a0aSReid Kleckner          "Expected an availability diagnostic here");
688dd8e0a0aSReid Kleckner 
689dd8e0a0aSReid Kleckner   DD.Triggered = true;
690dd8e0a0aSReid Kleckner   DoEmitAvailabilityWarning(
691dd8e0a0aSReid Kleckner       *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),
692dd8e0a0aSReid Kleckner       DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),
693dd8e0a0aSReid Kleckner       DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),
694dd8e0a0aSReid Kleckner       DD.getObjCProperty(), false);
695dd8e0a0aSReid Kleckner }
696dd8e0a0aSReid Kleckner 
697dd8e0a0aSReid Kleckner static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
698dd8e0a0aSReid Kleckner                                     const NamedDecl *ReferringDecl,
699dd8e0a0aSReid Kleckner                                     const NamedDecl *OffendingDecl,
700dd8e0a0aSReid Kleckner                                     StringRef Message,
701dd8e0a0aSReid Kleckner                                     ArrayRef<SourceLocation> Locs,
702dd8e0a0aSReid Kleckner                                     const ObjCInterfaceDecl *UnknownObjCClass,
703dd8e0a0aSReid Kleckner                                     const ObjCPropertyDecl *ObjCProperty,
704dd8e0a0aSReid Kleckner                                     bool ObjCPropertyAccess) {
705dd8e0a0aSReid Kleckner   // Delay if we're currently parsing a declaration.
706dd8e0a0aSReid Kleckner   if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
707dd8e0a0aSReid Kleckner     S.DelayedDiagnostics.add(
708dd8e0a0aSReid Kleckner         DelayedDiagnostic::makeAvailability(
709dd8e0a0aSReid Kleckner             AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
710dd8e0a0aSReid Kleckner             ObjCProperty, Message, ObjCPropertyAccess));
711dd8e0a0aSReid Kleckner     return;
712dd8e0a0aSReid Kleckner   }
713dd8e0a0aSReid Kleckner 
714dd8e0a0aSReid Kleckner   Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
715dd8e0a0aSReid Kleckner   DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
716dd8e0a0aSReid Kleckner                             Message, Locs, UnknownObjCClass, ObjCProperty,
717dd8e0a0aSReid Kleckner                             ObjCPropertyAccess);
718dd8e0a0aSReid Kleckner }
719dd8e0a0aSReid Kleckner 
720dd8e0a0aSReid Kleckner namespace {
721dd8e0a0aSReid Kleckner 
722dd8e0a0aSReid Kleckner /// Returns true if the given statement can be a body-like child of \p Parent.
723dd8e0a0aSReid Kleckner bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {
724dd8e0a0aSReid Kleckner   switch (Parent->getStmtClass()) {
725dd8e0a0aSReid Kleckner   case Stmt::IfStmtClass:
726dd8e0a0aSReid Kleckner     return cast<IfStmt>(Parent)->getThen() == S ||
727dd8e0a0aSReid Kleckner            cast<IfStmt>(Parent)->getElse() == S;
728dd8e0a0aSReid Kleckner   case Stmt::WhileStmtClass:
729dd8e0a0aSReid Kleckner     return cast<WhileStmt>(Parent)->getBody() == S;
730dd8e0a0aSReid Kleckner   case Stmt::DoStmtClass:
731dd8e0a0aSReid Kleckner     return cast<DoStmt>(Parent)->getBody() == S;
732dd8e0a0aSReid Kleckner   case Stmt::ForStmtClass:
733dd8e0a0aSReid Kleckner     return cast<ForStmt>(Parent)->getBody() == S;
734dd8e0a0aSReid Kleckner   case Stmt::CXXForRangeStmtClass:
735dd8e0a0aSReid Kleckner     return cast<CXXForRangeStmt>(Parent)->getBody() == S;
736dd8e0a0aSReid Kleckner   case Stmt::ObjCForCollectionStmtClass:
737dd8e0a0aSReid Kleckner     return cast<ObjCForCollectionStmt>(Parent)->getBody() == S;
738dd8e0a0aSReid Kleckner   case Stmt::CaseStmtClass:
739dd8e0a0aSReid Kleckner   case Stmt::DefaultStmtClass:
740dd8e0a0aSReid Kleckner     return cast<SwitchCase>(Parent)->getSubStmt() == S;
741dd8e0a0aSReid Kleckner   default:
742dd8e0a0aSReid Kleckner     return false;
743dd8e0a0aSReid Kleckner   }
744dd8e0a0aSReid Kleckner }
745dd8e0a0aSReid Kleckner 
746*dde802b1SSirraide class StmtUSEFinder : public DynamicRecursiveASTVisitor {
747dd8e0a0aSReid Kleckner   const Stmt *Target;
748dd8e0a0aSReid Kleckner 
749dd8e0a0aSReid Kleckner public:
750*dde802b1SSirraide   bool VisitStmt(Stmt *S) override { return S != Target; }
751dd8e0a0aSReid Kleckner 
752dd8e0a0aSReid Kleckner   /// Returns true if the given statement is present in the given declaration.
753dd8e0a0aSReid Kleckner   static bool isContained(const Stmt *Target, const Decl *D) {
754dd8e0a0aSReid Kleckner     StmtUSEFinder Visitor;
755dd8e0a0aSReid Kleckner     Visitor.Target = Target;
756dd8e0a0aSReid Kleckner     return !Visitor.TraverseDecl(const_cast<Decl *>(D));
757dd8e0a0aSReid Kleckner   }
758dd8e0a0aSReid Kleckner };
759dd8e0a0aSReid Kleckner 
760dd8e0a0aSReid Kleckner /// Traverses the AST and finds the last statement that used a given
761dd8e0a0aSReid Kleckner /// declaration.
762*dde802b1SSirraide class LastDeclUSEFinder : public DynamicRecursiveASTVisitor {
763dd8e0a0aSReid Kleckner   const Decl *D;
764dd8e0a0aSReid Kleckner 
765dd8e0a0aSReid Kleckner public:
766*dde802b1SSirraide   bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
767dd8e0a0aSReid Kleckner     if (DRE->getDecl() == D)
768dd8e0a0aSReid Kleckner       return false;
769dd8e0a0aSReid Kleckner     return true;
770dd8e0a0aSReid Kleckner   }
771dd8e0a0aSReid Kleckner 
772dd8e0a0aSReid Kleckner   static const Stmt *findLastStmtThatUsesDecl(const Decl *D,
773dd8e0a0aSReid Kleckner                                               const CompoundStmt *Scope) {
774dd8e0a0aSReid Kleckner     LastDeclUSEFinder Visitor;
775dd8e0a0aSReid Kleckner     Visitor.D = D;
77600ca004dSNico Weber     for (const Stmt *S : llvm::reverse(Scope->body())) {
777dd8e0a0aSReid Kleckner       if (!Visitor.TraverseStmt(const_cast<Stmt *>(S)))
778dd8e0a0aSReid Kleckner         return S;
779dd8e0a0aSReid Kleckner     }
780dd8e0a0aSReid Kleckner     return nullptr;
781dd8e0a0aSReid Kleckner   }
782dd8e0a0aSReid Kleckner };
783dd8e0a0aSReid Kleckner 
784dd8e0a0aSReid Kleckner /// This class implements -Wunguarded-availability.
785dd8e0a0aSReid Kleckner ///
786dd8e0a0aSReid Kleckner /// This is done with a traversal of the AST of a function that makes reference
787dd8e0a0aSReid Kleckner /// to a partially available declaration. Whenever we encounter an \c if of the
788dd8e0a0aSReid Kleckner /// form: \c if(@available(...)), we use the version from the condition to visit
789dd8e0a0aSReid Kleckner /// the then statement.
790*dde802b1SSirraide class DiagnoseUnguardedAvailability : public DynamicRecursiveASTVisitor {
791dd8e0a0aSReid Kleckner   Sema &SemaRef;
792dd8e0a0aSReid Kleckner   Decl *Ctx;
793dd8e0a0aSReid Kleckner 
794dd8e0a0aSReid Kleckner   /// Stack of potentially nested 'if (@available(...))'s.
795dd8e0a0aSReid Kleckner   SmallVector<VersionTuple, 8> AvailabilityStack;
796dd8e0a0aSReid Kleckner   SmallVector<const Stmt *, 16> StmtStack;
797dd8e0a0aSReid Kleckner 
798dd8e0a0aSReid Kleckner   void DiagnoseDeclAvailability(NamedDecl *D, SourceRange Range,
799dd8e0a0aSReid Kleckner                                 ObjCInterfaceDecl *ClassReceiver = nullptr);
800dd8e0a0aSReid Kleckner 
801dd8e0a0aSReid Kleckner public:
802dd8e0a0aSReid Kleckner   DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
803dd8e0a0aSReid Kleckner       : SemaRef(SemaRef), Ctx(Ctx) {
804dd8e0a0aSReid Kleckner     AvailabilityStack.push_back(
805dd8e0a0aSReid Kleckner         SemaRef.Context.getTargetInfo().getPlatformMinVersion());
806dd8e0a0aSReid Kleckner   }
807dd8e0a0aSReid Kleckner 
808*dde802b1SSirraide   bool TraverseStmt(Stmt *S) override {
809dd8e0a0aSReid Kleckner     if (!S)
810dd8e0a0aSReid Kleckner       return true;
811dd8e0a0aSReid Kleckner     StmtStack.push_back(S);
812*dde802b1SSirraide     bool Result = DynamicRecursiveASTVisitor::TraverseStmt(S);
813dd8e0a0aSReid Kleckner     StmtStack.pop_back();
814dd8e0a0aSReid Kleckner     return Result;
815dd8e0a0aSReid Kleckner   }
816dd8e0a0aSReid Kleckner 
817dd8e0a0aSReid Kleckner   void IssueDiagnostics(Stmt *S) { TraverseStmt(S); }
818dd8e0a0aSReid Kleckner 
819*dde802b1SSirraide   bool TraverseIfStmt(IfStmt *If) override;
820dd8e0a0aSReid Kleckner 
821dd8e0a0aSReid Kleckner   // for 'case X:' statements, don't bother looking at the 'X'; it can't lead
822dd8e0a0aSReid Kleckner   // to any useful diagnostics.
823*dde802b1SSirraide   bool TraverseCaseStmt(CaseStmt *CS) override {
824*dde802b1SSirraide     return TraverseStmt(CS->getSubStmt());
825*dde802b1SSirraide   }
826dd8e0a0aSReid Kleckner 
827*dde802b1SSirraide   bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) override {
828dd8e0a0aSReid Kleckner     if (ObjCMethodDecl *D = Msg->getMethodDecl()) {
829dd8e0a0aSReid Kleckner       ObjCInterfaceDecl *ID = nullptr;
830dd8e0a0aSReid Kleckner       QualType ReceiverTy = Msg->getClassReceiver();
831dd8e0a0aSReid Kleckner       if (!ReceiverTy.isNull() && ReceiverTy->getAsObjCInterfaceType())
832dd8e0a0aSReid Kleckner         ID = ReceiverTy->getAsObjCInterfaceType()->getInterface();
833dd8e0a0aSReid Kleckner 
834dd8e0a0aSReid Kleckner       DiagnoseDeclAvailability(
835dd8e0a0aSReid Kleckner           D, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
836dd8e0a0aSReid Kleckner     }
837dd8e0a0aSReid Kleckner     return true;
838dd8e0a0aSReid Kleckner   }
839dd8e0a0aSReid Kleckner 
840*dde802b1SSirraide   bool VisitDeclRefExpr(DeclRefExpr *DRE) override {
841dd8e0a0aSReid Kleckner     DiagnoseDeclAvailability(DRE->getDecl(),
842dd8e0a0aSReid Kleckner                              SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
843dd8e0a0aSReid Kleckner     return true;
844dd8e0a0aSReid Kleckner   }
845dd8e0a0aSReid Kleckner 
846*dde802b1SSirraide   bool VisitMemberExpr(MemberExpr *ME) override {
847dd8e0a0aSReid Kleckner     DiagnoseDeclAvailability(ME->getMemberDecl(),
848dd8e0a0aSReid Kleckner                              SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
849dd8e0a0aSReid Kleckner     return true;
850dd8e0a0aSReid Kleckner   }
851dd8e0a0aSReid Kleckner 
852*dde802b1SSirraide   bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) override {
853dd8e0a0aSReid Kleckner     SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
854dd8e0a0aSReid Kleckner         << (!SemaRef.getLangOpts().ObjC);
855dd8e0a0aSReid Kleckner     return true;
856dd8e0a0aSReid Kleckner   }
857dd8e0a0aSReid Kleckner 
858*dde802b1SSirraide   bool VisitTypeLoc(TypeLoc Ty) override;
859dd8e0a0aSReid Kleckner };
860dd8e0a0aSReid Kleckner 
861dd8e0a0aSReid Kleckner void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
862dd8e0a0aSReid Kleckner     NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
863dd8e0a0aSReid Kleckner   AvailabilityResult Result;
864dd8e0a0aSReid Kleckner   const NamedDecl *OffendingDecl;
865dd8e0a0aSReid Kleckner   std::tie(Result, OffendingDecl) =
866dd8e0a0aSReid Kleckner       ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, ReceiverClass);
867dd8e0a0aSReid Kleckner   if (Result != AR_Available) {
868dd8e0a0aSReid Kleckner     // All other diagnostic kinds have already been handled in
869dd8e0a0aSReid Kleckner     // DiagnoseAvailabilityOfDecl.
870dd8e0a0aSReid Kleckner     if (Result != AR_NotYetIntroduced)
871dd8e0a0aSReid Kleckner       return;
872dd8e0a0aSReid Kleckner 
873dd8e0a0aSReid Kleckner     const AvailabilityAttr *AA =
874dd8e0a0aSReid Kleckner       getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl);
8751c0e7221Ssmanna12     assert(AA != nullptr && "expecting valid availability attribute");
8763f33c4c1SHelena Kotas     bool EnvironmentMatchesOrNone =
8773f33c4c1SHelena Kotas         hasMatchingEnvironmentOrNone(SemaRef.getASTContext(), AA);
878dd8e0a0aSReid Kleckner     VersionTuple Introduced = AA->getIntroduced();
879dd8e0a0aSReid Kleckner 
8803f33c4c1SHelena Kotas     if (EnvironmentMatchesOrNone && AvailabilityStack.back() >= Introduced)
881dd8e0a0aSReid Kleckner       return;
882dd8e0a0aSReid Kleckner 
883dd8e0a0aSReid Kleckner     // If the context of this function is less available than D, we should not
884dd8e0a0aSReid Kleckner     // emit a diagnostic.
8853f33c4c1SHelena Kotas     if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced,
8863f33c4c1SHelena Kotas                                              AA->getEnvironment(), Ctx,
887dd8e0a0aSReid Kleckner                                              OffendingDecl))
888dd8e0a0aSReid Kleckner       return;
889dd8e0a0aSReid Kleckner 
8903f33c4c1SHelena Kotas     const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
8913f33c4c1SHelena Kotas     std::string PlatformName(
8923f33c4c1SHelena Kotas         AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
89330efdce7SHelena Kotas     llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());
8943f33c4c1SHelena Kotas     llvm::StringRef AttrEnvironment =
89530efdce7SHelena Kotas         AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
8963f33c4c1SHelena Kotas     bool UseEnvironment =
8973f33c4c1SHelena Kotas         (!AttrEnvironment.empty() && !TargetEnvironment.empty());
8983f33c4c1SHelena Kotas 
89930efdce7SHelena Kotas     unsigned DiagKind = getAvailabilityDiagnosticKind(
90030efdce7SHelena Kotas         SemaRef.Context,
90130efdce7SHelena Kotas         SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,
90230efdce7SHelena Kotas         EnvironmentMatchesOrNone);
903dd8e0a0aSReid Kleckner 
904dd8e0a0aSReid Kleckner     SemaRef.Diag(Range.getBegin(), DiagKind)
9053f33c4c1SHelena Kotas         << Range << D << PlatformName << Introduced.getAsString()
9063f33c4c1SHelena Kotas         << UseEnvironment << TargetEnvironment;
907dd8e0a0aSReid Kleckner 
908dd8e0a0aSReid Kleckner     SemaRef.Diag(OffendingDecl->getLocation(),
909dd8e0a0aSReid Kleckner                  diag::note_partial_availability_specified_here)
910dd8e0a0aSReid Kleckner         << OffendingDecl << PlatformName << Introduced.getAsString()
9113f33c4c1SHelena Kotas         << SemaRef.Context.getTargetInfo().getPlatformMinVersion().getAsString()
9123f33c4c1SHelena Kotas         << UseEnvironment << AttrEnvironment << TargetEnvironment;
913dd8e0a0aSReid Kleckner 
9148890209eSHelena Kotas     // Do not offer to silence the warning or fixits for HLSL
9158890209eSHelena Kotas     if (SemaRef.getLangOpts().HLSL)
9168890209eSHelena Kotas       return;
9178890209eSHelena Kotas 
918dd8e0a0aSReid Kleckner     auto FixitDiag =
919dd8e0a0aSReid Kleckner         SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence)
920dd8e0a0aSReid Kleckner         << Range << D
921dd8e0a0aSReid Kleckner         << (SemaRef.getLangOpts().ObjC ? /*@available*/ 0
922dd8e0a0aSReid Kleckner                                        : /*__builtin_available*/ 1);
923dd8e0a0aSReid Kleckner 
924dd8e0a0aSReid Kleckner     // Find the statement which should be enclosed in the if @available check.
925dd8e0a0aSReid Kleckner     if (StmtStack.empty())
926dd8e0a0aSReid Kleckner       return;
927dd8e0a0aSReid Kleckner     const Stmt *StmtOfUse = StmtStack.back();
928dd8e0a0aSReid Kleckner     const CompoundStmt *Scope = nullptr;
929dd8e0a0aSReid Kleckner     for (const Stmt *S : llvm::reverse(StmtStack)) {
930dd8e0a0aSReid Kleckner       if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
931dd8e0a0aSReid Kleckner         Scope = CS;
932dd8e0a0aSReid Kleckner         break;
933dd8e0a0aSReid Kleckner       }
934dd8e0a0aSReid Kleckner       if (isBodyLikeChildStmt(StmtOfUse, S)) {
935dd8e0a0aSReid Kleckner         // The declaration won't be seen outside of the statement, so we don't
936dd8e0a0aSReid Kleckner         // have to wrap the uses of any declared variables in if (@available).
937dd8e0a0aSReid Kleckner         // Therefore we can avoid setting Scope here.
938dd8e0a0aSReid Kleckner         break;
939dd8e0a0aSReid Kleckner       }
940dd8e0a0aSReid Kleckner       StmtOfUse = S;
941dd8e0a0aSReid Kleckner     }
942dd8e0a0aSReid Kleckner     const Stmt *LastStmtOfUse = nullptr;
943dd8e0a0aSReid Kleckner     if (isa<DeclStmt>(StmtOfUse) && Scope) {
944dd8e0a0aSReid Kleckner       for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
945dd8e0a0aSReid Kleckner         if (StmtUSEFinder::isContained(StmtStack.back(), D)) {
946dd8e0a0aSReid Kleckner           LastStmtOfUse = LastDeclUSEFinder::findLastStmtThatUsesDecl(D, Scope);
947dd8e0a0aSReid Kleckner           break;
948dd8e0a0aSReid Kleckner         }
949dd8e0a0aSReid Kleckner       }
950dd8e0a0aSReid Kleckner     }
951dd8e0a0aSReid Kleckner 
952dd8e0a0aSReid Kleckner     const SourceManager &SM = SemaRef.getSourceManager();
953dd8e0a0aSReid Kleckner     SourceLocation IfInsertionLoc =
954dd8e0a0aSReid Kleckner         SM.getExpansionLoc(StmtOfUse->getBeginLoc());
955dd8e0a0aSReid Kleckner     SourceLocation StmtEndLoc =
956dd8e0a0aSReid Kleckner         SM.getExpansionRange(
957dd8e0a0aSReid Kleckner               (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())
958dd8e0a0aSReid Kleckner             .getEnd();
959dd8e0a0aSReid Kleckner     if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
960dd8e0a0aSReid Kleckner       return;
961dd8e0a0aSReid Kleckner 
962dd8e0a0aSReid Kleckner     StringRef Indentation = Lexer::getIndentationForLine(IfInsertionLoc, SM);
963dd8e0a0aSReid Kleckner     const char *ExtraIndentation = "    ";
964dd8e0a0aSReid Kleckner     std::string FixItString;
965dd8e0a0aSReid Kleckner     llvm::raw_string_ostream FixItOS(FixItString);
966dd8e0a0aSReid Kleckner     FixItOS << "if (" << (SemaRef.getLangOpts().ObjC ? "@available"
967dd8e0a0aSReid Kleckner                                                      : "__builtin_available")
968dd8e0a0aSReid Kleckner             << "("
969dd8e0a0aSReid Kleckner             << AvailabilityAttr::getPlatformNameSourceSpelling(
970dd8e0a0aSReid Kleckner                    SemaRef.getASTContext().getTargetInfo().getPlatformName())
971dd8e0a0aSReid Kleckner             << " " << Introduced.getAsString() << ", *)) {\n"
972dd8e0a0aSReid Kleckner             << Indentation << ExtraIndentation;
973dd8e0a0aSReid Kleckner     FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
974dd8e0a0aSReid Kleckner     SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
975dd8e0a0aSReid Kleckner         StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
976dd8e0a0aSReid Kleckner         /*SkipTrailingWhitespaceAndNewLine=*/false);
977dd8e0a0aSReid Kleckner     if (ElseInsertionLoc.isInvalid())
978dd8e0a0aSReid Kleckner       ElseInsertionLoc =
979dd8e0a0aSReid Kleckner           Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, SemaRef.getLangOpts());
980dd8e0a0aSReid Kleckner     FixItOS.str().clear();
981dd8e0a0aSReid Kleckner     FixItOS << "\n"
982dd8e0a0aSReid Kleckner             << Indentation << "} else {\n"
983dd8e0a0aSReid Kleckner             << Indentation << ExtraIndentation
984dd8e0a0aSReid Kleckner             << "// Fallback on earlier versions\n"
985dd8e0a0aSReid Kleckner             << Indentation << "}";
986dd8e0a0aSReid Kleckner     FixitDiag << FixItHint::CreateInsertion(ElseInsertionLoc, FixItOS.str());
987dd8e0a0aSReid Kleckner   }
988dd8e0a0aSReid Kleckner }
989dd8e0a0aSReid Kleckner 
990dd8e0a0aSReid Kleckner bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
991dd8e0a0aSReid Kleckner   const Type *TyPtr = Ty.getTypePtr();
992dd8e0a0aSReid Kleckner   SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
993dd8e0a0aSReid Kleckner 
994dd8e0a0aSReid Kleckner   if (Range.isInvalid())
995dd8e0a0aSReid Kleckner     return true;
996dd8e0a0aSReid Kleckner 
997dd8e0a0aSReid Kleckner   if (const auto *TT = dyn_cast<TagType>(TyPtr)) {
998dd8e0a0aSReid Kleckner     TagDecl *TD = TT->getDecl();
999dd8e0a0aSReid Kleckner     DiagnoseDeclAvailability(TD, Range);
1000dd8e0a0aSReid Kleckner 
1001dd8e0a0aSReid Kleckner   } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
1002dd8e0a0aSReid Kleckner     TypedefNameDecl *D = TD->getDecl();
1003dd8e0a0aSReid Kleckner     DiagnoseDeclAvailability(D, Range);
1004dd8e0a0aSReid Kleckner 
1005dd8e0a0aSReid Kleckner   } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
1006dd8e0a0aSReid Kleckner     if (NamedDecl *D = ObjCO->getInterface())
1007dd8e0a0aSReid Kleckner       DiagnoseDeclAvailability(D, Range);
1008dd8e0a0aSReid Kleckner   }
1009dd8e0a0aSReid Kleckner 
1010dd8e0a0aSReid Kleckner   return true;
1011dd8e0a0aSReid Kleckner }
1012dd8e0a0aSReid Kleckner 
10131553cb5dSGeorge Burgess IV struct ExtractedAvailabilityExpr {
10141553cb5dSGeorge Burgess IV   const ObjCAvailabilityCheckExpr *E = nullptr;
10151553cb5dSGeorge Burgess IV   bool isNegated = false;
10161553cb5dSGeorge Burgess IV };
1017dd8e0a0aSReid Kleckner 
10181553cb5dSGeorge Burgess IV ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) {
10191553cb5dSGeorge Burgess IV   const auto *E = IfCond;
10201553cb5dSGeorge Burgess IV   bool IsNegated = false;
10211553cb5dSGeorge Burgess IV   while (true) {
10221553cb5dSGeorge Burgess IV     E = E->IgnoreParens();
10231553cb5dSGeorge Burgess IV     if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(E)) {
10241553cb5dSGeorge Burgess IV       return ExtractedAvailabilityExpr{AE, IsNegated};
10251553cb5dSGeorge Burgess IV     }
10261553cb5dSGeorge Burgess IV 
10271553cb5dSGeorge Burgess IV     const auto *UO = dyn_cast<UnaryOperator>(E);
10281553cb5dSGeorge Burgess IV     if (!UO || UO->getOpcode() != UO_LNot) {
10291553cb5dSGeorge Burgess IV       return ExtractedAvailabilityExpr{};
10301553cb5dSGeorge Burgess IV     }
10311553cb5dSGeorge Burgess IV     E = UO->getSubExpr();
10321553cb5dSGeorge Burgess IV     IsNegated = !IsNegated;
10331553cb5dSGeorge Burgess IV   }
10341553cb5dSGeorge Burgess IV }
10351553cb5dSGeorge Burgess IV 
10361553cb5dSGeorge Burgess IV bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
10371553cb5dSGeorge Burgess IV   ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(If->getCond());
10381553cb5dSGeorge Burgess IV   if (!IfCond.E) {
1039dd8e0a0aSReid Kleckner     // This isn't an availability checking 'if', we can just continue.
1040*dde802b1SSirraide     return DynamicRecursiveASTVisitor::TraverseIfStmt(If);
1041dd8e0a0aSReid Kleckner   }
1042dd8e0a0aSReid Kleckner 
10431553cb5dSGeorge Burgess IV   VersionTuple CondVersion = IfCond.E->getVersion();
10441553cb5dSGeorge Burgess IV   // If we're using the '*' case here or if this check is redundant, then we
10451553cb5dSGeorge Burgess IV   // use the enclosing version to check both branches.
10461553cb5dSGeorge Burgess IV   if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) {
10471553cb5dSGeorge Burgess IV     return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
10481553cb5dSGeorge Burgess IV   }
10491553cb5dSGeorge Burgess IV 
10501553cb5dSGeorge Burgess IV   auto *Guarded = If->getThen();
10511553cb5dSGeorge Burgess IV   auto *Unguarded = If->getElse();
10521553cb5dSGeorge Burgess IV   if (IfCond.isNegated) {
10531553cb5dSGeorge Burgess IV     std::swap(Guarded, Unguarded);
10541553cb5dSGeorge Burgess IV   }
10551553cb5dSGeorge Burgess IV 
1056dd8e0a0aSReid Kleckner   AvailabilityStack.push_back(CondVersion);
10571553cb5dSGeorge Burgess IV   bool ShouldContinue = TraverseStmt(Guarded);
1058dd8e0a0aSReid Kleckner   AvailabilityStack.pop_back();
1059dd8e0a0aSReid Kleckner 
10601553cb5dSGeorge Burgess IV   return ShouldContinue && TraverseStmt(Unguarded);
1061dd8e0a0aSReid Kleckner }
1062dd8e0a0aSReid Kleckner 
1063dd8e0a0aSReid Kleckner } // end anonymous namespace
1064dd8e0a0aSReid Kleckner 
1065dd8e0a0aSReid Kleckner void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
1066dd8e0a0aSReid Kleckner   Stmt *Body = nullptr;
1067dd8e0a0aSReid Kleckner 
1068dd8e0a0aSReid Kleckner   if (auto *FD = D->getAsFunction()) {
1069dd8e0a0aSReid Kleckner     Body = FD->getBody();
1070143ec502SAkira Hatanaka 
1071143ec502SAkira Hatanaka     if (auto *CD = dyn_cast<CXXConstructorDecl>(FD))
1072143ec502SAkira Hatanaka       for (const CXXCtorInitializer *CI : CD->inits())
1073143ec502SAkira Hatanaka         DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit());
1074143ec502SAkira Hatanaka 
1075dd8e0a0aSReid Kleckner   } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
1076dd8e0a0aSReid Kleckner     Body = MD->getBody();
1077dd8e0a0aSReid Kleckner   else if (auto *BD = dyn_cast<BlockDecl>(D))
1078dd8e0a0aSReid Kleckner     Body = BD->getBody();
1079dd8e0a0aSReid Kleckner 
1080dd8e0a0aSReid Kleckner   assert(Body && "Need a body here!");
1081dd8e0a0aSReid Kleckner 
1082dd8e0a0aSReid Kleckner   DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
1083dd8e0a0aSReid Kleckner }
1084dd8e0a0aSReid Kleckner 
1085a5a3efa8SLogan Smith FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() {
1086a5a3efa8SLogan Smith   if (FunctionScopes.empty())
1087a5a3efa8SLogan Smith     return nullptr;
1088a5a3efa8SLogan Smith 
1089a5a3efa8SLogan Smith   // Conservatively search the entire current function scope context for
1090a5a3efa8SLogan Smith   // availability violations. This ensures we always correctly analyze nested
1091a5a3efa8SLogan Smith   // classes, blocks, lambdas, etc. that may or may not be inside if(@available)
1092a5a3efa8SLogan Smith   // checks themselves.
1093a5a3efa8SLogan Smith   return FunctionScopes.front();
1094a5a3efa8SLogan Smith }
1095a5a3efa8SLogan Smith 
1096dd8e0a0aSReid Kleckner void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
1097dd8e0a0aSReid Kleckner                                       ArrayRef<SourceLocation> Locs,
1098dd8e0a0aSReid Kleckner                                       const ObjCInterfaceDecl *UnknownObjCClass,
1099dd8e0a0aSReid Kleckner                                       bool ObjCPropertyAccess,
1100dd8e0a0aSReid Kleckner                                       bool AvoidPartialAvailabilityChecks,
1101dd8e0a0aSReid Kleckner                                       ObjCInterfaceDecl *ClassReceiver) {
1102dd8e0a0aSReid Kleckner   std::string Message;
1103dd8e0a0aSReid Kleckner   AvailabilityResult Result;
1104dd8e0a0aSReid Kleckner   const NamedDecl* OffendingDecl;
1105dd8e0a0aSReid Kleckner   // See if this declaration is unavailable, deprecated, or partial.
1106dd8e0a0aSReid Kleckner   std::tie(Result, OffendingDecl) =
1107dd8e0a0aSReid Kleckner       ShouldDiagnoseAvailabilityOfDecl(*this, D, &Message, ClassReceiver);
1108dd8e0a0aSReid Kleckner   if (Result == AR_Available)
1109dd8e0a0aSReid Kleckner     return;
1110dd8e0a0aSReid Kleckner 
1111dd8e0a0aSReid Kleckner   if (Result == AR_NotYetIntroduced) {
1112dd8e0a0aSReid Kleckner     if (AvoidPartialAvailabilityChecks)
1113dd8e0a0aSReid Kleckner       return;
1114dd8e0a0aSReid Kleckner 
1115dd8e0a0aSReid Kleckner     // We need to know the @available context in the current function to
1116dd8e0a0aSReid Kleckner     // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
1117dd8e0a0aSReid Kleckner     // when we're done parsing the current function.
1118a5a3efa8SLogan Smith     if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) {
1119a5a3efa8SLogan Smith       Context->HasPotentialAvailabilityViolations = true;
1120dd8e0a0aSReid Kleckner       return;
1121dd8e0a0aSReid Kleckner     }
1122dd8e0a0aSReid Kleckner   }
1123dd8e0a0aSReid Kleckner 
1124dd8e0a0aSReid Kleckner   const ObjCPropertyDecl *ObjCPDecl = nullptr;
1125dd8e0a0aSReid Kleckner   if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
1126dd8e0a0aSReid Kleckner     if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
1127dd8e0a0aSReid Kleckner       AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
1128dd8e0a0aSReid Kleckner       if (PDeclResult == Result)
1129dd8e0a0aSReid Kleckner         ObjCPDecl = PD;
1130dd8e0a0aSReid Kleckner     }
1131dd8e0a0aSReid Kleckner   }
1132dd8e0a0aSReid Kleckner 
1133dd8e0a0aSReid Kleckner   EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
1134dd8e0a0aSReid Kleckner                           UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
1135dd8e0a0aSReid Kleckner }
1136