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