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
29 using namespace clang;
30 using namespace sema;
31
hasMatchingEnvironmentOrNone(const ASTContext & Context,const AvailabilityAttr * AA)32 static 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(IIEnvironment->getName());
41 return Environment == ET;
42 }
43
getAttrForPlatform(ASTContext & Context,const Decl * D)44 static 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>(D))
53 D = FTD->getTemplatedDecl();
54 for (const auto *A : D->attrs()) {
55 if (const auto *Avail = dyn_cast<AvailabilityAttr>(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("_app_extension");
65 if (suffix != StringRef::npos)
66 RealizedPlatform = RealizedPlatform.slice(0, 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, 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.
91 static std::pair<AvailabilityResult, const NamedDecl *>
ShouldDiagnoseAvailabilityOfDecl(Sema & S,const NamedDecl * D,std::string * Message,ObjCInterfaceDecl * ClassReceiver)92 ShouldDiagnoseAvailabilityOfDecl(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>(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>(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>(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>(D))
125 if (Result == AR_Available) {
126 const DeclContext *DC = ECD->getDeclContext();
127 if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(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>(D)) {
135 if (S.ObjC().NSAPIObj && ClassReceiver) {
136 ObjCMethodDecl *Init = ClassReceiver->lookupInstanceMethod(
137 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.
ShouldDiagnoseAvailabilityInContext(Sema & S,AvailabilityResult K,VersionTuple DeclVersion,const IdentifierInfo * DeclEnv,Decl * Ctx,const NamedDecl * OffendingDecl)154 static 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>(OffendingDecl)) {
164 StringRef MacroName = S.getPreprocessor().getImmediateMacroName(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(S.Context, 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>(OffendingDecl)) {
200 if (const auto *Impl = dyn_cast<ObjCImplDecl>(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>(Ctx))
219 if (MethodD->isClassMethod() &&
220 MethodD->getSelector().getAsString() == "load")
221 return true;
222
223 if (const auto *CatOrImpl = dyn_cast<ObjCImplDecl>(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>(Ctx))
230 if (const ObjCInterfaceDecl *Interface = CatD->getClassInterface())
231 if (CheckContext(Interface))
232 return false;
233 } while ((Ctx = cast_or_null<Decl>(Ctx->getDeclContext())));
234
235 return true;
236 }
237
getAvailabilityDiagnosticKind(const ASTContext & Context,const VersionTuple & DeploymentVersion,const VersionTuple & DeclVersion,bool HasMatchingEnv)238 static 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
findEnclosingDeclToAnnotate(Decl * OrigCtx)281 static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
282 for (Decl *Ctx = OrigCtx; Ctx;
283 Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) {
284 if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx))
285 return cast<NamedDecl>(Ctx);
286 if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) {
287 if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx))
288 return Imp->getClassInterface();
289 return CD;
290 }
291 }
292
293 return dyn_cast<NamedDecl>(OrigCtx);
294 }
295
296 namespace {
297
298 struct AttributeInsertion {
299 StringRef Prefix;
300 SourceLocation Loc;
301 StringRef Suffix;
302
createInsertionAfter__anon2feca2d00211::AttributeInsertion303 static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
304 return {" ", D->getEndLoc(), ""};
305 }
createInsertionAfter__anon2feca2d00211::AttributeInsertion306 static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
307 return {" ", Loc, ""};
308 }
createInsertionBefore__anon2feca2d00211::AttributeInsertion309 static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
310 return {"", D->getBeginLoc(), "\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.
324 static std::optional<unsigned>
tryParseObjCMethodName(StringRef Name,SmallVectorImpl<StringRef> & SlotNames,const LangOptions & LangOpts)325 tryParseObjCMethodName(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(1);
330 if (Name.empty())
331 return std::nullopt;
332 Name.split(SlotNames, ':');
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.
357 static std::optional<AttributeInsertion>
createAttributeInsertion(const NamedDecl * D,const SourceManager & SM,const LangOptions & LangOpts)358 createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
359 const LangOptions &LangOpts) {
360 if (isa<ObjCPropertyDecl>(D))
361 return AttributeInsertion::createInsertionAfter(D);
362 if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
363 if (MD->hasBody())
364 return std::nullopt;
365 return AttributeInsertion::createInsertionAfter(D);
366 }
367 if (const auto *TD = dyn_cast<TagDecl>(D)) {
368 SourceLocation Loc =
369 Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 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.
DoEmitAvailabilityWarning(Sema & S,AvailabilityResult K,Decl * Ctx,const NamedDecl * ReferringDecl,const NamedDecl * OffendingDecl,StringRef Message,ArrayRef<SourceLocation> Locs,const ObjCInterfaceDecl * UnknownObjCClass,const ObjCPropertyDecl * ObjCProperty,bool ObjCPropertyAccess)388 static 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(S.Context, 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, 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(S.Context, 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(S.getASTContext(), AA);
447
448 const TargetInfo &TI = S.getASTContext().getTargetInfo();
449 std::string PlatformName(
450 AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
451 llvm::StringRef TargetEnvironment(
452 llvm::Triple::getEnvironmentTypeName(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 S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
460 Introduced, EnvironmentMatchesOrNone);
461
462 S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
463 << Introduced.getAsString() << UseEnvironment
464 << TargetEnvironment;
465
466 S.Diag(OffendingDecl->getLocation(),
467 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(Ctx)) {
477 if (const auto *TD = dyn_cast<TagDecl>(Enclosing))
478 if (TD->getDeclName().isEmpty()) {
479 S.Diag(TD->getLocation(),
480 diag::note_decl_unguarded_availability_silence)
481 << /*Anonymous*/ 1 << TD->getKindName();
482 return;
483 }
484 auto FixitNoteDiag =
485 S.Diag(Enclosing->getLocation(),
486 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("API_AVAILABLE"))
492 return;
493 std::optional<AttributeInsertion> Insertion = createAttributeInsertion(
494 Enclosing, S.getSourceManager(), S.getLangOpts());
495 if (!Insertion)
496 return;
497 std::string PlatformName =
498 AvailabilityAttr::getPlatformNameSourceSpelling(
499 S.getASTContext().getTargetInfo().getPlatformName())
500 .lower();
501 std::string Introduced =
502 OffendingDecl->getVersionIntroduced().getAsString();
503 FixitNoteDiag << FixItHint::CreateInsertion(
504 Insertion->Loc,
505 (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 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(S.Context, OffendingDecl))
585 Replacement = AL->getReplacement();
586
587 CharSourceRange UseRange;
588 if (!Replacement.empty())
589 UseRange =
590 CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc));
591 if (UseRange.isValid()) {
592 if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(ReferringDecl)) {
593 Selector Sel = MethodDecl->getSelector();
594 SmallVector<StringRef, 12> SelectorSlotNames;
595 std::optional<unsigned> NumParams = tryParseObjCMethodName(
596 Replacement, SelectorSlotNames, 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(I).empty()) {
601 CharSourceRange NameRange = CharSourceRange::getCharRange(
602 Locs[I], S.getLocForEndOfToken(Locs[I]));
603 FixIts.push_back(FixItHint::CreateReplacement(
604 NameRange, SelectorSlotNames[I]));
605 } else
606 FixIts.push_back(
607 FixItHint::CreateInsertion(Locs[I], SelectorSlotNames[I]));
608 }
609 } else
610 FixIts.push_back(FixItHint::CreateReplacement(UseRange, Replacement));
611 } else
612 FixIts.push_back(FixItHint::CreateReplacement(UseRange, 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(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, diag_message) << ReferringDecl << Message << FixIts;
641 if (ObjCProperty)
642 S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
643 << ObjCProperty->getDeclName() << property_note_select;
644 } else if (!UnknownObjCClass) {
645 S.Diag(Loc, diag) << ReferringDecl << FixIts;
646 if (ObjCProperty)
647 S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute)
648 << ObjCProperty->getDeclName() << property_note_select;
649 } else {
650 S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << FixIts;
651 S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class);
652 }
653
654 S.Diag(NoteLocation, diag_available_here)
655 << OffendingDecl << available_here_select_kind;
656 }
657
handleDelayedAvailabilityCheck(DelayedDiagnostic & DD,Decl * Ctx)658 void 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 *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),
665 DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),
666 DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),
667 DD.getObjCProperty(), false);
668 }
669
EmitAvailabilityWarning(Sema & S,AvailabilityResult AR,const NamedDecl * ReferringDecl,const NamedDecl * OffendingDecl,StringRef Message,ArrayRef<SourceLocation> Locs,const ObjCInterfaceDecl * UnknownObjCClass,const ObjCPropertyDecl * ObjCProperty,bool ObjCPropertyAccess)670 static 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 DelayedDiagnostic::makeAvailability(
682 AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
683 ObjCProperty, Message, ObjCPropertyAccess));
684 return;
685 }
686
687 Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
688 DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
689 Message, Locs, UnknownObjCClass, ObjCProperty,
690 ObjCPropertyAccess);
691 }
692
693 namespace {
694
695 /// Returns true if the given statement can be a body-like child of \p Parent.
isBodyLikeChildStmt(const Stmt * S,const Stmt * Parent)696 bool isBodyLikeChildStmt(const Stmt *S, const Stmt *Parent) {
697 switch (Parent->getStmtClass()) {
698 case Stmt::IfStmtClass:
699 return cast<IfStmt>(Parent)->getThen() == S ||
700 cast<IfStmt>(Parent)->getElse() == S;
701 case Stmt::WhileStmtClass:
702 return cast<WhileStmt>(Parent)->getBody() == S;
703 case Stmt::DoStmtClass:
704 return cast<DoStmt>(Parent)->getBody() == S;
705 case Stmt::ForStmtClass:
706 return cast<ForStmt>(Parent)->getBody() == S;
707 case Stmt::CXXForRangeStmtClass:
708 return cast<CXXForRangeStmt>(Parent)->getBody() == S;
709 case Stmt::ObjCForCollectionStmtClass:
710 return cast<ObjCForCollectionStmt>(Parent)->getBody() == S;
711 case Stmt::CaseStmtClass:
712 case Stmt::DefaultStmtClass:
713 return cast<SwitchCase>(Parent)->getSubStmt() == S;
714 default:
715 return false;
716 }
717 }
718
719 class StmtUSEFinder : public RecursiveASTVisitor<StmtUSEFinder> {
720 const Stmt *Target;
721
722 public:
VisitStmt(Stmt * S)723 bool VisitStmt(Stmt *S) { return S != Target; }
724
725 /// Returns true if the given statement is present in the given declaration.
isContained(const Stmt * Target,const Decl * D)726 static bool isContained(const Stmt *Target, const Decl *D) {
727 StmtUSEFinder Visitor;
728 Visitor.Target = Target;
729 return !Visitor.TraverseDecl(const_cast<Decl *>(D));
730 }
731 };
732
733 /// Traverses the AST and finds the last statement that used a given
734 /// declaration.
735 class LastDeclUSEFinder : public RecursiveASTVisitor<LastDeclUSEFinder> {
736 const Decl *D;
737
738 public:
VisitDeclRefExpr(DeclRefExpr * DRE)739 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
740 if (DRE->getDecl() == D)
741 return false;
742 return true;
743 }
744
findLastStmtThatUsesDecl(const Decl * D,const CompoundStmt * Scope)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(Scope->body())) {
750 if (!Visitor.TraverseStmt(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.
763 class 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
777 public:
DiagnoseUnguardedAvailability(Sema & SemaRef,Decl * Ctx)778 DiagnoseUnguardedAvailability(Sema &SemaRef, Decl *Ctx)
779 : SemaRef(SemaRef), Ctx(Ctx) {
780 AvailabilityStack.push_back(
781 SemaRef.Context.getTargetInfo().getPlatformMinVersion());
782 }
783
TraverseStmt(Stmt * S)784 bool TraverseStmt(Stmt *S) {
785 if (!S)
786 return true;
787 StmtStack.push_back(S);
788 bool Result = Base::TraverseStmt(S);
789 StmtStack.pop_back();
790 return Result;
791 }
792
IssueDiagnostics(Stmt * S)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.
TraverseCaseStmt(CaseStmt * CS)799 bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); }
800
VisitObjCPropertyRefExpr(ObjCPropertyRefExpr * PRE)801 bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { return true; }
802
VisitObjCMessageExpr(ObjCMessageExpr * Msg)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, SourceRange(Msg->getSelectorStartLoc(), Msg->getEndLoc()), ID);
812 }
813 return true;
814 }
815
VisitDeclRefExpr(DeclRefExpr * DRE)816 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
817 DiagnoseDeclAvailability(DRE->getDecl(),
818 SourceRange(DRE->getBeginLoc(), DRE->getEndLoc()));
819 return true;
820 }
821
VisitMemberExpr(MemberExpr * ME)822 bool VisitMemberExpr(MemberExpr *ME) {
823 DiagnoseDeclAvailability(ME->getMemberDecl(),
824 SourceRange(ME->getBeginLoc(), ME->getEndLoc()));
825 return true;
826 }
827
VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr * E)828 bool VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
829 SemaRef.Diag(E->getBeginLoc(), diag::warn_at_available_unchecked_use)
830 << (!SemaRef.getLangOpts().ObjC);
831 return true;
832 }
833
834 bool VisitTypeLoc(TypeLoc Ty);
835 };
836
DiagnoseDeclAvailability(NamedDecl * D,SourceRange Range,ObjCInterfaceDecl * ReceiverClass)837 void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
838 NamedDecl *D, SourceRange Range, ObjCInterfaceDecl *ReceiverClass) {
839 AvailabilityResult Result;
840 const NamedDecl *OffendingDecl;
841 std::tie(Result, OffendingDecl) =
842 ShouldDiagnoseAvailabilityOfDecl(SemaRef, D, nullptr, 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(SemaRef.getASTContext(), OffendingDecl);
851 assert(AA != nullptr && "expecting valid availability attribute");
852 bool EnvironmentMatchesOrNone =
853 hasMatchingEnvironmentOrNone(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(SemaRef, Result, Introduced,
862 AA->getEnvironment(), Ctx,
863 OffendingDecl))
864 return;
865
866 const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
867 std::string PlatformName(
868 AvailabilityAttr::getPrettyPlatformName(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 SemaRef.Context,
877 SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,
878 EnvironmentMatchesOrNone);
879
880 SemaRef.Diag(Range.getBegin(), DiagKind)
881 << Range << D << PlatformName << Introduced.getAsString()
882 << UseEnvironment << TargetEnvironment;
883
884 SemaRef.Diag(OffendingDecl->getLocation(),
885 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(Range.getBegin(), 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(StmtStack)) {
906 if (const auto *CS = dyn_cast<CompoundStmt>(S)) {
907 Scope = CS;
908 break;
909 }
910 if (isBodyLikeChildStmt(StmtOfUse, 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>(StmtOfUse) && Scope) {
920 for (const Decl *D : cast<DeclStmt>(StmtOfUse)->decls()) {
921 if (StmtUSEFinder::isContained(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(StmtOfUse->getBeginLoc());
931 SourceLocation StmtEndLoc =
932 SM.getExpansionRange(
933 (LastStmtOfUse ? LastStmtOfUse : StmtOfUse)->getEndLoc())
934 .getEnd();
935 if (SM.getFileID(IfInsertionLoc) != SM.getFileID(StmtEndLoc))
936 return;
937
938 StringRef Indentation = Lexer::getIndentationForLine(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 SemaRef.getASTContext().getTargetInfo().getPlatformName())
947 << " " << Introduced.getAsString() << ", *)) {\n"
948 << Indentation << ExtraIndentation;
949 FixitDiag << FixItHint::CreateInsertion(IfInsertionLoc, FixItOS.str());
950 SourceLocation ElseInsertionLoc = Lexer::findLocationAfterToken(
951 StmtEndLoc, tok::semi, SM, SemaRef.getLangOpts(),
952 /*SkipTrailingWhitespaceAndNewLine=*/false);
953 if (ElseInsertionLoc.isInvalid())
954 ElseInsertionLoc =
955 Lexer::getLocForEndOfToken(StmtEndLoc, 0, SM, 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(ElseInsertionLoc, FixItOS.str());
963 }
964 }
965
VisitTypeLoc(TypeLoc Ty)966 bool 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>(TyPtr)) {
974 TagDecl *TD = TT->getDecl();
975 DiagnoseDeclAvailability(TD, Range);
976
977 } else if (const auto *TD = dyn_cast<TypedefType>(TyPtr)) {
978 TypedefNameDecl *D = TD->getDecl();
979 DiagnoseDeclAvailability(D, Range);
980
981 } else if (const auto *ObjCO = dyn_cast<ObjCObjectType>(TyPtr)) {
982 if (NamedDecl *D = ObjCO->getInterface())
983 DiagnoseDeclAvailability(D, Range);
984 }
985
986 return true;
987 }
988
TraverseIfStmt(IfStmt * If)989 bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) {
990 VersionTuple CondVersion;
991 if (auto *E = dyn_cast<ObjCAvailabilityCheckExpr>(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(If->getThen()) && TraverseStmt(If->getElse());
998 } else {
999 // This isn't an availability checking 'if', we can just continue.
1000 return Base::TraverseIfStmt(If);
1001 }
1002
1003 AvailabilityStack.push_back(CondVersion);
1004 bool ShouldContinue = TraverseStmt(If->getThen());
1005 AvailabilityStack.pop_back();
1006
1007 return ShouldContinue && TraverseStmt(If->getElse());
1008 }
1009
1010 } // end anonymous namespace
1011
DiagnoseUnguardedAvailabilityViolations(Decl * D)1012 void 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>(FD))
1019 for (const CXXCtorInitializer *CI : CD->inits())
1020 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(CI->getInit());
1021
1022 } else if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
1023 Body = MD->getBody();
1024 else if (auto *BD = dyn_cast<BlockDecl>(D))
1025 Body = BD->getBody();
1026
1027 assert(Body && "Need a body here!");
1028
1029 DiagnoseUnguardedAvailability(*this, D).IssueDiagnostics(Body);
1030 }
1031
getCurFunctionAvailabilityContext()1032 FunctionScopeInfo *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
DiagnoseAvailabilityOfDecl(NamedDecl * D,ArrayRef<SourceLocation> Locs,const ObjCInterfaceDecl * UnknownObjCClass,bool ObjCPropertyAccess,bool AvoidPartialAvailabilityChecks,ObjCInterfaceDecl * ClassReceiver)1043 void 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(Result, OffendingDecl) =
1054 ShouldDiagnoseAvailabilityOfDecl(*this, D, &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>(D)) {
1073 if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) {
1074 AvailabilityResult PDeclResult = PD->getAvailability(nullptr);
1075 if (PDeclResult == Result)
1076 ObjCPDecl = PD;
1077 }
1078 }
1079
1080 EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs,
1081 UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess);
1082 }
1083