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