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 // If this attr has an inferred platform-specific attr (e.g. anyappleos
62 // → ios/macos/...), use that for platform matching but return the
63 // original.
64 const AvailabilityAttr *EffectiveAvail = Avail->getEffectiveAttr();
65
66 // Check if this is an App Extension "platform", and if so chop off
67 // the suffix for matching with the actual platform.
68 StringRef ActualPlatform = EffectiveAvail->getPlatform()->getName();
69 StringRef RealizedPlatform = ActualPlatform;
70 if (Context.getLangOpts().AppExt) {
71 size_t suffix = RealizedPlatform.rfind(Str: "_app_extension");
72 if (suffix != StringRef::npos)
73 RealizedPlatform = RealizedPlatform.slice(Start: 0, End: suffix);
74 }
75
76 StringRef TargetPlatform = Context.getTargetInfo().getPlatformName();
77
78 // Match the platform name.
79 if (RealizedPlatform == TargetPlatform) {
80 // Find the best matching attribute for this environment
81 if (hasMatchingEnvironmentOrNone(Context, AA: EffectiveAvail))
82 return Avail;
83 PartialMatch = Avail;
84 }
85 }
86 }
87 return PartialMatch;
88}
89
90/// The diagnostic we should emit for \c D, and the declaration that
91/// originated it, or \c AR_Available.
92///
93/// \param D The declaration to check.
94/// \param Message If non-null, this will be populated with the message from
95/// the availability attribute that is selected.
96/// \param ClassReceiver If we're checking the method of a class message
97/// send, the class. Otherwise nullptr.
98std::pair<AvailabilityResult, const NamedDecl *>
99Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message,
100 ObjCInterfaceDecl *ClassReceiver) {
101 AvailabilityResult Result = D->getAvailability(Message);
102
103 // For typedefs, if the typedef declaration appears available look
104 // to the underlying type to see if it is more restrictive.
105 while (const auto *TD = dyn_cast<TypedefNameDecl>(Val: D)) {
106 if (Result != AR_Available)
107 break;
108 for (const Type *T = TD->getUnderlyingType().getTypePtr(); /**/; /**/) {
109 if (auto *TT = dyn_cast<TagType>(Val: T)) {
110 D = TT->getDecl()->getDefinitionOrSelf();
111 } else if (isa<SubstTemplateTypeParmType>(Val: T)) {
112 // A Subst* node represents a use through a template.
113 // Any uses of the underlying declaration happened through it's template
114 // specialization.
115 goto done;
116 } else {
117 const Type *NextT =
118 T->getLocallyUnqualifiedSingleStepDesugaredType().getTypePtr();
119 if (NextT == T)
120 goto done;
121 T = NextT;
122 continue;
123 }
124 Result = D->getAvailability(Message);
125 break;
126 }
127 }
128done:
129 // Forward class declarations get their attributes from their definition.
130 if (const auto *IDecl = dyn_cast<ObjCInterfaceDecl>(Val: D)) {
131 if (IDecl->getDefinition()) {
132 D = IDecl->getDefinition();
133 Result = D->getAvailability(Message);
134 }
135 }
136
137 if (const auto *ECD = dyn_cast<EnumConstantDecl>(Val: D))
138 if (Result == AR_Available) {
139 const DeclContext *DC = ECD->getDeclContext();
140 if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(Val: DC)) {
141 Result = TheEnumDecl->getAvailability(Message);
142 D = TheEnumDecl;
143 }
144 }
145
146 // For +new, infer availability from -init.
147 if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: D)) {
148 if (ObjC().NSAPIObj && ClassReceiver) {
149 ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
150 Sel: ObjC().NSAPIObj->getInitSelector());
151 if (Init && Result == AR_Available && MD->isClassMethod() &&
152 MD->getSelector() == ObjC().NSAPIObj->getNewSelector() &&
153 MD->definedInNSObject(getASTContext())) {
154 Result = Init->getAvailability(Message);
155 D = Init;
156 }
157 }
158 }
159
160 return {Result, D};
161}
162
163/// whether we should emit a diagnostic for \c K and \c DeclVersion in
164/// the context of \c Ctx. For example, we should emit an unavailable diagnostic
165/// in a deprecated context, but not the other way around.
166static bool ShouldDiagnoseAvailabilityInContext(
167 Sema &S, AvailabilityResult K, VersionTuple DeclVersion,
168 const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) {
169 assert(K != AR_Available && "Expected an unavailable declaration here!");
170
171 // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
172 auto DeclLoc = Ctx->getBeginLoc();
173 // This is only a problem in Foundation's C++ implementation for CF_OPTIONS.
174 if (DeclLoc.isMacroID() && S.getLangOpts().CPlusPlus &&
175 isa<TypedefDecl>(Val: OffendingDecl)) {
176 StringRef MacroName = S.getPreprocessor().getImmediateMacroName(Loc: DeclLoc);
177 if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS" ||
178 MacroName == "SWIFT_OPTIONS" || MacroName == "NS_OPTIONS") {
179 return false;
180 }
181 }
182
183 // In HLSL, skip emitting diagnostic if the diagnostic mode is not set to
184 // strict (-fhlsl-strict-availability), or if the target is library and the
185 // availability is restricted to a specific environment/shader stage.
186 // For libraries the availability will be checked later in
187 // DiagnoseHLSLAvailability class once where the specific environment/shader
188 // stage of the caller is known.
189 // We only do this for APIs that are not explicitly deprecated. Any API that
190 // is explicitly deprecated we always issue a diagnostic on.
191 if (S.getLangOpts().HLSL && K != AR_Deprecated) {
192 if (!S.getLangOpts().HLSLStrictAvailability ||
193 (DeclEnv != nullptr &&
194 S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
195 llvm::Triple::EnvironmentType::Library))
196 return false;
197 }
198
199 if (K == AR_Deprecated) {
200 if (const auto *VD = dyn_cast<VarDecl>(Val: OffendingDecl))
201 if (VD->isLocalVarDeclOrParm() && VD->isDeprecated())
202 return true;
203 }
204
205 // Checks if we should emit the availability diagnostic in the context of C.
206 auto CheckContext = [&](const Decl *C) {
207 if (K == AR_NotYetIntroduced) {
208 if (const AvailabilityAttr *AA = getAttrForPlatform(Context&: S.Context, D: C))
209 if (AA->getEffectiveIntroduced() >= DeclVersion &&
210 AA->getEffectiveEnvironment() == DeclEnv)
211 return true;
212 } else if (K == AR_Deprecated) {
213 if (C->isDeprecated())
214 return true;
215 } else if (K == AR_Unavailable) {
216 // It is perfectly fine to refer to an 'unavailable' Objective-C method
217 // when it is referenced from within the @implementation itself. In this
218 // context, we interpret unavailable as a form of access control.
219 if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: OffendingDecl)) {
220 if (const auto *Impl = dyn_cast<ObjCImplDecl>(Val: C)) {
221 if (MD->getClassInterface() == Impl->getClassInterface())
222 return true;
223 }
224 }
225 }
226
227 if (C->isUnavailable())
228 return true;
229 return false;
230 };
231
232 do {
233 if (CheckContext(Ctx))
234 return false;
235
236 // An implementation implicitly has the availability of the interface.
237 // Unless it is "+load" method.
238 if (const auto *MethodD = dyn_cast<ObjCMethodDecl>(Val: Ctx))
239 if (MethodD->isClassMethod() &&
240 MethodD->getSelector().getAsString() == "load")
241 return true;
242
243 if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(Val: Ctx)) {
244 if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface())
245 if (CheckContext(Interface))
246 return false;
247 }
248 // A category implicitly has the availability of the interface.
249 else if (const auto *CatD = dyn_cast<ObjCCategoryDecl>(Val: Ctx))
250 if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
251 if (CheckContext(Interface))
252 return false;
253 } while ((Ctx = cast_or_null<Decl>(Val: Ctx->getDeclContext())));
254
255 return true;
256}
257
258static unsigned getAvailabilityDiagnosticKind(
259 const ASTContext &Context, const VersionTuple &DeploymentVersion,
260 const VersionTuple &DeclVersion, bool HasMatchingEnv) {
261 const auto &Triple = Context.getTargetInfo().getTriple();
262 VersionTuple ForceAvailabilityFromVersion;
263 switch (Triple.getOS()) {
264 // For iOS, emit the diagnostic even if -Wunguarded-availability is
265 // not specified for deployment targets >= to iOS 11 or equivalent or
266 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
267 // later.
268 case llvm::Triple::IOS:
269 case llvm::Triple::TvOS:
270 ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
271 break;
272 case llvm::Triple::WatchOS:
273 ForceAvailabilityFromVersion = VersionTuple(/*Major=*/4);
274 break;
275 case llvm::Triple::Darwin:
276 case llvm::Triple::MacOSX:
277 ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
278 break;
279 // For HLSL, use diagnostic from HLSLAvailability group which
280 // are reported as errors by default and in strict diagnostic mode
281 // (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
282 // mode (-Wno-error=hlsl-availability)
283 case llvm::Triple::ShaderModel:
284 return HasMatchingEnv ? diag::warn_hlsl_availability
285 : diag::warn_hlsl_availability_unavailable;
286 default:
287 // New Apple targets should always warn about availability.
288 ForceAvailabilityFromVersion =
289 (Triple.getVendor() == llvm::Triple::Apple)
290 ? VersionTuple(/*Major=*/0, 0)
291 : VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
292 }
293 if (DeploymentVersion >= ForceAvailabilityFromVersion ||
294 DeclVersion >= ForceAvailabilityFromVersion)
295 return HasMatchingEnv ? diag::warn_unguarded_availability_new
296 : diag::warn_unguarded_availability_unavailable_new;
297 return HasMatchingEnv ? diag::warn_unguarded_availability
298 : diag::warn_unguarded_availability_unavailable;
299}
300
301static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
302 for (Decl *Ctx = OrigCtx; Ctx;
303 Ctx = cast_or_null<Decl>(Val: Ctx->getDeclContext())) {
304 if (isa<TagDecl>(Val: Ctx) || isa<FunctionDecl>(Val: Ctx) || isa<ObjCMethodDecl>(Val: Ctx))
305 return cast<NamedDecl>(Val: Ctx);
306 if (auto *CD = dyn_cast<ObjCContainerDecl>(Val: Ctx)) {
307 if (auto *Imp = dyn_cast<ObjCImplDecl>(Val: Ctx))
308 return Imp->getClassInterface();
309 return CD;
310 }
311 }
312
313 return dyn_cast<NamedDecl>(Val: OrigCtx);
314}
315
316namespace {
317
318struct AttributeInsertion {
319 StringRef Prefix;
320 SourceLocation Loc;
321 StringRef Suffix;
322
323 static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
324 return {.Prefix: " ", .Loc: D->getEndLoc(), .Suffix: ""};
325 }
326 static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
327 return {.Prefix: " ", .Loc: Loc, .Suffix: ""};
328 }
329 static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
330 return {.Prefix: "", .Loc: D->getBeginLoc(), .Suffix: "\n"};
331 }
332};
333
334} // end anonymous namespace
335
336/// Tries to parse a string as ObjC method name.
337///
338/// \param Name The string to parse. Expected to originate from availability
339/// attribute argument.
340/// \param SlotNames The vector that will be populated with slot names. In case
341/// of unsuccessful parsing can contain invalid data.
342/// \returns A number of method parameters if parsing was successful,
343/// std::nullopt otherwise.
344static std::optional<unsigned>
345tryParseObjCMethodName(StringRef Name, SmallVectorImpl<StringRef> &SlotNames,
346 const LangOptions &LangOpts) {
347 // Accept replacements starting with - or + as valid ObjC method names.
348 if (!Name.empty() && (Name.front() == '-' || Name.front() == '+'))
349 Name = Name.drop_front(N: 1);
350 if (Name.empty())
351 return std::nullopt;
352 Name.split(A&: SlotNames, Separator: ':');
353 unsigned NumParams;
354 if (Name.back() == ':') {
355 // Remove an empty string at the end that doesn't represent any slot.
356 SlotNames.pop_back();
357 NumParams = SlotNames.size();
358 } else {
359 if (SlotNames.size() != 1)
360 // Not a valid method name, just a colon-separated string.
361 return std::nullopt;
362 NumParams = 0;
363 }
364 // Verify all slot names are valid.
365 bool AllowDollar = LangOpts.DollarIdents;
366 for (StringRef S : SlotNames) {
367 if (S.empty())
368 continue;
369 if (!isValidAsciiIdentifier(S, AllowDollar))
370 return std::nullopt;
371 }
372 return NumParams;
373}
374
375/// Returns a source location in which it's appropriate to insert a new
376/// attribute for the given declaration \D.
377static std::optional<AttributeInsertion>
378createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
379 const LangOptions &LangOpts) {
380 if (isa<ObjCPropertyDecl>(Val: D))
381 return AttributeInsertion::createInsertionAfter(D);
382 if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: D)) {
383 if (MD->hasBody())
384 return std::nullopt;
385 return AttributeInsertion::createInsertionAfter(D);
386 }
387 if (const auto *TD = dyn_cast<TagDecl>(Val: D)) {
388 SourceLocation Loc =
389 Lexer::getLocForEndOfToken(Loc: TD->getInnerLocStart(), Offset: 0, SM, LangOpts);
390 if (Loc.isInvalid())
391 return std::nullopt;
392 // Insert after the 'struct'/whatever keyword.
393 return AttributeInsertion::createInsertionAfter(Loc);
394 }
395 return AttributeInsertion::createInsertionBefore(D);
396}
397
398/// Actually emit an availability diagnostic for a reference to an unavailable
399/// decl.
400///
401/// \param Ctx The context that the reference occurred in
402/// \param ReferringDecl The exact declaration that was referenced.
403/// \param OffendingDecl A related decl to \c ReferringDecl that has an
404/// availability attribute corresponding to \c K attached to it. Note that this
405/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
406/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
407/// and OffendingDecl is the EnumDecl.
408static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
409 Decl *Ctx, const NamedDecl *ReferringDecl,
410 const NamedDecl *OffendingDecl,
411 StringRef Message,
412 ArrayRef<SourceLocation> Locs,
413 const ObjCInterfaceDecl *UnknownObjCClass,
414 const ObjCPropertyDecl *ObjCProperty,
415 bool ObjCPropertyAccess) {
416 // Diagnostics for deprecated or unavailable.
417 unsigned diag, diag_message, diag_fwdclass_message;
418 unsigned diag_available_here = diag::note_availability_specified_here;
419 SourceLocation NoteLocation = OffendingDecl->getLocation();
420
421 // Matches 'diag::note_property_attribute' options.
422 unsigned property_note_select;
423
424 // Matches diag::note_availability_specified_here.
425 unsigned available_here_select_kind;
426
427 VersionTuple DeclVersion;
428 const AvailabilityAttr *AA = getAttrForPlatform(Context&: S.Context, D: OffendingDecl);
429 const IdentifierInfo *IIEnv = nullptr;
430 if (AA) {
431 DeclVersion = AA->getEffectiveIntroduced();
432 IIEnv = AA->getEffectiveEnvironment();
433 }
434
435 if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, DeclEnv: IIEnv, Ctx,
436 OffendingDecl))
437 return;
438
439 SourceLocation Loc = Locs.front();
440
441 // The declaration can have multiple availability attributes, we are looking
442 // at one of them.
443 if (AA && AA->isInherited()) {
444 for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl;
445 Redecl = Redecl->getPreviousDecl()) {
446 const AvailabilityAttr *AForRedecl =
447 getAttrForPlatform(Context&: S.Context, D: Redecl);
448 if (AForRedecl && !AForRedecl->isInherited()) {
449 // If D is a declaration with inherited attributes, the note should
450 // point to the declaration with actual attributes.
451 NoteLocation = Redecl->getLocation();
452 break;
453 }
454 }
455 }
456
457 switch (K) {
458 case AR_NotYetIntroduced: {
459 // We would like to emit the diagnostic even if -Wunguarded-availability is
460 // not specified for deployment targets >= to iOS 11 or equivalent or
461 // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
462 // later.
463 assert(AA != nullptr && "expecting valid availability attribute");
464 VersionTuple Introduced = AA->getEffectiveIntroduced();
465 bool EnvironmentMatchesOrNone =
466 hasMatchingEnvironmentOrNone(Context: S.getASTContext(), AA: AA->getEffectiveAttr());
467
468 const TargetInfo &TI = S.getASTContext().getTargetInfo();
469 std::string PlatformName(
470 AvailabilityAttr::getPrettyPlatformName(Platform: TI.getPlatformName()));
471 llvm::StringRef TargetEnvironment(
472 llvm::Triple::getEnvironmentTypeName(Kind: TI.getTriple().getEnvironment()));
473 llvm::StringRef AttrEnvironment =
474 AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
475 bool UseEnvironment =
476 (!AttrEnvironment.empty() && !TargetEnvironment.empty());
477
478 unsigned DiagKind = getAvailabilityDiagnosticKind(
479 Context: S.Context, DeploymentVersion: S.Context.getTargetInfo().getPlatformMinVersion(),
480 DeclVersion: Introduced, HasMatchingEnv: EnvironmentMatchesOrNone);
481
482 S.Diag(Loc, DiagID: DiagKind) << OffendingDecl << PlatformName
483 << Introduced.getAsString() << UseEnvironment
484 << TargetEnvironment;
485
486 S.Diag(Loc: OffendingDecl->getLocation(),
487 DiagID: diag::note_partial_availability_specified_here)
488 << OffendingDecl << PlatformName << Introduced.getAsString()
489 << S.Context.getTargetInfo().getPlatformMinVersion().getAsString()
490 << UseEnvironment << AttrEnvironment << TargetEnvironment;
491
492 // Do not offer to silence the warning or fixits for HLSL
493 if (S.getLangOpts().HLSL)
494 return;
495
496 if (const auto *Enclosing = findEnclosingDeclToAnnotate(OrigCtx: Ctx)) {
497 if (const auto *TD = dyn_cast<TagDecl>(Val: Enclosing))
498 if (TD->getDeclName().isEmpty()) {
499 S.Diag(Loc: TD->getLocation(),
500 DiagID: diag::note_decl_unguarded_availability_silence)
501 << /*Anonymous*/ 1 << TD->getKindName();
502 return;
503 }
504 auto FixitNoteDiag =
505 S.Diag(Loc: Enclosing->getLocation(),
506 DiagID: diag::note_decl_unguarded_availability_silence)
507 << /*Named*/ 0 << Enclosing;
508 // Don't offer a fixit for declarations with availability attributes.
509 if (Enclosing->hasAttr<AvailabilityAttr>())
510 return;
511 Preprocessor &PP = S.getPreprocessor();
512 if (!PP.isMacroDefined(Id: "API_AVAILABLE"))
513 return;
514 std::optional<AttributeInsertion> Insertion = createAttributeInsertion(
515 D: Enclosing, SM: S.getSourceManager(), LangOpts: S.getLangOpts());
516 if (!Insertion)
517 return;
518 StringRef PlatformName =
519 S.getASTContext().getTargetInfo().getPlatformName();
520
521 // Apple's API_AVAILABLE macro expands roughly like this.
522 // API_AVAILABLE(ios(17.0))
523 // __attribute__((availability(__API_AVAILABLE_PLATFORM_ios(17.0)))
524 // __attribute__((availability(ios,introduced=17.0)))
525 // In order to figure out which platform name to use in the API_AVAILABLE
526 // macro, the associated __API_AVAILABLE_PLATFORM_ macro needs to be
527 // found. The __API_AVAILABLE_PLATFORM_ macros aren't consistent about
528 // using the canonical platform name, source spelling name, or one of the
529 // other supported names (i.e. one of the keys in canonicalizePlatformName
530 // that's neither). Check all of the supported names for a match.
531 std::vector<StringRef> EquivalentPlatforms =
532 AvailabilityAttr::equivalentPlatformNames(Platform: PlatformName);
533 llvm::Twine MacroPrefix = "__API_AVAILABLE_PLATFORM_";
534 auto AvailablePlatform =
535 llvm::find_if(Range&: EquivalentPlatforms, P: [&](StringRef EquivalentPlatform) {
536 return PP.isMacroDefined(Id: (MacroPrefix + EquivalentPlatform).str());
537 });
538 if (AvailablePlatform == EquivalentPlatforms.end())
539 return;
540 std::string Introduced =
541 OffendingDecl->getVersionIntroduced().getAsString();
542 FixitNoteDiag << FixItHint::CreateInsertion(
543 InsertionLoc: Insertion->Loc,
544 Code: (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" +
545 *AvailablePlatform + "(" + Introduced + "))" + Insertion->Suffix)
546 .str());
547 }
548 return;
549 }
550 case AR_Deprecated:
551 // Suppress -Wdeprecated-declarations in implicit
552 // functions.
553 if (const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: S.getCurFunctionDecl());
554 FD && FD->isImplicit())
555 return;
556
557 if (ObjCPropertyAccess)
558 diag = diag::warn_property_method_deprecated;
559 else if (S.currentEvaluationContext().IsCaseExpr)
560 diag = diag::warn_deprecated_switch_case;
561 else
562 diag = diag::warn_deprecated;
563
564 diag_message = diag::warn_deprecated_message;
565 diag_fwdclass_message = diag::warn_deprecated_fwdclass_message;
566 property_note_select = /* deprecated */ 0;
567 available_here_select_kind = /* deprecated */ 2;
568 if (const auto *AL = OffendingDecl->getAttr<DeprecatedAttr>())
569 NoteLocation = AL->getLocation();
570 break;
571
572 case AR_Unavailable:
573 diag = !ObjCPropertyAccess ? diag::err_unavailable
574 : diag::err_property_method_unavailable;
575 diag_message = diag::err_unavailable_message;
576 diag_fwdclass_message = diag::warn_unavailable_fwdclass_message;
577 property_note_select = /* unavailable */ 1;
578 available_here_select_kind = /* unavailable */ 0;
579
580 if (auto AL = OffendingDecl->getAttr<UnavailableAttr>()) {
581 if (AL->isImplicit() && AL->getImplicitReason()) {
582 // Most of these failures are due to extra restrictions in ARC;
583 // reflect that in the primary diagnostic when applicable.
584 auto flagARCError = [&] {
585 if (S.getLangOpts().ObjCAutoRefCount &&
586 S.getSourceManager().isInSystemHeader(
587 Loc: OffendingDecl->getLocation()))
588 diag = diag::err_unavailable_in_arc;
589 };
590
591 switch (AL->getImplicitReason()) {
592 case UnavailableAttr::IR_None: break;
593
594 case UnavailableAttr::IR_ARCForbiddenType:
595 flagARCError();
596 diag_available_here = diag::note_arc_forbidden_type;
597 break;
598
599 case UnavailableAttr::IR_ForbiddenWeak:
600 if (S.getLangOpts().ObjCWeakRuntime)
601 diag_available_here = diag::note_arc_weak_disabled;
602 else
603 diag_available_here = diag::note_arc_weak_no_runtime;
604 break;
605
606 case UnavailableAttr::IR_ARCForbiddenConversion:
607 flagARCError();
608 diag_available_here = diag::note_performs_forbidden_arc_conversion;
609 break;
610
611 case UnavailableAttr::IR_ARCInitReturnsUnrelated:
612 flagARCError();
613 diag_available_here = diag::note_arc_init_returns_unrelated;
614 break;
615
616 case UnavailableAttr::IR_ARCFieldWithOwnership:
617 flagARCError();
618 diag_available_here = diag::note_arc_field_with_ownership;
619 break;
620 }
621 }
622 }
623 break;
624
625 case AR_Available:
626 llvm_unreachable("Warning for availability of available declaration?");
627 }
628
629 SmallVector<FixItHint, 12> FixIts;
630 if (K == AR_Deprecated) {
631 StringRef Replacement;
632 if (auto AL = OffendingDecl->getAttr<DeprecatedAttr>())
633 Replacement = AL->getReplacement();
634 if (auto AL = getAttrForPlatform(Context&: S.Context, D: OffendingDecl))
635 Replacement = AL->getReplacement();
636
637 CharSourceRange UseRange;
638 if (!Replacement.empty())
639 UseRange =
640 CharSourceRange::getCharRange(B: Loc, E: S.getLocForEndOfToken(Loc));
641 if (UseRange.isValid()) {
642 if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(Val: ReferringDecl)) {
643 Selector Sel = MethodDecl->getSelector();
644 SmallVector<StringRef, 12> SelectorSlotNames;
645 std::optional<unsigned> NumParams = tryParseObjCMethodName(
646 Name: Replacement, SlotNames&: SelectorSlotNames, LangOpts: S.getLangOpts());
647 if (NumParams && *NumParams == Sel.getNumArgs()) {
648 assert(SelectorSlotNames.size() == Locs.size());
649 for (unsigned I = 0; I < Locs.size(); ++I) {
650 if (!Sel.getNameForSlot(argIndex: I).empty()) {
651 CharSourceRange NameRange = CharSourceRange::getCharRange(
652 B: Locs[I], E: S.getLocForEndOfToken(Loc: Locs[I]));
653 FixIts.push_back(Elt: FixItHint::CreateReplacement(
654 RemoveRange: NameRange, Code: SelectorSlotNames[I]));
655 } else
656 FixIts.push_back(
657 Elt: FixItHint::CreateInsertion(InsertionLoc: Locs[I], Code: SelectorSlotNames[I]));
658 }
659 } else
660 FixIts.push_back(Elt: FixItHint::CreateReplacement(RemoveRange: UseRange, Code: Replacement));
661 } else
662 FixIts.push_back(Elt: FixItHint::CreateReplacement(RemoveRange: UseRange, Code: Replacement));
663 }
664 }
665
666 // We emit deprecation warning for deprecated specializations
667 // when their instantiation stacks originate outside
668 // of a system header, even if the diagnostics is suppresed at the
669 // point of definition.
670 SourceLocation InstantiationLoc =
671 S.getTopMostPointOfInstantiation(ReferringDecl);
672 bool ShouldAllowWarningInSystemHeader =
673 InstantiationLoc != Loc &&
674 !S.getSourceManager().isInSystemHeader(Loc: InstantiationLoc);
675 struct AllowWarningInSystemHeaders {
676 AllowWarningInSystemHeaders(DiagnosticsEngine &E,
677 bool AllowWarningInSystemHeaders)
678 : Engine(E), Prev(E.getForceSystemWarnings()) {
679 if (AllowWarningInSystemHeaders)
680 Engine.setForceSystemWarnings(true);
681 }
682 ~AllowWarningInSystemHeaders() { Engine.setForceSystemWarnings(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 = hasMatchingEnvironmentOrNone(
901 Context: SemaRef.getASTContext(), AA: AA->getEffectiveAttr());
902 VersionTuple Introduced = AA->getEffectiveIntroduced();
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->getEffectiveEnvironment(), 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 StringRef FixItPlatformName;
991 VersionTuple FixItVersion;
992
993 if (AA->getInferredAttr()) {
994 FixItPlatformName = "anyAppleOS";
995 FixItVersion = AA->getIntroduced();
996 } else {
997 FixItPlatformName = AvailabilityAttr::getPlatformNameSourceSpelling(
998 Platform: SemaRef.getASTContext().getTargetInfo().getPlatformName());
999 FixItVersion = AA->getEffectiveIntroduced();
1000 }
1001 FixItOS << "if ("
1002 << (SemaRef.getLangOpts().ObjC ? "@available"
1003 : "__builtin_available")
1004 << "(" << FixItPlatformName << " " << FixItVersion.getAsString()
1005 << ", *)) {\n"
1006 << Indentation << ExtraIndentation;
1007 FixitDiag << FixItHint::CreateInsertion(InsertionLoc: IfInsertionLoc, Code: FixItOS.str());
1008 SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
1009 loc: StmtEndLoc, TKind: tok::semi, SM, LangOpts: SemaRef.getLangOpts(),
1010 /*SkipTrailingWhitespaceAndNewLine=*/false);
1011 if (ElseInsertionLoc.isInvalid())
1012 ElseInsertionLoc =
1013 Lexer::getLocForEndOfToken(Loc: StmtEndLoc, Offset: 0, SM, LangOpts: SemaRef.getLangOpts());
1014 FixItOS.str().clear();
1015 FixItOS << "\n"
1016 << Indentation << "} else {\n"
1017 << Indentation << ExtraIndentation
1018 << "// Fallback on earlier versions\n"
1019 << Indentation << "}";
1020 FixitDiag << FixItHint::CreateInsertion(InsertionLoc: ElseInsertionLoc, Code: FixItOS.str());
1021 }
1022}
1023
1024bool DiagnoseUnguardedAvailability::VisitTypeLoc(TypeLoc Ty) {
1025 const Type *TyPtr = Ty.getTypePtr();
1026 SourceRange Range{Ty.getBeginLoc(), Ty.getEndLoc()};
1027
1028 if (Range.isInvalid())
1029 return true;
1030
1031 if (const auto *TT = dyn_cast<TagType>(Val: TyPtr)) {
1032 TagDecl *TD = TT->getDecl()->getDefinitionOrSelf();
1033 DiagnoseDeclAvailability(D: TD, Range);
1034
1035 } else if (const auto *TD = dyn_cast<TypedefType>(Val: TyPtr)) {
1036 TypedefNameDecl *D = TD->getDecl();
1037 DiagnoseDeclAvailability(D, Range);
1038
1039 } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(Val: TyPtr)) {
1040 if (NamedDecl *D = ObjCO->getInterface())
1041 DiagnoseDeclAvailability(D, Range);
1042 }
1043
1044 return true;
1045}
1046
1047struct ExtractedAvailabilityExpr {
1048 const ObjCAvailabilityCheckExpr *E = nullptr;
1049 bool isNegated = false;
1050};
1051
1052ExtractedAvailabilityExpr extractAvailabilityExpr(const Expr *IfCond) {
1053 const auto *E = IfCond;
1054 bool IsNegated = false;
1055 while (true) {
1056 E = E->IgnoreParens();
1057 if (const auto *AE = dyn_cast<ObjCAvailabilityCheckExpr>(Val: E)) {
1058 return ExtractedAvailabilityExpr{.E: AE, .isNegated: IsNegated};
1059 }
1060
1061 const auto *UO = dyn_cast<UnaryOperator>(Val: E);
1062 if (!UO || UO->getOpcode() != UO_LNot) {
1063 return ExtractedAvailabilityExpr{};
1064 }
1065 E = UO->getSubExpr();
1066 IsNegated = !IsNegated;
1067 }
1068}
1069
1070bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
1071 ExtractedAvailabilityExpr IfCond = extractAvailabilityExpr(IfCond: If->getCond());
1072 if (!IfCond.E) {
1073 // This isn't an availability checking 'if', we can just continue.
1074 return DynamicRecursiveASTVisitor::TraverseIfStmt(S: If);
1075 }
1076
1077 VersionTuple CondVersion = IfCond.E->getVersion();
1078 // If we're using the '*' case here or if this check is redundant, then we
1079 // use the enclosing version to check both branches.
1080 if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) {
1081 return TraverseStmt(S: If->getThen()) && TraverseStmt(S: If->getElse());
1082 }
1083
1084 auto *Guarded = If->getThen();
1085 auto *Unguarded = If->getElse();
1086 if (IfCond.isNegated) {
1087 std::swap(a&: Guarded, b&: Unguarded);
1088 }
1089
1090 AvailabilityStack.push_back(Elt: CondVersion);
1091 bool ShouldContinue = TraverseStmt(S: Guarded);
1092 AvailabilityStack.pop_back();
1093
1094 return ShouldContinue && TraverseStmt(S: Unguarded);
1095}
1096
1097} // end anonymous namespace
1098
1099void Sema::DiagnoseUnguardedAvailabilityViolations(Decl *D) {
1100 Stmt *Body = nullptr;
1101
1102 if (auto *FD = D->getAsFunction()) {
1103 Body = FD->getBody();
1104
1105 if (auto *CD = dyn_cast<CXXConstructorDecl>(Val: FD))
1106 for (const CXXCtorInitializer *CI : CD->inits())
1107 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(S: CI->getInit());
1108
1109 } else if (auto *MD = dyn_cast<ObjCMethodDecl>(Val: D))
1110 Body = MD->getBody();
1111 else if (auto *BD = dyn_cast<BlockDecl>(Val: D))
1112 Body = BD->getBody();
1113
1114 assert(Body && "Need a body here!");
1115
1116 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(S: Body);
1117}
1118
1119FunctionScopeInfo *Sema::getCurFunctionAvailabilityContext() {
1120 if (FunctionScopes.empty())
1121 return nullptr;
1122
1123 // Conservatively search the entire current function scope context for
1124 // availability violations. This ensures we always correctly analyze nested
1125 // classes, blocks, lambdas, etc. that may or may not be inside if(@available)
1126 // checks themselves.
1127 return FunctionScopes.front();
1128}
1129
1130void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
1131 ArrayRef<SourceLocation> Locs,
1132 const ObjCInterfaceDecl *UnknownObjCClass,
1133 bool ObjCPropertyAccess,
1134 bool AvoidPartialAvailabilityChecks,
1135 ObjCInterfaceDecl *ClassReceiver) {
1136
1137 std::string Message;
1138 AvailabilityResult Result;
1139 const NamedDecl* OffendingDecl;
1140 // See if this declaration is unavailable, deprecated, or partial.
1141 std::tie(args&: Result, args&: OffendingDecl) =
1142 ShouldDiagnoseAvailabilityOfDecl(D, Message: &Message, ClassReceiver);
1143 if (Result == AR_Available)
1144 return;
1145
1146 if (Result == AR_NotYetIntroduced) {
1147 if (AvoidPartialAvailabilityChecks)
1148 return;
1149
1150 // We need to know the @available context in the current function to
1151 // diagnose this use, let DiagnoseUnguardedAvailabilityViolations do that
1152 // when we're done parsing the current function.
1153 if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) {
1154 Context->HasPotentialAvailabilityViolations = true;
1155 return;
1156 }
1157 }
1158
1159 const ObjCPropertyDecl *ObjCPDecl = nullptr;
1160 if (const auto *MD = dyn_cast<ObjCMethodDecl>(Val: D)) {
1161 if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
1162 AvailabilityResult PDeclResult = PD->getAvailability(Message: nullptr);
1163 if (PDeclResult == Result)
1164 ObjCPDecl = PD;
1165 }
1166 }
1167
1168 EmitAvailabilityWarning(S&: *this, AR: Result, ReferringDecl: D, OffendingDecl, Message, Locs,
1169 UnknownObjCClass, ObjCProperty: ObjCPDecl, ObjCPropertyAccess);
1170}
1171
1172void Sema::DiagnoseAvailabilityOfDecl(NamedDecl *D,
1173 ArrayRef<SourceLocation> Locs) {
1174 DiagnoseAvailabilityOfDecl(D, Locs, /*UnknownObjCClass=*/nullptr,
1175 /*ObjCPropertyAccess=*/false,
1176 /*AvoidPartialAvailabilityChecks=*/false,
1177 /*ClassReceiver=*/nullptr);
1178}
1179