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